libazureinit/
distro.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use std::io::Write;
5use std::process::Command;
6use std::process::Stdio;
7
8pub trait Distribution {
9    fn create_user(
10        &self,
11        username: &str,
12        password: &str,
13    ) -> Result<i32, String>;
14    fn set_hostname(&self, hostname: &str) -> Result<i32, String>;
15}
16
17pub enum Distributions {
18    Debian,
19    Ubuntu,
20}
21
22impl Distribution for Distributions {
23    fn create_user(
24        &self,
25        username: &str,
26        password: &str,
27    ) -> Result<i32, String> {
28        match self {
29            Distributions::Debian | Distributions::Ubuntu => {
30                let mut home_path = "/home/".to_string();
31                home_path.push_str(username);
32
33                match Command::new("useradd")
34                    .arg(username)
35                    .arg("--comment")
36                    .arg(
37                      "Provisioning agent created this user based on username provided in IMDS",
38                    )
39                    .arg("--groups")
40                    .arg("adm,audio,cdrom,dialout,dip,floppy,lxd,netdev,plugdev,sudo,video")
41                    .arg("-d")
42                    .arg(home_path.clone())
43                    .arg("-m")
44                    .status(){
45                        Ok(_)=>(),
46                        Err(err) => return Err(err.to_string()),
47                    };
48
49                if password.is_empty() {
50                    match Command::new("passwd")
51                        .arg("-d")
52                        .arg(username)
53                        .status()
54                    {
55                        Ok(status_code) => {
56                            if !status_code.success() {
57                                return Err("Failed to create user".to_string());
58                            }
59                        }
60                        Err(err) => return Err(err.to_string()),
61                    };
62                } else {
63                    let input = format!("{}:{}", username, password);
64
65                    let mut output = Command::new("chpasswd")
66                        .stdin(Stdio::piped())
67                        .stdout(Stdio::null())
68                        .stderr(Stdio::inherit())
69                        .spawn()
70                        .expect("Failed to run chpasswd.");
71
72                    let mut stdin =
73                        output.stdin.as_ref().ok_or("Failed to open stdin")?;
74
75                    stdin.write_all(input.as_bytes()).map_err(|error| {
76                        format!("Failed to write to stdin: {}", error)
77                    })?;
78
79                    let status = output.wait().map_err(|error| {
80                        format!("Failed to wait for stdin command: {}", error)
81                    })?;
82
83                    if !status.success() {
84                        return Err(format!(
85                            "Chpasswd command failed with exit code {}",
86                            status.code().unwrap_or(-1)
87                        ));
88                    }
89                }
90
91                Ok(0)
92            }
93        }
94    }
95    fn set_hostname(&self, hostname: &str) -> Result<i32, String> {
96        match self {
97            Distributions::Debian | Distributions::Ubuntu => {
98                match Command::new("hostnamectl")
99                    .arg("set-hostname")
100                    .arg(hostname)
101                    .status()
102                {
103                    Ok(status_code) => Ok(status_code.code().unwrap_or(1)),
104                    Err(err) => Err(err.to_string()),
105                }
106            }
107        }
108    }
109}
110impl From<&str> for Distributions {
111    fn from(s: &str) -> Self {
112        match s {
113            "debian" => Distributions::Debian,
114            "ubuntu" => Distributions::Ubuntu,
115            _ => panic!("Unknown distribution"),
116        }
117    }
118}