use std::io::Result as IoResult; use std::process::Command; use std::process::Output; pub trait CommandRunner { fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult; } #[derive(Debug)] pub struct StdCommandRunner; impl CommandRunner for StdCommandRunner { fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult { // FIXME: logger println!("{} {:?}", program, args); let res = Command::new(program).args(args).output(); println!("{:?}", res); res } } #[derive(Debug)] pub struct UserCommandRunner<'a, C> where C: 'a + CommandRunner { command_runner: &'a C, user_name: &'a str } impl<'a, C> UserCommandRunner<'a, C> where C: 'a + CommandRunner { pub fn new(user_name: &'a str, command_runner: &'a C) -> UserCommandRunner<'a, C> { UserCommandRunner { command_runner: command_runner, user_name: user_name } } } use std::os::unix::process::CommandExt; use std::env; use users::get_user_by_name; struct TempSetEnv<'a> { name: &'a str, old_value: Option } impl<'a> TempSetEnv<'a> { fn new(name: &'a str, new_value: String) -> TempSetEnv<'a> { let old_value = env::var(name); env::set_var(name, new_value); TempSetEnv { name: name, old_value: old_value.ok() } } } impl<'a> Drop for TempSetEnv<'a> { fn drop(&mut self) { match self.old_value { Some(ref val) => env::set_var(self.name, val), None => env::remove_var(self.name) } } } impl<'a, C> CommandRunner for UserCommandRunner<'a, C> where C: 'a + CommandRunner { fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult { let uid = get_user_by_name(self.user_name).unwrap().uid(); let set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name)); let set_dbus = TempSetEnv::new("DBUS_SESSION_BUS_ADDRESS", format!("unix:path=/run/user/{}/bus", uid)); let res = Command::new(program).uid(uid).gid(uid).args(args).output(); res } } #[cfg(test)] mod test { }