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.
216 lines
5.2 KiB
216 lines
5.2 KiB
use std::error::Error;
|
|
use std::ffi::OsStr;
|
|
use std::io::{Result as IoResult, Write};
|
|
use std::process::Command;
|
|
use std::process::Output;
|
|
use std::process::Stdio;
|
|
|
|
pub trait CommandRunner {
|
|
fn run_with_args_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
stdin: &str,
|
|
) -> IoResult<Output>;
|
|
fn run_with_args<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
) -> IoResult<Output> {
|
|
self.run_with_args_and_stdin(program, args, "")
|
|
}
|
|
fn get_output<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
) -> Result<Vec<u8>, Box<dyn 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<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
) -> Result<Vec<u8>, Box<dyn 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<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
) -> Result<(), Box<dyn 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_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
input: &str,
|
|
) -> IoResult<Output> {
|
|
// FIXME: logger
|
|
//println!("{} {:?}", program, args);
|
|
let mut child = Command::new(program)
|
|
.args(args)
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.expect("Failed to spawn child process");
|
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
|
stdin
|
|
.write_all(input.as_bytes())
|
|
.expect("Failed to write to stdin");
|
|
let res = child.wait_with_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,
|
|
user_name,
|
|
}
|
|
}
|
|
}
|
|
|
|
use std::env;
|
|
use std::os::unix::process::CommandExt;
|
|
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,
|
|
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_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
input: &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 mut child = Command::new(program)
|
|
.args(args)
|
|
.stdin(Stdio::piped())
|
|
.uid(uid)
|
|
.gid(uid)
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.expect("Failed to spawn child process");
|
|
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
|
|
stdin
|
|
.write_all(input.as_bytes())
|
|
.expect("Failed to write to stdin");
|
|
let res = child.wait_with_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,
|
|
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_and_stdin<S: AsRef<OsStr> + ?Sized>(
|
|
&self,
|
|
program: &str,
|
|
args: &[&S],
|
|
input: &str,
|
|
) -> IoResult<Output> {
|
|
let raw_new_args = [self.user_name, "-s", "/usr/bin/env", "--", program];
|
|
let mut new_args: Vec<&OsStr> = raw_new_args.iter().map(|s| s.as_ref()).collect();
|
|
let old_args: Vec<&OsStr> = args.iter().map(|s| s.as_ref()).collect();
|
|
new_args.extend_from_slice(&old_args);
|
|
self
|
|
.command_runner
|
|
.run_with_args_and_stdin("su", &new_args, input)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {}
|