rustfmt
This commit is contained in:
parent
42bb127d15
commit
431464e6f6
11
rustfmt.toml
Normal file
11
rustfmt.toml
Normal file
@ -0,0 +1,11 @@
|
||||
newline_style = "Unix"
|
||||
max_width = 140
|
||||
fn_call_width = 120
|
||||
ideal_width = 120
|
||||
tab_spaces = 4
|
||||
fn_args_density = "Compressed"
|
||||
fn_arg_indent = "Tabbed"
|
||||
single_line_if_else = true
|
||||
reorder_imports = true
|
||||
chain_base_indent = "Tabbed"
|
||||
chain_indent = "Tabbed"
|
@ -21,11 +21,8 @@ fn main() {
|
||||
.map(|sz| {
|
||||
buf.iter()
|
||||
.position(|&c| c == 0x20)
|
||||
.and_then(|p| if p < sz {
|
||||
unsafe { transmute::<_, &str>(&buf[..p]) }.parse::<f32>().ok()
|
||||
} else {
|
||||
None
|
||||
}).unwrap()
|
||||
.and_then(|p| if p < sz { unsafe { transmute::<_, &str>(&buf[..p]) }.parse::<f32>().ok() } else { None })
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
|
@ -10,14 +10,13 @@ use cronparse::{CrontabFile, CrontabFileError};
|
||||
use cronparse::crontab::UserCrontabEntry;
|
||||
use tempfile::NamedTempFile;
|
||||
use docopt::Docopt;
|
||||
use libc::{uid_t, gid_t};
|
||||
use users::User;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{stdin, stdout, stderr, Write, Read, self, copy};
|
||||
use std::io::{self, Read, Write, copy, stderr, stdin, stdout};
|
||||
use std::fs::File;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, exit};
|
||||
use std::ffi::CString;
|
||||
|
||||
@ -31,7 +30,7 @@ fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t)
|
||||
match unsafe { chown(CString::new(path.as_ref().to_str().unwrap().as_bytes()).unwrap().as_ptr(), owner, group) } {
|
||||
0 => Ok(()),
|
||||
-1 => Err(io::Error::last_os_error()),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,15 +77,23 @@ struct Args {
|
||||
flag_remove: bool,
|
||||
flag_edit: bool,
|
||||
flag_show: bool,
|
||||
flag_ask: bool
|
||||
flag_ask: bool,
|
||||
}
|
||||
|
||||
fn get_editor() -> Option<String> {
|
||||
env::var("EDITOR").ok()
|
||||
env::var("EDITOR")
|
||||
.ok()
|
||||
.or_else(|| env::var("VISUAL").ok())
|
||||
.or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
|
||||
.find(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
|
||||
.map(|&s| s.to_owned()))
|
||||
.or_else(|| {
|
||||
["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"]
|
||||
.iter()
|
||||
.find(|editor| {
|
||||
fs::metadata(editor)
|
||||
.map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|&s| s.to_owned())
|
||||
})
|
||||
}
|
||||
|
||||
fn confirm(msg: &str) -> bool {
|
||||
@ -99,7 +106,9 @@ fn confirm(msg: &str) -> bool {
|
||||
match stdin.read(&mut buf) {
|
||||
Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
|
||||
Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
|
||||
_ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
|
||||
_ => {
|
||||
stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -123,9 +132,10 @@ fn remove(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
|
||||
if let Err(e) = fs::remove_file(cron_file) {
|
||||
use std::io::ErrorKind::*;
|
||||
match e.kind() {
|
||||
NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
|
||||
_ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
|
||||
}.unwrap();
|
||||
NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
|
||||
_ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e),
|
||||
}
|
||||
.unwrap();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@ -160,8 +170,8 @@ fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
|
||||
None => {
|
||||
writeln!(stderr, "no editor found").unwrap();
|
||||
return 1;
|
||||
},
|
||||
Some(editor) => editor
|
||||
}
|
||||
Some(editor) => editor,
|
||||
};
|
||||
|
||||
let mut tmpfile = match NamedTempFile::new_in(USERS_CRONTAB_DIR) {
|
||||
@ -169,7 +179,7 @@ fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
|
||||
Err(err) => {
|
||||
writeln!(stderr, "unable to create a temporary file in {}: {}", USERS_CRONTAB_DIR, err).unwrap();
|
||||
return 1;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = File::open(cron_file).map(|mut file| copy(&mut file, &mut tmpfile)) {
|
||||
@ -215,13 +225,17 @@ fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
|
||||
Err(err) => {
|
||||
writeln!(stderr, "unable to create a temporary file in {}: {}", USERS_CRONTAB_DIR, err).unwrap();
|
||||
return 1;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
match args.arg_file {
|
||||
Some(ref name) if &**name == "-" => { let _ = copy(&mut stdin(), &mut tmpfile); },
|
||||
Some(ref name) => { let _ = copy(&mut File::open(&**name).unwrap(), &mut tmpfile); },
|
||||
None => unreachable!()
|
||||
Some(ref name) if &**name == "-" => {
|
||||
let _ = copy(&mut stdin(), &mut tmpfile);
|
||||
}
|
||||
Some(ref name) => {
|
||||
let _ = copy(&mut File::open(&**name).unwrap(), &mut tmpfile);
|
||||
}
|
||||
None => unreachable!(),
|
||||
}
|
||||
|
||||
tmpfile.flush().unwrap();
|
||||
@ -244,21 +258,23 @@ fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
|
||||
fn main() {
|
||||
let mut stderr = stderr();
|
||||
let args: Args = Docopt::new(USAGE)
|
||||
.and_then(|d| d.decode())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
.and_then(|d| d.decode())
|
||||
.unwrap_or_else(|e| e.exit());
|
||||
|
||||
let cron_user = match args.flag_user {
|
||||
Some(_) if users::get_current_uid() != 0 => {
|
||||
writeln!(stderr, "must be privileged to use -u").unwrap();
|
||||
exit(1);
|
||||
},
|
||||
Some(ref user) => match users::get_user_by_name(&**user) {
|
||||
Some(user) => user,
|
||||
None => {
|
||||
writeln!(stderr, "unknown user: {}", user).unwrap();
|
||||
exit(1);
|
||||
}
|
||||
Some(ref user) => {
|
||||
match users::get_user_by_name(&**user) {
|
||||
Some(user) => user,
|
||||
None => {
|
||||
writeln!(stderr, "unknown user: {}", user).unwrap();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => users::get_user_by_uid(users::get_current_uid()).unwrap(),
|
||||
};
|
||||
|
||||
@ -266,12 +282,14 @@ fn main() {
|
||||
Ok(ref meta) if !meta.is_dir() => {
|
||||
writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
|
||||
exit(1);
|
||||
},
|
||||
Err(_) => if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
|
||||
writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
|
||||
exit(1);
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
Err(_) => {
|
||||
if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
|
||||
writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name.clone());
|
||||
@ -282,13 +300,13 @@ fn main() {
|
||||
Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
|
||||
Args { flag_edit: true, .. } => replace(&*cron_file, &cron_user, &args),
|
||||
Args { flag_remove: true, .. } => remove(&*cron_file, &cron_user, &args),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
|
||||
match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
|
||||
Some(Err(err)) => Err(err),
|
||||
_ => Ok(())
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::io::{Write, Result};
|
||||
use std::io::{Result, Write};
|
||||
|
||||
macro_rules! try_log {
|
||||
($exp:expr) => {
|
||||
@ -19,10 +19,11 @@ fn get_systemd_unit_property(unit: &str, prop: &str) -> Result<String> {
|
||||
.arg("--property")
|
||||
.arg(prop)
|
||||
.output()
|
||||
.map(|out|
|
||||
String::from_utf8_lossy(&out.stdout[prop.len() + 1..])
|
||||
.trim_right_matches('\n')
|
||||
.to_owned())
|
||||
.map(|out| {
|
||||
String::from_utf8_lossy(&out.stdout[prop.len() + 1..])
|
||||
.trim_right_matches('\n')
|
||||
.to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
@ -56,9 +57,9 @@ fn main() {
|
||||
}
|
||||
|
||||
let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
|
||||
.arg("-n")
|
||||
.output())
|
||||
.stdout[..])
|
||||
.arg("-n")
|
||||
.output())
|
||||
.stdout[..])
|
||||
.trim_right_matches('\n')
|
||||
.to_owned();
|
||||
|
||||
@ -95,8 +96,7 @@ Auto-Submitted: auto-generated
|
||||
.spawn());
|
||||
|
||||
if let Some(ref mut stdin) = mailer.stdin {
|
||||
try_log!(stdin.write_all(head.as_bytes()).and_then(|_|
|
||||
stdin.write_all(&*status.stdout)));
|
||||
try_log!(stdin.write_all(head.as_bytes()).and_then(|_| stdin.write_all(&*status.stdout)));
|
||||
}
|
||||
|
||||
mailer.wait().unwrap();
|
||||
|
@ -9,19 +9,17 @@ use std::collections::BTreeSet;
|
||||
use time::{Duration, get_time};
|
||||
use glob::glob;
|
||||
|
||||
static KNOWN_STAMPS: [&'static str; 6] = [
|
||||
"/var/lib/systemd/timers/stamp-cron-daily.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-weekly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-monthly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-quarterly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-semi-annually.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-yearly.timer"
|
||||
];
|
||||
static KNOWN_STAMPS: [&'static str; 6] = ["/var/lib/systemd/timers/stamp-cron-daily.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-weekly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-monthly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-quarterly.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-semi-annually.timer",
|
||||
"/var/lib/systemd/timers/stamp-cron-yearly.timer"];
|
||||
|
||||
static ACTUAL_STAMPS_GLOB: &'static str = "/var/lib/systemd/timers/stamp-cron-*.timer";
|
||||
static TIMER_STAMPS_GLOB: &'static str = "/run/systemd/generator/cron-*.timer";
|
||||
|
||||
fn cleanup<P: AsRef<Path>, I: IntoIterator<Item=P>>(iter: I) {
|
||||
fn cleanup<P: AsRef<Path>, I: IntoIterator<Item = P>>(iter: I) {
|
||||
let ten_days_ago = get_time() - Duration::days(10);
|
||||
for stamp in iter {
|
||||
if let Ok(meta) = metadata(&stamp) {
|
||||
@ -33,17 +31,21 @@ fn cleanup<P: AsRef<Path>, I: IntoIterator<Item=P>>(iter: I) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let stale_stamps =
|
||||
&(&glob(ACTUAL_STAMPS_GLOB).unwrap()
|
||||
.flat_map(Result::into_iter)
|
||||
.collect::<BTreeSet<_>>() -
|
||||
&glob(TIMER_STAMPS_GLOB).unwrap()
|
||||
.flat_map(Result::into_iter)
|
||||
.map(|s| PathBuf::from(s.to_string_lossy().replace("/run/systemd/generator/cron-", "/var/lib/systemd/timers/stamp-cron-")))
|
||||
.collect::<BTreeSet<_>>()) -
|
||||
&KNOWN_STAMPS.iter()
|
||||
.map(PathBuf::from)
|
||||
.collect::<BTreeSet<_>>();
|
||||
let stale_stamps = &(&glob(ACTUAL_STAMPS_GLOB)
|
||||
.unwrap()
|
||||
.flat_map(Result::into_iter)
|
||||
.collect::<BTreeSet<_>>() -
|
||||
&glob(TIMER_STAMPS_GLOB)
|
||||
.unwrap()
|
||||
.flat_map(Result::into_iter)
|
||||
.map(|s| {
|
||||
PathBuf::from(s.to_string_lossy()
|
||||
.replace("/run/systemd/generator/cron-", "/var/lib/systemd/timers/stamp-cron-"))
|
||||
})
|
||||
.collect::<BTreeSet<_>>()) -
|
||||
&KNOWN_STAMPS.iter()
|
||||
.map(PathBuf::from)
|
||||
.collect::<BTreeSet<_>>();
|
||||
|
||||
cleanup(&stale_stamps);
|
||||
}
|
||||
|
247
src/generate.rs
247
src/generate.rs
@ -1,19 +1,19 @@
|
||||
use std::io::{self, Write};
|
||||
use std::fs::{File, create_dir_all, set_permissions, metadata};
|
||||
use std::fs::{File, create_dir_all, metadata, set_permissions};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt, symlink};
|
||||
use std::path::Path;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ffi::CStr;
|
||||
|
||||
use cronparse::Limited;
|
||||
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
|
||||
use cronparse::schedule::{Schedule, Period, Calendar};
|
||||
use cronparse::schedule::{Calendar, Period, Schedule};
|
||||
use cronparse::interval::Interval;
|
||||
|
||||
use getpwent::{PwEntIter, User};
|
||||
|
||||
use super::{REBOOT_FILE, PACKAGE, LIB_DIR};
|
||||
use super::{LIB_DIR, PACKAGE, REBOOT_FILE};
|
||||
|
||||
pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String>, path: &Path, dstdir: &Path) -> io::Result<()> {
|
||||
use cronparse::crontab::CrontabEntry::*;
|
||||
@ -22,118 +22,122 @@ pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String
|
||||
|
||||
let owner = try!(metadata(path)).uid();
|
||||
|
||||
let mut persistent = env.get("PERSISTENT").and_then(|v| match &**v {
|
||||
"yes" | "true" | "1" => Some(true),
|
||||
"auto" | "" => None,
|
||||
_ => Some(false)
|
||||
}).unwrap_or_else(|| match entry {
|
||||
Anacron(_) | User(UserCrontabEntry { sched: Schedule::Period(_), .. }) | System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
|
||||
_ => false
|
||||
});
|
||||
let mut persistent = env.get("PERSISTENT")
|
||||
.and_then(|v| {
|
||||
match &**v {
|
||||
"yes" | "true" | "1" => Some(true),
|
||||
"auto" | "" => None,
|
||||
_ => Some(false),
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
match entry {
|
||||
Anacron(_) |
|
||||
User(UserCrontabEntry { sched: Schedule::Period(_), .. }) |
|
||||
System(SystemCrontabEntry { sched: Schedule::Period(_), .. }) => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
let batch = env.get("BATCH").map(|v| match &**v {
|
||||
"yes" | "true" | "1" => true,
|
||||
_ => false
|
||||
}).unwrap_or(false);
|
||||
let batch = env.get("BATCH")
|
||||
.map(|v| {
|
||||
match &**v {
|
||||
"yes" | "true" | "1" => true,
|
||||
_ => false,
|
||||
}
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let random_delay = env.get("RANDOM_DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(1);
|
||||
let mut delay = env.get("DELAY").and_then(|v| v.parse::<u64>().ok()).unwrap_or(0);
|
||||
let hour = env.get("START_HOURS_RANGE").and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok())).unwrap_or(0);
|
||||
let hour = env.get("START_HOURS_RANGE")
|
||||
.and_then(|v| v.splitn(1, '-').next().and_then(|v| v.parse::<u64>().ok()))
|
||||
.unwrap_or(0);
|
||||
let shell = env.get("SHELL").map(|v| &**v).unwrap_or("/bin/sh");
|
||||
let daemon_reload = metadata(REBOOT_FILE).map(|m| m.is_file()).unwrap_or(false);
|
||||
|
||||
let schedule = entry.period().and_then(|period| match *period {
|
||||
Period::Reboot => {
|
||||
persistent = false;
|
||||
if delay == 0 {
|
||||
delay = 1;
|
||||
}
|
||||
None
|
||||
},
|
||||
Period::Minutely => {
|
||||
persistent = false;
|
||||
Some("minutely".to_owned())
|
||||
},
|
||||
Period::Hourly => {
|
||||
if delay == 0 {
|
||||
Some("hourly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-* *:{}:0", delay))
|
||||
}
|
||||
},
|
||||
Period::Midnight => {
|
||||
if delay == 0 {
|
||||
Some("daily".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-* 0:{}:0", delay))
|
||||
}
|
||||
},
|
||||
Period::Daily => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("daily".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-* {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Weekly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("weekly".to_owned())
|
||||
} else {
|
||||
Some(format!("Mon *-*-* {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Monthly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("monthly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Quaterly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("quaterly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Biannually => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("semiannually".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1,7-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Yearly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("yearly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
},
|
||||
Period::Days(days) => {
|
||||
// workaround for anacrontab
|
||||
if days > 31 {
|
||||
Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
|
||||
} else {
|
||||
Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
|
||||
}
|
||||
},
|
||||
}).or_else(|| entry.calendar().and_then(|cal| {
|
||||
let Calendar {
|
||||
ref dows,
|
||||
ref days,
|
||||
ref mons,
|
||||
ref hrs,
|
||||
ref mins
|
||||
} = *cal;
|
||||
let schedule = entry.period()
|
||||
.and_then(|period| {
|
||||
match *period {
|
||||
Period::Reboot => {
|
||||
persistent = false;
|
||||
if delay == 0 {
|
||||
delay = 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
Period::Minutely => {
|
||||
persistent = false;
|
||||
Some("minutely".to_owned())
|
||||
}
|
||||
Period::Hourly => if delay == 0 { Some("hourly".to_owned()) } else { Some(format!("*-*-* *:{}:0", delay)) },
|
||||
Period::Midnight => {
|
||||
if delay == 0 { Some("daily".to_owned()) } else { Some(format!("*-*-* 0:{}:0", delay)) }
|
||||
}
|
||||
Period::Daily => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("daily".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-* {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Weekly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("weekly".to_owned())
|
||||
} else {
|
||||
Some(format!("Mon *-*-* {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Monthly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("monthly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-*-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Quaterly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("quaterly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1,4,7,10-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Biannually => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("semiannually".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1,7-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Yearly => {
|
||||
if delay == 0 && hour == 0 {
|
||||
Some("yearly".to_owned())
|
||||
} else {
|
||||
Some(format!("*-1-1 {}:{}:0", hour, delay))
|
||||
}
|
||||
}
|
||||
Period::Days(days) => {
|
||||
// workaround for anacrontab
|
||||
if days > 31 {
|
||||
Some(format!("*-1/{}-1 {}:{}:0", days / 30, hour, delay))
|
||||
} else {
|
||||
Some(format!("*-*-1/{} {}:{}:0", days, hour, delay))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.or_else(|| {
|
||||
entry.calendar().and_then(|cal| {
|
||||
let Calendar { ref dows, ref days, ref mons, ref hrs, ref mins } = *cal;
|
||||
|
||||
Some(format!("{} *-{}-{} {}:{}:00",
|
||||
linearize(&**dows, "", ToString::to_string),
|
||||
linearize(&**mons, "*", |&mon| (mon as u8).to_string()),
|
||||
linearize(&**days, "*", ToString::to_string),
|
||||
linearize(&**hrs, "*", ToString::to_string),
|
||||
linearize(&**mins, "*", ToString::to_string)))
|
||||
}));
|
||||
Some(format!("{} *-{}-{} {}:{}:00",
|
||||
linearize(&**dows, "", ToString::to_string),
|
||||
linearize(&**mons, "*", |&mon| (mon as u8).to_string()),
|
||||
linearize(&**days, "*", ToString::to_string),
|
||||
linearize(&**hrs, "*", ToString::to_string),
|
||||
linearize(&**mins, "*", ToString::to_string)))
|
||||
})
|
||||
});
|
||||
|
||||
if daemon_reload && schedule.is_none() {
|
||||
warn!("skipping job from {}: \"{}\"", path.display(), entry);
|
||||
@ -143,12 +147,17 @@ pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String
|
||||
if let Some(cmd) = entry.command() {
|
||||
|
||||
// make sure we know the user
|
||||
let user = try!(entry.user().and_then(
|
||||
|user| PwEntIter::new()
|
||||
.and_then(|mut iter| iter.find(|&pw| unsafe {
|
||||
(*pw).pw_uid == owner || CStr::from_ptr((*pw).pw_name).to_bytes() == user.as_bytes()
|
||||
})).map(|pw| unsafe { User::from_ptr(pw) }))
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));
|
||||
let user = try!(entry.user()
|
||||
.and_then(|user| {
|
||||
PwEntIter::new()
|
||||
.and_then(|mut iter| {
|
||||
iter.find(|&pw| unsafe {
|
||||
(*pw).pw_uid == owner || CStr::from_ptr((*pw).pw_name).to_bytes() == user.as_bytes()
|
||||
})
|
||||
})
|
||||
.map(|pw| unsafe { User::from_ptr(pw) })
|
||||
})
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));
|
||||
|
||||
// generate unique cron job id
|
||||
let mut md5ctx = ::md5::Context::new();
|
||||
@ -265,7 +274,7 @@ Unit={service_unit_name}"###,
|
||||
service_unit_name = service_unit_name,
|
||||
));
|
||||
|
||||
if cfg![feature="persistent"] {
|
||||
if cfg![feature = "persistent"] {
|
||||
try!(writeln!(timer_unit_file, "Persistent={}", persistent));
|
||||
}
|
||||
|
||||
@ -285,7 +294,10 @@ Unit={service_unit_name}"###,
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn linearize<T, C>(input: &[Interval<T>], star: &str, conv: C) -> String where T: Limited, C: Fn(&T) -> String {
|
||||
fn linearize<T, C>(input: &[Interval<T>], star: &str, conv: C) -> String
|
||||
where T: Limited,
|
||||
C: Fn(&T) -> String
|
||||
{
|
||||
if input.len() == 1 && input[0] == Interval::Full(1) {
|
||||
star.to_owned()
|
||||
} else {
|
||||
@ -305,7 +317,7 @@ fn tohex(input: &[u8]) -> String {
|
||||
match d {
|
||||
0...9 => (d + 0x30) as char,
|
||||
10...15 => (d + 0x57) as char,
|
||||
_ => unreachable!("unexpected value: {}", d)
|
||||
_ => unreachable!("unexpected value: {}", d),
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,4 +328,3 @@ fn tohex(input: &[u8]) -> String {
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ extern crate libc;
|
||||
use std::ptr;
|
||||
use std::path::Path;
|
||||
use std::mem::uninitialized;
|
||||
use libc::{uid_t, gid_t, c_int, FILE, c_char, size_t, fopen, fclose};
|
||||
use libc::{FILE, c_char, c_int, fclose, fopen, gid_t, size_t, uid_t};
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[repr(C)]
|
||||
@ -18,12 +18,7 @@ pub struct PwEnt {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn fgetpwent_r(stream: *mut FILE,
|
||||
pwbuf: *mut PwEnt,
|
||||
buf: *mut c_char,
|
||||
buflen: size_t,
|
||||
pwbufp: *mut *mut PwEnt)
|
||||
-> c_int;
|
||||
fn fgetpwent_r(stream: *mut FILE, pwbuf: *mut PwEnt, buf: *mut c_char, buflen: size_t, pwbufp: *mut *mut PwEnt) -> c_int;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
@ -89,24 +84,15 @@ impl PwEntIter {
|
||||
}
|
||||
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Option<PwEntIter> {
|
||||
unsafe {
|
||||
path.as_ref().to_str().and_then(|p| {
|
||||
PwEntIter::from_ptr(CStr::from_ptr(p.as_ptr() as *const c_char).as_ptr())
|
||||
})
|
||||
}
|
||||
unsafe { path.as_ref().to_str().and_then(|p| PwEntIter::from_ptr(CStr::from_ptr(p.as_ptr() as *const c_char).as_ptr())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PwEntIter {
|
||||
type Item = *const PwEnt;
|
||||
fn next(&mut self) -> Option<*const PwEnt> {
|
||||
if unsafe {
|
||||
fgetpwent_r(self.stream,
|
||||
&mut self.pwbuf,
|
||||
&mut self.buf as *mut _ as *mut c_char,
|
||||
BUFLEN,
|
||||
&mut self.pwbufp)
|
||||
} != 0 || self.pwbufp.is_null() {
|
||||
if unsafe { fgetpwent_r(self.stream, &mut self.pwbuf, &mut self.buf as *mut _ as *mut c_char, BUFLEN, &mut self.pwbufp) } != 0 ||
|
||||
self.pwbufp.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(self.pwbufp)
|
||||
@ -117,12 +103,8 @@ impl Iterator for PwEntIter {
|
||||
#[test]
|
||||
fn find_root() {
|
||||
let root = PwEntIter::new()
|
||||
.and_then(|mut iter| {
|
||||
iter.find(|&pw| unsafe {
|
||||
(*pw).pw_uid == 0 || CStr::from_ptr((*pw).pw_name).to_bytes() == b"root"
|
||||
})
|
||||
})
|
||||
.map(|pw| unsafe { User::from_ptr(pw) });
|
||||
.and_then(|mut iter| iter.find(|&pw| unsafe { (*pw).pw_uid == 0 || CStr::from_ptr((*pw).pw_name).to_bytes() == b"root" }))
|
||||
.map(|pw| unsafe { User::from_ptr(pw) });
|
||||
assert_eq!(root,
|
||||
Some(User {
|
||||
name: String::from("root"),
|
||||
|
13
src/main.rs
13
src/main.rs
@ -13,7 +13,7 @@ use std::os::unix::fs::symlink;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use cronparse::crontab::{UserCrontabEntry, SystemCrontabEntry, AnacrontabEntry};
|
||||
use cronparse::crontab::{AnacrontabEntry, SystemCrontabEntry, UserCrontabEntry};
|
||||
|
||||
mod getpwent;
|
||||
mod generate;
|
||||
@ -41,8 +41,8 @@ fn main() {
|
||||
None => {
|
||||
println!("Usage: systemd-crontab-generator <destination-directory>");
|
||||
return;
|
||||
},
|
||||
Some(path) => path
|
||||
}
|
||||
Some(path) => path,
|
||||
};
|
||||
|
||||
let s = dest_dir.clone();
|
||||
@ -74,7 +74,8 @@ fn main() {
|
||||
fn generate_after_var_unit(dest_dir: &str) {
|
||||
let cron_after_var_unit_path = Path::new(dest_dir).join("cron-after-var.service");
|
||||
let mut cron_after_var_unit_file = try_!(File::create(&cron_after_var_unit_path));
|
||||
try_!(writeln!(cron_after_var_unit_file, r###"[Unit]
|
||||
try_!(writeln!(cron_after_var_unit_file,
|
||||
r###"[Unit]
|
||||
Description=Rerun systemd-crontab-generator because /var is a separate mount
|
||||
Documentation=man:systemd.cron(7)
|
||||
After=cron.target
|
||||
@ -83,8 +84,8 @@ ConditionDirectoryNotEmpty={statedir}
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/sh -c "{bindir}/systemctl daemon-reload ; {bindir}/systemctl try-restart cron.target""###,
|
||||
statedir = USERS_CRONTAB_DIR,
|
||||
bindir = BIN_DIR));
|
||||
statedir = USERS_CRONTAB_DIR,
|
||||
bindir = BIN_DIR));
|
||||
|
||||
let multiuser_wants_path = Path::new(dest_dir).join("multi-user.target.wants");
|
||||
try_!(create_dir_all(&multiuser_wants_path));
|
||||
|
@ -1,43 +1,69 @@
|
||||
use std::convert::AsRef;
|
||||
use std::fs::{read_dir, metadata};
|
||||
use std::fs::{metadata, read_dir};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::collections::BTreeMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind};
|
||||
use cronparse::crontab::{EnvVarEntry, CrontabEntry};
|
||||
use cronparse::crontab::{CrontabEntry, EnvVarEntry};
|
||||
|
||||
use generate::generate_systemd_units;
|
||||
|
||||
pub fn process_crontab_dir<T: FromStr, D: AsRef<Path>>(srcdir: &str, dstdir: D) where CrontabEntry: From<T>, CrontabFileError: From<<T as FromStr>::Err> {
|
||||
let files = read_dir(srcdir).and_then(|fs| fs.map(|r| r.map(|p| p.path()))
|
||||
.filter(|r| r.as_ref().map(|p| !p.file_name().and_then(|n| n.to_str().map(|n| n.starts_with("."))).unwrap_or(true)
|
||||
&& metadata(p).map(|m| m.is_file()).unwrap_or(true)).unwrap_or(true))
|
||||
.collect::<Result<Vec<PathBuf>, _>>());
|
||||
pub fn process_crontab_dir<T: FromStr, D: AsRef<Path>>(srcdir: &str, dstdir: D)
|
||||
where CrontabEntry: From<T>,
|
||||
CrontabFileError: From<<T as FromStr>::Err>
|
||||
{
|
||||
let files = read_dir(srcdir).and_then(|fs| {
|
||||
fs.map(|r| r.map(|p| p.path()))
|
||||
.filter(|r| {
|
||||
r.as_ref()
|
||||
.map(|p| {
|
||||
!p.file_name()
|
||||
.and_then(|n| n.to_str().map(|n| n.starts_with(".")))
|
||||
.unwrap_or(true) && metadata(p).map(|m| m.is_file()).unwrap_or(true)
|
||||
})
|
||||
.unwrap_or(true)
|
||||
})
|
||||
.collect::<Result<Vec<PathBuf>, _>>()
|
||||
});
|
||||
match files {
|
||||
Err(err) => warn!("error processing directory {}: {}", srcdir, err),
|
||||
Ok(files) => for file in files {
|
||||
process_crontab_file::<T, _, _>(file, dstdir.as_ref());
|
||||
Ok(files) => {
|
||||
for file in files {
|
||||
process_crontab_file::<T, _, _>(file, dstdir.as_ref());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_crontab_file<T: FromStr, P: AsRef<Path>, D: AsRef<Path>>(path: P, dstdir: D) where CrontabEntry: From<T>, CrontabFileError: From<<T as FromStr>::Err> {
|
||||
CrontabFile::<T>::new(path.as_ref()).map(|crontab| {
|
||||
let mut env = BTreeMap::new();
|
||||
for entry in crontab {
|
||||
match entry {
|
||||
Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => { env.insert(name, value); },
|
||||
Ok(data) => match generate_systemd_units(data, &env, path.as_ref(), dstdir.as_ref()) {
|
||||
Ok(_) => (),
|
||||
Err(err) => warn!("error generating unit from {}: {}", path.as_ref().display(), err)
|
||||
},
|
||||
Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => warn!("error accessing file {}: {}", path.as_ref().display(), err),
|
||||
Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => warn!("skipping file {} due to parsing error: {}", path.as_ref().display(), err),
|
||||
pub fn process_crontab_file<T: FromStr, P: AsRef<Path>, D: AsRef<Path>>(path: P, dstdir: D)
|
||||
where CrontabEntry: From<T>,
|
||||
CrontabFileError: From<<T as FromStr>::Err>
|
||||
{
|
||||
CrontabFile::<T>::new(path.as_ref())
|
||||
.map(|crontab| {
|
||||
let mut env = BTreeMap::new();
|
||||
for entry in crontab {
|
||||
match entry {
|
||||
Ok(CrontabEntry::EnvVar(EnvVarEntry(name, value))) => {
|
||||
env.insert(name, value);
|
||||
}
|
||||
Ok(data) => {
|
||||
match generate_systemd_units(data, &env, path.as_ref(), dstdir.as_ref()) {
|
||||
Ok(_) => (),
|
||||
Err(err) => warn!("error generating unit from {}: {}", path.as_ref().display(), err),
|
||||
}
|
||||
}
|
||||
Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Io(_), .. }) => {
|
||||
warn!("error accessing file {}: {}", path.as_ref().display(), err)
|
||||
}
|
||||
Err(err @ CrontabFileError { kind: CrontabFileErrorKind::Parse(_), .. }) => {
|
||||
warn!("skipping file {} due to parsing error: {}", path.as_ref().display(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap_or_else(|err| {
|
||||
warn!("error parsing file {}: {}", path.as_ref().display(), err);
|
||||
});
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
warn!("error parsing file {}: {}", path.as_ref().display(), err);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user