diff --git a/Build.toml b/Build.toml new file mode 100644 index 0000000..dfc0c82 --- /dev/null +++ b/Build.toml @@ -0,0 +1,43 @@ +[tasks.build_all] +info = "build all" +script = [ + # install packages + "apt update --color always", + "apt install zip mingw-w64 -y --color always", + "mkdir build", + + # build linux (x86_64) + "cargo zigbuild -r --color always", + "mv target/release/pmc build/pmc", + "zip build/pmc_%{env.VERSION}_linux_amd64.zip build/pmc", + "rm build/pmc", + + # build windows (x86_64) + "cargo zigbuild -r --target x86_64-pc-windows-gnu --color always", + "mv target/x86_64-pc-windows-gnu/release/pmc.exe build/pmc.exe", + "zip build/pmc_%{env.VERSION}_windows_amd64.zip build/pmc.exe", + "rm build/pmc.exe", + + # build macos (x86_64) + "cargo zigbuild -r --target x86_64-apple-darwin --color always", + "mv target/x86_64-apple-darwin/release/pmc build/pmc", + "zip build/pmc_%{env.VERSION}_darwin_amd64.zip build/pmc", + "rm build/pmc", + + # build macos (aarch64) + "cargo zigbuild -r --target aarch64-apple-darwin --color always", + "mv target/aarch64-apple-darwin/release/pmc build/pmc", + "zip build/pmc_%{env.VERSION}_darwin_arm.zip build/pmc", + "rm build/pmc", + + # post build + "ls -sh build", +] + +[tasks.build_all.remote] +silent = false +exclusive = true +shell = "/bin/bash" +image = "messense/cargo-zigbuild:latest" +push = ["src", "Cargo.toml", "Cargo.lock"] +pull = "build" diff --git a/Cargo.lock b/Cargo.lock index c75d6f7..4e9a32d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,1333 +1,1368 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[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 = "ansi-str" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cf4578926a981ab0ca955dc023541d19de37112bc24c1a197bd806d3d86ad1d" dependencies = [ "ansitok", ] [[package]] name = "ansitok" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83" dependencies = [ "nom", "vte", ] [[package]] name = "anstream" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayref" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" [[package]] name = "arrayvec" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "blake3" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" dependencies = [ "arrayref", "arrayvec 0.7.4", "cc", "cfg-if", "constant_time_eq", ] [[package]] name = "bumpalo" version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytecount" version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "camino" version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" [[package]] name = "cc" version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f8e7c90afad890484a21653d08b6e209ae34770fb5ee298f9c699fcc1e5c856" dependencies = [ "libc", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", "wasm-bindgen", "windows-targets", ] [[package]] name = "clap" version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap-verbosity-flag" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" dependencies = [ "clap", "log", ] [[package]] name = "clap_builder" version = "4.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ "heck", "proc-macro2", "quote", "syn 2.0.39", ] [[package]] name = "clap_lex" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "codespan-reporting" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" dependencies = [ "termcolor", "unicode-width", ] [[package]] name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "colored" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6" dependencies = [ "is-terminal", "lazy_static", "windows-sys", ] [[package]] name = "constant_time_eq" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" [[package]] name = "core-foundation-sys" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "crossbeam-deque" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" dependencies = [ "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", "memoffset 0.9.0", "scopeguard", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "cxx" version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7129e341034ecb940c9072817cd9007974ea696844fc4dd582dc1653a7fbe2e8" dependencies = [ "cc", "cxxbridge-flags", "cxxbridge-macro", "link-cplusplus", ] [[package]] name = "cxx-build" version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a24f3f5f8eed71936f21e570436f024f5c2e25628f7496aa7ccd03b90109d5" dependencies = [ "cc", "codespan-reporting", "once_cell", "proc-macro2", "quote", "scratch", "syn 2.0.39", ] [[package]] name = "cxxbridge-flags" version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06fdd177fc61050d63f67f5bd6351fac6ab5526694ea8e359cd9cd3b75857f44" [[package]] name = "cxxbridge-macro" version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", ] [[package]] name = "darwin-libproc" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fb90051930c9a0f09e585762152048e23ac74d20c10590ef7cf01c0343c3046" dependencies = [ "darwin-libproc-sys", "libc", "memchr", ] [[package]] name = "darwin-libproc-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57cebb5bde66eecdd30ddc4b9cd208238b15db4982ccc72db59d699ea10867c1" dependencies = [ "libc", ] [[package]] name = "derive_more" version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "env_logger" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", "log", "regex", "termcolor", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c18ee0ed65a5f1f81cac6b1d213b69c35fa47d4252ad41f1486dbd8226fe36e" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "global_placeholders" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d70af3f3fd800923fa445d6fa562e054d8abaf06df926f77dd6dbead1fefb275" dependencies = [ "parking_lot", ] [[package]] name = "hashbrown" version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" [[package]] name = "heck" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "home" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" dependencies = [ "windows-sys", ] [[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "iana-time-zone" version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "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 = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is-terminal" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", "rustix", "windows-sys", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "link-cplusplus" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" dependencies = [ "cc", ] [[package]] name = "linux-raw-sys" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" [[package]] name = "lock_api" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "mach" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" dependencies = [ "libc", ] [[package]] name = "macros-rs" -version = "0.4.2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fb0c4278fff6f86cf9a14ed6c8e1a393d798587303418a5706dbec35f667946" +checksum = "e7c58c98c7eca51b61c85c1cb5201582377b21ccf82731910d7bef8c12115024" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "memoffset" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] [[package]] name = "merkle_hash" version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2019c22dd07fa00549b671a150d126929eb1af56a4f4808a2e62aca8676995d" dependencies = [ "anyhow", "blake3", "camino", "rayon", ] [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nix" version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3790c00a0150112de0f4cd161e3d7fc4b2d8a5542ffc35f099a2562aecb35c" dependencies = [ "bitflags 1.3.2", "cc", "cfg-if", "libc", "memoffset 0.6.5", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] name = "once_cell" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "papergrid" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8" dependencies = [ "ansi-str", "ansitok", "bytecount", "fnv", "unicode-width", ] [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall 0.4.1", "smallvec", "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "platforms" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8d0eef3571242013a0d5dc84861c3ae4a652e56e12adf8bdc26ff5f8cb34c94" [[package]] name = "pmc" -version = "1.3.1" +version = "1.4.0" dependencies = [ "anyhow", "chrono", "clap", "clap-verbosity-flag", "colored", "cxx", "cxx-build", "env_logger", "global_placeholders", "home", "libc", "log", "macros-rs", "merkle_hash", "once_cell", "pretty_env_logger", "psutil", "regex", + "rmp-serde", "serde", "serde_json", "simple-logging", "tabled", "termcolor", "toml", ] [[package]] name = "pretty_env_logger" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ "env_logger", "log", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn 1.0.109", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "psutil" version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f866af2b0f8e4b0d2d00aad8a9c5fc48fad33466cd99a64cbb3a4c1505f1a62d" dependencies = [ "cfg-if", "darwin-libproc", "derive_more", "glob", "mach", "nix", "num_cpus", "once_cell", "platforms", "thiserror", "unescape", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rmp" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9860a6cc38ed1da53456442089b4dfa35e7cedaa326df63017af88385e6b20" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffea85eea980d8a74453e5d02a8d93028f3c34725de143085a844ebe953258a" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustix" version = "0.38.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80109a168d9bc0c7f483083244543a6eb0dba02295d33ca268145e6190d6df0c" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3cf7c11c38cb994f3d40e8a8cde3bbd1f72a435e4c49e85d6553d8312306152" [[package]] name = "serde" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.192" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", ] [[package]] name = "serde_json" version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] [[package]] name = "simple-logging" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b00d48e85675326bb182a2286ea7c1a0b264333ae10f27a937a72be08628b542" dependencies = [ "lazy_static", "log", "thread-id", ] [[package]] name = "smallvec" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tabled" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfe9c3632da101aba5131ed63f9eed38665f8b3c68703a6bb18124835c1a5d22" dependencies = [ "ansi-str", "ansitok", "papergrid", "tabled_derive", "unicode-width", ] [[package]] name = "tabled_derive" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99f688a08b54f4f02f0a3c382aefdb7884d3d69609f785bd253dc033243e3fe4" dependencies = [ "heck", "proc-macro-error", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "termcolor" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", ] [[package]] name = "thread-id" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" dependencies = [ "libc", "redox_syscall 0.1.57", "winapi", ] [[package]] name = "toml" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "unescape" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vte" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" dependencies = [ "arrayvec 0.5.2", "utf8parse", "vte_generate_state_changes", ] [[package]] name = "vte_generate_state_changes" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "wasm-bindgen" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn 2.0.39", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", "syn 2.0.39", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 26294f7..e26c7da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,36 +1,37 @@ [package] name = "pmc" -version = "1.3.1" +version = "1.4.0" edition = "2021" license = "MIT" repository = "https://lab.themackabu.dev/crates/pmc" description = "Process management controller" [dependencies] clap = "4.4.8" log = "0.4.20" toml = "0.8.8" home = "0.5.5" cxx = "1.0.110" psutil = "3.2.2" regex = "1.10.2" libc = "0.2.150" anyhow = "1.0.75" colored = "2.0.4" -macros-rs = "0.4.2" +macros-rs = "0.4.4" termcolor = "1.4.0" +rmp-serde = "1.1.2" once_cell = "1.18.0" env_logger = "0.10.1" merkle_hash = "3.5.0" serde_json = "1.0.108" simple-logging = "2.0.2" pretty_env_logger = "0.5.0" clap-verbosity-flag = "2.1.0" global_placeholders = "0.1.0" tabled = { version = "0.14.0", features = ["color"] } chrono = { version = "0.4.23", features = ["serde"] } serde = { version = "1.0.192", features = ["derive"] } [build-dependencies] chrono = "0.4.23" cxx-build = "1.0.110" diff --git a/Maidfile.toml b/Maidfile.toml index 6bde9da..7ba9139 100644 --- a/Maidfile.toml +++ b/Maidfile.toml @@ -1,24 +1,26 @@ +import = ["Build.toml"] + [project] name = "pmc" -version = "1.3.1" +version = "1.4.0" [tasks] clean = { script = ["rm -rf bin", "mkdir bin"] } [tasks.build] depends = ["clean"] script = [ "cargo zigbuild --release", "cp target/release/pmc bin/pmc" ] [tasks.build.cache] path = "src" target = ["bin/pmc"] [tasks.install] script = [ "maid build -q", "sudo cp bin/pmc /usr/local/bin", "echo Copied binary!" ] diff --git a/index.js b/index.js new file mode 100644 index 0000000..6be0237 --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +console.log('hello world'); diff --git a/src/api/mod.rs b/src/api/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/cc/process.cc b/src/cc/process.cc index a92024c..abf7f6a 100644 --- a/src/cc/process.cc +++ b/src/cc/process.cc @@ -1,84 +1,93 @@ #include "../include/process.h" #include <fcntl.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #include <iostream> using namespace std; namespace process { volatile sig_atomic_t childExitStatus = 0; pair<std::string, std::string> split(const std::string& str) { size_t length = str.length(); size_t midpoint = length / 2; std::string firstHalf = str.substr(0, midpoint); std::string secondHalf = str.substr(midpoint); return make_pair(firstHalf, secondHalf); } void sigchld_handler(int signo) { (void)signo; int status; while (waitpid(-1, &status, WNOHANG) > 0) { childExitStatus = status; } } void Runner::New(const std::string &name, const std::string &logPath) { std::string stdoutFileName = logPath + "/" + name + "-out.log"; std::string stderrFileName = logPath + "/" + name + "-error.log"; stdout_fd = open(stdoutFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); stderr_fd = open(stderrFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, NULL) == -1) { std::cerr << "[PMC] (cc) Error setting up SIGCHLD handler\n"; } } Runner::~Runner() { if (stdout_fd != -1) { close(stdout_fd); } if (stderr_fd != -1) { close(stderr_fd); } } int64_t Runner::Run(const std::string &command, const std::string &shell, Vec<String> args) { pid_t pid = fork(); if (pid == -1) { std::cerr << "[PMC] (cc) Unable to fork\n"; return -1; } else if (pid == 0) { setsid(); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); dup2(stdout_fd, STDOUT_FILENO); dup2(stderr_fd, STDERR_FILENO); + + std::vector<const char*> argsArray; + argsArray.push_back(shell.c_str()); - if (execl(shell.c_str(), args[0].c_str(), args[1].c_str(), command.c_str(), (char *)nullptr) == -1) { + transform(args.begin(), args.end(), std::back_inserter(argsArray), + [](rust::String& arg) { return arg.c_str(); }); + + argsArray.push_back(command.c_str()); + argsArray.push_back((char *)nullptr); + + if (execvp(shell.c_str(), const_cast<char* const*>(argsArray.data())) == -1) { std::cerr << "[PMC] (cc) Unable to execute the command\n"; exit(EXIT_FAILURE); } } else { close(stdout_fd); close(stderr_fd); return pid; } return -1; }} diff --git a/src/cli.rs b/src/cli.rs index faf57a1..629d8b9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,303 +1,338 @@ use crate::structs::Args; use colored::Colorize; use global_placeholders::global; use macros_rs::{crashln, string, ternary}; -use pmc::file; -use pmc::helpers::{self, ColoredString}; -use pmc::process::Runner; use psutil::process::{MemoryInfo, Process}; use serde::Serialize; use serde_json::json; use std::env; +use pmc::{ + config, + file::{self, Exists}, + helpers::{self, ColoredString}, + process::Runner, +}; + use tabled::{ settings::{ object::{Columns, Rows}, style::{BorderColor, Style}, themes::Colorization, Color, Rotate, }, Table, Tabled, }; pub fn get_version(short: bool) -> String { return match short { true => format!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")), false => format!("{} ({} {}) [{}]", env!("CARGO_PKG_VERSION"), env!("GIT_HASH"), env!("BUILD_DATE"), env!("PROFILE")), }; } pub fn start(name: &Option<String>, args: &Option<Args>, watch: &Option<String>) { let mut runner = Runner::new(); + let config = config::read(); match args { Some(Args::Id(id)) => { println!("{} Applying action restartProcess on ({id})", *helpers::SUCCESS); - runner.restart(*id, name, watch, false); + + let item = runner.get(*id).restart(); + + if let Some(name) = name { + item.rename(name.clone()) + } + + if let Some(path) = watch { + item.watch(path.clone()) + } println!("{} restarted ({id}) ✓", *helpers::SUCCESS); list(&string!("default")); } Some(Args::Script(script)) => { let name = match name { Some(name) => string!(name), None => string!(script.split_whitespace().next().unwrap_or_default()), }; println!("{} Creating process with ({name})", *helpers::SUCCESS); - runner.start(&name, script, watch); + if name.ends_with(".ts") || name.ends_with(".js") { + let script = format!("{} {script}", config.runner.node); + runner.start(&name, &script, watch).save(); + } else { + runner.start(&name, script, watch).save(); + } println!("{} created ({name}) ✓", *helpers::SUCCESS); list(&string!("default")); } None => {} } } pub fn stop(id: &usize) { println!("{} Applying action stopProcess on ({id})", *helpers::SUCCESS); let mut runner = Runner::new(); - runner.stop(*id); + runner.get(*id).stop(); println!("{} stopped ({id}) ✓", *helpers::SUCCESS); list(&string!("default")); } pub fn remove(id: &usize) { println!("{} Applying action removeProcess on ({id})", *helpers::SUCCESS); - let mut runner = Runner::new(); - runner.remove(*id); + Runner::new().remove(*id); println!("{} removed ({id}) ✓", *helpers::SUCCESS); } pub fn info(id: &usize, format: &String) { - let runner = Runner::new(); - #[derive(Clone, Debug, Tabled)] struct Info { #[tabled(rename = "error log path ")] log_error: String, #[tabled(rename = "out log path")] log_out: String, #[tabled(rename = "cpu percent")] cpu_percent: String, #[tabled(rename = "memory usage")] memory_usage: String, + #[tabled(rename = "path hash")] + hash: String, #[tabled(rename = "watching")] watch: String, #[tabled(rename = "exec cwd")] path: String, #[tabled(rename = "script command ")] command: String, #[tabled(rename = "script id")] id: String, restarts: u64, uptime: String, pid: String, name: String, status: ColoredString, } impl Serialize for Info { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { let trimmed_json = json!({ "id": &self.id.trim(), "pid": &self.pid.trim(), "name": &self.name.trim(), "path": &self.path.trim(), "restarts": &self.restarts, "watch": &self.watch.trim(), + "watch": &self.hash.trim(), "uptime": &self.uptime.trim(), "status": &self.status.0.trim(), "log_out": &self.log_out.trim(), "cpu": &self.cpu_percent.trim(), "command": &self.command.trim(), "mem": &self.memory_usage.trim(), "log_error": &self.log_error.trim(), }); trimmed_json.serialize(serializer) } } if let Some(home) = home::home_dir() { - if let Some(item) = runner.info(*id) { - let mut memory_usage: Option<MemoryInfo> = None; - let mut cpu_percent: Option<f32> = None; + let item = Runner::new().get(*id).clone(); + let mut memory_usage: Option<MemoryInfo> = None; + let mut cpu_percent: Option<f32> = None; - if let Ok(mut process) = Process::new(item.pid as u32) { - memory_usage = process.memory_info().ok(); - cpu_percent = process.cpu_percent().ok(); - } + if let Ok(mut process) = Process::new(item.pid as u32) { + memory_usage = process.memory_info().ok(); + cpu_percent = process.cpu_percent().ok(); + } - let cpu_percent = match cpu_percent { + let cpu_percent = + match cpu_percent { Some(percent) => format!("{:.2}%", percent), None => string!("0%"), }; - let memory_usage = match memory_usage { - Some(usage) => helpers::format_memory(usage.rss()), - None => string!("0b"), + let memory_usage = match memory_usage { + Some(usage) => helpers::format_memory(usage.rss()), + None => string!("0b"), + }; + + let status = + if item.running { + "online ".green().bold() + } else { + match item.crash.crashed { + true => "crashed ", + false => "stopped ", + } + .red() + .bold() }; - let path = file::make_relative(&item.path, &home) - .map(|relative_path| relative_path.to_string_lossy().into_owned()) - .unwrap_or_else(|| crashln!("{} Unable to get your current directory", *helpers::FAIL)); - - let data = vec![Info { - cpu_percent, - memory_usage, - id: string!(id), - restarts: item.restarts, - name: item.name.clone(), - path: format!("{} ", path), - log_out: global!("pmc.logs.out", item.name.as_str()), - log_error: global!("pmc.logs.error", item.name.as_str()), - command: format!("/bin/bash -c '{}'", item.script.clone()), - pid: ternary!(item.running, format!("{}", item.pid), string!("n/a")), - status: ColoredString(ternary!(item.running, "online".green().bold(), "stopped".red().bold())), - watch: ternary!(item.watch.enabled, format!("{path}/{} ", item.watch.path), string!("disabled ")), - uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")), - }]; - - let table = Table::new(data.clone()) - .with(Rotate::Left) - .with(Style::rounded().remove_horizontals()) - .with(Colorization::exact([Color::FG_CYAN], Columns::first())) - .with(BorderColor::filled(Color::FG_BRIGHT_BLACK)) - .to_string(); - - if let Ok(json) = serde_json::to_string(&data[0]) { - match format.as_str() { - "raw" => println!("{:?}", data[0]), - "json" => println!("{json}"), - _ => { - println!("{}\n{table}\n", format!("Describing process with id ({id})").on_bright_white().black()); - println!(" {}", format!("Use `pmc logs {id} [--lines <num>]` to display logs").white()); - println!(" {}", format!("Use `pmc env {id}` to display environment variables").white()); - } - }; + let path = file::make_relative(&item.path, &home) + .map(|relative_path| relative_path.to_string_lossy().into_owned()) + .unwrap_or_else(|| crashln!("{} Unable to get your current directory", *helpers::FAIL)); + + let data = vec![Info { + cpu_percent, + memory_usage, + id: string!(id), + restarts: item.restarts, + name: item.name.clone(), + path: format!("{} ", path), + status: ColoredString(status), + log_out: global!("pmc.logs.out", item.name.as_str()), + log_error: global!("pmc.logs.error", item.name.as_str()), + command: format!("/bin/bash -c '{}'", item.script.clone()), + pid: ternary!(item.running, format!("{}", item.pid), string!("n/a")), + hash: ternary!(item.watch.enabled, format!("{} ", item.watch.hash), string!("none ")), + watch: ternary!(item.watch.enabled, format!("{path}/{} ", item.watch.path), string!("disabled ")), + uptime: ternary!(item.running, format!("{}", helpers::format_duration(item.started)), string!("none")), + }]; + + let table = Table::new(data.clone()) + .with(Rotate::Left) + .with(Style::rounded().remove_horizontals()) + .with(Colorization::exact([Color::FG_CYAN], Columns::first())) + .with(BorderColor::filled(Color::FG_BRIGHT_BLACK)) + .to_string(); + + if let Ok(json) = serde_json::to_string(&data[0]) { + match format.as_str() { + "raw" => println!("{:?}", data[0]), + "json" => println!("{json}"), + _ => { + println!("{}\n{table}\n", format!("Describing process with id ({id})").on_bright_white().black()); + println!(" {}", format!("Use `pmc logs {id} [--lines <num>]` to display logs").white()); + println!(" {}", format!("Use `pmc env {id}` to display environment variables").white()); + } }; - } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); - } + }; } else { crashln!("{} Impossible to get your home directory", *helpers::FAIL); } } pub fn logs(id: &usize, lines: &usize) { - let runner = Runner::new(); + let item = Runner::new().get(*id).clone(); + let log_error = global!("pmc.logs.error", item.name.as_str()); + let log_out = global!("pmc.logs.out", item.name.as_str()); - if let Some(item) = runner.info(*id) { + if Exists::file(log_error.clone()).unwrap() && Exists::file(log_out.clone()).unwrap() { println!("{}", format!("Showing last {lines} lines for process [{id}] (change the value with --lines option)").yellow()); - let log_error = global!("pmc.logs.error", item.name.as_str()); - let log_out = global!("pmc.logs.out", item.name.as_str()); - file::logs(*lines, &log_error, *id, "error", &item.name); file::logs(*lines, &log_out, *id, "out", &item.name); } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); + crashln!("{} Logs for process ({id}) not found", *helpers::FAIL); } } #[cfg(target_os = "macos")] pub fn env(id: &usize) { - let runner = Runner::new(); - - if let Some(item) = runner.info(*id) { - for (key, value) in item.env.iter() { - println!("{}: {}", key, value.green()); - } - } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); + let item = Runner::new().get(*id).clone(); + for (key, value) in item.env.iter() { + println!("{}: {}", key, value.green()); } } pub fn list(format: &String) { - let runner = Runner::new(); + let mut runner = Runner::new(); let mut processes: Vec<ProcessItem> = Vec::new(); #[derive(Tabled, Debug)] struct ProcessItem { id: ColoredString, name: String, pid: String, uptime: String, #[tabled(rename = "↺")] restarts: String, status: ColoredString, cpu: String, mem: String, #[tabled(rename = "watching")] watch: String, } impl serde::Serialize for ProcessItem { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { let trimmed_json = json!({ "cpu": &self.cpu.trim(), "mem": &self.mem.trim(), "id": &self.id.0.trim(), "pid": &self.pid.trim(), "name": &self.name.trim(), "watch": &self.watch.trim(), "uptime": &self.uptime.trim(), "status": &self.status.0.trim(), "restarts": &self.restarts.trim(), }); trimmed_json.serialize(serializer) } } - if runner.list().is_empty() { + if runner.is_empty() { println!("{} Process table empty", *helpers::SUCCESS); } else { - for (id, item) in runner.list() { + for (id, item) in runner.items() { let mut memory_usage: Option<MemoryInfo> = None; let mut cpu_percent: Option<f32> = None; if let Ok(mut process) = Process::new(item.pid as u32) { memory_usage = process.memory_info().ok(); cpu_percent = process.cpu_percent().ok(); } let cpu_percent = match cpu_percent { Some(percent) => format!("{:.0}%", percent), None => string!("0%"), }; let memory_usage = match memory_usage { Some(usage) => helpers::format_memory(usage.rss()), None => string!("0b"), }; + let status = if item.running { + "online ".green().bold() + } else { + match item.crash.crashed { + true => "crashed ", + false => "stopped ", + } + .red() + .bold() + }; + processes.push(ProcessItem { + status: ColoredString(status), cpu: format!("{cpu_percent} "), mem: format!("{memory_usage} "), - id: ColoredString(id.cyan().bold()), restarts: format!("{} ", item.restarts), name: format!("{} ", item.name.clone()), + id: ColoredString(id.to_string().cyan().bold()), pid: ternary!(item.running, format!("{} ", item.pid), string!("n/a ")), watch: ternary!(item.watch.enabled, format!("{} ", item.watch.path), string!("disabled ")), - status: ColoredString(ternary!(item.running, "online ".green().bold(), "stopped ".red().bold())), uptime: ternary!(item.running, format!("{} ", helpers::format_duration(item.started)), string!("none ")), }); } let table = Table::new(&processes) .with(Style::rounded().remove_verticals()) .with(BorderColor::filled(Color::FG_BRIGHT_BLACK)) .with(Colorization::exact([Color::FG_BRIGHT_CYAN], Rows::first())) .to_string(); if let Ok(json) = serde_json::to_string(&processes) { match format.as_str() { "raw" => println!("{:?}", processes), "json" => println!("{json}"), "default" => println!("{table}"), _ => {} }; }; } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 10cc14b..4f50b2a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,45 +1,47 @@ pub mod structs; use crate::file::{self, Exists}; use crate::helpers; use colored::Colorize; use macros_rs::{crashln, string}; use std::fs; use structs::{Config, Daemon, Runner}; pub fn read() -> Config { match home::home_dir() { Some(path) => { let path = path.display(); let config_path = format!("{path}/.pmc/config.toml"); if !Exists::file(config_path.clone()).unwrap() { let config = Config { runner: Runner { - shell: string!("/bin/bash"), - args: vec![string!("bash"), string!("-c")], + shell: string!("bash"), + args: vec![string!("-c")], + node: string!("node"), log_path: format!("{path}/.pmc/logs"), }, daemon: Daemon { + restarts: 10, interval: 1000, kind: string!("default"), }, }; let contents = match toml::to_string(&config) { Ok(contents) => contents, Err(err) => crashln!("{} Cannot parse config.\n{}", *helpers::FAIL, string!(err).white()), }; if let Err(err) = fs::write(&config_path, contents) { crashln!("{} Error writing config.\n{}", *helpers::FAIL, string!(err).white()) } log::info!("created config file"); } file::read(config_path) } None => crashln!("{} Impossible to get your home directory", *helpers::FAIL), } } diff --git a/src/config/structs.rs b/src/config/structs.rs index a3d8a0e..6a516b1 100644 --- a/src/config/structs.rs +++ b/src/config/structs.rs @@ -1,20 +1,22 @@ use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] pub struct Config { pub runner: Runner, pub daemon: Daemon, } #[derive(Debug, Deserialize, Serialize)] pub struct Runner { pub shell: String, pub args: Vec<String>, + pub node: String, pub log_path: String, } #[derive(Debug, Deserialize, Serialize)] pub struct Daemon { + pub restarts: u64, pub interval: u64, pub kind: String, } diff --git a/src/daemon/mod.rs b/src/daemon/mod.rs index b3202db..97a97d4 100644 --- a/src/daemon/mod.rs +++ b/src/daemon/mod.rs @@ -1,267 +1,263 @@ mod fork; mod log; use chrono::{DateTime, Utc}; use colored::Colorize; use fork::{daemon, Fork}; use global_placeholders::global; use log::Logger; use macros_rs::{crashln, fmtstr, str, string, ternary, then}; use psutil::process::{MemoryInfo, Process}; use serde::Serialize; use serde_json::json; -use std::{collections::BTreeMap, process, thread::sleep, time::Duration}; +use std::{process, thread::sleep, time::Duration}; use pmc::{ config, file, helpers::{self, ColoredString}, process::{hash, id::Id, Runner, Status}, }; use tabled::{ settings::{ object::Columns, style::{BorderColor, Style}, themes::Colorization, Color, Rotate, }, Table, Tabled, }; extern "C" fn handle_termination_signal(_: libc::c_int) { pid::remove(); let mut log = Logger::new().unwrap(); log.write(fmtstr!("daemon killed (pid={})", process::id())); unsafe { libc::_exit(0) } } -fn restart_process(mut log: Logger, items: &BTreeMap<String, pmc::process::Process>) { - let items = items.iter().filter_map(|(id, item)| Some((id.trim().parse::<usize>().ok()?, item))); - - for (id, item) in items { - if item.running { +fn restart_process(mut log: Logger) { + for (id, item) in Runner::new().items() { + if item.running && item.watch.enabled { let path = item.path.join(item.watch.path.clone()); let hash = hash::create(path); - if item.watch.enabled && hash != item.watch.hash { - let name = &Some(item.name.clone()); - let watch = &Some(item.watch.path.clone()); - let mut runner_instance = Runner::new(); - - runner_instance.restart(id, name, watch, false); + if hash != item.watch.hash { + item.restart(); log.write(fmtstr!("watch reload {} (id={id}, hash={hash})", item.name)); continue; } } if !item.running && pid::running(item.pid as i32) { - let mut runner_instance = Runner::new(); - runner_instance.set_status(id, Status::Running); + Runner::new().set_status(*id, Status::Running); continue; } then!(!item.running || pid::running(item.pid as i32), continue); - let name = &Some(item.name.clone()); - let watch = &Some(item.watch.path.clone()); - let mut runner_instance = Runner::new(); - runner_instance.restart(id, name, watch, true); - log.write(fmtstr!("restarted {} ({id})", item.name)); + if item.running && item.crash.value == 10 { + log.write(fmtstr!("{} has crashed ({id})", item.name)); + item.stop(); + Runner::new().set_crashed(*id).save(); + continue; + } else { + item.crashed(); + log.write(fmtstr!("restarted {} (id={id}, crashes={})", item.name, item.crash.value)); + } } } pub fn health(format: &String) { let mut pid: Option<i32> = None; let mut cpu_percent: Option<f32> = None; let mut uptime: Option<DateTime<Utc>> = None; let mut memory_usage: Option<MemoryInfo> = None; - let runner: Runner = file::read(global!("pmc.dump")); + let mut runner: Runner = file::read_rmp(global!("pmc.dump")); #[derive(Clone, Debug, Tabled)] struct Info { #[tabled(rename = "pid file")] pid_file: String, #[tabled(rename = "fork path")] path: String, #[tabled(rename = "cpu percent")] cpu_percent: String, #[tabled(rename = "memory usage")] memory_usage: String, #[tabled(rename = "daemon type")] external: String, #[tabled(rename = "process count")] process_count: usize, uptime: String, pid: String, status: ColoredString, } impl Serialize for Info { fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { let trimmed_json = json!({ "pid_file": &self.pid_file.trim(), "path": &self.path.trim(), "cpu": &self.cpu_percent.trim(), "mem": &self.memory_usage.trim(), "process_count": &self.process_count.to_string(), "uptime": &self.uptime.trim(), "pid": &self.pid.trim(), "status": &self.status.0.trim(), }); trimmed_json.serialize(serializer) } } if pid::exists() { if let Ok(process_id) = pid::read() { if let Ok(mut process) = Process::new(process_id as u32) { pid = Some(process_id); uptime = Some(pid::uptime().unwrap()); memory_usage = process.memory_info().ok(); cpu_percent = process.cpu_percent().ok(); } } } let cpu_percent = match cpu_percent { Some(percent) => format!("{:.2}%", percent), None => string!("0%"), }; - let memory_usage = match memory_usage { - Some(usage) => helpers::format_memory(usage.rss()), - None => string!("0b"), - }; + let memory_usage = + match memory_usage { + Some(usage) => helpers::format_memory(usage.rss()), + None => string!("0b"), + }; let uptime = match uptime { Some(uptime) => helpers::format_duration(uptime), None => string!("none"), }; let pid = match pid { Some(pid) => string!(pid), None => string!("n/a"), }; let data = vec![Info { pid: pid, cpu_percent, memory_usage, uptime: uptime, path: global!("pmc.base"), external: global!("pmc.daemon.kind"), - process_count: runner.list().keys().len(), + process_count: runner.count(), pid_file: format!("{} ", global!("pmc.pid")), status: ColoredString(ternary!(pid::exists(), "online".green().bold(), "stopped".red().bold())), }]; let table = Table::new(data.clone()) .with(Rotate::Left) .with(Style::rounded().remove_horizontals()) .with(Colorization::exact([Color::FG_CYAN], Columns::first())) .with(BorderColor::filled(Color::FG_BRIGHT_BLACK)) .to_string(); if let Ok(json) = serde_json::to_string(&data[0]) { match format.as_str() { "raw" => println!("{:?}", data[0]), "json" => println!("{json}"), "default" => { println!("{}\n{table}\n", format!("PMC daemon information").on_bright_white().black()); println!(" {}", format!("Use `pmc daemon restart` to restart the daemon").white()); println!(" {}", format!("Use `pmc daemon reset` to clean process id values").white()); } _ => {} }; }; } pub fn stop() { if pid::exists() { let mut log = Logger::new().unwrap(); println!("{} Stopping PMC daemon", *helpers::SUCCESS); match pid::read() { Ok(pid) => { pmc::service::stop(pid as i64); pid::remove(); log.write(fmtstr!("daemon stopped (pid={pid})")); println!("{} PMC daemon stopped", *helpers::SUCCESS); } Err(err) => crashln!("{} Failed to read PID file: {}", *helpers::FAIL, err), } } else { crashln!("{} The daemon is not running", *helpers::FAIL) } } pub fn start() { - let external = match global!("pmc.daemon.kind").as_str() { - "external" => true, - "default" => false, - "rust" => false, - "cc" => true, - _ => false, - }; + let external = + match global!("pmc.daemon.kind").as_str() { + "external" => true, + "default" => false, + "rust" => false, + "cc" => true, + _ => false, + }; pid::name("PMC Restart Handler Daemon"); println!("{} Spawning PMC daemon (pmc_base={})", *helpers::SUCCESS, global!("pmc.base")); if pid::exists() { match pid::read() { Ok(pid) => then!(!pid::running(pid), pid::remove()), Err(_) => crashln!("{} The daemon is already running", *helpers::FAIL), } } extern "C" fn init() { let config = config::read(); let mut log = Logger::new().unwrap(); unsafe { libc::signal(libc::SIGTERM, handle_termination_signal as usize) }; pid::write(process::id()); log.write(fmtstr!("new daemon forked (pid={})", process::id())); loop { - let runner = Runner::new(); - let items = runner.list(); - - then!(!runner.list().is_empty(), restart_process(log.clone(), items)); + then!(!Runner::new().is_empty(), restart_process(log.clone())); sleep(Duration::from_millis(config.daemon.interval)); } } println!("{} PMC Successfully daemonized (type={})", *helpers::SUCCESS, global!("pmc.daemon.kind")); if external { let callback = pmc::Callback(init); pmc::service::try_fork(false, false, callback); } else { match daemon(false, false) { Ok(Fork::Parent(_)) => {} Ok(Fork::Child) => init(), Err(err) => crashln!("{} Daemon creation failed with code {err}", *helpers::FAIL), } } } pub fn restart() { if pid::exists() { stop(); } start(); } pub fn reset() { let mut runner = Runner::new(); - let largest = runner.list().keys().last().cloned(); + let largest = runner.list().map(|(key, _)| *key).max(); match largest { - Some(id) => runner.set_id(Id::from(str!(id))), + Some(id) => runner.set_id(Id::from(str!(id.to_string()))), None => println!("{} Cannot reset index, no ID found", *helpers::FAIL), } println!("{} PMC Successfully reset (index={})", *helpers::SUCCESS, runner.id); } pub mod pid; diff --git a/src/file.rs b/src/file.rs index ab525aa..d93327e 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,84 +1,121 @@ use crate::helpers; use anyhow::Error; use colored::Colorize; use macros_rs::{crashln, str, string, ternary}; use std::{ env, fs::{self, File}, io::{self, BufRead, BufReader}, path::{Path, PathBuf, StripPrefixError}, thread::sleep, time::Duration, }; pub fn logs(lines_to_tail: usize, log_file: &str, id: usize, log_type: &str, item_name: &str) { let file = File::open(log_file).unwrap(); let reader = BufReader::new(file); let lines: Vec<String> = reader.lines().collect::<io::Result<_>>().unwrap(); let color = ternary!(log_type == "out", "green", "red"); println!("{}", format!("\n{log_file} last {lines_to_tail} lines:").bright_black()); let start_index = if lines.len() > lines_to_tail { lines.len() - lines_to_tail } else { 0 }; for (_, line) in lines.iter().skip(start_index).enumerate() { println!("{} {}", format!("{}|{} |", id, item_name).color(color), line); } } pub fn cwd() -> PathBuf { match env::current_dir() { Ok(path) => path, Err(_) => crashln!("{} Unable to find current working directory", *helpers::FAIL), } } pub fn make_relative(current: &Path, home: &Path) -> Option<std::path::PathBuf> { match current.strip_prefix(home) { Ok(relative_path) => Some(Path::new("~").join(relative_path)), Err(StripPrefixError { .. }) => None, } } pub struct Exists; impl Exists { pub fn folder(dir_name: String) -> Result<bool, Error> { Ok(Path::new(str!(dir_name)).is_dir()) } pub fn file(file_name: String) -> Result<bool, Error> { Ok(Path::new(str!(file_name)).exists()) } } pub fn read<T: serde::de::DeserializeOwned>(path: String) -> T { let mut retry_count = 0; let max_retries = 5; let contents = loop { match fs::read_to_string(&path) { Ok(contents) => break contents, Err(err) => { retry_count += 1; if retry_count >= max_retries { crashln!("{} Cannot find dumpfile.\n{}", *helpers::FAIL, string!(err).white()); } else { println!("{} Error reading dumpfile. Retrying... (Attempt {}/{})", *helpers::FAIL, retry_count, max_retries); } } } sleep(Duration::from_secs(1)); }; retry_count = 0; loop { match toml::from_str(&contents).map_err(|err| string!(err)) { Ok(parsed) => break parsed, Err(err) => { retry_count += 1; if retry_count >= max_retries { crashln!("{} Cannot parse dumpfile.\n{}", *helpers::FAIL, err.white()); } else { println!("{} Error parsing dumpfile. Retrying... (Attempt {}/{})", *helpers::FAIL, retry_count, max_retries); } } } sleep(Duration::from_secs(1)); } } + +pub fn read_rmp<T: serde::de::DeserializeOwned>(path: String) -> T { + let mut retry_count = 0; + let max_retries = 5; + + let bytes = loop { + match fs::read(&path) { + Ok(contents) => break contents, + Err(err) => { + retry_count += 1; + if retry_count >= max_retries { + crashln!("{} Cannot find dumpfile.\n{}", *helpers::FAIL, string!(err).white()); + } else { + println!("{} Error reading dumpfile. Retrying... (Attempt {}/{})", *helpers::FAIL, retry_count, max_retries); + } + } + } + sleep(Duration::from_secs(1)); + }; + + retry_count = 0; + + loop { + match rmp_serde::from_slice(&bytes) { + Ok(parsed) => break parsed, + Err(err) => { + retry_count += 1; + if retry_count >= max_retries { + crashln!("{} Cannot parse dumpfile.\n{}", *helpers::FAIL, string!(err).white()); + } else { + println!("{} Error parsing dumpfile. Retrying... (Attempt {}/{})", *helpers::FAIL, retry_count, max_retries); + } + } + } + sleep(Duration::from_secs(1)); + } +} diff --git a/src/main.rs b/src/main.rs index 0514367..ab5d0f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,134 +1,135 @@ +mod api; mod cli; mod daemon; mod globals; mod structs; use crate::structs::Args; use clap::{Parser, Subcommand}; use clap_verbosity_flag::Verbosity; use macros_rs::{str, string, then}; fn validate_id_script(s: &str) -> Result<Args, String> { if let Ok(id) = s.parse::<usize>() { Ok(Args::Id(id)) } else { Ok(Args::Script(s.to_owned())) } } #[derive(Parser)] #[command(version = str!(cli::get_version(false)))] struct Cli { #[command(subcommand)] command: Commands, #[clap(flatten)] verbose: Verbosity, } #[derive(Subcommand)] enum Daemon { /// Reset process index #[command(alias = "clean")] Reset, /// Stop daemon #[command(alias = "kill")] Stop, /// Restart daemon #[command(alias = "restart", alias = "start")] Restore, /// Check daemon #[command(alias = "info")] Health { /// Format output #[arg(long, default_value_t = string!("default"))] format: String, }, } // add pmc restore command #[derive(Subcommand)] enum Commands { /// Start/Restart a process #[command(alias = "restart")] Start { /// Process name #[arg(long)] name: Option<String>, #[clap(value_parser = validate_id_script)] args: Option<Args>, /// Watch to reload path #[arg(long)] watch: Option<String>, }, /// Stop/Kill a process #[command(alias = "kill")] Stop { id: usize }, /// Stop then remove a process #[command(alias = "rm")] Remove { id: usize }, /// Get env of a process #[command(alias = "cmdline")] Env { id: usize }, /// Get information of a process #[command(alias = "info")] Details { id: usize, /// Format output #[arg(long, default_value_t = string!("default"))] format: String, }, /// List all processes #[command(alias = "ls")] List { /// Format output #[arg(long, default_value_t = string!("default"))] format: String, }, /// Get logs from a process Logs { id: usize, #[arg(long, default_value_t = 15, help = "")] lines: usize, }, /// Daemon management Daemon { #[command(subcommand)] command: Daemon, }, } fn main() { globals::init(); let cli = Cli::parse(); let mut env = env_logger::Builder::new(); env.filter_level(cli.verbose.log_level_filter()).init(); match &cli.command { Commands::Start { name, args, watch } => cli::start(name, args, watch), Commands::Stop { id } => cli::stop(id), Commands::Remove { id } => cli::remove(id), Commands::Env { id } => cli::env(id), Commands::Details { id, format } => cli::info(id, format), Commands::List { format } => cli::list(format), Commands::Logs { id, lines } => cli::logs(id, lines), Commands::Daemon { command } => match command { Daemon::Stop => daemon::stop(), Daemon::Reset => daemon::reset(), Daemon::Restore => daemon::restart(), Daemon::Health { format } => daemon::health(format), }, }; if !matches!(&cli.command, Commands::Daemon { .. }) { then!(!daemon::pid::exists(), daemon::start()); } } diff --git a/src/process/dump.rs b/src/process/dump.rs index 47f2e44..458c989 100644 --- a/src/process/dump.rs +++ b/src/process/dump.rs @@ -1,35 +1,35 @@ use crate::{ file::{self, Exists}, helpers, process::{id::Id, Runner}, }; use colored::Colorize; use global_placeholders::global; use macros_rs::{crashln, string}; use std::{collections::BTreeMap, fs}; pub fn read() -> Runner { if !Exists::file(global!("pmc.dump")).unwrap() { let runner = Runner { id: Id::new(0), - process_list: BTreeMap::new(), + list: BTreeMap::new(), }; write(&runner); log::info!("created dump file"); } - file::read(global!("pmc.dump")) + file::read_rmp(global!("pmc.dump")) } pub fn write(dump: &Runner) { - let contents = match toml::to_string(dump) { + let encoded: Vec<u8> = match rmp_serde::to_vec(&dump) { Ok(contents) => contents, - Err(err) => crashln!("{} Cannot parse dump.\n{}", *helpers::FAIL, string!(err).white()), + Err(err) => crashln!("{} Cannot encode dump.\n{}", *helpers::FAIL, string!(err).white()), }; - if let Err(err) = fs::write(global!("pmc.dump"), contents) { + if let Err(err) = fs::write(global!("pmc.dump"), encoded) { crashln!("{} Error writing dumpfile.\n{}", *helpers::FAIL, string!(err).white()) } } diff --git a/src/process/hash.rs b/src/process/hash.rs index 389cf7b..a7dfa4e 100644 --- a/src/process/hash.rs +++ b/src/process/hash.rs @@ -1,12 +1,14 @@ use macros_rs::crashln; use merkle_hash::{bytes_to_hex, Algorithm, MerkleTree}; use std::path::PathBuf; pub fn create(path: PathBuf) -> String { + log::debug!("creating hash for {:?}", path); let tree = match MerkleTree::builder(&path.to_str().unwrap()).algorithm(Algorithm::Blake3).hash_names(false).build() { Ok(v) => v, Err(e) => crashln!("Invalid UTF-8 sequence: {}", e), }; + log::trace!("hash {:?}", tree.root.item.hash); bytes_to_hex(tree.root.item.hash) } diff --git a/src/process/mod.rs b/src/process/mod.rs index 43f4925..fba7498 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -1,204 +1,246 @@ +macro_rules! assign { + ($obj:expr, {$($field:ident),* $(,)?}) => {$($obj.$field = $field.clone();)*}; +} + mod dump; mod log; use crate::{ config, file, helpers, service::{run, stop, ProcessMetadata}, }; use chrono::serde::ts_milliseconds; use chrono::{DateTime, Utc}; -use macros_rs::{crashln, string, ternary}; +use macros_rs::{clone, crashln, string, then}; use serde::{Deserialize, Serialize}; use std::collections::{BTreeMap, HashMap}; use std::{env, path::PathBuf}; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Process { + pub id: usize, pub pid: i64, pub name: String, pub path: PathBuf, pub script: String, pub env: HashMap<String, String>, #[serde(with = "ts_milliseconds")] pub started: DateTime<Utc>, pub restarts: u64, pub running: bool, + pub crash: Crash, pub watch: Watch, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Crash { + pub crashed: bool, + pub value: u64, +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Watch { pub enabled: bool, pub path: String, pub hash: String, } #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Runner { pub id: id::Id, - pub process_list: BTreeMap<String, Process>, + pub list: BTreeMap<usize, Process>, } pub enum Status { Offline, Running, } impl Status { pub fn to_bool(&self) -> bool { match self { Status::Offline => false, Status::Running => true, } } } impl Runner { pub fn new() -> Self { let dump = dump::read(); - - let runner = Runner { - id: dump.id, - process_list: dump.process_list, - }; + let runner = Runner { id: dump.id, list: dump.list }; dump::write(&runner); return runner; } - pub fn start(&mut self, name: &String, command: &String, watch: &Option<String>) { + pub fn start(&mut self, name: &String, command: &String, watch: &Option<String>) -> &mut Self { + let id = self.id.next(); let config = config::read().runner; + let crash = Crash { crashed: false, value: 0 }; let watch = match watch { Some(watch) => Watch { enabled: true, path: string!(watch), hash: hash::create(file::cwd().join(watch)), }, - None => Watch { - enabled: false, - path: string!(""), - hash: string!(""), - }, + None => { + Watch { + enabled: false, + path: string!(""), + hash: string!(""), + } + } }; let pid = run(ProcessMetadata { args: config.args, name: name.clone(), shell: config.shell, command: command.clone(), log_path: config.log_path, }); - self.process_list.insert( - self.id.next().to_string(), + self.list.insert( + id, Process { + id, pid, watch, + crash, restarts: 0, running: true, path: file::cwd(), name: name.clone(), started: Utc::now(), script: command.clone(), env: env::vars().collect(), }, ); dump::write(&self); - } - pub fn stop(&mut self, id: usize) { - if let Some(item) = self.process_list.get_mut(&string!(id)) { - stop(item.pid); - self.set_status(id, Status::Offline); - dump::write(&self); - } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); - } + return self; } - pub fn restart(&mut self, id: usize, name: &Option<String>, watch: &Option<String>, dead: bool) { - if let Some(item) = self.info(id) { - let Process { path, script, .. } = item.clone(); - let restarts = ternary!(dead, item.restarts + 1, item.restarts); - - let watch = match watch { - Some(watch) => Watch { - enabled: true, - path: string!(watch), - hash: hash::create(path.join(watch)), - }, - None => Watch { - enabled: false, - path: string!(""), - hash: string!(""), - }, - }; + pub fn restart(&mut self, id: usize, name: String, dead: bool) -> &mut Self { + let item = self.get(id); + let Process { path, script, .. } = item.clone(); - let name = match name { - Some(name) => string!(name.trim()), - None => string!(item.name.clone()), - }; + if let Err(err) = std::env::set_current_dir(&item.path) { + crashln!("{} Failed to set working directory {:?}\nError: {:#?}", *helpers::FAIL, path, err); + }; - if let Err(err) = std::env::set_current_dir(&item.path) { - crashln!("{} Failed to set working directory {:?}\nError: {:#?}", *helpers::FAIL, path, err); - }; + item.stop(); - self.stop(id); + let config = config::read().runner; - let config = config::read().runner; - let pid = run(ProcessMetadata { - command: script, - args: config.args, - name: name.clone(), - shell: config.shell, - log_path: config.log_path, - }); + item.crash.crashed = false; + item.pid = run(ProcessMetadata { + command: script, + args: config.args, + name: name.clone(), + shell: config.shell, + log_path: config.log_path, + }); - self.process_list.insert(string!(id), Process { pid, name, watch, restarts, ..item }); - self.set_status(id, Status::Running); - self.set_started(id, Utc::now()); + item.watch = Watch { + enabled: false, + path: string!(""), + hash: string!(""), + }; - dump::write(&self); - } else { - crashln!("{} Failed to restart process ({})", *helpers::FAIL, id); - } + item.name = name; + item.running = true; + item.started = Utc::now(); + then!(dead, item.restarts += 1); + + // assign!(item, {name, pid, watch}); + + return self; } pub fn remove(&mut self, id: usize) { self.stop(id); - self.process_list.remove(&string!(id)); + self.list.remove(&id); dump::write(&self); } pub fn set_id(&mut self, id: id::Id) { self.id = id; self.id.next(); dump::write(&self); } pub fn set_status(&mut self, id: usize, status: Status) { - if let Some(item) = self.process_list.get_mut(&string!(id)) { - item.running = status.to_bool(); - dump::write(&self); - } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); - } + let item = self.get(id); + item.running = status.to_bool(); + dump::write(&self); } - pub fn set_started(&mut self, id: usize, time: DateTime<Utc>) { - if let Some(item) = self.process_list.get_mut(&string!(id)) { - item.started = time; - dump::write(&self); - } else { - crashln!("{} Process ({id}) not found", *helpers::FAIL); - } + pub fn save(&self) { dump::write(&self); } + pub fn count(&mut self) -> usize { self.list().count() } + pub fn is_empty(&self) -> bool { self.list.is_empty() } + pub fn items(&mut self) -> &mut BTreeMap<usize, Process> { &mut self.list } + pub fn list<'a>(&'a mut self) -> impl Iterator<Item = (&'a usize, &'a mut Process)> { self.list.iter_mut().map(|(k, v)| (k, v)) } + pub fn get(&mut self, id: usize) -> &mut Process { self.list.get_mut(&id).unwrap_or_else(|| crashln!("{} Process ({id}) not found", *helpers::FAIL)) } + + pub fn set_crashed(&mut self, id: usize) -> &mut Self { + let item = self.get(id); + item.crash.crashed = true; + return self; + } + + pub fn new_crash(&mut self, id: usize) -> &mut Self { + let item = self.get(id); + item.crash.value += 1; + return self; + } + + pub fn stop(&mut self, id: usize) -> &mut Self { + let item = self.get(id); + stop(item.pid); + item.running = false; + item.crash.crashed = false; + item.crash.value = 0; + return self; } - pub fn info(&self, id: usize) -> Option<Process> { self.process_list.get(&string!(id)).cloned() } - pub fn list(&self) -> &BTreeMap<String, Process> { &self.process_list } + pub fn rename(&mut self, id: usize, name: String) -> &mut Self { + let item = self.get(id); + item.name = name; + return self; + } + + pub fn watch(&mut self, id: usize, path: String) -> &mut Self { + let item = self.get(id); + item.watch = Watch { + enabled: true, + path: clone!(path), + hash: hash::create(item.path.join(path)), + }; + + return self; + } +} + +impl Process { + pub fn stop(&mut self) { Runner::new().stop(self.id).save(); } + pub fn watch(&mut self, path: String) { Runner::new().watch(self.id, path).save(); } + pub fn rename(&mut self, name: String) { Runner::new().rename(self.id, name).save(); } + + pub fn restart(&mut self) -> &mut Process { + Runner::new().restart(self.id, clone!(self.name), false).save(); + return self; + } + + pub fn crashed(&mut self) -> &mut Process { + Runner::new().new_crash(self.id).save(); + Runner::new().restart(self.id, clone!(self.name), true).save(); + return self; + } } pub mod hash; pub mod id;