A library for writing host-specific, single-binary configuration management and deployment tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

216 lines
5.2 KiB

7 years ago
5 years ago
8 years ago
5 years ago
8 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
8 years ago
5 years ago
5 years ago
5 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
7 years ago
5 years ago
5 years ago
7 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
8 years ago
5 years ago
  1. use std::error::Error;
  2. use std::ffi::OsStr;
  3. use std::io::{Result as IoResult, Write};
  4. use std::process::Command;
  5. use std::process::Output;
  6. use std::process::Stdio;
  7. pub trait CommandRunner {
  8. fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
  9. &self,
  10. program: &str,
  11. args: &[&S],
  12. stdin: &str,
  13. ) -> IoResult<Output>;
  14. fn run_with_args<S: AsRef<OsStr> + ?Sized>(
  15. &self,
  16. program: &str,
  17. args: &[&S],
  18. ) -> IoResult<Output> {
  19. self.run_with_args_and_stdin(program, args, "")
  20. }
  21. fn get_output<S: AsRef<OsStr> + ?Sized>(
  22. &self,
  23. program: &str,
  24. args: &[&S],
  25. ) -> Result<Vec<u8>, Box<dyn Error>> {
  26. let output = self.run_with_args(program, args)?;
  27. if !output.status.success() {
  28. return Err(String::from_utf8(output.stderr)?.into());
  29. }
  30. Ok(output.stdout)
  31. }
  32. fn get_stderr<S: AsRef<OsStr> + ?Sized>(
  33. &self,
  34. program: &str,
  35. args: &[&S],
  36. ) -> Result<Vec<u8>, Box<dyn Error>> {
  37. let output = self.run_with_args(program, args)?;
  38. if !output.status.success() {
  39. return Err(String::from_utf8(output.stderr)?.into());
  40. }
  41. Ok(output.stderr)
  42. }
  43. fn run_successfully<S: AsRef<OsStr> + ?Sized>(
  44. &self,
  45. program: &str,
  46. args: &[&S],
  47. ) -> Result<(), Box<dyn Error>> {
  48. let output = self.run_with_args(program, args)?;
  49. if output.status.success() {
  50. Ok(())
  51. } else {
  52. Err(String::from_utf8(output.stderr)?.into())
  53. }
  54. }
  55. }
  56. #[derive(Debug)]
  57. pub struct StdCommandRunner;
  58. impl CommandRunner for StdCommandRunner {
  59. fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
  60. &self,
  61. program: &str,
  62. args: &[&S],
  63. input: &str,
  64. ) -> IoResult<Output> {
  65. // FIXME: logger
  66. //println!("{} {:?}", program, args);
  67. let mut child = Command::new(program)
  68. .args(args)
  69. .stdin(Stdio::piped())
  70. .stdout(Stdio::piped())
  71. .stderr(Stdio::piped())
  72. .spawn()
  73. .expect("Failed to spawn child process");
  74. let stdin = child.stdin.as_mut().expect("Failed to open stdin");
  75. stdin
  76. .write_all(input.as_bytes())
  77. .expect("Failed to write to stdin");
  78. let res = child.wait_with_output();
  79. println!("{:?}", res);
  80. res
  81. }
  82. }
  83. #[derive(Debug)]
  84. pub struct SetuidCommandRunner<'a, C>
  85. where
  86. C: 'a + CommandRunner,
  87. {
  88. command_runner: &'a C,
  89. user_name: &'a str,
  90. }
  91. impl<'a, C> SetuidCommandRunner<'a, C>
  92. where
  93. C: 'a + CommandRunner,
  94. {
  95. pub fn new(user_name: &'a str, command_runner: &'a C) -> SetuidCommandRunner<'a, C> {
  96. SetuidCommandRunner {
  97. command_runner,
  98. user_name,
  99. }
  100. }
  101. }
  102. use std::env;
  103. use std::os::unix::process::CommandExt;
  104. use users::get_user_by_name;
  105. struct TempSetEnv<'a> {
  106. name: &'a str,
  107. old_value: Option<String>,
  108. }
  109. impl<'a> TempSetEnv<'a> {
  110. fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> {
  111. let old_value = env::var(name);
  112. env::set_var(name, new_value);
  113. TempSetEnv {
  114. name,
  115. old_value: old_value.ok(),
  116. }
  117. }
  118. }
  119. impl<'a> Drop for TempSetEnv<'a> {
  120. fn drop(&mut self) {
  121. match self.old_value {
  122. Some(ref val) => env::set_var(self.name, val),
  123. None => env::remove_var(self.name),
  124. }
  125. }
  126. }
  127. impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C>
  128. where
  129. C: 'a + CommandRunner,
  130. {
  131. fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
  132. &self,
  133. program: &str,
  134. args: &[&S],
  135. input: &str,
  136. ) -> IoResult<Output> {
  137. let uid = get_user_by_name(self.user_name)
  138. .expect("User does not exist")
  139. .uid();
  140. let set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name));
  141. let set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
  142. //println!("{} {:?}", program, args);
  143. let mut child = Command::new(program)
  144. .args(args)
  145. .stdin(Stdio::piped())
  146. .uid(uid)
  147. .gid(uid)
  148. .stdout(Stdio::piped())
  149. .stderr(Stdio::piped())
  150. .spawn()
  151. .expect("Failed to spawn child process");
  152. let stdin = child.stdin.as_mut().expect("Failed to open stdin");
  153. stdin
  154. .write_all(input.as_bytes())
  155. .expect("Failed to write to stdin");
  156. let res = child.wait_with_output();
  157. println!("{:?}", res);
  158. res
  159. }
  160. }
  161. #[derive(Debug)]
  162. pub struct SuCommandRunner<'a, C>
  163. where
  164. C: 'a + CommandRunner,
  165. {
  166. command_runner: &'a C,
  167. user_name: &'a str,
  168. }
  169. impl<'a, C> SuCommandRunner<'a, C>
  170. where
  171. C: 'a + CommandRunner,
  172. {
  173. pub fn new(user_name: &'a str, command_runner: &'a C) -> SuCommandRunner<'a, C> {
  174. SuCommandRunner {
  175. command_runner,
  176. user_name,
  177. }
  178. }
  179. }
  180. // Su doesn't set XDG_RUNTIME_DIR
  181. // https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
  182. impl<'a, C> CommandRunner for SuCommandRunner<'a, C>
  183. where
  184. C: 'a + CommandRunner,
  185. {
  186. fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
  187. &self,
  188. program: &str,
  189. args: &[&S],
  190. input: &str,
  191. ) -> IoResult<Output> {
  192. let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
  193. let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
  194. let old_args: Vec<&OsStr> = args.iter().map(|s| s.as_ref()).collect();
  195. new_args.extend_from_slice(&old_args);
  196. self
  197. .command_runner
  198. .run_with_args_and_stdin("su", &new_args, input)
  199. }
  200. }
  201. #[cfg(test)]
  202. mod test {}