# lxcrond - cron for lxc containers ![lxcrond](doc/lxcrond-128.png) ## Features - Single binary, statically compiled with musl for easy lxc deployment. - No requirement for a shell. - No external deps, neither sendmail nor syslog. - Run deamon as root, jobs run as a lower privileged users. - No special editor, or reloading of files. - Also an entr/inotify replacement or simple ESB to detect and process incoming files. - Can be used as a cron library in rust code. - Generates a pid file so can be configured in lxinitd with `service /run/lxcrond.pid /sbin/lxcrond` let cron = Cron::new(some_jobs); std::thread::spawn(move|| { cron.run(); }); ## File format [full syntax](syntax.md) Single file `/etc/lxcrontab` is read on start up, unless a different file is specified with `-c`. Normal cron format as per `/etc/crontab` except the command is _not_ a bash line. it is command, plus optionally `"` quoted args. # m h dom mon dow user command 23 1 * * * teknopaul /usr/bin/linci admin "backup" Supports all `*`, & a list of values `5,10,15` Supports standard aliases `@daily` etc per busybox, and a few others. Supports absolute file name, if the file is changed or created run the job. # path user command /mnt/cipublish/nginx.conf root /bin/update-nginx Support absolute directories, if a file arrives the job is run. The job should delete or move the files or it may get re-run every 60 seconds # path user command /mnt/uploads clam /bin/scan-uploads ## Usage Usage: `lxcrond [options]` Options: -c, --config [conf_file] specify config file, default is /etc/lxcrontab -h, --help print this help menu -v, --verbose print debug information to stdout -V, --version print version number and exit -f, --filesystem-watch use filesystem polling every 60 seconds to handle file jobs instead of inotify If there are no jobs to run lxcrond exits. ## Software design Main loop syncs to run once every 60 seconds at :00, then ask is "now" the time to run any job. File operations use the Linux kernels inotify interface, or as a fallback, checks directories every 60 seconds. ### Time considerations There are many caveats to working with time in computers this program takes a simple approach that presumes time jumps are for a reason that the owner of the machine understands without making presumptions. If the system time jumps more than one minute its possible that jobs do not fire because that time "did not happen" on this machine. If time jumps backwards jobs may fire twice because that time "happened twice". Provided time jumps are significantly less than 60 seconds no funny stuff should happen, `ntpd` ensures time is kept in sync without large time jumps. Thus, if you set a cron to run at `23 12 * * *` every day the job will run at `12:23:00`, provided that time or any time up to `12:23:59` actually happens on this machine when the main loop triggers. Main loop triggers once every 60 seconds. This duration time is determined by the same clock that can be manipulated as interpreted by Rust. Rust does a significant amount of interpretation of the system time when considering Duration: specifically it attempts to ensure that Durations do not occur twice, no matter how skewed the system clock is. see [Struct std::time::Instant](https://doc.rust-lang.org/std/time/struct.Instant.html) If the system is very heavily loaded or the process is excessively niced it is possible that the time happens but no code runs when the time happens. Time _now_ is calculated once per loop, so if any job runs with a given time spec all jobs withe same time spec are guaranteed to run, even on a very heavily loaded system, so the risk that jobs themselves consume 100% of CPU is mitigated. ### TimeZone considerations N.B. all times are local time. Jobs run in the current timezone so if you travel with a laptop and switch timezones, jobs may not fire or may fire twice. ### Daylight Savings considerations Some times happen twice in the same day if you are unfortunate enough to live in a timezone that does daylight saving. Busybox crond handles this by not re-firing jobs if the time jumps by one hour, `lxcrond` does _not_ do that. This means you can schedule jobs to run every 5 mins and they will run correctly on daylight savings days. ### inotify considerations If files are polled with inotify there are various caveats, most significantly directories are not polled recursively. There are many other details to inotify that can cause jobs not to run, see `man 7 inotify`. ## TODO see .later ## Bugs see .later ## LXC considerations If compiled with `cargo build --release --target=x86_64-unknown-linux-musl` the only dependencies of this program are the config file `/etc/lxcrontab` and `/etc/passwd`. `/etc/passwd` is required to determine the user for executing jobs. `lxcrond` will not continue if `/ect/passwd` can not be read to avoid accidentally running scripts with no `$HOME` or and unexpected `pwd`. ## Build By default `make` builds with cargo and the `musl` target for to install this target run rustup target add x86_64-unknown-linux-musl make make deb make install ## author (c) teknopaul 2020