Merge pull request #50 from agrover/edition-2018
Update to Edition 2018 and fix Clippy warnings
This commit is contained in:
commit
d69afa94b0
140
Cargo.lock
generated
140
Cargo.lock
generated
@ -1,22 +1,5 @@
|
||||
[root]
|
||||
name = "systemd-crontab-generator"
|
||||
version = "1.0.1"
|
||||
dependencies = [
|
||||
"cronparse 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"handlebars 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernlog 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"md5 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pgs-files 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"users 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.5.2"
|
||||
@ -25,6 +8,21 @@ dependencies = [
|
||||
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cronparse"
|
||||
version = "0.5.0"
|
||||
@ -36,7 +34,7 @@ version = "0.6.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -55,7 +53,7 @@ dependencies = [
|
||||
"num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -87,7 +85,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.11"
|
||||
version = "0.2.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -105,7 +103,19 @@ name = "memchr"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -129,7 +139,7 @@ dependencies = [
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -138,7 +148,7 @@ version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -166,7 +176,7 @@ dependencies = [
|
||||
"num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -192,7 +202,7 @@ name = "rand"
|
||||
version = "0.3.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -214,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.19"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -222,13 +232,33 @@ name = "strsim"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "systemd-crontab-generator"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"cronparse 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"handlebars 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernlog 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"md5 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pgs-files 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"users 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -239,7 +269,7 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -256,7 +286,7 @@ version = "0.1.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -265,7 +295,7 @@ name = "users"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -273,6 +303,11 @@ name = "utf8-ranges"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.7"
|
||||
@ -283,3 +318,44 @@ name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b3fb52b09c1710b961acb35390d514be82e4ac96a9969a8e38565a29b878dc9"
|
||||
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
"checksum cc 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
|
||||
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
"checksum cronparse 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffab399727c0d7ff383c9abbad03a24cfd468bed510616c7acabd8331916a8d3"
|
||||
"checksum docopt 0.6.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4cc0acb4ce0828c6a5a11d47baa432fe885881c27428c3a4e473e454ffe57a76"
|
||||
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
|
||||
"checksum handlebars 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "120b3b01f000620d46466d37a1c062d65ac7e8dfa11bfeecc2d35398bfd3512a"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum kernlog 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d998b430f80e016e2bb08eb0fc9b61631ef200ec9f7adef7ceec56db89cd2c48"
|
||||
"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
|
||||
"checksum libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122"
|
||||
"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
|
||||
"checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054"
|
||||
"checksum md5 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5539a8dee9b4ae308c9c406a379838b435a8f2c84cf9fedc6d5a576be9888db"
|
||||
"checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20"
|
||||
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||
"checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2"
|
||||
"checksum num-bigint 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "41655c8d667be847a0b72fe0888857a7b3f052f691cf40852be5fcf87b274a65"
|
||||
"checksum num-complex 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ccac67baf893ac97474f8d70eff7761dabb1f6c66e71f8f1c67a6859218db810"
|
||||
"checksum num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "fb24d9bfb3f222010df27995441ded1e954f8f69cd35021f6bef02ca9552fb92"
|
||||
"checksum num-iter 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "287a1c9969a847055e1122ec0ea7a5c5d6f72aad97934e131c83d5c08ab4e45c"
|
||||
"checksum num-rational 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "48cdcc9ff4ae2a8296805ac15af88b3d88ce62128ded0cb74ffb63a587502a84"
|
||||
"checksum num-traits 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "51eab148f171aefad295f8cece636fc488b9b392ef544da31ea4b8ef6b9e9c39"
|
||||
"checksum pgs-files 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8de73733f1d567f1188c2cf51eaf072272d24c1d6d9549456a11f59a8c248410"
|
||||
"checksum quick-error 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb6ccf8db7bbcb9c2eae558db5ab4f3da1c2a87e4e597ed394726bc8ea6ca1d"
|
||||
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
|
||||
"checksum regex 0.1.71 (registry+https://github.com/rust-lang/crates.io-index)" = "e58a1b7d2bfecc0746e8587c30a53d01ea7bc0e98fac54e5aaa375b94338a0cc"
|
||||
"checksum regex-syntax 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "baa04823ba7be7ed0bed3d0704c7b923019d9c4e4931c5af2804c7c7a0e3d00b"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum strsim 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e4d73a2c36a4d095ed1a6df5cbeac159863173447f7a82b3f4757426844ab825"
|
||||
"checksum tempfile 1.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ea9234ab6f388c147f9a44dfd331f2c5ad956387acbb9fa03e99abc5a28e5eaa"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread_local 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "55dd963dbaeadc08aa7266bf7f91c3154a7805e32bb94b820b769d2ef3b4744d"
|
||||
"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af"
|
||||
"checksum users 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d6d73ff26a6a57e6328f6e0b31738dfe27478e90ea828c3aba85a774d815c971"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3969e500d618a5e974917ddefd0ba152e4bcaae5eb5d9b8c1fbc008e9e28c24e"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
@ -8,6 +8,7 @@ name = "systemd-crontab-generator"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/systemd-cron/systemd-cron-next"
|
||||
version = "1.0.2"
|
||||
edition = "2018"
|
||||
|
||||
[build-dependencies]
|
||||
handlebars = "0.12.0"
|
||||
@ -26,6 +27,7 @@ rustc-serialize = "0.3.20"
|
||||
tempfile = "1.1.3"
|
||||
time = "0.1.34"
|
||||
users = "0.5.1"
|
||||
nix = "0.17.0"
|
||||
|
||||
[features]
|
||||
persistent = []
|
||||
|
88
build.rs
88
build.rs
@ -1,13 +1,13 @@
|
||||
extern crate handlebars;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::fs::{self, File, create_dir_all};
|
||||
use std::io::{Read, Write};
|
||||
use std::collections::BTreeMap;
|
||||
use std::env;
|
||||
use std::fs::{self, create_dir_all, File};
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
use handlebars::{Handlebars, Context, Template};
|
||||
use handlebars::{Context, Handlebars, Template};
|
||||
use rustc_serialize::json::{Json, ToJson};
|
||||
|
||||
static UNITS_DIR: &'static str = "units";
|
||||
@ -24,21 +24,29 @@ fn main() {
|
||||
let data = build_render_data();
|
||||
|
||||
let mut config = File::create(out_dir.clone() + "/config.rs").unwrap();
|
||||
writeln!(config, "pub static USERS_CRONTAB_DIR: &'static str = {:?};", data["statedir"].as_string().unwrap()).unwrap();
|
||||
writeln!(config, "pub static PACKAGE: &'static str = {:?};", data["package"].as_string().unwrap()).unwrap();
|
||||
writeln!(config, "pub static BIN_DIR: &'static str = {:?};", data["bindir"].as_string().unwrap()).unwrap();
|
||||
writeln!(config, "pub static LIB_DIR: &'static str = {:?};", data["libdir"].as_string().unwrap()).unwrap();
|
||||
writeln!(
|
||||
config,
|
||||
"pub static USERS_CRONTAB_DIR: &str = {:?};",
|
||||
data["statedir"].as_string().unwrap()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(config, "pub static PACKAGE: &str = {:?};", data["package"].as_string().unwrap()).unwrap();
|
||||
writeln!(config, "pub static BIN_DIR: &str = {:?};", data["bindir"].as_string().unwrap()).unwrap();
|
||||
writeln!(config, "pub static LIB_DIR: &str = {:?};", data["libdir"].as_string().unwrap()).unwrap();
|
||||
|
||||
let mut data = Json::Object(data);
|
||||
let schedules = get_required_schedules();
|
||||
|
||||
for schedule in schedules.iter() {
|
||||
data.as_object_mut().unwrap().insert("schedule".to_owned(), Json::String(schedule.clone()));
|
||||
for schedule_unit in [ "target", "timer", "service" ].iter() {
|
||||
data.as_object_mut()
|
||||
.unwrap()
|
||||
.insert("schedule".to_owned(), Json::String(schedule.clone()));
|
||||
for schedule_unit in ["target", "timer", "service"].iter() {
|
||||
compile_template(
|
||||
format!("{}/cron-schedule.{}.in", UNITS_DIR, schedule_unit),
|
||||
output.join("units").join(format!("cron-{}.{}", schedule, schedule_unit)),
|
||||
&data);
|
||||
&data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,12 +57,18 @@ fn main() {
|
||||
}
|
||||
|
||||
fn compile_template<S: AsRef<Path>, T: AsRef<Path>>(source_file: S, target_file: T, data: &Json) {
|
||||
println!("compiling from template: {:?} -> {:?}...", source_file.as_ref(), target_file.as_ref());
|
||||
println!(
|
||||
"compiling from template: {:?} -> {:?}...",
|
||||
source_file.as_ref(),
|
||||
target_file.as_ref()
|
||||
);
|
||||
|
||||
let tmpl = File::open(source_file).and_then(|mut file| {
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf).map(|_| Template::compile(&*buf).unwrap())
|
||||
}).unwrap();
|
||||
let tmpl = File::open(source_file)
|
||||
.and_then(|mut file| {
|
||||
let mut buf = String::new();
|
||||
file.read_to_string(&mut buf).map(|_| Template::compile(&*buf).unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut handle = Handlebars::new();
|
||||
handle.register_template("default", tmpl);
|
||||
@ -68,7 +82,7 @@ fn compile_templates<P: AsRef<Path>>(source_dir: &str, output_dir: P, data: &Jso
|
||||
let entry = entry.unwrap();
|
||||
let name = entry.file_name().into_string().unwrap();
|
||||
if name.ends_with(".in") && !name.starts_with("cron-schedule.") {
|
||||
let target = output_dir.as_ref().join(&name[..name.len()-3]);
|
||||
let target = output_dir.as_ref().join(&name[..name.len() - 3]);
|
||||
compile_template(entry.path(), target, data);
|
||||
}
|
||||
}
|
||||
@ -82,23 +96,44 @@ fn build_render_data() -> BTreeMap<String, Json> {
|
||||
|
||||
ctx.insert("package".to_owned(), Json::String(package.to_owned()));
|
||||
|
||||
ctx.insert("bindir".to_owned(), Json::String(env::var("BIN_DIR").unwrap_or_else(|_| prefix.clone() + "/bin")));
|
||||
ctx.insert("confdir".to_owned(), Json::String(env::var("CONF_DIR").unwrap_or_else(|_| prefix.clone() + "/etc")));
|
||||
ctx.insert(
|
||||
"bindir".to_owned(),
|
||||
Json::String(env::var("BIN_DIR").unwrap_or_else(|_| prefix.clone() + "/bin")),
|
||||
);
|
||||
ctx.insert(
|
||||
"confdir".to_owned(),
|
||||
Json::String(env::var("CONF_DIR").unwrap_or_else(|_| prefix.clone() + "/etc")),
|
||||
);
|
||||
|
||||
let datadir = env::var("DATA_DIR").unwrap_or_else(|_| prefix.clone() + "/share");
|
||||
let libdir = env::var("LIB_DIR").unwrap_or_else(|_| prefix.clone() + "/lib");
|
||||
|
||||
ctx.insert("mandir".to_owned(), Json::String(env::var("MAN_DIR").unwrap_or_else(|_| datadir.clone() + "/man")));
|
||||
ctx.insert("docdir".to_owned(), Json::String(env::var("DOC_DIR").unwrap_or_else(|_| datadir.clone() + "/doc/" + package)));
|
||||
ctx.insert("unitdir".to_owned(), Json::String(env::var("UNIT_DIR").unwrap_or_else(|_| libdir.clone() + "/systemd/system")));
|
||||
ctx.insert(
|
||||
"mandir".to_owned(),
|
||||
Json::String(env::var("MAN_DIR").unwrap_or_else(|_| datadir.clone() + "/man")),
|
||||
);
|
||||
ctx.insert(
|
||||
"docdir".to_owned(),
|
||||
Json::String(env::var("DOC_DIR").unwrap_or_else(|_| datadir.clone() + "/doc/" + package)),
|
||||
);
|
||||
ctx.insert(
|
||||
"unitdir".to_owned(),
|
||||
Json::String(env::var("UNIT_DIR").unwrap_or_else(|_| libdir.clone() + "/systemd/system")),
|
||||
);
|
||||
|
||||
ctx.insert("libdir".to_owned(), Json::String(libdir));
|
||||
ctx.insert("datadir".to_owned(), Json::String(datadir));
|
||||
ctx.insert("prefix".to_owned(), Json::String(prefix));
|
||||
|
||||
ctx.insert("statedir".to_owned(), Json::String(env::var("STATE_DIR").unwrap_or_else(|_| "/var/spool/cron".to_owned())));
|
||||
ctx.insert(
|
||||
"statedir".to_owned(),
|
||||
Json::String(env::var("STATE_DIR").unwrap_or_else(|_| "/var/spool/cron".to_owned())),
|
||||
);
|
||||
|
||||
ctx.insert("runparts".to_owned(), Json::String(env::var("RUN_PARTS").unwrap_or_else(|_| "/usr/bin/run-parts".to_owned())));
|
||||
ctx.insert(
|
||||
"runparts".to_owned(),
|
||||
Json::String(env::var("RUN_PARTS").unwrap_or_else(|_| "/usr/bin/run-parts".to_owned())),
|
||||
);
|
||||
|
||||
ctx.insert("persistent".to_owned(), Json::Boolean(env::var("CARGO_FEATURE_PERSISTENT").is_ok()));
|
||||
|
||||
@ -118,7 +153,8 @@ fn get_required_schedules() -> Vec<String> {
|
||||
("CARGO_FEATURE_SCHED_SEMI_ANNUALLY", "semi-annually"),
|
||||
];
|
||||
|
||||
features.iter()
|
||||
features
|
||||
.iter()
|
||||
.filter(|&&(e, _)| env::var(e).is_ok())
|
||||
.map(|&(_, f)| f.to_owned())
|
||||
.collect::<Vec<_>>()
|
||||
|
@ -1,11 +1,9 @@
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::mem::transmute;
|
||||
use std::thread::sleep;
|
||||
use std::env;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
fn main() {
|
||||
let delay = match env::args().nth(1).and_then(|s| s.parse::<f32>().ok()) {
|
||||
Some(d) => 60.0 * d,
|
||||
@ -20,9 +18,15 @@ fn main() {
|
||||
.and_then(|ref mut file| file.read(&mut buf))
|
||||
.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()
|
||||
.position(|&c| c == 0x20)
|
||||
.and_then(|p| {
|
||||
if p < sz {
|
||||
std::str::from_utf8(&buf[..p]).unwrap().parse::<f32>().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
|
@ -1,40 +1,33 @@
|
||||
extern crate rustc_serialize;
|
||||
extern crate docopt;
|
||||
extern crate users;
|
||||
extern crate glob;
|
||||
extern crate tempfile;
|
||||
extern crate libc;
|
||||
extern crate cronparse;
|
||||
extern crate docopt;
|
||||
extern crate glob;
|
||||
extern crate libc;
|
||||
extern crate nix;
|
||||
extern crate rustc_serialize;
|
||||
extern crate tempfile;
|
||||
extern crate users;
|
||||
|
||||
use cronparse::{CrontabFile, CrontabFileError};
|
||||
use cronparse::crontab::UserCrontabEntry;
|
||||
use tempfile::NamedTempFile;
|
||||
use cronparse::{CrontabFile, CrontabFileError};
|
||||
use docopt::Docopt;
|
||||
use users::User;
|
||||
use nix::unistd::{chown, Gid, Uid};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{self, Read, Write, copy, stderr, stdin, stdout};
|
||||
use std::fs::File;
|
||||
use std::io::{copy, stderr, stdin, stdout, Read, Write};
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, exit};
|
||||
use std::ffi::CString;
|
||||
use std::process::{exit, Command};
|
||||
use tempfile::NamedTempFile;
|
||||
use users::User;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/config.rs"));
|
||||
|
||||
extern "C" {
|
||||
fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
|
||||
fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), nix::Error> {
|
||||
chown(path.as_ref(), Some(Uid::from_raw(owner)), Some(Gid::from_raw(group)))
|
||||
}
|
||||
|
||||
fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
||||
static USAGE: &'static str = r#"
|
||||
static USAGE: &str = r#"
|
||||
Usage: crontab [-u <user>] -l
|
||||
crontab [-u <user>] -e [<file>]
|
||||
crontab [-u <user>] -s
|
||||
@ -81,19 +74,16 @@ struct Args {
|
||||
}
|
||||
|
||||
fn get_editor() -> Option<String> {
|
||||
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())
|
||||
})
|
||||
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())
|
||||
})
|
||||
}
|
||||
|
||||
fn confirm(msg: &str) -> bool {
|
||||
@ -107,7 +97,7 @@ fn confirm(msg: &str) -> bool {
|
||||
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(b"Please reply \"y\" or \"n\"\n").unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -132,10 +122,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;
|
||||
}
|
||||
}
|
||||
@ -211,7 +201,13 @@ fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
|
||||
}
|
||||
|
||||
if let Err(err) = tmpfile.persist(cron_file) {
|
||||
writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
|
||||
writeln!(
|
||||
stderr,
|
||||
"unexpected error: {}, your edit is kept here: {}",
|
||||
err.error,
|
||||
err.file.path().display()
|
||||
)
|
||||
.unwrap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -246,7 +242,14 @@ fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
|
||||
}
|
||||
|
||||
if let Err(e) = tmpfile.persist(cron_file) {
|
||||
writeln!(stderr, "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
|
||||
writeln!(
|
||||
stderr,
|
||||
"error renaming {} to {}: {}",
|
||||
e.file.path().display(),
|
||||
cron_file.display(),
|
||||
e.error
|
||||
)
|
||||
.unwrap();
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -257,39 +260,36 @@ 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());
|
||||
let args: Args = Docopt::new(USAGE).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(),
|
||||
};
|
||||
|
||||
match fs::metadata(USERS_CRONTAB_DIR) {
|
||||
Ok(ref meta) if !meta.is_dir() => {
|
||||
writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
|
||||
exit(1);
|
||||
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) {
|
||||
if fs::create_dir_all(USERS_CRONTAB_DIR).is_err() {
|
||||
writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name());
|
||||
@ -297,7 +297,11 @@ fn main() {
|
||||
exit(match args {
|
||||
Args { flag_show: true, .. } => show(&*cron_file, &cron_user, &args),
|
||||
Args { flag_list: true, .. } => list(&*cron_file, &cron_user, &args),
|
||||
Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
|
||||
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!(),
|
||||
@ -305,7 +309,7 @@ fn main() {
|
||||
}
|
||||
|
||||
fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
|
||||
match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
|
||||
match CrontabFile::<UserCrontabEntry>::new(path)?.find(Result::is_err) {
|
||||
Some(Err(err)) => Err(err),
|
||||
_ => Ok(()),
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
|
||||
use std::env;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::io::{Result, Write};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
macro_rules! try_log {
|
||||
($exp:expr) => {
|
||||
match $exp {
|
||||
Ok(v) => v,
|
||||
Err(e) => { println!("<3>{}", e); return; }
|
||||
Err(e) => {
|
||||
println!("<3>{}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn get_systemd_unit_property(unit: &str, prop: &str) -> Result<String> {
|
||||
@ -36,11 +38,10 @@ fn main() {
|
||||
};
|
||||
|
||||
let mut user = try_log!(get_systemd_unit_property(&*unit, "User"));
|
||||
if user.len() == 0 {
|
||||
if user.is_empty() {
|
||||
user = "root".to_owned();
|
||||
}
|
||||
|
||||
|
||||
let job_env = try_log!(get_systemd_unit_property(&*unit, "Environment"));
|
||||
for pair in job_env.split(' ') {
|
||||
let mut p = pair.splitn(2, '=');
|
||||
@ -52,18 +53,15 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
if user.len() == 0 {
|
||||
if user.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname")
|
||||
.arg("-n")
|
||||
.output())
|
||||
.stdout[..])
|
||||
let mut hostname = String::from_utf8_lossy(&try_log!(Command::new("uname").arg("-n").output()).stdout[..])
|
||||
.trim_end_matches('\n')
|
||||
.to_owned();
|
||||
|
||||
if hostname.len() == 0 {
|
||||
if hostname.is_empty() {
|
||||
hostname = "localhost".to_owned();
|
||||
}
|
||||
|
||||
@ -74,19 +72,17 @@ fn main() {
|
||||
head.push_str(&*hostname);
|
||||
head.push_str("] job ");
|
||||
head.push_str(&*unit);
|
||||
head.push_str(r###" failed
|
||||
head.push_str(
|
||||
r###" failed
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
Auto-Submitted: auto-generated
|
||||
|
||||
"###);
|
||||
"###,
|
||||
);
|
||||
|
||||
let status = Command::new("systemctl")
|
||||
.arg("status")
|
||||
.arg(&*unit)
|
||||
.output()
|
||||
.unwrap();
|
||||
let status = Command::new("systemctl").arg("status").arg(&*unit).output().unwrap();
|
||||
|
||||
let mut mailer = try_log!(Command::new("sendmail")
|
||||
.arg("-i")
|
||||
|
@ -1,23 +1,25 @@
|
||||
extern crate time;
|
||||
extern crate glob;
|
||||
extern crate time;
|
||||
|
||||
use std::fs::{metadata, remove_file};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::collections::BTreeSet;
|
||||
use std::fs::{metadata, remove_file};
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use time::{Duration, get_time};
|
||||
use glob::glob;
|
||||
use time::{get_time, Duration};
|
||||
|
||||
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: [&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";
|
||||
static ACTUAL_STAMPS_GLOB: &str = "/var/lib/systemd/timers/stamp-cron-*.timer";
|
||||
static TIMER_STAMPS_GLOB: &str = "/run/systemd/generator/cron-*.timer";
|
||||
|
||||
fn cleanup<P: AsRef<Path>, I: IntoIterator<Item = P>>(iter: I) {
|
||||
let ten_days_ago = get_time() - Duration::days(10);
|
||||
@ -34,18 +36,18 @@ 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<_>>();
|
||||
.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);
|
||||
}
|
||||
|
343
src/generate.rs
343
src/generate.rs
@ -1,14 +1,14 @@
|
||||
use std::io::{self, Write};
|
||||
use std::fs::{File, create_dir_all, metadata, set_permissions};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt, symlink};
|
||||
use std::path::Path;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fs::{create_dir_all, metadata, set_permissions, File};
|
||||
use std::io::{self, Write};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{symlink, MetadataExt, PermissionsExt};
|
||||
use std::path::Path;
|
||||
|
||||
use cronparse::Limited;
|
||||
use cronparse::crontab::{CrontabEntry, SystemCrontabEntry, UserCrontabEntry};
|
||||
use cronparse::schedule::{Calendar, Period, Schedule};
|
||||
use cronparse::interval::Interval;
|
||||
use cronparse::schedule::{Calendar, Period, Schedule};
|
||||
use cronparse::Limited;
|
||||
|
||||
use pgs_files::passwd::{get_entry_by_name, get_entry_by_uid};
|
||||
|
||||
@ -19,124 +19,146 @@ pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String
|
||||
|
||||
info!("generating units for {}: \"{}\", {:?}", path.display(), entry, env);
|
||||
|
||||
let owner = try!(metadata(path)).uid();
|
||||
let owner = 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);
|
||||
@ -144,12 +166,12 @@ 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(get_entry_by_name)
|
||||
.or_else(|| get_entry_by_uid(owner))
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user")));
|
||||
let user = entry
|
||||
.user()
|
||||
.and_then(get_entry_by_name)
|
||||
.or_else(|| get_entry_by_uid(owner))
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "unknown user"))?;
|
||||
|
||||
// generate unique cron job id
|
||||
let mut md5ctx = ::md5::Context::new();
|
||||
@ -170,7 +192,7 @@ pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String
|
||||
|
||||
// make sure cron.target.wants dir exists
|
||||
let cron_target_wants_path = dstdir.join("cron.target.wants");
|
||||
try!(create_dir_all(&cron_target_wants_path));
|
||||
create_dir_all(&cron_target_wants_path)?;
|
||||
|
||||
// process command in case it should be put into script
|
||||
let command = if metadata(cmd).map(|m| m.is_file()).unwrap_or(false) {
|
||||
@ -180,22 +202,24 @@ pub fn generate_systemd_units(entry: CrontabEntry, env: &BTreeMap<String, String
|
||||
|
||||
debug!("generating script {:?} from {:?}", script_command_path, path);
|
||||
{
|
||||
let mut script_command_file = try!(File::create(&script_command_path));
|
||||
try!(writeln!(script_command_file, "#!{}", shell));
|
||||
try!(writeln!(script_command_file, "{}", cmd));
|
||||
let mut script_command_file = File::create(&script_command_path)?;
|
||||
writeln!(script_command_file, "#!{}", shell)?;
|
||||
writeln!(script_command_file, "{}", cmd)?;
|
||||
}
|
||||
|
||||
let mut perms = try!(metadata(&script_command_path)).permissions();
|
||||
let mut perms = metadata(&script_command_path)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
try!(set_permissions(&script_command_path, perms));
|
||||
set_permissions(&script_command_path, perms)?;
|
||||
script_command_path.to_str().unwrap().to_owned()
|
||||
};
|
||||
|
||||
debug!("generating service {:?} from {:?}", service_unit_path, path);
|
||||
{
|
||||
let mut service_unit_file = try!(File::create(service_unit_path));
|
||||
let mut service_unit_file = File::create(service_unit_path)?;
|
||||
|
||||
try!(writeln!(service_unit_file, r###"[Unit]
|
||||
writeln!(
|
||||
service_unit_file,
|
||||
r###"[Unit]
|
||||
Description=[Cron] "{entry}"
|
||||
Documentation=man:systemd-crontab-generator(8)
|
||||
RefuseManualStart=true
|
||||
@ -203,56 +227,60 @@ RefuseManualStop=true
|
||||
SourcePath={source_crontab_path}"###,
|
||||
entry = entry,
|
||||
source_crontab_path = path.display(),
|
||||
));
|
||||
)?;
|
||||
|
||||
if env.contains_key("MAILTO") {
|
||||
try!(writeln!(service_unit_file, "OnFailure=cron-failure@%i.service"));
|
||||
writeln!(service_unit_file, "OnFailure=cron-failure@%i.service")?;
|
||||
}
|
||||
|
||||
if user.uid != 0 {
|
||||
try!(writeln!(service_unit_file, "Requires=systemd-user-sessions.service"));
|
||||
writeln!(service_unit_file, "Requires=systemd-user-sessions.service")?;
|
||||
if !user.dir.is_empty() {
|
||||
try!(writeln!(service_unit_file, "RequiresMountsFor={}", user.dir));
|
||||
writeln!(service_unit_file, "RequiresMountsFor={}", user.dir)?;
|
||||
}
|
||||
}
|
||||
|
||||
try!(writeln!(service_unit_file, r###"
|
||||
writeln!(
|
||||
service_unit_file,
|
||||
r###"
|
||||
[Service]
|
||||
Type=oneshot
|
||||
IgnoreSIGPIPE=false
|
||||
ExecStart={command}"###,
|
||||
command = command,
|
||||
));
|
||||
)?;
|
||||
|
||||
if schedule.is_some() && delay > 0 {
|
||||
try!(writeln!(service_unit_file, "ExecStartPre=-{}/{}/boot-delay {}", LIB_DIR, PACKAGE, delay));
|
||||
writeln!(service_unit_file, "ExecStartPre=-{}/{}/boot-delay {}", LIB_DIR, PACKAGE, delay)?;
|
||||
}
|
||||
|
||||
if user.uid != 0 {
|
||||
try!(writeln!(service_unit_file, "User={}", user.name));
|
||||
try!(writeln!(service_unit_file, "WorkingDirectory=~"));
|
||||
writeln!(service_unit_file, "User={}", user.name)?;
|
||||
writeln!(service_unit_file, "WorkingDirectory=~")?;
|
||||
}
|
||||
|
||||
if let Some(group) = entry.group() {
|
||||
try!(writeln!(service_unit_file, "Group={}", group));
|
||||
writeln!(service_unit_file, "Group={}", group)?;
|
||||
}
|
||||
if batch {
|
||||
try!(writeln!(service_unit_file, "CPUSchedulingPolicy=idle"));
|
||||
try!(writeln!(service_unit_file, "IOSchedulingClass=idle"));
|
||||
writeln!(service_unit_file, "CPUSchedulingPolicy=idle")?;
|
||||
writeln!(service_unit_file, "IOSchedulingClass=idle")?;
|
||||
}
|
||||
|
||||
if !env.is_empty() {
|
||||
for (name, value) in env.iter() {
|
||||
try!(writeln!(service_unit_file, r#"Environment="{}={}""#, name, value));
|
||||
writeln!(service_unit_file, r#"Environment="{}={}""#, name, value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("generating timer {:?} from {:?}", timer_unit_path, path);
|
||||
{
|
||||
let mut timer_unit_file = try!(File::create(&timer_unit_path));
|
||||
let mut timer_unit_file = File::create(&timer_unit_path)?;
|
||||
|
||||
try!(writeln!(timer_unit_file, r###"[Unit]
|
||||
writeln!(
|
||||
timer_unit_file,
|
||||
r###"[Unit]
|
||||
Description=[Timer] "{entry}"
|
||||
Documentation=man:systemd-crontab-generator(8)
|
||||
PartOf=cron.target
|
||||
@ -265,35 +293,36 @@ Unit={service_unit_name}"###,
|
||||
entry = entry,
|
||||
source_crontab_path = path.display(),
|
||||
service_unit_name = service_unit_name,
|
||||
));
|
||||
)?;
|
||||
|
||||
if cfg![feature = "persistent"] {
|
||||
try!(writeln!(timer_unit_file, "Persistent={}", persistent));
|
||||
writeln!(timer_unit_file, "Persistent={}", persistent)?;
|
||||
}
|
||||
|
||||
if let Some(schedule) = schedule {
|
||||
try!(writeln!(timer_unit_file, "OnCalendar={}", schedule));
|
||||
writeln!(timer_unit_file, "OnCalendar={}", schedule)?;
|
||||
} else {
|
||||
try!(writeln!(timer_unit_file, "OnBootSec={}m", delay));
|
||||
writeln!(timer_unit_file, "OnBootSec={}m", delay)?;
|
||||
}
|
||||
|
||||
if random_delay != 1 {
|
||||
if cfg!(feature="randomized-delay") {
|
||||
try!(writeln!(timer_unit_file, "RandomizedDelaySec={}m", random_delay));
|
||||
if cfg!(feature = "randomized-delay") {
|
||||
writeln!(timer_unit_file, "RandomizedDelaySec={}m", random_delay)?;
|
||||
} else {
|
||||
try!(writeln!(timer_unit_file, "AccuracySec={}m", random_delay));
|
||||
writeln!(timer_unit_file, "AccuracySec={}m", random_delay)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
try!(symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name)));
|
||||
symlink(timer_unit_path, cron_target_wants_path.join(timer_unit_name))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn linearize<T, C>(input: &[Interval<T>], star: &str, conv: C) -> String
|
||||
where T: Limited,
|
||||
C: Fn(&T) -> String
|
||||
where
|
||||
T: Limited,
|
||||
C: Fn(&T) -> String,
|
||||
{
|
||||
if input.len() == 1 && input[0] == Interval::Full(1) {
|
||||
star.to_owned()
|
||||
@ -312,14 +341,14 @@ fn tohex(input: &[u8]) -> String {
|
||||
#[inline]
|
||||
fn hex(d: u8) -> char {
|
||||
match d {
|
||||
0...9 => (d + 0x30) as char,
|
||||
10...15 => (d + 0x57) as char,
|
||||
0..=9 => (d + 0x30) as char,
|
||||
10..=15 => (d + 0x57) as char,
|
||||
_ => unreachable!("unexpected value: {}", d),
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = String::with_capacity(32);
|
||||
for b in input.into_iter() {
|
||||
for b in input {
|
||||
buf.push(hex(b >> 4));
|
||||
buf.push(hex(b & 0xf));
|
||||
}
|
||||
|
52
src/main.rs
52
src/main.rs
@ -7,12 +7,13 @@ extern crate pgs_files;
|
||||
extern crate log;
|
||||
extern crate kernlog;
|
||||
|
||||
use std::thread::spawn;
|
||||
use std::env;
|
||||
use std::fs::{File, create_dir_all, metadata};
|
||||
use std::fs::{create_dir_all, metadata, File};
|
||||
use std::io::{self, Error, Write};
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use std::thread::spawn;
|
||||
|
||||
use cronparse::crontab::{AnacrontabEntry, SystemCrontabEntry, UserCrontabEntry};
|
||||
|
||||
@ -20,27 +21,18 @@ mod generate;
|
||||
mod process;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/config.rs"));
|
||||
static SYSTEM_CRONTAB_DIR: &'static str = "/etc/cron.d"; // SystemCrontabEntry
|
||||
static SYSTEM_CRONTAB_FILE: &'static str = "/etc/crontab";
|
||||
static ANACRONTAB_FILE: &'static str = "/etc/anacrontab"; // AnacrontabEntry
|
||||
static REBOOT_FILE: &'static str = "/run/crond.reboot";
|
||||
static SYSTEM_CRONTAB_DIR: &str = "/etc/cron.d"; // SystemCrontabEntry
|
||||
static SYSTEM_CRONTAB_FILE: &str = "/etc/crontab";
|
||||
static ANACRONTAB_FILE: &str = "/etc/anacrontab"; // AnacrontabEntry
|
||||
static REBOOT_FILE: &str = "/run/crond.reboot";
|
||||
|
||||
macro_rules! try_ {
|
||||
($exp:expr) => {
|
||||
match $exp {
|
||||
Ok(value) => value,
|
||||
Err(err) => { error!("{}", err); return; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
fn main() -> Result<(), Error> {
|
||||
log::set_logger(|filter| kernlog::KernelLog::init_level(log::LogLevelFilter::Error, filter)).unwrap();
|
||||
|
||||
let dest_dir = match env::args().nth(1) {
|
||||
None => {
|
||||
println!("Usage: systemd-crontab-generator <destination-directory>");
|
||||
return;
|
||||
exit(1);
|
||||
}
|
||||
Some(path) => path,
|
||||
};
|
||||
@ -53,6 +45,7 @@ fn main() {
|
||||
|
||||
process::process_crontab_dir::<UserCrontabEntry, _>(USERS_CRONTAB_DIR, s);
|
||||
create_reboot_lock_file();
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let s = dest_dir.clone();
|
||||
@ -61,7 +54,7 @@ fn main() {
|
||||
process::process_crontab_dir::<SystemCrontabEntry, _>(SYSTEM_CRONTAB_DIR, &s);
|
||||
});
|
||||
|
||||
let s = dest_dir.clone();
|
||||
let s = dest_dir;
|
||||
let anacron_thread = spawn(move || {
|
||||
process::process_crontab_file::<AnacrontabEntry, _, _>(ANACRONTAB_FILE, &s);
|
||||
});
|
||||
@ -69,13 +62,16 @@ fn main() {
|
||||
let _ = user_thread.join();
|
||||
let _ = system_thread.join();
|
||||
let _ = anacron_thread.join();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn generate_after_var_unit(dest_dir: &str) {
|
||||
fn generate_after_var_unit(dest_dir: &str) -> Result<(), io::Error> {
|
||||
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]
|
||||
let mut cron_after_var_unit_file = File::create(&cron_after_var_unit_path)?;
|
||||
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
|
||||
@ -84,12 +80,14 @@ 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));
|
||||
try_!(symlink(cron_after_var_unit_path, multiuser_wants_path.join("cron-after-var.service")));
|
||||
create_dir_all(&multiuser_wants_path)?;
|
||||
symlink(cron_after_var_unit_path, multiuser_wants_path.join("cron-after-var.service"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_reboot_lock_file() {
|
||||
|
@ -1,30 +1,30 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::AsRef;
|
||||
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::{CrontabEntry, EnvVarEntry};
|
||||
use cronparse::{CrontabFile, CrontabFileError, CrontabFileErrorKind};
|
||||
|
||||
use generate::generate_systemd_units;
|
||||
use crate::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>
|
||||
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>, _>>()
|
||||
.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),
|
||||
@ -37,8 +37,9 @@ pub fn process_crontab_dir<T: FromStr, D: AsRef<Path>>(srcdir: &str, dstdir: D)
|
||||
}
|
||||
|
||||
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>
|
||||
where
|
||||
CrontabEntry: From<T>,
|
||||
CrontabFileError: From<<T as FromStr>::Err>,
|
||||
{
|
||||
CrontabFile::<T>::new(path.as_ref())
|
||||
.map(|crontab| {
|
||||
@ -48,18 +49,26 @@ pub fn process_crontab_file<T: FromStr, P: AsRef<Path>, D: AsRef<Path>>(path: P,
|
||||
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)
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user