diff --git a/.gitignore b/.gitignore index b3d8ad0..27012f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ /target result result-* - -Cargo.lock - - -# Added by cargo -# -# already existing elements were commented out - -#/target diff --git a/Cargo.lock b/Cargo.lock index 16f1ff5..773742f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.21" @@ -38,7 +47,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -49,14 +58,74 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "cc" +version = "1.2.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", ] [[package]] name = "clap" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -64,14 +133,15 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", + "terminal_size", ] [[package]] @@ -98,12 +168,160 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dns-lookup" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e39034cee21a2f5bbb66ba0e3689819c4bb5d00382a282006e802a7ffa6c41d" +dependencies = [ + "cfg-if", + "libc", + "socket2", + "windows-sys 0.60.2", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" + +[[package]] +name = "fluent" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8137a6d5a2c50d6b0ebfcb9aaa91a28154e0a70605f112d30cb0cd4a78670477" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01203cb8918f5711e73891b347816d932046f95f54207710bda99beaeb423bf4" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eebbe59450baee8282d71676f3bfed5689aeab00b27545e83e5f14b1195e8b0" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" +dependencies = [ + "memchr", + "thiserror", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "intl-memoizer" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310da2e345f5eb861e7a07ee182262e94975051db9e4223e909ba90f392f163f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972" +dependencies = [ + "unic-langid", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -111,10 +329,148 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] -name = "libc" -version = "0.2.178" +name = "itoa" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jiff" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8" +dependencies = [ + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-sys 0.61.2", +] + +[[package]] +name = "jiff-static" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68971ebff725b9e2ca27a601c5eb38a4c5d64422c4cbab0c535f248087eda5c2" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.179" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" @@ -122,6 +478,61 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "os_display" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5fd71b79026fb918650dde6d125000a233764f1c2f1659a1c71118e33ea08f" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_shared", + "serde", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "portable-atomic" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.104" @@ -144,17 +555,101 @@ dependencies = [ name = "roowho2" version = "0.1.0" dependencies = [ + "anyhow", + "chrono", "clap", - "sd-notify", + "nix", + "tokio", + "uucore", ] [[package]] -name = "sd-notify" -version = "0.4.5" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b943eadf71d8b69e661330cb0e2656e31040acf21ee7708e2c238a0ec6af2bf4" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "self_cell" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", + "windows-sys 0.60.2", ] [[package]] @@ -165,33 +660,308 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.112" +version = "2.0.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4" +checksum = "678faa00651c9eb72dd2020cbdf275d92eccb2400d568e419efdd64838145cb4" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "terminal_size" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +dependencies = [ + "rustix", + "windows-sys 0.60.2", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "serde_core", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.49.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +dependencies = [ + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "type-map" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" +dependencies = [ + "rustc-hash", +] + +[[package]] +name = "unic-langid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ba52c9b05311f4f6e62d5d9d46f094bd6e84cb8df7b3ef952748d752a7d05" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce1bf08044d4b7a94028c93786f8566047edc11110595914de93362559bc658" +dependencies = [ + "tinystr", +] + [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uucore" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eddd390f3fdef74f104a948559e6de29203f60f8f563c8c9f528cd4c88ee78" +dependencies = [ + "clap", + "dns-lookup", + "fluent", + "fluent-bundle", + "fluent-syntax", + "jiff", + "libc", + "nix", + "os_display", + "phf", + "thiserror", + "time", + "unic-langid", + "uucore_procs", + "wild", +] + +[[package]] +name = "uucore_procs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47148309a1f7a989d165dabbbc7f2bf156d7ff6affe7d69c1c5bfb822e663ae6" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wild" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3131afc8c575281e1e80f36ed6a092aa502c08b18ed7524e86fbbb12bb410e1" +dependencies = [ + "glob", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -200,3 +970,84 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "serde", + "zerofrom", +] diff --git a/Cargo.toml b/Cargo.toml index ffc240a..84f7492 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,16 @@ autobins = false autolib = false [dependencies] +anyhow = "1.0.100" +chrono = { version = "0.4.42", features = ["serde"] } clap = { version = "4.5.53", features = ["derive"] } -sd-notify = "0.4.5" +nix = { version = "0.30.1", features = ["hostname", "net"] } +tokio = { version = "1.49.0", features = ["net", "rt-multi-thread"] } +# onc-rpc = "0.3.2" +# sd-notify = "0.4.5" +# serde = { version = "1.0.228", features = ["derive"] } +# serde_json = "1.0.148" +uucore = { version = "0.5.0", features = ["utmpx"] } [lib] name = "roowho2_lib" diff --git a/src/bin/roowhod.rs b/src/bin/roowhod.rs index 16b04d5..0b8f907 100644 --- a/src/bin/roowhod.rs +++ b/src/bin/roowhod.rs @@ -1,4 +1,10 @@ fn main() { - unimplemented!() + println!( + "{:#?}", + roowho2_lib::server::rwhod::generate_rwhod_status_update(), + ); + println!( + "{:#?}", + roowho2_lib::server::rwhod::determine_relevant_interfaces(), + ); } - diff --git a/src/lib.rs b/src/lib.rs index febacec..c6eebbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod proto; +pub mod server; diff --git a/src/proto.rs b/src/proto.rs index dee9cb9..ecc6999 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -1,3 +1,9 @@ pub mod finger_protocol; pub mod rusers_protocol; +pub mod rwhod_protocol; pub mod write_protocol; + +pub use finger_protocol::*; +pub use rusers_protocol::*; +pub use rwhod_protocol::*; +pub use write_protocol::*; diff --git a/src/proto/rwhod_protocol.rs b/src/proto/rwhod_protocol.rs new file mode 100644 index 0000000..93bceba --- /dev/null +++ b/src/proto/rwhod_protocol.rs @@ -0,0 +1,334 @@ +use std::array; + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Outmp { + /// tty name + pub out_line: [u8; Self::MAX_TTY_NAME_LEN], + /// user id + pub out_name: [u8; Self::MAX_USER_ID_LEN], + /// time on + pub out_time: i32, +} + +impl Outmp { + pub const MAX_TTY_NAME_LEN: usize = 8; + pub const MAX_USER_ID_LEN: usize = 8; +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Whoent { + /// active tty info + pub we_utmp: Outmp, + /// tty idle time + pub we_idle: i32, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Whod { + /// protocol version + pub wd_vers: u8, + /// packet type, see below + pub wd_type: u8, + pub wd_pad: [u8; 2], + /// time stamp by sender + pub wd_sendtime: i32, + /// time stamp applied by receiver + pub wd_recvtime: i32, + /// host's name + pub wd_hostname: [u8; Self::MAX_HOSTNAME_LEN], + /// load average as in uptime + pub wd_loadav: [i32; 3], + /// time system booted + pub wd_boottime: i32, + pub wd_we: [Whoent; Self::MAX_WHOENTRIES], +} + +impl Whod { + pub const SIZE: usize = std::mem::size_of::(); + pub const MAX_HOSTNAME_LEN: usize = 32; + pub const MAX_WHOENTRIES: usize = 1024 / std::mem::size_of::(); + + pub const WHODVERSION: u8 = 1; + + // NOTE: there was probably meant to be more packet types, but only status is defined. + pub const WHODTYPE_STATUS: u8 = 1; + + pub fn to_bytes(&self) -> [u8; std::mem::size_of::()] { + unsafe { std::mem::transmute_copy(self) } + } + + // TODO: we should probably make a safer parser. + pub fn from_bytes(bytes: &[u8; std::mem::size_of::()]) -> Self { + unsafe { std::mem::transmute_copy(bytes) } + } +} + +// ------------------------------------------------ + +#[derive(Debug, Clone)] +pub struct WhodStatusUpdate { + // NOTE: there is only one defined packet type, so we just omit it here + /// Timestamp by sender + sendtime: chrono::DateTime, + + /// Timestamp applied by receiver + recvtime: Option>, + + /// Name of the host sending the status update (max 32 characters) + hostname: String, + + /// load average over 5 minutes multiplied by 100 + load_average_5_min: i32, + /// load average over 10 minutes multiplied by 100 + load_average_10_min: i32, + /// load average over 15 minutes multiplied by 100 + load_average_15_min: i32, + + /// Which time the system was booted + boot_time: chrono::DateTime, + + /// List of users currently logged in to the host (max 42 entries) + users: Vec, +} + +impl WhodStatusUpdate { + pub fn new( + sendtime: chrono::DateTime, + recvtime: Option>, + hostname: String, + load_average_5_min: i32, + load_average_10_min: i32, + load_average_15_min: i32, + boot_time: chrono::DateTime, + users: Vec, + ) -> Self { + Self { + sendtime, + recvtime, + hostname, + load_average_5_min, + load_average_10_min, + load_average_15_min, + boot_time, + users, + } + } +} + +#[derive(Debug, Clone)] +pub struct WhodUserEntry { + /// TTY name (max 8 characters) + tty: String, + + /// User ID (max 8 characters) + user_id: String, + + /// Time when the user logged in + login_time: chrono::DateTime, + + /// Idle time in seconds + idle_time_seconds: i32, +} + +impl WhodUserEntry { + pub fn new( + tty: String, + user_id: String, + login_time: chrono::DateTime, + idle_time_seconds: i32, + ) -> Self { + Self { + tty, + user_id, + login_time, + idle_time_seconds, + } + } +} + +impl TryFrom for WhodUserEntry { + type Error = String; + + fn try_from(value: Whoent) -> Result { + let tty_end = value + .we_utmp + .out_line + .iter() + .position(|&c| c == 0) + .unwrap_or(value.we_utmp.out_line.len()); + let tty = String::from_utf8(value.we_utmp.out_line[..tty_end].to_vec()) + .map_err(|e| format!("Invalid UTF-8 in TTY name: {}", e))?; + + let user_id_end = value + .we_utmp + .out_name + .iter() + .position(|&c| c == 0) + .unwrap_or(value.we_utmp.out_name.len()); + let user_id = String::from_utf8(value.we_utmp.out_name[..user_id_end].to_vec()) + .map_err(|e| format!("Invalid UTF-8 in user ID: {}", e))?; + + let login_time = chrono::DateTime::from_timestamp_secs(value.we_utmp.out_time as i64) + .ok_or(format!( + "Invalid login time timestamp: {}", + value.we_utmp.out_time + ))?; + + Ok(WhodUserEntry { + tty, + user_id, + login_time, + idle_time_seconds: value.we_idle, + }) + } +} + +impl TryFrom for WhodStatusUpdate { + type Error = String; + + fn try_from(value: Whod) -> Result { + if value.wd_vers != Whod::WHODVERSION { + return Err(format!( + "Unsupported whod protocol version: {}", + value.wd_vers + )); + } + + let sendtime = chrono::DateTime::from_timestamp_secs(value.wd_sendtime as i64).ok_or( + format!("Invalid send time timestamp: {}", value.wd_sendtime), + )?; + + let recvtime = if value.wd_recvtime == 0 { + None + } else { + Some( + chrono::DateTime::from_timestamp_secs(value.wd_recvtime as i64).ok_or(format!( + "Invalid receive time timestamp: {}", + value.wd_recvtime + ))?, + ) + }; + + let hostname_end = value + .wd_hostname + .iter() + .position(|&c| c == 0) + .unwrap_or(value.wd_hostname.len()); + let hostname = String::from_utf8(value.wd_hostname[..hostname_end].to_vec()) + .map_err(|e| format!("Invalid UTF-8 in hostname: {}", e))?; + + let boot_time = chrono::DateTime::from_timestamp_secs(value.wd_boottime as i64).ok_or( + format!("Invalid boot time timestamp: {}", value.wd_boottime), + )?; + + let mut users = Vec::new(); + for whoent in &value.wd_we { + let user_entry = WhodUserEntry::try_from(whoent.clone())?; + users.push(user_entry); + } + + Ok(WhodStatusUpdate { + sendtime, + recvtime, + hostname, + load_average_5_min: value.wd_loadav[0], + load_average_10_min: value.wd_loadav[1], + load_average_15_min: value.wd_loadav[2], + boot_time, + users, + }) + } +} + +// TODO: support less strict conversions with truncation + +impl TryFrom for Whoent { + type Error = String; + + fn try_from(value: WhodUserEntry) -> Result { + let mut out_line = [0u8; 8]; + let tty_bytes = value.tty.as_bytes(); + if tty_bytes.len() > out_line.len() { + return Err(format!( + "TTY name too long: {} (max {})", + value.tty, + out_line.len() + )); + } + out_line[..tty_bytes.len()].copy_from_slice(tty_bytes); + + let mut out_name = [0u8; 8]; + let user_id_bytes = value.user_id.as_bytes(); + if user_id_bytes.len() > out_name.len() { + return Err(format!( + "User ID too long: {} (max {})", + value.user_id, + out_name.len() + )); + } + out_name[..user_id_bytes.len()].copy_from_slice(user_id_bytes); + + Ok(Whoent { + we_utmp: Outmp { + out_line, + out_name, + out_time: value.login_time.timestamp() as i32, + }, + we_idle: value.idle_time_seconds, + }) + } +} + +// TODO: support less strict conversions with truncation + +impl TryFrom for Whod { + type Error = String; + + fn try_from(value: WhodStatusUpdate) -> Result { + let mut wd_hostname = [0u8; Whod::MAX_HOSTNAME_LEN]; + let hostname_bytes = value.hostname.as_bytes(); + if hostname_bytes.len() > wd_hostname.len() { + return Err(format!( + "Hostname too long: {} (max {})", + value.hostname, + wd_hostname.len() + )); + } + wd_hostname[..hostname_bytes.len()].copy_from_slice(hostname_bytes); + + let mut wd_we = array::from_fn(|_| Whoent { + we_utmp: Outmp { + out_line: [0u8; Outmp::MAX_TTY_NAME_LEN], + out_name: [0u8; Outmp::MAX_USER_ID_LEN], + out_time: 0, + }, + we_idle: 0, + }); + + for (i, user_entry) in value.users.into_iter().enumerate() { + if i >= Whod::MAX_WHOENTRIES { + break; + } + wd_we[i] = Whoent::try_from(user_entry)?; + } + + Ok(Whod { + wd_vers: Whod::WHODVERSION, + wd_type: Whod::WHODTYPE_STATUS, + wd_pad: [0u8; 2], + wd_sendtime: value.sendtime.timestamp() as i32, + wd_recvtime: value.recvtime.map_or(0, |dt| dt.timestamp() as i32), + wd_hostname, + wd_loadav: [ + value.load_average_5_min, + value.load_average_10_min, + value.load_average_15_min, + ], + wd_boottime: value.boot_time.timestamp() as i32, + wd_we, + }) + } +} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..7f83c33 --- /dev/null +++ b/src/server.rs @@ -0,0 +1 @@ +pub mod rwhod; diff --git a/src/server/rwhod.rs b/src/server/rwhod.rs new file mode 100644 index 0000000..58f9683 --- /dev/null +++ b/src/server/rwhod.rs @@ -0,0 +1,138 @@ +use std::{collections::HashSet, net::IpAddr, path::Path}; + +use nix::{ifaddrs::getifaddrs, net::if_::InterfaceFlags, sys::stat::stat}; +use uucore::utmpx::Utmpx; + +use crate::proto::{Whod, WhodStatusUpdate, WhodUserEntry}; + +/// Reads utmp entries to determine currently logged-in users. +pub fn generate_rwhod_user_entries() -> anyhow::Result> { + Utmpx::iter_all_records() + .filter(|entry| entry.is_user_process()) + .map(|entry| { + let login_time = entry + .login_time() + .checked_to_utc() + .and_then(|t| { + chrono::DateTime::::from_timestamp_secs(t.unix_timestamp()) + }) + .ok_or_else(|| anyhow::anyhow!("Failed to convert login time to UTC"))?; + + let idle_time = stat(&Path::new("/dev").join(entry.tty_device())) + .map(|st| (chrono::Utc::now().timestamp() - st.st_atime) as i32) + .unwrap_or(0); + + Ok(WhodUserEntry::new( + entry.tty_device(), + entry.user(), + login_time, + idle_time, + )) + }) + .collect() +} + +/// Generate a rwhod status update packet representing the current system state. +pub fn generate_rwhod_status_update() -> anyhow::Result { + let sysinfo = nix::sys::sysinfo::sysinfo().unwrap(); + let load_average = sysinfo.load_average(); + let uptime = sysinfo.uptime(); + let hostname = nix::unistd::gethostname()?.to_str().unwrap().to_string(); + + let result = WhodStatusUpdate::new( + chrono::Utc::now(), + None, + hostname, + (load_average.0 * 100.0).abs() as i32, + (load_average.1 * 100.0).abs() as i32, + (load_average.2 * 100.0).abs() as i32, + chrono::Utc::now() - uptime, + generate_rwhod_user_entries()?, + ) + .try_into() + .map_err(|e| anyhow::anyhow!("{}", e))?; + + Ok(result) +} + +#[derive(Debug)] +pub struct RwhodSendTarget { + /// Name of the network interface. + pub name: String, + + /// Address to send rwhod packets to. + /// This is either the broadcast address (for broadcast interfaces) + /// or the point-to-point destination address (for point-to-point interfaces). + pub addr: IpAddr, +} + +/// Find all networks network interfaces suitable for rwhod communication. +pub fn determine_relevant_interfaces() -> anyhow::Result> { + getifaddrs().map_err(|e| e.into()).map(|ifaces| { + ifaces + // interface must be up + .filter(|iface| iface.flags.contains(InterfaceFlags::IFF_UP)) + // interface must be broadcast or point-to-point + .filter(|iface| { + iface + .flags + .intersects(InterfaceFlags::IFF_BROADCAST | InterfaceFlags::IFF_POINTOPOINT) + }) + .filter_map(|iface| { + let neighbor_addr = if iface.flags.contains(InterfaceFlags::IFF_BROADCAST) { + iface.broadcast + } else if iface.flags.contains(InterfaceFlags::IFF_POINTOPOINT) { + iface.destination + } else { + None + }; + + match neighbor_addr { + Some(addr) => addr + .as_sockaddr_in() + .map(|sa| IpAddr::V4(sa.ip().into())) + .or_else(|| addr.as_sockaddr_in6().map(|sa| IpAddr::V6(sa.ip().into()))) + .map(|ip_addr| RwhodSendTarget { + name: iface.interface_name, + addr: ip_addr, + }), + None => None, + } + }) + /* keep first occurrence per interface name */ + .scan(HashSet::new(), |seen, n| { + if seen.insert(n.name.clone()) { + Some(n) + } else { + None + } + }) + .collect::>() + }) +} + +pub async fn send_rwhod_packet_to_interface( + socket: &mut tokio::net::UdpSocket, + interface: &RwhodSendTarget, + packet: &Whod, +) -> anyhow::Result<()> { + let serialized_packet = packet.to_bytes(); + + let target_addr = match interface.addr { + IpAddr::V4(addr) => std::net::SocketAddr::new(IpAddr::V4(addr), 0), + IpAddr::V6(addr) => std::net::SocketAddr::new(IpAddr::V6(addr), 0), + }; + + socket + .send_to(&serialized_packet, &target_addr) + .await + .map_err(|e| anyhow::anyhow!("Failed to send rwhod packet: {}", e))?; + + Ok(()) +} + +// TODO: implement receiving rwhod packets from other hosts + +// TODO: implement storing and loading rwhod packets to/from file + +// TODO: implement protocol for cli - daemon communication