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.

193 lines
4.9 KiB

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