|
|
use std::error::Error;
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<Output>;
fn get_output(&self, program: &str, args: &[&str]) -> Result<Vec<u8>, Box<Error>> {
let output = try!(self.run_with_args(program, args));
if !output.status.success() {
return Err(try!(String::from_utf8(output.stderr)).into());
}
Ok(output.stdout)
}
fn get_stderr(&self, program: &str, args: &[&str]) -> Result<Vec<u8>, Box<Error>> {
let output = try!(self.run_with_args(program, args));
if !output.status.success() {
return Err(try!(String::from_utf8(output.stderr)).into());
}
Ok(output.stderr)
}
fn run_successfully(&self, program: &str, args: &[&str]) -> Result<(), Box<Error>> {
let output = try!(self.run_with_args(program, args));
if output.status.success() {
Ok(())
} else {
Err(try!(String::from_utf8(output.stderr)).into())
}
}
}
#[derive(Debug)]
pub struct StdCommandRunner;
impl CommandRunner for StdCommandRunner {
fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult<Output> {
// FIXME: logger
println!("{} {:?}", program, args);
let res = Command::new(program).args(args).output();
println!("{:?}", res);
res
}
}
#[derive(Debug)]
pub struct SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
command_runner: &'a C,
user_name: &'a str
}
impl<'a, C> SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
pub fn new(user_name: &'a str, command_runner: &'a C) -> SetuidCommandRunner<'a, C> {
SetuidCommandRunner {
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<String> }
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 SetuidCommandRunner<'a, C> where C: 'a + CommandRunner {
fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult<Output> {
let uid = get_user_by_name(self.user_name).expect("User does not exist").uid();
let set_home = TempSetEnv::new("HOME", format!("/home/{}", self.user_name));
let set_dbus = TempSetEnv::new("XDG_RUNTIME_DIR", format!("/run/user/{}", uid));
println!("{} {:?}", program, args);
let res = Command::new(program).uid(uid).gid(uid).args(args).output();
println!("{:?}", res);
res
}
}
#[derive(Debug)]
pub struct SuCommandRunner<'a, C> where C: 'a + CommandRunner {
command_runner: &'a C,
user_name: &'a str
}
impl<'a, C> SuCommandRunner<'a, C> where C: 'a + CommandRunner {
pub fn new(user_name: &'a str, command_runner: &'a C) -> SuCommandRunner<'a, C> {
SuCommandRunner {
command_runner: command_runner,
user_name: user_name
}
}
}
// Su doesn't set XDG_RUNTIME_DIR
// https://github.com/systemd/systemd/blob/master/src/login/pam_systemd.c#L439
impl<'a, C> CommandRunner for SuCommandRunner<'a, C> where C: 'a + CommandRunner {
fn run_with_args(&self, program: &str, args: &[&str]) -> IoResult<Output> {
let mut new_args = vec![self.user_name, "-s", "/usr/bin/env", "--", program];
new_args.extend_from_slice(args);
self.command_runner.run_with_args("su", &new_args)
}
}
#[cfg(test)]
mod test {
}
|