From 7d6329a4099d051b8679c19dffbeb457cf966321 Mon Sep 17 00:00:00 2001 From: Adrian Heine Date: Thu, 19 Sep 2019 13:48:44 +0200 Subject: [PATCH] Add cron --- src/command_runner.rs | 59 +++++++++++++++++++++++++++----- src/symbols/cron.rs | 77 ++++++++++++++++++++++++++++++++++++++++++ src/symbols/factory.rs | 9 +++++ src/symbols/mod.rs | 1 + 4 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 src/symbols/cron.rs diff --git a/src/command_runner.rs b/src/command_runner.rs index 74a2967..7bf6181 100644 --- a/src/command_runner.rs +++ b/src/command_runner.rs @@ -1,12 +1,24 @@ use std::error::Error; use std::ffi::OsStr; -use std::io::Result as IoResult; +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 + ?Sized>(&self, program: &str, args: &[&S]) - -> IoResult; + fn run_with_args_and_stdin + ?Sized>( + &self, + program: &str, + args: &[&S], + stdin: &str, + ) -> IoResult; + fn run_with_args + ?Sized>( + &self, + program: &str, + args: &[&S], + ) -> IoResult { + self.run_with_args_and_stdin(program, args, "") + } fn get_output + ?Sized>( &self, program: &str, @@ -47,14 +59,26 @@ pub trait CommandRunner { pub struct StdCommandRunner; impl CommandRunner for StdCommandRunner { - fn run_with_args + ?Sized>( + fn run_with_args_and_stdin + ?Sized>( &self, program: &str, args: &[&S], + input: &str, ) -> IoResult { // FIXME: logger //println!("{} {:?}", program, args); - let res = Command::new(program).args(args).output(); + 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 } @@ -114,10 +138,11 @@ impl<'a, C> CommandRunner for SetuidCommandRunner<'a, C> where C: 'a + CommandRunner, { - fn run_with_args + ?Sized>( + fn run_with_args_and_stdin + ?Sized>( &self, program: &str, args: &[&S], + input: &str, ) -> IoResult { let uid = get_user_by_name(self.user_name) .expect("User does not exist") @@ -125,7 +150,20 @@ where 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(); + 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 } @@ -158,16 +196,19 @@ impl<'a, C> CommandRunner for SuCommandRunner<'a, C> where C: 'a + CommandRunner, { - fn run_with_args + ?Sized>( + fn run_with_args_and_stdin + ?Sized>( &self, program: &str, args: &[&S], + input: &str, ) -> IoResult { 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("su", &new_args) + self + .command_runner + .run_with_args_and_stdin("su", &new_args, input) } } diff --git a/src/symbols/cron.rs b/src/symbols/cron.rs new file mode 100644 index 0000000..861350a --- /dev/null +++ b/src/symbols/cron.rs @@ -0,0 +1,77 @@ +use std::error::Error; +use std::fmt; +use std::ops::Deref; + +use command_runner::CommandRunner; +use resources::Resource; +use symbols::{Action, OwnedSymbolAction, Symbol, SymbolAction, SymbolRunner}; + +pub struct Cron<'r, C, U, R: 'r + CommandRunner> +where + C: Deref, + U: Deref, +{ + user: U, + content: C, + command_runner: &'r R, +} + +impl<'r, U, R: 'r + CommandRunner> Cron<'r, String, U, R> +where + U: Deref, +{ + pub fn new>(user: U, content: C, command_runner: &'r R) -> Self { + Cron { + user, + content: String::from(&*content) + "\n", + command_runner, + } + } +} + +impl<'r, C, U, R: 'r + CommandRunner> Symbol for Cron<'r, C, U, R> +where + C: Deref, + U: Deref, +{ + fn target_reached(&self) -> Result> { + let tab = try!(self + .command_runner + .get_output("crontab", &["-l", "-u", &self.user])); + return Ok(tab == self.content.bytes().collect::>()); + } + + fn execute(&self) -> Result<(), Box> { + self.command_runner.run_with_args_and_stdin( + "crontab", + &["-u", &self.user, "-"], + &self.content, + )?; + Ok(()) + } + + fn get_prerequisites(&self) -> Vec { + vec![] + } + + fn as_action<'a>(&'a self, runner: &'a dyn SymbolRunner) -> Box { + Box::new(SymbolAction::new(runner, self)) + } + + fn into_action<'a>(self: Box, runner: &'a dyn SymbolRunner) -> Box + where + Self: 'a, + { + Box::new(OwnedSymbolAction::new(runner, *self)) + } +} + +impl<'r, C, U, R: 'r + CommandRunner> fmt::Display for Cron<'r, C, U, R> +where + C: Deref, + U: Deref, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "Cron {}", &*self.user) + } +} diff --git a/src/symbols/factory.rs b/src/symbols/factory.rs index 52eeb08..8bbb755 100644 --- a/src/symbols/factory.rs +++ b/src/symbols/factory.rs @@ -5,6 +5,7 @@ use std::path::Path; use command_runner::{CommandRunner, SetuidCommandRunner}; use storage::{SimpleStorage, Storage}; use symbols::acme::{AcmeCert, AcmeCertChain}; +use symbols::cron::Cron; use symbols::file::File; use symbols::git::checkout::GitCheckout; use symbols::hook::Hook; @@ -368,4 +369,12 @@ env[PATH] = /usr/local/bin:/usr/bin:/bin ) -> Box { Box::new(File::new(path, content)).into_action(self.symbol_runner) } + + pub fn get_cron<'a, T: 'a + Deref, U: 'a + Deref>( + &'a self, + user: U, + content: T, + ) -> Box { + Box::new(Cron::new(user, content, self.command_runner)).into_action(self.symbol_runner) + } } diff --git a/src/symbols/mod.rs b/src/symbols/mod.rs index 26edc37..20277c0 100644 --- a/src/symbols/mod.rs +++ b/src/symbols/mod.rs @@ -68,6 +68,7 @@ impl<'a, S: Symbol + 'a> Action for OwnedSymbolAction<'a, S> { } pub mod acme; +pub mod cron; pub mod dir; pub mod factory; pub mod file;