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.

175 lines
4.1 KiB

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