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.

125 lines
4.0 KiB

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