From e14c165d1d41aaec0ec44544133047cefdcd6a61 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Tue, 29 Aug 2023 15:34:45 +0800 Subject: [PATCH 1/9] runc: support runc sandboxer Signed-off-by: Abel Feng --- runc/Cargo.lock | 2109 +++++++++++++++++++++++++++++++++++++++++++ runc/Cargo.toml | 24 + runc/src/common.rs | 226 +++++ runc/src/main.rs | 77 ++ runc/src/runc.rs | 692 ++++++++++++++ runc/src/sandbox.rs | 252 ++++++ 6 files changed, 3380 insertions(+) create mode 100644 runc/Cargo.lock create mode 100644 runc/Cargo.toml create mode 100644 runc/src/common.rs create mode 100644 runc/src/main.rs create mode 100644 runc/src/runc.rs create mode 100644 runc/src/sandbox.rs diff --git a/runc/Cargo.lock b/runc/Cargo.lock new file mode 100644 index 00000000..35332f1b --- /dev/null +++ b/runc/Cargo.lock @@ -0,0 +1,2109 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "axum" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acee9fd5073ab6b045a275b3e709c163dd36c90685219cb21804a147b58dba43" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes 1.4.0", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde", + "sync_wrapper", + "tokio", + "tower", + "tower-http", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e5939e02c56fecd5c017c37df4238c0a839fa76b7f97acdd7efb804fd181cc" +dependencies = [ + "async-trait", + "bytes 1.4.0", + "futures-util", + "http", + "http-body", + "mime", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgroups-rs" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3845d8ddaca63e9975f07b7a32262afe284561c2f0f620aa968913a65f671fd2" +dependencies = [ + "libc", + "log", + "nix 0.24.3", + "regex", +] + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "command-fds" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35cc819120a403d0eb3d0c404088c5a9f9b06066147ec69a21919fbc0833ef68" +dependencies = [ + "nix 0.22.3", + "thiserror", +] + +[[package]] +name = "containerd-sandbox" +version = "0.1.0" +source = "git+https://github.com/kuasar-io/rust-extensions.git?rev=6ae99540b754cd28c5389d5d6fdeff6ec7290ec5#6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "futures", + "go-flag", + "libc", + "log", + "nix 0.23.2", + "oci-spec", + "pin-project-lite", + "prost 0.10.4", + "prost-types 0.10.1", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "time", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", +] + +[[package]] +name = "containerd-shim" +version = "0.3.0" +source = "git+https://github.com/kuasar-io/rust-extensions.git?rev=6ae99540b754cd28c5389d5d6fdeff6ec7290ec5#6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" +dependencies = [ + "async-trait", + "cgroups-rs", + "command-fds", + "containerd-shim-protos", + "futures", + "go-flag", + "lazy_static", + "libc", + "log", + "nix 0.25.1", + "oci-spec", + "page_size", + "pin-project-lite", + "prctl", + "regex", + "serde", + "serde_derive", + "serde_json", + "signal-hook", + "signal-hook-tokio", + "thiserror", + "time", + "tokio", + "uuid", +] + +[[package]] +name = "containerd-shim-protos" +version = "0.2.0" +source = "git+https://github.com/kuasar-io/rust-extensions.git?rev=6ae99540b754cd28c5389d5d6fdeff6ec7290ec5#6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" +dependencies = [ + "async-trait", + "protobuf 3.2.0", + "ttrpc", + "ttrpc-codegen", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", +] + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07adf7be193b71cc36b193d0f5fe60b918a3a9db4dad0449f57bcfd519704a3" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f91d4cfa921f1c05904dc3c57b4a32c38aed3340cce209f3a6fd1478babafc4" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_builder_macro" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f0314b72bed045f3a68671b3c86328386762c93f82d98c65c3cb5e5f573dd68" +dependencies = [ + "derive_builder_core", + "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.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "go-flag" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4a40c9ca507513f573aabaf6a8558173a1ac9aa1363d8de30c7f89b34f8d2b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes 1.4.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[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.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes 1.4.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes 1.4.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes 1.4.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +dependencies = [ + "hyper", + "pin-project-lite", + "tokio", + "tokio-io-timeout", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[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.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "matchit" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73cbba799671b762df5a175adf59ce145165747bb891505c43d09aefbbf38beb" + +[[package]] +name = "memchr" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset", +] + +[[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 1.0.0", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset", + "pin-utils", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.0", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "object" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe" +dependencies = [ + "memchr", +] + +[[package]] +name = "oci-spec" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98135224dd4faeb24c05a2fac911ed53ea6b09ecb09d7cada1cb79963ab2ee34" +dependencies = [ + "derive_builder", + "getset", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "os_pipe" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "path-absolutize" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43eb3595c63a214e1b37b44f44b0a84900ef7ae0b4c5efce59e123d246d7a0de" +dependencies = [ + "path-dedot", +] + +[[package]] +name = "path-dedot" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d55e486337acb9973cdea3ec5638c1b3bcb22e573b2b7b41969e0c744d5a15e" +dependencies = [ + "once_cell", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset 0.2.0", + "indexmap 1.9.3", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset 0.4.2", + "indexmap 2.0.0", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prctl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059a34f111a9dee2ce1ac2826a68b24601c4298cfeb1a587c3cb493d5ab46f52" +dependencies = [ + "libc", + "nix 0.27.1", +] + +[[package]] +name = "prettyplease" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86" +dependencies = [ + "proc-macro2", + "syn 1.0.109", +] + +[[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.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes 1.4.0", + "prost-derive 0.8.0", +] + +[[package]] +name = "prost" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71adf41db68aa0daaefc69bb30bcd68ded9b9abaad5d1fbb6304c4fb390e083e" +dependencies = [ + "bytes 1.4.0", + "prost-derive 0.10.1", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes 1.4.0", + "heck 0.3.3", + "itertools", + "log", + "multimap", + "petgraph 0.5.1", + "prost 0.8.0", + "prost-types 0.8.0", + "tempfile", + "which", +] + +[[package]] +name = "prost-build" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae5a4388762d5815a9fc0dea33c56b021cdc8dde0c55e0c9ca57197254b0cab" +dependencies = [ + "bytes 1.4.0", + "cfg-if 1.0.0", + "cmake", + "heck 0.4.1", + "itertools", + "lazy_static", + "log", + "multimap", + "petgraph 0.6.4", + "prost 0.10.4", + "prost-types 0.10.1", + "regex", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b670f45da57fb8542ebdbb6105a925fe571b67f9e7ed9f47a06a84e72b4e7cc" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes 1.4.0", + "prost 0.8.0", +] + +[[package]] +name = "prost-types" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0a014229361011dc8e69c8a1ec6c2e8d0f2af7c91e3ea3f5b2170298461e68" +dependencies = [ + "bytes 1.4.0", + "prost 0.10.4", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[package]] +name = "protobuf" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e" +dependencies = [ + "once_cell", + "protobuf-support", + "thiserror", +] + +[[package]] +name = "protobuf-codegen" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "033460afb75cf755fcfc16dfaed20b86468082a2ea24e05ac35ab4a099a017d6" +dependencies = [ + "protobuf 2.28.0", +] + +[[package]] +name = "protobuf-codegen" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd418ac3c91caa4032d37cb80ff0d44e2ebe637b2fb243b6234bf89cdac4901" +dependencies = [ + "anyhow", + "once_cell", + "protobuf 3.2.0", + "protobuf-parse", + "regex", + "tempfile", + "thiserror", +] + +[[package]] +name = "protobuf-parse" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49" +dependencies = [ + "anyhow", + "indexmap 1.9.3", + "log", + "protobuf 3.2.0", + "protobuf-support", + "tempfile", + "thiserror", + "which", +] + +[[package]] +name = "protobuf-support" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372" +dependencies = [ + "thiserror", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "runc" +version = "0.2.0" +source = "git+https://github.com/kuasar-io/rust-extensions.git?rev=6ae99540b754cd28c5389d5d6fdeff6ec7290ec5#6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" +dependencies = [ + "async-trait", + "futures", + "libc", + "log", + "nix 0.25.1", + "oci-spec", + "os_pipe", + "path-absolutize", + "rand", + "serde", + "serde_json", + "tempfile", + "thiserror", + "time", + "tokio", + "tokio-pipe", + "uuid", +] + +[[package]] +name = "runc-sandboxer" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "containerd-sandbox", + "containerd-shim", + "env_logger", + "futures", + "log", + "nix 0.25.1", + "oci-spec", + "runc", + "serde_json", + "signal-hook-tokio", + "time", + "tokio", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964" +dependencies = [ + "bitflags 2.4.0", + "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 = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signal-hook-tokio" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" +dependencies = [ + "futures-core", + "libc", + "signal-hook", + "tokio", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +dependencies = [ + "libc", + "windows-sys", +] + +[[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.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "time" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +dependencies = [ + "deranged", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +dependencies = [ + "time-core", +] + +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", + "bytes 1.4.0", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.3", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-io-timeout" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" +dependencies = [ + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "tokio-pipe" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f213a84bffbd61b8fa0ba8a044b4bbe35d471d0b518867181e82bd5c15542784" +dependencies = [ + "libc", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +dependencies = [ + "bytes 1.4.0", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tokio-vsock" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b33556828911d16e24d8b5d336446b0bf6b4b9bfda52cbdc2fa35b7a2862ebc" +dependencies = [ + "bytes 0.4.12", + "futures", + "libc", + "tokio", + "vsock", +] + +[[package]] +name = "tonic" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9d60db39854b30b835107500cf0aca0b0d14d6e1c3de124217c23a29c2ddb" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes 1.4.0", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-timeout", + "percent-encoding", + "pin-project", + "prost 0.10.4", + "prost-derive 0.10.1", + "tokio", + "tokio-stream", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "tracing-futures", +] + +[[package]] +name = "tonic-build" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9263bf4c9bfaae7317c1c2faf7f18491d2fe476f70c414b73bf5d445b00ffa1" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build 0.10.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858" +dependencies = [ + "bitflags 1.3.2", + "bytes 1.4.0", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if 1.0.0", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "ttrpc" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35f22a2964bea14afee161665bb260b83cb48e665e0260ca06ec0e775c8b06c" +dependencies = [ + "async-trait", + "byteorder", + "futures", + "libc", + "log", + "nix 0.23.2", + "protobuf 3.2.0", + "protobuf-codegen 3.2.0", + "thiserror", + "tokio", + "tokio-vsock", +] + +[[package]] +name = "ttrpc-codegen" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94d7f7631d7a9ebed715a47cd4cb6072cbc7ae1d4ec01598971bbec0024340c2" +dependencies = [ + "protobuf 2.28.0", + "protobuf-codegen 3.2.0", + "protobuf-support", + "ttrpc-compiler", +] + +[[package]] +name = "ttrpc-compiler" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3cb5dbf1f0865a34fe3f722290fe776cacb16f50428610b779467b76ddf647" +dependencies = [ + "derive-new", + "prost 0.8.0", + "prost-build 0.8.0", + "prost-types 0.8.0", + "protobuf 2.28.0", + "protobuf-codegen 2.28.0", + "tempfile", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsock" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32675ee2b3ce5df274c0ab52d19b28789632406277ca26bffee79a8e27dc133" +dependencies = [ + "libc", + "nix 0.23.2", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +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-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" diff --git a/runc/Cargo.toml b/runc/Cargo.toml new file mode 100644 index 00000000..87c1c990 --- /dev/null +++ b/runc/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "runc-sandboxer" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +env_logger = "0.9.0" +anyhow = { version = "=1.0.66", default-features = false, features = ["std"] } +tokio = "1.13.0" +signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } +async-trait = "0.1.51" +nix = "0.25" +futures = { version = "0.3.21" } +log = { version = "0.4.17", features = ["std"] } +oci-spec = "0.5.4" +time = "0.3.5" +serde_json = "1.0.74" + +containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" } +containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } +runc = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } + diff --git a/runc/src/common.rs b/runc/src/common.rs new file mode 100644 index 00000000..936e9adc --- /dev/null +++ b/runc/src/common.rs @@ -0,0 +1,226 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use std::{io::IoSliceMut, ops::Deref, os::unix::io::RawFd, path::Path, sync::Arc}; + +use containerd_shim::{ + api::{ExecProcessRequest, Options}, + io::Stdio, + io_error, other, other_error, + util::IntoOption, + Error, +}; +use log::{debug, warn}; +use nix::{ + cmsg_space, + sys::{ + socket::{recvmsg, ControlMessageOwned, MsgFlags, UnixAddr}, + termios::tcgetattr, + }, +}; +use oci_spec::runtime::{LinuxNamespaceType, Spec}; +use runc::{ + io::{Io, NullIo, FIFO}, + options::GlobalOpts, + Runc, Spawner, +}; + +pub const GROUP_LABELS: [&str; 2] = [ + "io.containerd.runc.v2.group", + "io.kubernetes.cri.sandbox-id", +]; +pub const INIT_PID_FILE: &str = "init.pid"; + +pub struct ProcessIO { + pub uri: Option, + pub io: Option>, + pub copy: bool, +} + +pub fn create_io( + id: &str, + _io_uid: u32, + _io_gid: u32, + stdio: &Stdio, +) -> containerd_shim::Result { + if stdio.is_null() { + let nio = NullIo::new().map_err(io_error!(e, "new Null Io"))?; + let pio = ProcessIO { + uri: None, + io: Some(Arc::new(nio)), + copy: false, + }; + return Ok(pio); + } + let stdout = stdio.stdout.as_str(); + let scheme_path = stdout.trim().split("://").collect::>(); + let scheme: &str; + let uri: String; + if scheme_path.len() <= 1 { + // no scheme specified + // default schema to fifo + uri = format!("fifo://{}", stdout); + scheme = "fifo" + } else { + uri = stdout.to_string(); + scheme = scheme_path[0]; + } + + let mut pio = ProcessIO { + uri: Some(uri), + io: None, + copy: false, + }; + + if scheme == "fifo" { + debug!( + "create named pipe io for container {}, stdin: {}, stdout: {}, stderr: {}", + id, + stdio.stdin.as_str(), + stdio.stdout.as_str(), + stdio.stderr.as_str() + ); + let io = FIFO { + stdin: stdio.stdin.to_string().none_if(|x| x.is_empty()), + stdout: stdio.stdout.to_string().none_if(|x| x.is_empty()), + stderr: stdio.stderr.to_string().none_if(|x| x.is_empty()), + }; + pio.io = Some(Arc::new(io)); + pio.copy = false; + } + Ok(pio) +} + +#[derive(Default, Debug)] +pub struct ShimExecutor {} + +pub fn get_spec_from_request( + req: &ExecProcessRequest, +) -> containerd_shim::Result { + if let Some(val) = req.spec.as_ref() { + let mut p = serde_json::from_slice::(val.value.as_slice())?; + p.set_terminal(Some(req.terminal)); + Ok(p) + } else { + Err(Error::InvalidArgument("no spec in request".to_string())) + } +} + +pub fn check_kill_error(emsg: String) -> Error { + let emsg = emsg.to_lowercase(); + if emsg.contains("process already finished") + || emsg.contains("container not running") + || emsg.contains("no such process") + { + Error::NotFoundError("process already finished".to_string()) + } else if emsg.contains("does not exist") { + Error::NotFoundError("no such container".to_string()) + } else { + other!("unknown error after kill {}", emsg) + } +} + +const DEFAULT_RUNC_ROOT: &str = "/run/containerd/runc"; +const DEFAULT_COMMAND: &str = "runc"; + +pub fn create_runc( + runtime: &str, + namespace: &str, + bundle: impl AsRef, + opts: &Options, + spawner: Option>, +) -> containerd_shim::Result { + let runtime = if runtime.is_empty() { + DEFAULT_COMMAND + } else { + runtime + }; + let root = opts.root.as_str(); + let root = Path::new(if root.is_empty() { + DEFAULT_RUNC_ROOT + } else { + root + }) + .join(namespace); + + let log = bundle.as_ref().join("log.json"); + let mut gopts = GlobalOpts::default() + .command(runtime) + .root(root) + .log(log) + .log_json() + .systemd_cgroup(opts.systemd_cgroup); + if let Some(s) = spawner { + gopts.custom_spawner(s); + } + gopts + .build() + .map_err(other_error!(e, "unable to create runc instance")) +} + +#[derive(Default)] +pub(crate) struct CreateConfig {} + +pub fn receive_socket(stream_fd: RawFd) -> containerd_shim::Result { + let mut buf = [0u8; 4096]; + let mut iovec = [IoSliceMut::new(&mut buf)]; + let mut space = cmsg_space!([RawFd; 2]); + let (path, fds) = + match recvmsg::(stream_fd, &mut iovec, Some(&mut space), MsgFlags::empty()) { + Ok(msg) => { + let mut iter = msg.cmsgs(); + if let Some(ControlMessageOwned::ScmRights(fds)) = iter.next() { + (iovec[0].deref(), fds) + } else { + return Err(other!("received message is empty")); + } + } + Err(e) => { + return Err(other!("failed to receive message: {}", e)); + } + }; + if fds.is_empty() { + return Err(other!("received message is empty")); + } + let path = String::from_utf8(Vec::from(path)).unwrap_or_else(|e| { + warn!("failed to get path from array {}", e); + "".to_string() + }); + let path = path.trim_matches(char::from(0)); + debug!( + "copy_console: console socket get path: {}, fd: {}", + path, &fds[0] + ); + tcgetattr(fds[0])?; + Ok(fds[0]) +} + +pub fn has_shared_pid_namespace(spec: &Spec) -> bool { + match spec.linux() { + None => true, + Some(linux) => match linux.namespaces() { + None => true, + Some(namespaces) => { + for ns in namespaces { + if ns.typ() == LinuxNamespaceType::Pid && ns.path().is_none() { + return false; + } + } + true + } + }, + } +} diff --git a/runc/src/main.rs b/runc/src/main.rs new file mode 100644 index 00000000..d8d08543 --- /dev/null +++ b/runc/src/main.rs @@ -0,0 +1,77 @@ +use containerd_shim::asynchronous::monitor::monitor_notify_by_pid; +use futures::StreamExt; +use log::{debug, error, warn}; +use nix::{ + errno::Errno, + libc, + sys::{ + wait, + wait::{WaitPidFlag, WaitStatus}, + }, + unistd::Pid, +}; +use signal_hook_tokio::Signals; +use crate::sandbox::RuncSandboxer; + +mod sandbox; +mod runc; +mod common; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + env_logger::builder().format_timestamp_micros().init(); + tokio::spawn(async move { + let signals = Signals::new([libc::SIGTERM, libc::SIGINT, libc::SIGPIPE, libc::SIGCHLD]) + .expect("new signal failed"); + handle_signals(signals).await; + }); + + let sandboxer = RuncSandboxer::default(); + containerd_sandbox::run("runc-sandboxer", sandboxer) + .await + .unwrap(); + Ok(()) +} + +async fn handle_signals(signals: Signals) { + let mut signals = signals.fuse(); + while let Some(sig) = signals.next().await { + match sig { + libc::SIGTERM | libc::SIGINT => { + debug!("received {}", sig); + } + libc::SIGCHLD => loop { + // Note: see comment at the counterpart in synchronous/mod.rs for details. + match wait::waitpid(Some(Pid::from_raw(-1)), Some(WaitPidFlag::WNOHANG)) { + Ok(WaitStatus::Exited(pid, status)) => { + debug!("child {} exit ({})", pid, status); + monitor_notify_by_pid(pid.as_raw(), status) + .await + .unwrap_or_else(|e| error!("failed to send exit event {}", e)) + } + Ok(WaitStatus::Signaled(pid, sig, _)) => { + debug!("child {} terminated({})", pid, sig); + let exit_code = 128 + sig as i32; + monitor_notify_by_pid(pid.as_raw(), exit_code) + .await + .unwrap_or_else(|e| error!("failed to send signal event {}", e)) + } + Err(Errno::ECHILD) => { + break; + } + Err(e) => { + warn!("error occurred in signal handler: {}", e); + } + _ => {} + } + }, + _ => { + if let Ok(sig) = nix::sys::signal::Signal::try_from(sig) { + debug!("received {}", sig); + } else { + warn!("received invalid signal {}", sig); + } + } + } + } +} diff --git a/runc/src/runc.rs b/runc/src/runc.rs new file mode 100644 index 00000000..b95cbd3c --- /dev/null +++ b/runc/src/runc.rs @@ -0,0 +1,692 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +use std::{ + convert::TryFrom, + os::unix::{ + io::{AsRawFd, FromRawFd, RawFd}, + prelude::ExitStatusExt, + }, + path::{Path, PathBuf}, + process::ExitStatus, + sync::Arc, +}; + +use async_trait::async_trait; +use containerd_shim::{ + api::{CreateTaskRequest, ExecProcessRequest, Options, Status}, + asynchronous::{ + console::ConsoleSocket, + container::{ContainerFactory, ContainerTemplate, ProcessFactory}, + monitor::{monitor_subscribe, monitor_unsubscribe, Subscription}, + processes::{ProcessLifecycle, ProcessTemplate}, + }, + Console, + Error, + ExitSignal, + io::Stdio, io_error, + monitor::{ExitEvent, Subject, Topic}, + other, + other_error, protos::{ + api::ProcessInfo, + cgroups::metrics::Metrics, + protobuf::{CodedInputStream, Message}, + }, Result, util::{asyncify, mkdir, mount_rootfs, read_file_to_str, write_options, write_runtime}, +}; +use log::{debug, error}; +use nix::{sys::signal::kill, unistd::Pid}; +use oci_spec::runtime::{LinuxResources, Process}; +use runc::{Command, Runc, Spawner}; +use tokio::{ + fs::{File, OpenOptions}, + io::{AsyncRead, AsyncReadExt, AsyncWrite}, +}; + +use crate::common::{ + check_kill_error, create_io, create_runc, CreateConfig, get_spec_from_request, INIT_PID_FILE, + ProcessIO, receive_socket, ShimExecutor, +}; + +pub type ExecProcess = ProcessTemplate; +pub type InitProcess = ProcessTemplate; + +pub type RuncContainer = ContainerTemplate; + +#[derive(Clone, Default)] +pub(crate) struct RuncFactory { + netns: String, +} + +impl RuncFactory { + pub fn new(netns: &str) -> Self { + Self { + netns: netns.to_string() + } + } +} + +#[async_trait] +impl ContainerFactory for RuncFactory { + async fn create( + &self, + ns: &str, + req: &CreateTaskRequest, + ) -> containerd_shim::Result { + let bundle = req.bundle(); + let mut opts = Options::new(); + if let Some(any) = req.options.as_ref() { + let mut input = CodedInputStream::from_bytes(any.value.as_ref()); + opts.merge_from(&mut input)?; + } + if opts.compute_size() > 0 { + debug!("create options: {:?}", &opts); + } + let runtime = opts.binary_name.as_str(); + write_options(bundle, &opts).await?; + write_runtime(bundle, runtime).await?; + + let rootfs_vec = req.rootfs().to_vec(); + let rootfs = if !rootfs_vec.is_empty() { + let tmp_rootfs = Path::new(bundle).join("rootfs"); + mkdir(&tmp_rootfs, 0o711).await?; + tmp_rootfs + } else { + PathBuf::new() + }; + + for m in rootfs_vec { + mount_rootfs(&m, rootfs.as_path()).await? + } + + let runc = create_runc( + runtime, + ns, + bundle, + &opts, + Some(Arc::new(ShimExecutor::default())), + )?; + + let id = req.id(); + let stdio = Stdio::new(req.stdin(), req.stdout(), req.stderr(), req.terminal()); + + let mut init = InitProcess::new( + id, + stdio, + RuncInitLifecycle::new(runc.clone(), opts.clone(), bundle), + ); + + let config = CreateConfig::default(); + self.do_create(&mut init, config).await?; + let container = RuncContainer { + id: id.to_string(), + bundle: bundle.to_string(), + init, + process_factory: RuncExecFactory { + runtime: runc, + bundle: bundle.to_string(), + io_uid: opts.io_uid, + io_gid: opts.io_gid, + }, + processes: Default::default(), + }; + Ok(container) + } + + async fn cleanup(&self, _ns: &str, _c: &RuncContainer) -> containerd_shim::Result<()> { + Ok(()) + } +} + +impl RuncFactory { + async fn do_create(&self, init: &mut InitProcess, _config: CreateConfig) -> Result<()> { + let id = init.id.to_string(); + let stdio = &init.stdio; + let opts = &init.lifecycle.opts; + let bundle = &init.lifecycle.bundle; + let pid_path = Path::new(bundle).join(INIT_PID_FILE); + let mut create_opts = runc::options::CreateOpts::new() + .pid_file(&pid_path) + .no_pivot(opts.no_pivot_root) + .no_new_keyring(opts.no_new_keyring) + .detach(false); + let (socket, pio) = if stdio.terminal { + let s = ConsoleSocket::new().await?; + create_opts.console_socket = Some(s.path.to_owned()); + (Some(s), None) + } else { + let pio = create_io(&id, opts.io_uid, opts.io_gid, stdio)?; + create_opts.io = pio.io.as_ref().cloned(); + (None, Some(pio)) + }; + + let resp = init + .lifecycle + .runtime + .create(&id, bundle, Some(&create_opts)) + .await; + if let Err(e) = resp { + if let Some(s) = socket { + s.clean().await; + } + return Err(other!("failed to create runc container: {}", e)); + } + copy_io_or_console(init, socket, pio, init.lifecycle.exit_signal.clone()).await?; + let pid = read_file_to_str(pid_path).await?.parse::()?; + init.pid = pid; + Ok(()) + } +} + +pub struct RuncExecFactory { + runtime: Runc, + bundle: String, + io_uid: u32, + io_gid: u32, +} + +#[async_trait] +impl ProcessFactory for RuncExecFactory { + async fn create(&self, req: &ExecProcessRequest) -> Result { + let p = get_spec_from_request(req)?; + Ok(ExecProcess { + state: Status::CREATED, + id: req.exec_id.to_string(), + stdio: Stdio { + stdin: req.stdin.to_string(), + stdout: req.stdout.to_string(), + stderr: req.stderr.to_string(), + terminal: req.terminal, + }, + pid: 0, + exit_code: 0, + exited_at: None, + wait_chan_tx: vec![], + console: None, + lifecycle: Arc::from(RuncExecLifecycle { + runtime: self.runtime.clone(), + bundle: self.bundle.to_string(), + container_id: req.id.to_string(), + io_uid: self.io_uid, + io_gid: self.io_gid, + spec: p, + exit_signal: Default::default(), + }), + }) + } +} + +pub struct RuncInitLifecycle { + runtime: Runc, + opts: Options, + bundle: String, + exit_signal: Arc, +} + +#[async_trait] +impl ProcessLifecycle for RuncInitLifecycle { + async fn start(&self, p: &mut InitProcess) -> containerd_shim::Result<()> { + self.runtime + .start(p.id.as_str()) + .await + .map_err(other_error!(e, "failed start"))?; + p.state = Status::RUNNING; + Ok(()) + } + + async fn kill( + &self, + p: &mut InitProcess, + signal: u32, + all: bool, + ) -> containerd_shim::Result<()> { + self.runtime + .kill( + p.id.as_str(), + signal, + Some(&runc::options::KillOpts { all }), + ) + .await + .map_err(|e| check_kill_error(e.to_string())) + } + + async fn delete(&self, p: &mut InitProcess) -> containerd_shim::Result<()> { + self.runtime + .delete( + p.id.as_str(), + Some(&runc::options::DeleteOpts { force: true }), + ) + .await + .or_else(|e| { + if !e.to_string().to_lowercase().contains("does not exist") { + Err(e) + } else { + Ok(()) + } + }) + .map_err(other_error!(e, "failed delete"))?; + self.exit_signal.signal(); + Ok(()) + } + + #[cfg(target_os = "linux")] + async fn update(&self, p: &mut InitProcess, resources: &LinuxResources) -> Result<()> { + if p.pid <= 0 { + return Err(other!( + "failed to update resources because init process is {}", + p.pid + )); + } + containerd_shim::cgroup::update_resources(p.pid as u32, resources) + } + + #[cfg(not(target_os = "linux"))] + async fn update(&self, _p: &mut InitProcess, _resources: &LinuxResources) -> Result<()> { + Err(Error::Unimplemented("update resource".to_string())) + } + + #[cfg(target_os = "linux")] + async fn stats(&self, p: &InitProcess) -> Result { + if p.pid <= 0 { + return Err(other!( + "failed to collect metrics because init process is {}", + p.pid + )); + } + containerd_shim::cgroup::collect_metrics(p.pid as u32) + } + + #[cfg(not(target_os = "linux"))] + async fn stats(&self, _p: &InitProcess) -> Result { + Err(Error::Unimplemented("process stats".to_string())) + } + + async fn ps(&self, p: &InitProcess) -> Result> { + let pids = self + .runtime + .ps(&p.id) + .await + .map_err(other_error!(e, "failed to execute runc ps"))?; + Ok(pids + .iter() + .map(|&x| ProcessInfo { + pid: x as u32, + ..Default::default() + }) + .collect()) + } +} + +impl RuncInitLifecycle { + pub fn new(runtime: Runc, opts: Options, bundle: &str) -> Self { + let work_dir = Path::new(bundle).join("work"); + let mut opts = opts; + if opts.criu_path().is_empty() { + opts.criu_path = work_dir.to_string_lossy().to_string(); + } + Self { + runtime, + opts, + bundle: bundle.to_string(), + exit_signal: Default::default(), + } + } +} + +pub struct RuncExecLifecycle { + runtime: Runc, + bundle: String, + container_id: String, + io_uid: u32, + io_gid: u32, + spec: Process, + exit_signal: Arc, +} + +#[async_trait] +impl ProcessLifecycle for RuncExecLifecycle { + async fn start(&self, p: &mut ExecProcess) -> containerd_shim::Result<()> { + let pid_path = Path::new(self.bundle.as_str()).join(format!("{}.pid", &p.id)); + let mut exec_opts = runc::options::ExecOpts { + io: None, + pid_file: Some(pid_path.to_owned()), + console_socket: None, + detach: true, + }; + let (socket, pio) = if p.stdio.terminal { + let s = ConsoleSocket::new().await?; + exec_opts.console_socket = Some(s.path.to_owned()); + (Some(s), None) + } else { + let pio = create_io(&p.id, self.io_uid, self.io_gid, &p.stdio)?; + exec_opts.io = pio.io.as_ref().cloned(); + (None, Some(pio)) + }; + //TODO checkpoint support + let exec_result = self + .runtime + .exec(&self.container_id, &self.spec, Some(&exec_opts)) + .await; + if let Err(e) = exec_result { + if let Some(s) = socket { + s.clean().await; + } + return Err(other!("failed to start runc exec: {}", e)); + } + copy_io_or_console(p, socket, pio, p.lifecycle.exit_signal.clone()).await?; + let pid = read_file_to_str(pid_path).await?.parse::()?; + p.pid = pid; + p.state = Status::RUNNING; + Ok(()) + } + + async fn kill( + &self, + p: &mut ExecProcess, + signal: u32, + _all: bool, + ) -> containerd_shim::Result<()> { + if p.pid <= 0 { + Err(Error::FailedPreconditionError( + "process not created".to_string(), + )) + } else if p.exited_at.is_some() { + Err(Error::NotFoundError("process already finished".to_string())) + } else { + // TODO this is kill from nix crate, it is os specific, maybe have annotated with target os + kill( + Pid::from_raw(p.pid as i32), + nix::sys::signal::Signal::try_from(signal as i32).unwrap(), + ) + .map_err(Into::into) + } + } + + async fn delete(&self, _p: &mut ExecProcess) -> containerd_shim::Result<()> { + self.exit_signal.signal(); + Ok(()) + } + + async fn update(&self, _p: &mut ExecProcess, _resources: &LinuxResources) -> Result<()> { + Err(Error::Unimplemented("exec update".to_string())) + } + + async fn stats(&self, _p: &ExecProcess) -> Result { + Err(Error::Unimplemented("exec stats".to_string())) + } + + async fn ps(&self, _p: &ExecProcess) -> Result> { + Err(Error::Unimplemented("exec ps".to_string())) + } +} + +async fn copy_console( + console_socket: &ConsoleSocket, + stdio: &Stdio, + exit_signal: Arc, +) -> Result { + debug!("copy_console: waiting for runtime to send console fd"); + let stream = console_socket.accept().await?; + let fd = asyncify(move || -> Result { receive_socket(stream.as_raw_fd()) }).await?; + let f = unsafe { File::from_raw_fd(fd) }; + if !stdio.stdin.is_empty() { + debug!("copy_console: pipe stdin to console"); + let console_stdin = f + .try_clone() + .await + .map_err(io_error!(e, "failed to clone console file"))?; + let stdin_fut = async { + OpenOptions::new() + .read(true) + .open(stdio.stdin.as_str()) + .await + }; + let stdin_w_fut = async { + OpenOptions::new() + .write(true) + .open(stdio.stdin.as_str()) + .await + }; + let (stdin, stdin_w) = + tokio::try_join!(stdin_fut, stdin_w_fut).map_err(io_error!(e, "open stdin"))?; + spawn_copy( + stdin, + console_stdin, + exit_signal.clone(), + Some(move || { + drop(stdin_w); + }), + ); + } + + if !stdio.stdout.is_empty() { + let console_stdout = f + .try_clone() + .await + .map_err(io_error!(e, "failed to clone console file"))?; + debug!("copy_console: pipe stdout from console"); + let stdout = OpenOptions::new() + .write(true) + .open(stdio.stdout.as_str()) + .await + .map_err(io_error!(e, "open stdout"))?; + // open a read to make sure even if the read end of containerd shutdown, + // copy still continue until the restart of containerd succeed + let stdout_r = OpenOptions::new() + .read(true) + .open(stdio.stdout.as_str()) + .await + .map_err(io_error!(e, "open stdout for read"))?; + spawn_copy( + console_stdout, + stdout, + exit_signal, + Some(move || { + drop(stdout_r); + }), + ); + } + let console = Console { + file: f.into_std().await, + }; + Ok(console) +} + +pub async fn copy_io(pio: &ProcessIO, stdio: &Stdio, exit_signal: Arc) -> Result<()> { + if !pio.copy { + return Ok(()); + }; + if let Some(io) = &pio.io { + if let Some(w) = io.stdin() { + debug!("copy_io: pipe stdin from {}", stdio.stdin.as_str()); + if !stdio.stdin.is_empty() { + let stdin = OpenOptions::new() + .read(true) + .open(stdio.stdin.as_str()) + .await + .map_err(io_error!(e, "open stdin"))?; + spawn_copy(stdin, w, exit_signal.clone(), None::); + } + } + + if let Some(r) = io.stdout() { + debug!("copy_io: pipe stdout from to {}", stdio.stdout.as_str()); + if !stdio.stdout.is_empty() { + let stdout = OpenOptions::new() + .write(true) + .open(stdio.stdout.as_str()) + .await + .map_err(io_error!(e, "open stdout"))?; + // open a read to make sure even if the read end of containerd shutdown, + // copy still continue until the restart of containerd succeed + let stdout_r = OpenOptions::new() + .read(true) + .open(stdio.stdout.as_str()) + .await + .map_err(io_error!(e, "open stdout for read"))?; + spawn_copy( + r, + stdout, + exit_signal.clone(), + Some(move || { + drop(stdout_r); + }), + ); + } + } + + if let Some(r) = io.stderr() { + if !stdio.stderr.is_empty() { + debug!("copy_io: pipe stderr from to {}", stdio.stderr.as_str()); + let stderr = OpenOptions::new() + .write(true) + .open(stdio.stderr.as_str()) + .await + .map_err(io_error!(e, "open stderr"))?; + // open a read to make sure even if the read end of containerd shutdown, + // copy still continue until the restart of containerd succeed + let stderr_r = OpenOptions::new() + .read(true) + .open(stdio.stderr.as_str()) + .await + .map_err(io_error!(e, "open stderr for read"))?; + spawn_copy( + r, + stderr, + exit_signal, + Some(move || { + drop(stderr_r); + }), + ); + } + } + } + + Ok(()) +} + +fn spawn_copy(from: R, to: W, exit_signal: Arc, on_close: Option) + where + R: AsyncRead + Send + Unpin + 'static, + W: AsyncWrite + Send + Unpin + 'static, + F: FnOnce() + Send + 'static, +{ + let mut src = from; + let mut dst = to; + tokio::spawn(async move { + tokio::select! { + _ = exit_signal.wait() => { + debug!("container exit, copy task should exit too"); + }, + res = tokio::io::copy(&mut src, &mut dst) => { + if let Err(e) = res { + error!("copy io failed {}", e); + } + } + } + if let Some(f) = on_close { + f(); + } + }); +} + +async fn copy_io_or_console

( + p: &mut ProcessTemplate

, + socket: Option, + pio: Option, + exit_signal: Arc, +) -> Result<()> { + if p.stdio.terminal { + if let Some(console_socket) = socket { + let console_result = copy_console(&console_socket, &p.stdio, exit_signal).await; + console_socket.clean().await; + match console_result { + Ok(c) => { + p.console = Some(c); + } + Err(e) => { + return Err(e); + } + } + } + } else if let Some(pio) = pio { + copy_io(&pio, &p.stdio, exit_signal).await?; + } + Ok(()) +} + +#[async_trait] +impl Spawner for ShimExecutor { + async fn execute(&self, cmd: Command, after_start: Box, wait_output: bool) -> runc::Result<(ExitStatus, u32, String, String)> { + let mut cmd = cmd; + let subscription = monitor_subscribe(Topic::Pid) + .await + .map_err(|e| runc::error::Error::Other(Box::new(e)))?; + let sid = subscription.id; + let child = match cmd.spawn() { + Ok(c) => c, + Err(e) => { + monitor_unsubscribe(sid).await.unwrap_or_default(); + return Err(runc::error::Error::ProcessSpawnFailed(e)); + } + }; + after_start(); + let pid = child.id().unwrap(); + let (stdout, stderr, exit_code) = if wait_output { + tokio::join!( + read_std(child.stdout), + read_std(child.stderr), + wait_pid(pid as i32, subscription) + ) + } else { + ("".to_string(), "".to_string(), wait_pid(pid as i32, subscription).await) + }; + let status = ExitStatus::from_raw(exit_code); + monitor_unsubscribe(sid).await.unwrap_or_default(); + Ok((status, pid, stdout, stderr)) + } +} + +async fn read_std(std: Option) -> String + where + T: AsyncRead + Unpin, +{ + let mut std = std; + if let Some(mut std) = std.take() { + let mut out = String::new(); + std.read_to_string(&mut out).await.unwrap_or_else(|e| { + error!("failed to read stdout {}", e); + 0 + }); + return out; + } + "".to_string() +} + +async fn wait_pid(pid: i32, s: Subscription) -> i32 { + let mut s = s; + loop { + if let Some(ExitEvent { + subject: Subject::Pid(epid), + exit_code: code, + }) = s.rx.recv().await + { + if pid == epid { + monitor_unsubscribe(s.id).await.unwrap_or_default(); + return code; + } + } + } +} diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs new file mode 100644 index 00000000..556de326 --- /dev/null +++ b/runc/src/sandbox.rs @@ -0,0 +1,252 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use anyhow::anyhow; +use async_trait::async_trait; +use containerd_sandbox::{Container, ContainerOption, Sandbox, Sandboxer, SandboxOption, SandboxStatus}; +use containerd_sandbox::data::{ContainerData, SandboxData}; +use containerd_sandbox::error::{Error, Result}; +use containerd_sandbox::signal::ExitSignal; +use containerd_shim::asynchronous::monitor::{monitor_subscribe, monitor_unsubscribe}; +use containerd_shim::asynchronous::task::TaskService; +use containerd_shim::monitor::{Subject, Topic}; +use containerd_shim::processes::Process; +use containerd_shim::protos::shim_async::create_task; +use containerd_shim::protos::ttrpc::asynchronous::Server; +use log::debug; +use tokio::fs::create_dir_all; +use tokio::sync::{Mutex, RwLock}; +use tokio::sync::mpsc::channel; + +use crate::runc::{RuncContainer, RuncFactory}; + +#[derive(Default)] +pub struct RuncSandboxer { + #[allow(clippy::type_complexity)] + pub(crate) sandboxes: Arc>>>>, +} + +pub struct RuncSandbox { + pub(crate) id: String, + pub(crate) base_dir: String, + pub(crate) data: SandboxData, + pub(crate) status: SandboxStatus, + pub(crate) exit_signal: Arc, + pub(crate) containers: HashMap, + pub(crate) server: Option, +} + +pub struct RuncContainerData { + data: ContainerData, +} + +impl Container for RuncContainerData { + fn get_data(&self) -> Result { + Ok(self.data.clone()) + } +} + +#[async_trait] +impl Sandboxer for RuncSandboxer { + type Sandbox = RuncSandbox; + + async fn create(&self, id: &str, s: SandboxOption) -> Result<()> { + let sandbox = RuncSandbox { + id: id.to_string(), + base_dir: s.base_dir, + data: s.sandbox, + status: SandboxStatus::Created, + exit_signal: Arc::new(Default::default()), + containers: Default::default(), + server: None, + }; + create_dir_all(&sandbox.base_dir) + .await + .map_err(|e| anyhow!("failed to create {}, {}", sandbox.base_dir, e))?; + let mut sandboxes = self.sandboxes.write().await; + sandboxes.insert(id.to_string(), Arc::new(Mutex::new(sandbox))); + Ok(()) + } + + async fn start(&self, id: &str) -> Result<()> { + let sandbox = self.sandbox(id).await?; + sandbox.lock().await.start().await?; + Ok(()) + } + + async fn sandbox(&self, id: &str) -> Result>> { + return Ok(self + .sandboxes + .read() + .await + .get(id) + .ok_or_else(|| Error::NotFound(id.to_string()))? + .clone()); + } + + async fn stop(&self, id: &str, _force: bool) -> Result<()> { + let sandbox = self.sandbox(id).await?; + sandbox.lock().await.stop().await?; + Ok(()) + } + + async fn delete(&self, id: &str) -> Result<()> { + if let Some(sandbox) = self.sandboxes.write().await.remove(id) { + let mut sandbox = sandbox.lock().await; + if let Some(mut server) = sandbox.server.take() { + server + .shutdown() + .await + .map_err(|e| anyhow!("failed to shutdown task server, {}", e))?; + } + } + Ok(()) + } +} + +impl RuncSandbox { + async fn stop(&mut self) -> Result<()> { + if let Some(mut server) = self.server.take() { + server + .shutdown() + .await + .map_err(|e| anyhow!("failed to shutdown task server, {}", e))?; + } + let ts = time::OffsetDateTime::now_utc().unix_timestamp_nanos(); + self.status = SandboxStatus::Stopped(0, ts); + self.exit_signal.signal(); + Ok(()) + } + + async fn start(&mut self) -> Result<()> { + let task = self.start_task_service().await?; + let task_address = format!("unix://{}/task.sock", self.base_dir); + self.data.task_address = task_address.clone(); + let task_service = create_task(Arc::new(Box::new(task))); + let mut server = Server::new().register_service(task_service); + server = server + .bind(&task_address) + .map_err(|e| anyhow!("failed to bind socket {}, {}", task_address, e))?; + server + .start() + .await + .map_err(|e| anyhow!("failed to start task server, {}", e))?; + self.server = Some(server); + self.status = SandboxStatus::Running(0); + Ok(()) + } + + async fn start_task_service( + &self, + ) -> Result> { + let (tx, mut rx) = channel(128); + let factory = RuncFactory::new(&self.data.netns); + let task = TaskService { + factory, + containers: Arc::new(Default::default()), + namespace: "k8s.io".to_string(), + exit: Arc::new(Default::default()), + tx: tx.clone(), + }; + + process_exits(&task).await; + + tokio::spawn(async move { + while let Some((_topic, e)) = rx.recv().await { + debug!("received event {:?}", e); + } + }); + Ok(task) + } +} + +#[async_trait] +impl Sandbox for RuncSandbox { + type Container = RuncContainerData; + + fn status(&self) -> Result { + Ok(self.status.clone()) + } + + async fn ping(&self) -> Result<()> { + Ok(()) + } + + async fn container(&self, id: &str) -> Result<&Self::Container> { + return self.containers.get(id).ok_or(Error::NotFound(format!( + "failed to find container by id {id}" + ))); + } + + async fn append_container(&mut self, id: &str, option: ContainerOption) -> Result<()> { + self.containers.insert(id.to_string(), RuncContainerData { + data: option.container + }); + Ok(()) + } + + async fn update_container(&mut self, _id: &str, _option: ContainerOption) -> Result<()> { + Ok(()) + } + + async fn remove_container(&mut self, id: &str) -> Result<()> { + self.containers.remove(id); + Ok(()) + } + + async fn exit_signal(&self) -> Result> { + Ok(self.exit_signal.clone()) + } + + fn get_data(&self) -> Result { + Ok(self.data.clone()) + } +} + +// any wasm runtime implementation should implement this function +pub async fn process_exits(task: &TaskService) { + let containers = task.containers.clone(); + let exit_signal = task.exit.clone(); + let mut s = monitor_subscribe(Topic::Pid) + .await + .expect("monitor subscribe failed"); + tokio::spawn(async move { + loop { + tokio::select! { + _ = exit_signal.wait() => { + debug!("sandbox exit, should break"); + monitor_unsubscribe(s.id).await.unwrap_or_default(); + return; + }, + res = s.rx.recv() => { + if let Some(e) = res { + if let Subject::Pid(pid) = e.subject { + debug!("receive exit event: {}", &e); + let exit_code = e.exit_code; + for (_k, cont) in containers.lock().await.iter_mut() { + // pid belongs to container init process + if cont.init.pid == pid { + // set exit for init process + cont.init.set_exited(exit_code).await; + break; + } + + // pid belongs to container common process + for (_exec_id, p) in cont.processes.iter_mut() { + // set exit for exec process + if p.pid == pid { + p.set_exited(exit_code).await; + break; + } + } + } + } + } else { + monitor_unsubscribe(s.id).await.unwrap_or_default(); + return; + } + } + } + } + }); +} \ No newline at end of file From f389f6e23b6d37ba14933647c7383f8a3dc63feb Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Tue, 29 Aug 2023 16:28:39 +0800 Subject: [PATCH 2/9] runc: support get runc command error from log Signed-off-by: Abel Feng --- runc/Cargo.lock | 1 + runc/Cargo.toml | 1 + runc/src/runc.rs | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/runc/Cargo.lock b/runc/Cargo.lock index 35332f1b..9fcc76f3 100644 --- a/runc/Cargo.lock +++ b/runc/Cargo.lock @@ -1432,6 +1432,7 @@ dependencies = [ "nix 0.25.1", "oci-spec", "runc", + "serde", "serde_json", "signal-hook-tokio", "time", diff --git a/runc/Cargo.toml b/runc/Cargo.toml index 87c1c990..91df096e 100644 --- a/runc/Cargo.toml +++ b/runc/Cargo.toml @@ -17,6 +17,7 @@ log = { version = "0.4.17", features = ["std"] } oci-spec = "0.5.4" time = "0.3.5" serde_json = "1.0.74" +serde = { version = "1.0.133", features = ["derive"] } containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" } containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } diff --git a/runc/src/runc.rs b/runc/src/runc.rs index b95cbd3c..9834fce5 100644 --- a/runc/src/runc.rs +++ b/runc/src/runc.rs @@ -50,10 +50,12 @@ use log::{debug, error}; use nix::{sys::signal::kill, unistd::Pid}; use oci_spec::runtime::{LinuxResources, Process}; use runc::{Command, Runc, Spawner}; +use serde::Deserialize; use tokio::{ fs::{File, OpenOptions}, io::{AsyncRead, AsyncReadExt, AsyncWrite}, }; +use tokio::io::{AsyncBufReadExt, BufReader}; use crate::common::{ check_kill_error, create_io, create_runc, CreateConfig, get_spec_from_request, INIT_PID_FILE, @@ -181,7 +183,8 @@ impl RuncFactory { if let Some(s) = socket { s.clean().await; } - return Err(other!("failed to create runc container: {}", e)); + let runtime_e = runtime_error(e, &*bundle).await; + return Err(runtime_e); } copy_io_or_console(init, socket, pio, init.lifecycle.exit_signal.clone()).await?; let pid = read_file_to_str(pid_path).await?.parse::()?; @@ -690,3 +693,31 @@ async fn wait_pid(pid: i32, s: Subscription) -> i32 { } } } + +#[derive(Deserialize)] +struct Log { + level: String, + msg: String, +} + +// runtime_error will read the OCI runtime logfile collecting runtime error +async fn runtime_error(e: runc::error::Error, bundle: &str) -> Error { + let mut msg = String::new(); + if let Ok(file) = File::open(Path::new(bundle).join("log.json")).await { + let mut lines = BufReader::new(file).lines(); + while let Ok(Some(line)) = lines.next_line().await { + if let Ok(log) = serde_json::from_str::(&line) { + // according to golang shim, take the last runtime error as error + if log.level == "error" { + msg = log.msg.trim().to_string(); + } + } + } + } + if !msg.is_empty() { + other!("{}", msg) + } else { + other!("unable to retrieve OCI runtime error {:?}", e) + } +} + From 8b0dabe1e8359b35efaabc517900ea1899f9edee Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Tue, 29 Aug 2023 20:36:29 +0800 Subject: [PATCH 3/9] runc: fork a process as the sandbox process Signed-off-by: Abel Feng --- runc/Cargo.lock | 318 +++++++++++++++++++++++++++++++++++++++++--- runc/Cargo.toml | 2 + runc/src/main.rs | 3 +- runc/src/sandbox.rs | 45 ++++++- 4 files changed, 348 insertions(+), 20 deletions(-) diff --git a/runc/Cargo.lock b/runc/Cargo.lock index 9fcc76f3..a156247c 100644 --- a/runc/Cargo.lock +++ b/runc/Cargo.lock @@ -26,6 +26,21 @@ 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 = "anyhow" version = "1.0.66" @@ -160,6 +175,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + [[package]] name = "byteorder" version = "1.4.3" @@ -215,6 +236,18 @@ dependencies = [ "regex", ] +[[package]] +name = "chrono" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f56b4c72906975ca04becb8a30e102dfecddd0c06181e3e95ddc444be28881f8" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets 0.48.5", +] + [[package]] name = "cmake" version = "0.1.50" @@ -304,6 +337,21 @@ dependencies = [ "ttrpc-codegen", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -423,7 +471,7 @@ checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -454,6 +502,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -648,6 +706,12 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.9" @@ -730,6 +794,29 @@ dependencies = [ "tokio-io-timeout", ] +[[package]] +name = "iana-time-zone" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[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 = "ident_case" version = "1.0.1" @@ -756,6 +843,17 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "iovec" version = "0.1.4" @@ -780,6 +878,15 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -792,6 +899,12 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "linux-raw-sys" version = "0.4.5" @@ -858,7 +971,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -929,6 +1042,15 @@ dependencies = [ "libc", ] +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.16.0" @@ -974,7 +1096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1007,7 +1129,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1145,6 +1267,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ca7f9f29bab5844ecd8fdb3992c5969b6622bb9609b9502fef9b4310e3f1f" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "chrono", + "flate2", + "hex", + "lazy_static", + "rustix 0.36.15", +] + [[package]] name = "prost" version = "0.8.0" @@ -1431,6 +1568,8 @@ dependencies = [ "log", "nix 0.25.1", "oci-spec", + "prctl", + "procfs", "runc", "serde", "serde_json", @@ -1445,6 +1584,20 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.36.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c37f1bd5ef1b5422177b7646cba67430579cfe2ace80f284fee876bca52ad941" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + [[package]] name = "rustix" version = "0.38.10" @@ -1454,8 +1607,8 @@ dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys", - "windows-sys", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", ] [[package]] @@ -1564,7 +1717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1610,8 +1763,8 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall", - "rustix", - "windows-sys", + "rustix 0.38.10", + "windows-sys 0.48.0", ] [[package]] @@ -1686,7 +1839,7 @@ dependencies = [ "signal-hook-registry", "socket2 0.5.3", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -2001,6 +2154,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.29", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + [[package]] name = "which" version = "4.4.0" @@ -2043,13 +2250,46 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -2058,51 +2298,93 @@ 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", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[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.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/runc/Cargo.toml b/runc/Cargo.toml index 91df096e..54310d91 100644 --- a/runc/Cargo.toml +++ b/runc/Cargo.toml @@ -18,6 +18,8 @@ oci-spec = "0.5.4" time = "0.3.5" serde_json = "1.0.74" serde = { version = "1.0.133", features = ["derive"] } +procfs = "0.15.1" +prctl = "1.0.0" containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" } containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } diff --git a/runc/src/main.rs b/runc/src/main.rs index d8d08543..54be9bdf 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -25,6 +25,7 @@ async fn main() -> anyhow::Result<()> { .expect("new signal failed"); handle_signals(signals).await; }); + prctl::set_child_subreaper(true).unwrap(); let sandboxer = RuncSandboxer::default(); containerd_sandbox::run("runc-sandboxer", sandboxer) @@ -56,7 +57,7 @@ async fn handle_signals(signals: Signals) { .await .unwrap_or_else(|e| error!("failed to send signal event {}", e)) } - Err(Errno::ECHILD) => { + Err(Errno::ECHILD) | Ok(WaitStatus::StillAlive) => { break; } Err(e) => { diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index 556de326..de7c9261 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -14,11 +14,20 @@ use containerd_shim::processes::Process; use containerd_shim::protos::shim_async::create_task; use containerd_shim::protos::ttrpc::asynchronous::Server; use log::debug; +use nix::errno::Errno; +use nix::fcntl::OFlag; +use nix::sched::{CloneFlags, setns, unshare}; +use nix::sys::signal::{kill, Signal}; +use nix::sys::stat::Mode; +use nix::unistd::{fork, ForkResult, pause, Pid, close}; use tokio::fs::create_dir_all; use tokio::sync::{Mutex, RwLock}; use tokio::sync::mpsc::channel; use crate::runc::{RuncContainer, RuncFactory}; +use std::ffi::CString; +use prctl::PrctlMM; +use nix::NixPath; #[derive(Default)] pub struct RuncSandboxer { @@ -112,6 +121,10 @@ impl RuncSandbox { .await .map_err(|e| anyhow!("failed to shutdown task server, {}", e))?; } + if let SandboxStatus::Running(pid) = self.status { + kill(Pid::from_raw(pid as i32), Signal::SIGKILL) + .map_err(|e| anyhow!("failed to kill sandbox process {}", e))?; + } let ts = time::OffsetDateTime::now_utc().unix_timestamp_nanos(); self.status = SandboxStatus::Stopped(0, ts); self.exit_signal.signal(); @@ -132,7 +145,37 @@ impl RuncSandbox { .await .map_err(|e| anyhow!("failed to start task server, {}", e))?; self.server = Some(server); - self.status = SandboxStatus::Running(0); + unsafe { + match fork().map_err(|e| Error::Other(e.into()))? { + ForkResult::Parent { child } => { + self.status = SandboxStatus::Running(child.as_raw() as u32); + } + ForkResult::Child => { + let me = procfs::process::Process::myself().unwrap(); + for fd in me.fd().unwrap() { + if let Ok(fd) = fd { + close(fd.fd).unwrap_or_default(); + } + } + let comm = format!("[sandbox-{}]", self.id); + let comm_cstr = CString::new(comm).unwrap(); + let addr = comm_cstr.as_ptr(); + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr as u64).unwrap(); + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr as u64 + comm_cstr.len() as u64 + 1).unwrap(); + if !self.data.netns.is_empty() { + // TODO + let netns_fd = + nix::fcntl::open(&*self.data.netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); + setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); + } + unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID).unwrap(); + loop { + pause(); + } + } + } + } + Ok(()) } From f8da0c4057e2c30950e49018fe8a916917306980 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Wed, 30 Aug 2023 18:06:41 +0800 Subject: [PATCH 4/9] runc: use early fork as parent sandbox process Signed-off-by: Abel Feng --- runc/Cargo.lock | 2 + runc/Cargo.toml | 4 +- runc/src/main.rs | 186 ++++++++++++++++++++++++++++++++++++++++---- runc/src/sandbox.rs | 94 +++++++++++++--------- 4 files changed, 235 insertions(+), 51 deletions(-) diff --git a/runc/Cargo.lock b/runc/Cargo.lock index a156247c..31091385 100644 --- a/runc/Cargo.lock +++ b/runc/Cargo.lock @@ -1561,6 +1561,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "byteorder", "containerd-sandbox", "containerd-shim", "env_logger", @@ -1568,6 +1569,7 @@ dependencies = [ "log", "nix 0.25.1", "oci-spec", + "os_pipe", "prctl", "procfs", "runc", diff --git a/runc/Cargo.toml b/runc/Cargo.toml index 54310d91..a17a955a 100644 --- a/runc/Cargo.toml +++ b/runc/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] env_logger = "0.9.0" anyhow = { version = "=1.0.66", default-features = false, features = ["std"] } -tokio = "1.13.0" +tokio = { version = "1.19.2", features = ["full"] } signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] } async-trait = "0.1.51" nix = "0.25" @@ -20,6 +20,8 @@ serde_json = "1.0.74" serde = { version = "1.0.133", features = ["derive"] } procfs = "0.15.1" prctl = "1.0.0" +os_pipe = "1.1.4" +byteorder = "1.4.3" containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" } containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } diff --git a/runc/src/main.rs b/runc/src/main.rs index 54be9bdf..a206c2b8 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -1,25 +1,165 @@ +use std::ffi::CString; +use std::fs::File; +use std::io::{Read, Write}; +use std::os::fd::RawFd; +use std::process::{exit, id}; + +use anyhow::anyhow; +use byteorder::WriteBytesExt; use containerd_shim::asynchronous::monitor::monitor_notify_by_pid; use futures::StreamExt; use log::{debug, error, warn}; -use nix::{ - errno::Errno, - libc, - sys::{ - wait, - wait::{WaitPidFlag, WaitStatus}, - }, - unistd::Pid, -}; +use nix::{errno::Errno, libc, NixPath, sys::{ + wait, + wait::{WaitPidFlag, WaitStatus}, +}, unistd::Pid}; +use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag}; +use nix::sched::{CloneFlags, setns, unshare}; +use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, sigaction, SIGCHLD}; +use nix::sys::stat::Mode; +use nix::unistd::{close, fork, ForkResult, pause, pipe, read, write}; +use prctl::PrctlMM; use signal_hook_tokio::Signals; -use crate::sandbox::RuncSandboxer; + +use crate::sandbox::{RuncSandboxer, SandboxParent}; mod sandbox; mod runc; mod common; -#[tokio::main] -async fn main() -> anyhow::Result<()> { +fn main() { env_logger::builder().format_timestamp_micros().init(); + let sandbox_parent = fork_sandbox_parent().unwrap(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async move { + start_sandboxer(sandbox_parent).await.unwrap(); + }); +} + +// Call it instantly after enter the main function, +// it will fork a process and make it as the parent of all the sandbox processes. +// any time we want to fork a sandbox process, we will send a message to this parent process +// and this parent will fork a process for sandbox and return the pid. +fn fork_sandbox_parent() -> Result { + let (reqr, reqw) = pipe().map_err(|e| anyhow!("failed to create pipe {}", e))?; + let (respr, respw) = pipe().map_err(|e| anyhow!("failed to create pipe {}", e))?; + + match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox parent {}", e))? } { + ForkResult::Parent { child } => { + debug!("forked process {} for the sandbox parent", child); + close(reqr).unwrap_or_default(); + close(respw).unwrap_or_default(); + } + ForkResult::Child => { + close(reqw).unwrap_or_default(); + close(respr).unwrap_or_default(); + let comm = format!("[sandbox-parent]"); + let comm_cstr = CString::new(comm).unwrap(); + let addr = comm_cstr.as_ptr(); + set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); + let sig_action = SigAction::new( + SigHandler::Handler(sandbox_parent_handle_signals), + SaFlags::empty(), + SigSet::empty() + ); + unsafe {sigaction(SIGCHLD, &sig_action).unwrap();} + loop { + let buffer = read_count(reqr, 512).unwrap(); + let id = String::from_utf8_lossy(&buffer[0..64]).to_string(); + let mut zero_index = 64; + for i in 64..512 { + if buffer[i] == 0 { + zero_index = i; + break; + } + } + let netns = String::from_utf8_lossy(&buffer[64..zero_index]).to_string(); + let sandbox_pid = fork_sandbox(&id, &netns).unwrap(); + write_all(respw, sandbox_pid.to_le_bytes().as_slice()).unwrap(); + } + } + } + fcntl(reqw, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default(); + fcntl(respr, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).unwrap_or_default(); + Ok(SandboxParent::new(reqw, respr)) +} + +pub fn read_count(fd: RawFd, count: usize) -> Result, anyhow::Error> { + let mut buf = vec![0u8; count]; + let mut idx = 0; + loop { + let l = match read(fd, &mut buf[idx..]) { + Ok(l) => l, + Err(e) => { + if e == Errno::EINTR { + continue; + } else { + return Err(anyhow!("failed to read from pipe {}", e)); + } + } + }; + idx += l; + if idx == count || l == 0 { + return Ok(buf); + } + } +} + +pub fn write_all(fd: RawFd, buf: &[u8]) -> Result<(), anyhow::Error> { + let mut idx = 0; + let count = buf.len(); + loop { + let l = match write(fd, &buf[idx..]) { + Ok(l) => l, + Err(e) => { + if e == Errno::EINTR { + continue; + } else { + return Err(anyhow!("failed to write to pipe {}", e)); + } + } + }; + idx += l; + if idx == count { + return Ok(()); + } + } +} + +fn fork_sandbox(id: &str, netns: &str) -> Result { + match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox {}", e))? } { + ForkResult::Parent { child } => { + debug!("forked process {} for the sandbox {}", child, id); + return Ok(child.as_raw()); + } + ForkResult::Child => { + let comm = format!("[sandbox-{}]", id); + let comm_cstr = CString::new(comm).unwrap(); + let addr = comm_cstr.as_ptr(); + set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); + if !netns.is_empty() { + let netns_fd = + nix::fcntl::open(&*netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); + setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); + } + unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID).unwrap(); + loop { + pause(); + } + } + } +} + +fn set_process_comm(addr: u64, len: u64) { + if let Err(_) = prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr) { + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr).unwrap() + } else { + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); + } +} + +async fn start_sandboxer(sandbox_parent: SandboxParent) -> anyhow::Result<()> { tokio::spawn(async move { let signals = Signals::new([libc::SIGTERM, libc::SIGINT, libc::SIGPIPE, libc::SIGCHLD]) .expect("new signal failed"); @@ -27,13 +167,33 @@ async fn main() -> anyhow::Result<()> { }); prctl::set_child_subreaper(true).unwrap(); - let sandboxer = RuncSandboxer::default(); + let sandboxer = RuncSandboxer::new(sandbox_parent); containerd_sandbox::run("runc-sandboxer", sandboxer) .await .unwrap(); Ok(()) } +extern "C" fn sandbox_parent_handle_signals(_: libc::c_int) { + loop { + match wait::waitpid(Some(Pid::from_raw(-1)), Some(WaitPidFlag::WNOHANG)) { + Ok(WaitStatus::Exited(pid, status)) => { + debug!("child {} exit ({})", pid, status); + } + Ok(WaitStatus::Signaled(pid, sig, _)) => { + debug!("child {} terminated({})", pid, sig); + } + Err(Errno::ECHILD) | Ok(WaitStatus::StillAlive) => { + break; + } + Err(e) => { + warn!("error occurred in signal handler: {}", e); + } + _ => {} + } + } +} + async fn handle_signals(signals: Signals) { let mut signals = signals.fuse(); while let Some(sig) = signals.next().await { diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index de7c9261..2001d9af 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::ffi::CString; use std::sync::Arc; use anyhow::anyhow; @@ -16,23 +17,26 @@ use containerd_shim::protos::ttrpc::asynchronous::Server; use log::debug; use nix::errno::Errno; use nix::fcntl::OFlag; +use nix::NixPath; use nix::sched::{CloneFlags, setns, unshare}; use nix::sys::signal::{kill, Signal}; use nix::sys::stat::Mode; -use nix::unistd::{fork, ForkResult, pause, Pid, close}; +use nix::unistd::{close, fork, ForkResult, pause, Pid}; +use os_pipe::{PipeReader, PipeWriter}; +use prctl::PrctlMM; use tokio::fs::create_dir_all; use tokio::sync::{Mutex, RwLock}; use tokio::sync::mpsc::channel; use crate::runc::{RuncContainer, RuncFactory}; -use std::ffi::CString; -use prctl::PrctlMM; -use nix::NixPath; +use std::io::{Write, Read}; +use std::os::fd::RawFd; +use crate::{write_all, read_count}; -#[derive(Default)] pub struct RuncSandboxer { #[allow(clippy::type_complexity)] pub(crate) sandboxes: Arc>>>>, + sandbox_parent: Arc>, } pub struct RuncSandbox { @@ -55,6 +59,50 @@ impl Container for RuncContainerData { } } +pub struct SandboxParent { + req: RawFd, + resp: RawFd, +} + +impl SandboxParent { + pub fn new(req: RawFd, resp: RawFd) -> Self { + Self { + req, + resp, + } + } + pub fn fork_sandbox_process(&mut self, id: &str, netns: &str) -> Result { + let mut req = [0u8; 512]; + use std::io::Write; + unsafe { + (&mut req[0..64]).write_all(id.as_bytes())?; + (&mut req[64..]).write_all(netns.as_bytes())?; + } + write_all(self.req, &req)?; + let mut resp = [0u8;4]; + let mut r = read_count(self.resp, 4)?; + resp[..].copy_from_slice(r.as_slice()); + let pid = i32::from_le_bytes(resp); + Ok(pid) + } +} + +impl Drop for SandboxParent { + fn drop(&mut self) { + close(self.req).unwrap_or_default(); + close(self.resp).unwrap_or_default(); + } +} + +impl RuncSandboxer { + pub fn new(sandbox_parent: SandboxParent) -> Self { + Self { + sandboxes: Default::default(), + sandbox_parent: Arc::new(Mutex::new(sandbox_parent)), + } + } +} + #[async_trait] impl Sandboxer for RuncSandboxer { type Sandbox = RuncSandbox; @@ -79,7 +127,10 @@ impl Sandboxer for RuncSandboxer { async fn start(&self, id: &str) -> Result<()> { let sandbox = self.sandbox(id).await?; - sandbox.lock().await.start().await?; + let mut sandbox = sandbox.lock().await; + let mut sandbox_parent = self.sandbox_parent.lock().await; + let sandbox_pid = sandbox_parent.fork_sandbox_process(id, &sandbox.data.netns)?; + sandbox.status = SandboxStatus::Running(sandbox_pid as u32); Ok(()) } @@ -145,37 +196,6 @@ impl RuncSandbox { .await .map_err(|e| anyhow!("failed to start task server, {}", e))?; self.server = Some(server); - unsafe { - match fork().map_err(|e| Error::Other(e.into()))? { - ForkResult::Parent { child } => { - self.status = SandboxStatus::Running(child.as_raw() as u32); - } - ForkResult::Child => { - let me = procfs::process::Process::myself().unwrap(); - for fd in me.fd().unwrap() { - if let Ok(fd) = fd { - close(fd.fd).unwrap_or_default(); - } - } - let comm = format!("[sandbox-{}]", self.id); - let comm_cstr = CString::new(comm).unwrap(); - let addr = comm_cstr.as_ptr(); - prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr as u64).unwrap(); - prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr as u64 + comm_cstr.len() as u64 + 1).unwrap(); - if !self.data.netns.is_empty() { - // TODO - let netns_fd = - nix::fcntl::open(&*self.data.netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); - setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); - } - unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID).unwrap(); - loop { - pause(); - } - } - } - } - Ok(()) } From 5e7f95867ebe328786d72920a7aa12f24adcc167 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Wed, 6 Sep 2023 14:44:53 +0800 Subject: [PATCH 5/9] runc: use a unique task server to serve all task api Signed-off-by: Abel Feng --- runc/src/main.rs | 27 ++++++++++---- runc/src/runc.rs | 12 +----- runc/src/sandbox.rs | 90 ++++++++++++++++++++++----------------------- 3 files changed, 64 insertions(+), 65 deletions(-) diff --git a/runc/src/main.rs b/runc/src/main.rs index a206c2b8..68a114fc 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use std::fs::File; use std::io::{Read, Write}; use std::os::fd::RawFd; +use std::path::Path; use std::process::{exit, id}; use anyhow::anyhow; @@ -15,11 +16,12 @@ use nix::{errno::Errno, libc, NixPath, sys::{ }, unistd::Pid}; use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag}; use nix::sched::{CloneFlags, setns, unshare}; -use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, sigaction, SIGCHLD}; +use nix::sys::signal::{SaFlags, SigAction, sigaction, SIGCHLD, SigHandler, SigSet}; use nix::sys::stat::Mode; use nix::unistd::{close, fork, ForkResult, pause, pipe, read, write}; use prctl::PrctlMM; use signal_hook_tokio::Signals; +use tokio::fs::create_dir_all; use crate::sandbox::{RuncSandboxer, SandboxParent}; @@ -27,6 +29,8 @@ mod sandbox; mod runc; mod common; +pub const TASK_ADDRESS_SOCK: &str = "/run/kuasar/task.sock"; + fn main() { env_logger::builder().format_timestamp_micros().init(); let sandbox_parent = fork_sandbox_parent().unwrap(); @@ -60,9 +64,9 @@ fn fork_sandbox_parent() -> Result { let sig_action = SigAction::new( SigHandler::Handler(sandbox_parent_handle_signals), SaFlags::empty(), - SigSet::empty() + SigSet::empty(), ); - unsafe {sigaction(SIGCHLD, &sig_action).unwrap();} + unsafe { sigaction(SIGCHLD, &sig_action).unwrap(); } loop { let buffer = read_count(reqr, 512).unwrap(); let id = String::from_utf8_lossy(&buffer[0..64]).to_string(); @@ -151,11 +155,11 @@ fn fork_sandbox(id: &str, netns: &str) -> Result { } fn set_process_comm(addr: u64, len: u64) { - if let Err(_) = prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr) { + if let Err(_) = prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr) { prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr).unwrap() } else { - prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); + prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); } } @@ -166,8 +170,17 @@ async fn start_sandboxer(sandbox_parent: SandboxParent) -> anyhow::Result<()> { handle_signals(signals).await; }); prctl::set_child_subreaper(true).unwrap(); - - let sandboxer = RuncSandboxer::new(sandbox_parent); + let sock_path = Path::new(TASK_ADDRESS_SOCK); + if sock_path.exists() { + tokio::fs::remove_file(sock_path).await?; + } + if let Some(sock_parent) = sock_path.parent() { + create_dir_all(sock_parent) + .await + .map_err(|e| anyhow!("failed to create {}, {}", sock_parent.display(), e))?; + } + let sock_addr = format!("unix://{}", TASK_ADDRESS_SOCK); + let sandboxer = RuncSandboxer::new(sandbox_parent, &sock_addr).await?; containerd_sandbox::run("runc-sandboxer", sandboxer) .await .unwrap(); diff --git a/runc/src/runc.rs b/runc/src/runc.rs index 9834fce5..956d42c2 100644 --- a/runc/src/runc.rs +++ b/runc/src/runc.rs @@ -68,17 +68,7 @@ pub type InitProcess = ProcessTemplate; pub type RuncContainer = ContainerTemplate; #[derive(Clone, Default)] -pub(crate) struct RuncFactory { - netns: String, -} - -impl RuncFactory { - pub fn new(netns: &str) -> Self { - Self { - netns: netns.to_string() - } - } -} +pub(crate) struct RuncFactory; #[async_trait] impl ContainerFactory for RuncFactory { diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index 2001d9af..c99313d8 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; use std::ffi::CString; +use std::io::{Read, Write}; +use std::os::fd::RawFd; use std::sync::Arc; use anyhow::anyhow; @@ -28,14 +30,14 @@ use tokio::fs::create_dir_all; use tokio::sync::{Mutex, RwLock}; use tokio::sync::mpsc::channel; +use crate::{read_count, write_all}; use crate::runc::{RuncContainer, RuncFactory}; -use std::io::{Write, Read}; -use std::os::fd::RawFd; -use crate::{write_all, read_count}; pub struct RuncSandboxer { #[allow(clippy::type_complexity)] pub(crate) sandboxes: Arc>>>>, + task_address: String, + server: Server, sandbox_parent: Arc>, } @@ -79,7 +81,7 @@ impl SandboxParent { (&mut req[64..]).write_all(netns.as_bytes())?; } write_all(self.req, &req)?; - let mut resp = [0u8;4]; + let mut resp = [0u8; 4]; let mut r = read_count(self.resp, 4)?; resp[..].copy_from_slice(r.as_slice()); let pid = i32::from_le_bytes(resp); @@ -95,11 +97,23 @@ impl Drop for SandboxParent { } impl RuncSandboxer { - pub fn new(sandbox_parent: SandboxParent) -> Self { - Self { + pub async fn new(sandbox_parent: SandboxParent, task_address: &str) -> Result { + let task = start_task_service().await?; + let task_service = create_task(Arc::new(Box::new(task))); + let mut server = Server::new().register_service(task_service); + server = server + .bind(&task_address) + .map_err(|e| anyhow!("failed to bind socket {}, {}", task_address, e))?; + server + .start() + .await + .map_err(|e| anyhow!("failed to start task server, {}", e))?; + Ok(Self { + task_address: task_address.to_string(), + server, sandboxes: Default::default(), sandbox_parent: Arc::new(Mutex::new(sandbox_parent)), - } + }) } } @@ -131,6 +145,7 @@ impl Sandboxer for RuncSandboxer { let mut sandbox_parent = self.sandbox_parent.lock().await; let sandbox_pid = sandbox_parent.fork_sandbox_process(id, &sandbox.data.netns)?; sandbox.status = SandboxStatus::Running(sandbox_pid as u32); + sandbox.data.task_address = self.task_address.clone(); Ok(()) } @@ -181,46 +196,6 @@ impl RuncSandbox { self.exit_signal.signal(); Ok(()) } - - async fn start(&mut self) -> Result<()> { - let task = self.start_task_service().await?; - let task_address = format!("unix://{}/task.sock", self.base_dir); - self.data.task_address = task_address.clone(); - let task_service = create_task(Arc::new(Box::new(task))); - let mut server = Server::new().register_service(task_service); - server = server - .bind(&task_address) - .map_err(|e| anyhow!("failed to bind socket {}, {}", task_address, e))?; - server - .start() - .await - .map_err(|e| anyhow!("failed to start task server, {}", e))?; - self.server = Some(server); - Ok(()) - } - - async fn start_task_service( - &self, - ) -> Result> { - let (tx, mut rx) = channel(128); - let factory = RuncFactory::new(&self.data.netns); - let task = TaskService { - factory, - containers: Arc::new(Default::default()), - namespace: "k8s.io".to_string(), - exit: Arc::new(Default::default()), - tx: tx.clone(), - }; - - process_exits(&task).await; - - tokio::spawn(async move { - while let Some((_topic, e)) = rx.recv().await { - debug!("received event {:?}", e); - } - }); - Ok(task) - } } #[async_trait] @@ -312,4 +287,25 @@ pub async fn process_exits(task: &TaskService) { } } }); +} + +async fn start_task_service() -> Result> { + let (tx, mut rx) = channel(128); + let factory = RuncFactory::default(); + let task = TaskService { + factory, + containers: Arc::new(Default::default()), + namespace: "k8s.io".to_string(), + exit: Arc::new(Default::default()), + tx: tx.clone(), + }; + + process_exits(&task).await; + + tokio::spawn(async move { + while let Some((_topic, e)) = rx.recv().await { + debug!("received event {:?}", e); + } + }); + Ok(task) } \ No newline at end of file From c6ff8175448eac7bb9e2861efa2f07fbcbaadec4 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Thu, 7 Sep 2023 09:50:25 +0800 Subject: [PATCH 6/9] runc: add restart recovery Signed-off-by: Abel Feng --- runc/Cargo.lock | 20 ++++---- runc/Cargo.toml | 4 +- runc/src/runc.rs | 111 +++++++++++++++++++++++++++++++++++++++++++- runc/src/sandbox.rs | 72 ++++++++++++++++++++-------- 4 files changed, 174 insertions(+), 33 deletions(-) diff --git a/runc/Cargo.lock b/runc/Cargo.lock index 31091385..686c3935 100644 --- a/runc/Cargo.lock +++ b/runc/Cargo.lock @@ -1627,18 +1627,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", @@ -1647,9 +1647,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -1800,9 +1800,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.28" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +checksum = "a79d09ac6b08c1ab3906a2f7cc2e81a0e27c7ae89c63812df75e52bef0751e07" dependencies = [ "deranged", "serde", @@ -1818,9 +1818,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +checksum = "75c65469ed6b3a4809d987a41eb1dc918e9bc1d92211cbad7ae82931846f7451" dependencies = [ "time-core", ] diff --git a/runc/Cargo.toml b/runc/Cargo.toml index a17a955a..11ab143e 100644 --- a/runc/Cargo.toml +++ b/runc/Cargo.toml @@ -16,8 +16,8 @@ futures = { version = "0.3.21" } log = { version = "0.4.17", features = ["std"] } oci-spec = "0.5.4" time = "0.3.5" -serde_json = "1.0.74" -serde = { version = "1.0.133", features = ["derive"] } +serde_json = "1.0.96" +serde = { version = "1.0.163", features = ["derive"] } procfs = "0.15.1" prctl = "1.0.0" os_pipe = "1.1.4" diff --git a/runc/src/runc.rs b/runc/src/runc.rs index 956d42c2..951c4b51 100644 --- a/runc/src/runc.rs +++ b/runc/src/runc.rs @@ -24,6 +24,7 @@ use std::{ process::ExitStatus, sync::Arc, }; +use std::collections::HashMap; use async_trait::async_trait; use containerd_shim::{ @@ -46,11 +47,13 @@ use containerd_shim::{ protobuf::{CodedInputStream, Message}, }, Result, util::{asyncify, mkdir, mount_rootfs, read_file_to_str, write_options, write_runtime}, }; +use containerd_shim::asynchronous::util::{read_options, write_str_to_file}; use log::{debug, error}; use nix::{sys::signal::kill, unistd::Pid}; use oci_spec::runtime::{LinuxResources, Process}; use runc::{Command, Runc, Spawner}; -use serde::Deserialize; +use runc::options::DeleteOpts; +use serde::{Deserialize, Serialize}; use tokio::{ fs::{File, OpenOptions}, io::{AsyncRead, AsyncReadExt, AsyncWrite}, @@ -70,6 +73,36 @@ pub type RuncContainer = ContainerTemplate for Stdio { + fn from(value: SerializableStdio) -> Self { + Self { + stdin: value.stdin, + stdout: value.stdout, + stderr: value.stderr, + terminal: value.terminal, + } + } +} + +impl From for SerializableStdio { + fn from(value: Stdio) -> Self { + Self { + stdin: value.stdin, + stdout: value.stdout, + stderr: value.stderr, + terminal: value.terminal, + } + } +} + #[async_trait] impl ContainerFactory for RuncFactory { async fn create( @@ -113,6 +146,7 @@ impl ContainerFactory for RuncFactory { let id = req.id(); let stdio = Stdio::new(req.stdin(), req.stdout(), req.stderr(), req.terminal()); + write_stdio(bundle, &stdio).await?; let mut init = InitProcess::new( id, @@ -652,6 +686,67 @@ impl Spawner for ShimExecutor { } } +pub async fn recover() -> Result> { + let mut opts = Options::new(); + let mut runc_containers = HashMap::new(); + for ns in ["k8s.io", "moby", "default"] { + let runc = create_runc("", ns, Path::new("/tmp"), &opts, Some(Arc::new(ShimExecutor::default())))?; + let containers = runc.list().await + .map_err(other_error!(e, "failed to call runc list"))?; + for c in containers { + if !Path::new(&c.bundle).exists() { + runc.delete(&c.id, Some(&DeleteOpts { force: true })).await.unwrap_or_default(); + continue; + } + let runc_container = recover_one(&c, &runc).await; + runc_containers.insert(c.id.to_string(), runc_container); + } + } + Ok(runc_containers) +} + +pub async fn recover_one(c: &runc::container::Container, runtime: &Runc) -> RuncContainer { + let stdio = if let Ok(s) = read_stdio(&*c.bundle).await { + s + } else { + Stdio { + stdin: "".to_string(), + stdout: "".to_string(), + stderr: "".to_string(), + terminal: false, + } + }; + let opts = if let Ok(r) = read_options(&c.bundle).await { + r + } else { + Options::new() + }; + let mut init = InitProcess::new( + &c.id, + stdio, + RuncInitLifecycle::new(runtime.clone(), opts.clone(), &c.bundle), + ); + init.state = match &*c.status { + "created" => Status::CREATED, + "running" => Status::RUNNING, + "stopped" => Status::STOPPED, + _ => Status::CREATED, + }; + init.pid = c.pid as i32; + RuncContainer { + id: "".to_string(), + bundle: "".to_string(), + init, + process_factory: RuncExecFactory { + runtime: runtime.clone(), + bundle: c.bundle.to_string(), + io_uid: opts.io_uid, + io_gid: opts.io_gid, + }, + processes: Default::default(), + } +} + async fn read_std(std: Option) -> String where T: AsyncRead + Unpin, @@ -711,3 +806,17 @@ async fn runtime_error(e: runc::error::Error, bundle: &str) -> Error { } } +async fn write_stdio(bundle: &str, stdio: &Stdio) -> Result<()> { + let serializable_stdio: SerializableStdio = stdio.clone().into(); + let stdio_str = serde_json::to_string(&serializable_stdio) + .map_err(other_error!(e, "failed to serialize stdio"))?; + write_str_to_file(Path::new(bundle).join("stdio"), stdio_str).await +} + +async fn read_stdio(bundle: &str) -> Result { + let stdio_str = read_file_to_str(Path::new(bundle).join("stdio")).await?; + let serializable_stdio: SerializableStdio = serde_json::from_str(&stdio_str) + .map_err(other_error!(e, "failed to deserialize stdio"))?; + Ok(serializable_stdio.into()) +} + diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index c99313d8..77ae6df0 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use std::ffi::CString; use std::io::{Read, Write}; use std::os::fd::RawFd; +use std::path::Path; use std::sync::Arc; use anyhow::anyhow; @@ -10,6 +11,7 @@ use containerd_sandbox::{Container, ContainerOption, Sandbox, Sandboxer, Sandbox use containerd_sandbox::data::{ContainerData, SandboxData}; use containerd_sandbox::error::{Error, Result}; use containerd_sandbox::signal::ExitSignal; +use containerd_shim::api::Options; use containerd_shim::asynchronous::monitor::{monitor_subscribe, monitor_unsubscribe}; use containerd_shim::asynchronous::task::TaskService; use containerd_shim::monitor::{Subject, Topic}; @@ -26,12 +28,16 @@ use nix::sys::stat::Mode; use nix::unistd::{close, fork, ForkResult, pause, Pid}; use os_pipe::{PipeReader, PipeWriter}; use prctl::PrctlMM; -use tokio::fs::create_dir_all; +use runc::options::DeleteOpts; +use serde::{Deserialize, Serialize}; +use tokio::fs::{create_dir_all, OpenOptions}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::sync::{Mutex, RwLock}; use tokio::sync::mpsc::channel; use crate::{read_count, write_all}; -use crate::runc::{RuncContainer, RuncFactory}; +use crate::common::{create_runc, ShimExecutor}; +use crate::runc::{recover, RuncContainer, RuncFactory}; pub struct RuncSandboxer { #[allow(clippy::type_complexity)] @@ -41,16 +47,18 @@ pub struct RuncSandboxer { sandbox_parent: Arc>, } +#[derive(Serialize, Deserialize)] pub struct RuncSandbox { pub(crate) id: String, pub(crate) base_dir: String, pub(crate) data: SandboxData, pub(crate) status: SandboxStatus, + #[serde(skip, default)] pub(crate) exit_signal: Arc, pub(crate) containers: HashMap, - pub(crate) server: Option, } +#[derive(Serialize, Deserialize)] pub struct RuncContainerData { data: ContainerData, } @@ -129,11 +137,11 @@ impl Sandboxer for RuncSandboxer { status: SandboxStatus::Created, exit_signal: Arc::new(Default::default()), containers: Default::default(), - server: None, }; create_dir_all(&sandbox.base_dir) .await .map_err(|e| anyhow!("failed to create {}, {}", sandbox.base_dir, e))?; + sandbox.dump().await?; let mut sandboxes = self.sandboxes.write().await; sandboxes.insert(id.to_string(), Arc::new(Mutex::new(sandbox))); Ok(()) @@ -146,6 +154,7 @@ impl Sandboxer for RuncSandboxer { let sandbox_pid = sandbox_parent.fork_sandbox_process(id, &sandbox.data.netns)?; sandbox.status = SandboxStatus::Running(sandbox_pid as u32); sandbox.data.task_address = self.task_address.clone(); + sandbox.dump().await?; Ok(()) } @@ -166,27 +175,13 @@ impl Sandboxer for RuncSandboxer { } async fn delete(&self, id: &str) -> Result<()> { - if let Some(sandbox) = self.sandboxes.write().await.remove(id) { - let mut sandbox = sandbox.lock().await; - if let Some(mut server) = sandbox.server.take() { - server - .shutdown() - .await - .map_err(|e| anyhow!("failed to shutdown task server, {}", e))?; - } - } + self.sandboxes.write().await.remove(id); Ok(()) } } impl RuncSandbox { async fn stop(&mut self) -> Result<()> { - if let Some(mut server) = self.server.take() { - server - .shutdown() - .await - .map_err(|e| anyhow!("failed to shutdown task server, {}", e))?; - } if let SandboxStatus::Running(pid) = self.status { kill(Pid::from_raw(pid as i32), Signal::SIGKILL) .map_err(|e| anyhow!("failed to kill sandbox process {}", e))?; @@ -196,6 +191,40 @@ impl RuncSandbox { self.exit_signal.signal(); Ok(()) } + + async fn recover>(base_dir: P) -> Result { + let dump_path = base_dir.as_ref().join("sandbox.json"); + let mut dump_file = OpenOptions::new() + .read(true) + .open(&dump_path) + .await + .map_err(Error::IO)?; + let mut content = vec![]; + dump_file + .read_to_end(&mut content) + .await + .map_err(Error::IO)?; + let mut sb = serde_json::from_slice::(content.as_slice()) + .map_err(|e| anyhow!("failed to deserialize sandbox, {}", e))?; + Ok(sb) + } + + async fn dump(&self) -> Result<()> { + let dump_data = + serde_json::to_vec(&self).map_err(|e| anyhow!("failed to serialize sandbox, {}", e))?; + let dump_path = format!("{}/sandbox.json", self.base_dir); + let mut dump_file = OpenOptions::new() + .write(true) + .create(true) + .open(&dump_path) + .await + .map_err(Error::IO)?; + dump_file + .write_all(dump_data.as_slice()) + .await + .map_err(Error::IO)?; + Ok(()) + } } #[async_trait] @@ -220,6 +249,7 @@ impl Sandbox for RuncSandbox { self.containers.insert(id.to_string(), RuncContainerData { data: option.container }); + self.dump().await?; Ok(()) } @@ -229,6 +259,7 @@ impl Sandbox for RuncSandbox { async fn remove_container(&mut self, id: &str) -> Result<()> { self.containers.remove(id); + self.dump().await?; Ok(()) } @@ -292,9 +323,10 @@ pub async fn process_exits(task: &TaskService) { async fn start_task_service() -> Result> { let (tx, mut rx) = channel(128); let factory = RuncFactory::default(); + let containers = recover().await.map_err(|e| anyhow!("failed to recover containers {}", e))?; let task = TaskService { factory, - containers: Arc::new(Default::default()), + containers: Arc::new(Mutex::new(containers)), namespace: "k8s.io".to_string(), exit: Arc::new(Default::default()), tx: tx.clone(), From 4e5c919da50a09421d450cf81054708d49a73212 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Tue, 17 Oct 2023 11:30:37 +0800 Subject: [PATCH 7/9] runc: use double fork to make a seperate pid ns Signed-off-by: Abel Feng --- runc/src/main.rs | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/runc/src/main.rs b/runc/src/main.rs index 68a114fc..112baa0d 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -131,24 +131,40 @@ pub fn write_all(fd: RawFd, buf: &[u8]) -> Result<(), anyhow::Error> { } fn fork_sandbox(id: &str, netns: &str) -> Result { + let (r, w) = pipe().map_err(|e| anyhow!("failed to create pipe {}", e))?; match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox {}", e))? } { ForkResult::Parent { child } => { debug!("forked process {} for the sandbox {}", child, id); - return Ok(child.as_raw()); + close(w); + let mut resp = [0u8; 4]; + let mut r = read_count(r, 4)?; + resp[..].copy_from_slice(r.as_slice()); + let pid = i32::from_le_bytes(resp); + return Ok(pid); } ForkResult::Child => { - let comm = format!("[sandbox-{}]", id); - let comm_cstr = CString::new(comm).unwrap(); - let addr = comm_cstr.as_ptr(); - set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); - if !netns.is_empty() { - let netns_fd = - nix::fcntl::open(&*netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); - setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); - } + close(r); unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID).unwrap(); - loop { - pause(); + match unsafe { fork().unwrap() } { + ForkResult::Parent { child } => { + debug!("forked process {} for the sandbox {}", child, id); + write_all(w, child.as_raw().to_le_bytes().as_slice()).unwrap(); + exit(0); + } + ForkResult::Child => { + let comm = format!("[sandbox-{}]", id); + let comm_cstr = CString::new(comm).unwrap(); + let addr = comm_cstr.as_ptr(); + set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); + if !netns.is_empty() { + let netns_fd = + nix::fcntl::open(&*netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); + setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); + } + loop { + pause(); + } + } } } } From b5b6b547cd4ecce9172575bfa1d75fea2a58d934 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Mon, 13 Nov 2023 10:06:10 +0800 Subject: [PATCH 8/9] runc: keep sandbox process only when share pid Signed-off-by: Abel Feng --- runc/Cargo.lock | 2 + runc/Cargo.toml | 3 +- runc/src/common.rs | 17 ++- runc/src/main.rs | 94 +++++++-------- runc/src/runc.rs | 180 +++++++++++++--------------- runc/src/sandbox.rs | 283 ++++++++++++++++++++++++-------------------- runc/src/task.rs | 185 +++++++++++++++++++++++++++++ 7 files changed, 483 insertions(+), 281 deletions(-) create mode 100644 runc/src/task.rs diff --git a/runc/Cargo.lock b/runc/Cargo.lock index 686c3935..63b493da 100644 --- a/runc/Cargo.lock +++ b/runc/Cargo.lock @@ -1566,6 +1566,7 @@ dependencies = [ "containerd-shim", "env_logger", "futures", + "go-flag", "log", "nix 0.25.1", "oci-spec", @@ -1578,6 +1579,7 @@ dependencies = [ "signal-hook-tokio", "time", "tokio", + "uuid", ] [[package]] diff --git a/runc/Cargo.toml b/runc/Cargo.toml index 11ab143e..c23bddec 100644 --- a/runc/Cargo.toml +++ b/runc/Cargo.toml @@ -22,8 +22,9 @@ procfs = "0.15.1" prctl = "1.0.0" os_pipe = "1.1.4" byteorder = "1.4.3" +go-flag = "0.1.0" +uuid = { version = "1.1.2", features = ["v4"] } containerd-sandbox = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5" } containerd-shim = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } runc = { git = "https://github.com/kuasar-io/rust-extensions.git", rev = "6ae99540b754cd28c5389d5d6fdeff6ec7290ec5", features = ["async"] } - diff --git a/runc/src/common.rs b/runc/src/common.rs index 936e9adc..e876a895 100644 --- a/runc/src/common.rs +++ b/runc/src/common.rs @@ -16,6 +16,7 @@ use std::{io::IoSliceMut, ops::Deref, os::unix::io::RawFd, path::Path, sync::Arc}; +use anyhow::anyhow; use containerd_shim::{ api::{ExecProcessRequest, Options}, io::Stdio, @@ -38,10 +39,6 @@ use runc::{ Runc, Spawner, }; -pub const GROUP_LABELS: [&str; 2] = [ - "io.containerd.runc.v2.group", - "io.kubernetes.cri.sandbox-id", -]; pub const INIT_PID_FILE: &str = "init.pid"; pub struct ProcessIO { @@ -224,3 +221,15 @@ pub fn has_shared_pid_namespace(spec: &Spec) -> bool { }, } } + +pub fn prepare_unix_socket(unix_socket: &str) -> Result<(), anyhow::Error> { + let sock_path = Path::new(unix_socket); + if sock_path.exists() { + std::fs::remove_file(sock_path)?; + } + if let Some(sock_parent) = sock_path.parent() { + std::fs::create_dir_all(sock_parent) + .map_err(|e| anyhow!("failed to create {}, {}", sock_parent.display(), e))?; + } + Ok(()) +} diff --git a/runc/src/main.rs b/runc/src/main.rs index 112baa0d..d53d5018 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -1,42 +1,50 @@ use std::ffi::CString; -use std::fs::File; -use std::io::{Read, Write}; use std::os::fd::RawFd; -use std::path::Path; -use std::process::{exit, id}; +use std::process::exit; use anyhow::anyhow; -use byteorder::WriteBytesExt; use containerd_shim::asynchronous::monitor::monitor_notify_by_pid; use futures::StreamExt; use log::{debug, error, warn}; -use nix::{errno::Errno, libc, NixPath, sys::{ - wait, - wait::{WaitPidFlag, WaitStatus}, -}, unistd::Pid}; use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag}; -use nix::sched::{CloneFlags, setns, unshare}; -use nix::sys::signal::{SaFlags, SigAction, sigaction, SIGCHLD, SigHandler, SigSet}; +use nix::sched::{setns, unshare, CloneFlags}; +use nix::sys::signal::{sigaction, SaFlags, SigAction, SigHandler, SigSet, SIGCHLD}; use nix::sys::stat::Mode; -use nix::unistd::{close, fork, ForkResult, pause, pipe, read, write}; +use nix::unistd::{close, fork, pause, pipe, read, write, ForkResult}; +use nix::{ + errno::Errno, + libc, + sys::{ + wait, + wait::{WaitPidFlag, WaitStatus}, + }, + unistd::Pid, +}; use prctl::PrctlMM; use signal_hook_tokio::Signals; -use tokio::fs::create_dir_all; +use uuid::Uuid; use crate::sandbox::{RuncSandboxer, SandboxParent}; +use crate::task::fork_task_server; -mod sandbox; -mod runc; mod common; - -pub const TASK_ADDRESS_SOCK: &str = "/run/kuasar/task.sock"; +mod runc; +mod sandbox; +mod task; fn main() { env_logger::builder().format_timestamp_micros().init(); let sandbox_parent = fork_sandbox_parent().unwrap(); + let os_args: Vec<_> = std::env::args_os().collect(); + // TODO avoid parse args multiple times + let flags = containerd_sandbox::args::parse(&os_args[1..]).unwrap(); + let task_socket = format!("{}/task-{}.sock", flags.dir, Uuid::new_v4().to_string()); + fork_task_server(&task_socket, &flags.dir).unwrap(); let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { - start_sandboxer(sandbox_parent).await.unwrap(); + start_sandboxer(sandbox_parent, task_socket, flags.dir) + .await + .unwrap(); }); } @@ -57,6 +65,7 @@ fn fork_sandbox_parent() -> Result { ForkResult::Child => { close(reqw).unwrap_or_default(); close(respr).unwrap_or_default(); + prctl::set_child_subreaper(true).unwrap(); let comm = format!("[sandbox-parent]"); let comm_cstr = CString::new(comm).unwrap(); let addr = comm_cstr.as_ptr(); @@ -66,7 +75,9 @@ fn fork_sandbox_parent() -> Result { SaFlags::empty(), SigSet::empty(), ); - unsafe { sigaction(SIGCHLD, &sig_action).unwrap(); } + unsafe { + sigaction(SIGCHLD, &sig_action).unwrap(); + } loop { let buffer = read_count(reqr, 512).unwrap(); let id = String::from_utf8_lossy(&buffer[0..64]).to_string(); @@ -135,16 +146,17 @@ fn fork_sandbox(id: &str, netns: &str) -> Result { match unsafe { fork().map_err(|e| anyhow!("failed to fork sandbox {}", e))? } { ForkResult::Parent { child } => { debug!("forked process {} for the sandbox {}", child, id); - close(w); + close(w).unwrap_or_default(); let mut resp = [0u8; 4]; - let mut r = read_count(r, 4)?; + let r = read_count(r, 4)?; resp[..].copy_from_slice(r.as_slice()); let pid = i32::from_le_bytes(resp); return Ok(pid); } ForkResult::Child => { - close(r); - unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID).unwrap(); + close(r).unwrap_or_default(); + unshare(CloneFlags::CLONE_NEWIPC | CloneFlags::CLONE_NEWUTS | CloneFlags::CLONE_NEWPID) + .unwrap(); match unsafe { fork().unwrap() } { ForkResult::Parent { child } => { debug!("forked process {} for the sandbox {}", child, id); @@ -179,30 +191,6 @@ fn set_process_comm(addr: u64, len: u64) { } } -async fn start_sandboxer(sandbox_parent: SandboxParent) -> anyhow::Result<()> { - tokio::spawn(async move { - let signals = Signals::new([libc::SIGTERM, libc::SIGINT, libc::SIGPIPE, libc::SIGCHLD]) - .expect("new signal failed"); - handle_signals(signals).await; - }); - prctl::set_child_subreaper(true).unwrap(); - let sock_path = Path::new(TASK_ADDRESS_SOCK); - if sock_path.exists() { - tokio::fs::remove_file(sock_path).await?; - } - if let Some(sock_parent) = sock_path.parent() { - create_dir_all(sock_parent) - .await - .map_err(|e| anyhow!("failed to create {}, {}", sock_parent.display(), e))?; - } - let sock_addr = format!("unix://{}", TASK_ADDRESS_SOCK); - let sandboxer = RuncSandboxer::new(sandbox_parent, &sock_addr).await?; - containerd_sandbox::run("runc-sandboxer", sandboxer) - .await - .unwrap(); - Ok(()) -} - extern "C" fn sandbox_parent_handle_signals(_: libc::c_int) { loop { match wait::waitpid(Some(Pid::from_raw(-1)), Some(WaitPidFlag::WNOHANG)) { @@ -223,6 +211,18 @@ extern "C" fn sandbox_parent_handle_signals(_: libc::c_int) { } } +async fn start_sandboxer( + sandbox_parent: SandboxParent, + task_socket: String, + dir: String, +) -> anyhow::Result<()> { + let task_address = format!("unix://{}", task_socket); + let sandboxer = RuncSandboxer::new(sandbox_parent, &task_address).await?; + sandboxer.recover(&dir).await?; + containerd_sandbox::run("runc-sandboxer", sandboxer).await?; + Ok(()) +} + async fn handle_signals(signals: Signals) { let mut signals = signals.fuse(); while let Some(sig) = signals.next().await { diff --git a/runc/src/runc.rs b/runc/src/runc.rs index 951c4b51..ebdc9d1a 100644 --- a/runc/src/runc.rs +++ b/runc/src/runc.rs @@ -24,9 +24,9 @@ use std::{ process::ExitStatus, sync::Arc, }; -use std::collections::HashMap; use async_trait::async_trait; +use containerd_sandbox::spec::{get_sandbox_id, JsonSpec}; use containerd_shim::{ api::{CreateTaskRequest, ExecProcessRequest, Options, Status}, asynchronous::{ @@ -35,34 +35,36 @@ use containerd_shim::{ monitor::{monitor_subscribe, monitor_unsubscribe, Subscription}, processes::{ProcessLifecycle, ProcessTemplate}, }, - Console, - Error, - ExitSignal, - io::Stdio, io_error, + io::Stdio, + io_error, monitor::{ExitEvent, Subject, Topic}, - other, - other_error, protos::{ + other, other_error, + protos::{ api::ProcessInfo, cgroups::metrics::Metrics, protobuf::{CodedInputStream, Message}, - }, Result, util::{asyncify, mkdir, mount_rootfs, read_file_to_str, write_options, write_runtime}, + }, + util::{asyncify, mkdir, mount_rootfs, read_file_to_str, write_options, write_runtime}, + Console, Error, ExitSignal, Result, +}; +use containerd_shim::{ + asynchronous::util::write_str_to_file, + util::{read_spec, CONFIG_FILE_NAME}, }; -use containerd_shim::asynchronous::util::{read_options, write_str_to_file}; use log::{debug, error}; use nix::{sys::signal::kill, unistd::Pid}; use oci_spec::runtime::{LinuxResources, Process}; use runc::{Command, Runc, Spawner}; -use runc::options::DeleteOpts; use serde::{Deserialize, Serialize}; +use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::{ fs::{File, OpenOptions}, io::{AsyncRead, AsyncReadExt, AsyncWrite}, }; -use tokio::io::{AsyncBufReadExt, BufReader}; use crate::common::{ - check_kill_error, create_io, create_runc, CreateConfig, get_spec_from_request, INIT_PID_FILE, - ProcessIO, receive_socket, ShimExecutor, + check_kill_error, create_io, create_runc, get_spec_from_request, receive_socket, CreateConfig, + ProcessIO, ShimExecutor, INIT_PID_FILE, }; pub type ExecProcess = ProcessTemplate; @@ -70,8 +72,10 @@ pub type InitProcess = ProcessTemplate; pub type RuncContainer = ContainerTemplate; -#[derive(Clone, Default)] -pub(crate) struct RuncFactory; +#[derive(Clone)] +pub(crate) struct RuncFactory { + sandbox_parent_dir: String, +} #[derive(Serialize, Deserialize)] pub struct SerializableStdio { @@ -123,6 +127,8 @@ impl ContainerFactory for RuncFactory { write_options(bundle, &opts).await?; write_runtime(bundle, runtime).await?; + self.rewrite_spec(bundle).await?; + let rootfs_vec = req.rootfs().to_vec(); let rootfs = if !rootfs_vec.is_empty() { let tmp_rootfs = Path::new(bundle).join("rootfs"); @@ -177,6 +183,12 @@ impl ContainerFactory for RuncFactory { } impl RuncFactory { + pub fn new(sandbox_parent_dir: &str) -> Self { + Self { + sandbox_parent_dir: sandbox_parent_dir.to_string(), + } + } + async fn do_create(&self, init: &mut InitProcess, _config: CreateConfig) -> Result<()> { let id = init.id.to_string(); let stdio = &init.stdio; @@ -215,6 +227,34 @@ impl RuncFactory { init.pid = pid; Ok(()) } + + async fn rewrite_spec(&self, bundle: &str) -> Result<()> { + let mut spec: JsonSpec = read_spec(bundle).await?; + let sandbox_id = if let Some(id) = get_sandbox_id(&spec.annotations) { + id + } else { + return Ok(()); + }; + + if let Some(l) = &mut spec.linux { + for ns in &mut l.namespaces { + if ns.r#type == "uts" && ns.path == "/proc/0/ns/uts" { + ns.path = format!("{}/{}/uts", self.sandbox_parent_dir, sandbox_id); + } + if ns.r#type == "ipc" && ns.path == "/proc/0/ns/ipc" { + ns.path = format!("{}/{}/ipc", self.sandbox_parent_dir, sandbox_id); + } + if ns.r#type == "network" && ns.path == "/proc/0/ns/net" { + ns.path = format!("{}/{}/net", self.sandbox_parent_dir, sandbox_id); + } + } + } + let config_file_path = format!("{}/{}", bundle, CONFIG_FILE_NAME); + let spec_content_new = serde_json::to_string(&spec) + .map_err(other_error!(e, "failed to marshal spec to string"))?; + write_str_to_file(&config_file_path, &spec_content_new).await?; + Ok(()) + } } pub struct RuncExecFactory { @@ -437,7 +477,7 @@ impl ProcessLifecycle for RuncExecLifecycle { Pid::from_raw(p.pid as i32), nix::sys::signal::Signal::try_from(signal as i32).unwrap(), ) - .map_err(Into::into) + .map_err(Into::into) } } @@ -605,10 +645,10 @@ pub async fn copy_io(pio: &ProcessIO, stdio: &Stdio, exit_signal: Arc(from: R, to: W, exit_signal: Arc, on_close: Option) - where - R: AsyncRead + Send + Unpin + 'static, - W: AsyncWrite + Send + Unpin + 'static, - F: FnOnce() + Send + 'static, +where + R: AsyncRead + Send + Unpin + 'static, + W: AsyncWrite + Send + Unpin + 'static, + F: FnOnce() + Send + 'static, { let mut src = from; let mut dst = to; @@ -656,7 +696,12 @@ async fn copy_io_or_console

( #[async_trait] impl Spawner for ShimExecutor { - async fn execute(&self, cmd: Command, after_start: Box, wait_output: bool) -> runc::Result<(ExitStatus, u32, String, String)> { + async fn execute( + &self, + cmd: Command, + after_start: Box, + wait_output: bool, + ) -> runc::Result<(ExitStatus, u32, String, String)> { let mut cmd = cmd; let subscription = monitor_subscribe(Topic::Pid) .await @@ -673,12 +718,16 @@ impl Spawner for ShimExecutor { let pid = child.id().unwrap(); let (stdout, stderr, exit_code) = if wait_output { tokio::join!( - read_std(child.stdout), - read_std(child.stderr), - wait_pid(pid as i32, subscription) - ) + read_std(child.stdout), + read_std(child.stderr), + wait_pid(pid as i32, subscription) + ) } else { - ("".to_string(), "".to_string(), wait_pid(pid as i32, subscription).await) + ( + "".to_string(), + "".to_string(), + wait_pid(pid as i32, subscription).await, + ) }; let status = ExitStatus::from_raw(exit_code); monitor_unsubscribe(sid).await.unwrap_or_default(); @@ -686,70 +735,9 @@ impl Spawner for ShimExecutor { } } -pub async fn recover() -> Result> { - let mut opts = Options::new(); - let mut runc_containers = HashMap::new(); - for ns in ["k8s.io", "moby", "default"] { - let runc = create_runc("", ns, Path::new("/tmp"), &opts, Some(Arc::new(ShimExecutor::default())))?; - let containers = runc.list().await - .map_err(other_error!(e, "failed to call runc list"))?; - for c in containers { - if !Path::new(&c.bundle).exists() { - runc.delete(&c.id, Some(&DeleteOpts { force: true })).await.unwrap_or_default(); - continue; - } - let runc_container = recover_one(&c, &runc).await; - runc_containers.insert(c.id.to_string(), runc_container); - } - } - Ok(runc_containers) -} - -pub async fn recover_one(c: &runc::container::Container, runtime: &Runc) -> RuncContainer { - let stdio = if let Ok(s) = read_stdio(&*c.bundle).await { - s - } else { - Stdio { - stdin: "".to_string(), - stdout: "".to_string(), - stderr: "".to_string(), - terminal: false, - } - }; - let opts = if let Ok(r) = read_options(&c.bundle).await { - r - } else { - Options::new() - }; - let mut init = InitProcess::new( - &c.id, - stdio, - RuncInitLifecycle::new(runtime.clone(), opts.clone(), &c.bundle), - ); - init.state = match &*c.status { - "created" => Status::CREATED, - "running" => Status::RUNNING, - "stopped" => Status::STOPPED, - _ => Status::CREATED, - }; - init.pid = c.pid as i32; - RuncContainer { - id: "".to_string(), - bundle: "".to_string(), - init, - process_factory: RuncExecFactory { - runtime: runtime.clone(), - bundle: c.bundle.to_string(), - io_uid: opts.io_uid, - io_gid: opts.io_gid, - }, - processes: Default::default(), - } -} - async fn read_std(std: Option) -> String - where - T: AsyncRead + Unpin, +where + T: AsyncRead + Unpin, { let mut std = std; if let Some(mut std) = std.take() { @@ -767,9 +755,9 @@ async fn wait_pid(pid: i32, s: Subscription) -> i32 { let mut s = s; loop { if let Some(ExitEvent { - subject: Subject::Pid(epid), - exit_code: code, - }) = s.rx.recv().await + subject: Subject::Pid(epid), + exit_code: code, + }) = s.rx.recv().await { if pid == epid { monitor_unsubscribe(s.id).await.unwrap_or_default(); @@ -812,11 +800,3 @@ async fn write_stdio(bundle: &str, stdio: &Stdio) -> Result<()> { .map_err(other_error!(e, "failed to serialize stdio"))?; write_str_to_file(Path::new(bundle).join("stdio"), stdio_str).await } - -async fn read_stdio(bundle: &str) -> Result { - let stdio_str = read_file_to_str(Path::new(bundle).join("stdio")).await?; - let serializable_stdio: SerializableStdio = serde_json::from_str(&stdio_str) - .map_err(other_error!(e, "failed to deserialize stdio"))?; - Ok(serializable_stdio.into()) -} - diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index 77ae6df0..ba66733c 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -1,49 +1,43 @@ use std::collections::HashMap; -use std::ffi::CString; -use std::io::{Read, Write}; +use std::io::Write; use std::os::fd::RawFd; use std::path::Path; use std::sync::Arc; use anyhow::anyhow; use async_trait::async_trait; -use containerd_sandbox::{Container, ContainerOption, Sandbox, Sandboxer, SandboxOption, SandboxStatus}; +use containerd_sandbox::cri::api::v1::NamespaceMode; use containerd_sandbox::data::{ContainerData, SandboxData}; use containerd_sandbox::error::{Error, Result}; use containerd_sandbox::signal::ExitSignal; -use containerd_shim::api::Options; -use containerd_shim::asynchronous::monitor::{monitor_subscribe, monitor_unsubscribe}; -use containerd_shim::asynchronous::task::TaskService; -use containerd_shim::monitor::{Subject, Topic}; -use containerd_shim::processes::Process; -use containerd_shim::protos::shim_async::create_task; -use containerd_shim::protos::ttrpc::asynchronous::Server; -use log::debug; -use nix::errno::Errno; -use nix::fcntl::OFlag; -use nix::NixPath; -use nix::sched::{CloneFlags, setns, unshare}; -use nix::sys::signal::{kill, Signal}; -use nix::sys::stat::Mode; -use nix::unistd::{close, fork, ForkResult, pause, Pid}; -use os_pipe::{PipeReader, PipeWriter}; -use prctl::PrctlMM; -use runc::options::DeleteOpts; +use containerd_sandbox::{ + Container, ContainerOption, Sandbox, SandboxOption, SandboxStatus, Sandboxer, +}; +use log::warn; +use nix::{ + mount::MsFlags, + unistd::{close, Pid}, +}; +use nix::{ + mount::{mount, umount}, + sys::signal::{kill, Signal}, +}; use serde::{Deserialize, Serialize}; -use tokio::fs::{create_dir_all, OpenOptions}; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::sync::{Mutex, RwLock}; -use tokio::sync::mpsc::channel; +use tokio::{ + fs::{create_dir_all, remove_dir_all}, + io::{AsyncReadExt, AsyncWriteExt}, +}; +use tokio::{ + fs::{File, OpenOptions}, + sync::{Mutex, RwLock}, +}; use crate::{read_count, write_all}; -use crate::common::{create_runc, ShimExecutor}; -use crate::runc::{recover, RuncContainer, RuncFactory}; pub struct RuncSandboxer { #[allow(clippy::type_complexity)] pub(crate) sandboxes: Arc>>>>, task_address: String, - server: Server, sandbox_parent: Arc>, } @@ -76,21 +70,15 @@ pub struct SandboxParent { impl SandboxParent { pub fn new(req: RawFd, resp: RawFd) -> Self { - Self { - req, - resp, - } + Self { req, resp } } pub fn fork_sandbox_process(&mut self, id: &str, netns: &str) -> Result { let mut req = [0u8; 512]; - use std::io::Write; - unsafe { - (&mut req[0..64]).write_all(id.as_bytes())?; - (&mut req[64..]).write_all(netns.as_bytes())?; - } + (&mut req[0..64]).write_all(id.as_bytes())?; + (&mut req[64..]).write_all(netns.as_bytes())?; write_all(self.req, &req)?; let mut resp = [0u8; 4]; - let mut r = read_count(self.resp, 4)?; + let r = read_count(self.resp, 4)?; resp[..].copy_from_slice(r.as_slice()); let pid = i32::from_le_bytes(resp); Ok(pid) @@ -106,22 +94,42 @@ impl Drop for SandboxParent { impl RuncSandboxer { pub async fn new(sandbox_parent: SandboxParent, task_address: &str) -> Result { - let task = start_task_service().await?; - let task_service = create_task(Arc::new(Box::new(task))); - let mut server = Server::new().register_service(task_service); - server = server - .bind(&task_address) - .map_err(|e| anyhow!("failed to bind socket {}, {}", task_address, e))?; - server - .start() - .await - .map_err(|e| anyhow!("failed to start task server, {}", e))?; - Ok(Self { + return Ok(Self { task_address: task_address.to_string(), - server, sandboxes: Default::default(), sandbox_parent: Arc::new(Mutex::new(sandbox_parent)), - }) + }); + } + + pub async fn recover(&self, dir: &str) -> Result<()> { + let mut subs = tokio::fs::read_dir(dir).await.map_err(Error::IO)?; + let mut pids = Vec::new(); + while let Some(entry) = subs.next_entry().await.unwrap() { + if let Ok(t) = entry.file_type().await { + if t.is_dir() { + let path = Path::new(dir).join(entry.file_name()); + match RuncSandbox::recover(&path).await { + Ok(sb) => { + if let SandboxStatus::Running(pid) = sb.status { + // TODO need to check if the sandbox process is still running. + pids.push(pid); + } + let sb_mutex = Arc::new(Mutex::new(sb)); + self.sandboxes + .write() + .await + .insert(entry.file_name().to_str().unwrap().to_string(), sb_mutex); + } + Err(e) => { + warn!("failed to recover sandbox {:?}, {:?}", entry.file_name(), e); + remove_dir_all(&path).await.unwrap_or_default(); + } + } + } + } + } + + Ok(()) } } @@ -152,9 +160,16 @@ impl Sandboxer for RuncSandboxer { let mut sandbox = sandbox.lock().await; let mut sandbox_parent = self.sandbox_parent.lock().await; let sandbox_pid = sandbox_parent.fork_sandbox_process(id, &sandbox.data.netns)?; - sandbox.status = SandboxStatus::Running(sandbox_pid as u32); + sandbox.prepare_sandbox_ns(sandbox_pid).await.map_err(|e| { + kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + e + })?; + sandbox.data.task_address = self.task_address.clone(); - sandbox.dump().await?; + sandbox.dump().await.map_err(|e| { + kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + e + })?; Ok(()) } @@ -183,8 +198,16 @@ impl Sandboxer for RuncSandboxer { impl RuncSandbox { async fn stop(&mut self) -> Result<()> { if let SandboxStatus::Running(pid) = self.status { - kill(Pid::from_raw(pid as i32), Signal::SIGKILL) - .map_err(|e| anyhow!("failed to kill sandbox process {}", e))?; + if pid != 0 { + kill(Pid::from_raw(pid as i32), Signal::SIGKILL).unwrap_or_default(); + } else { + let uts_path = format!("{}/uts", self.base_dir); + umount(&*uts_path) + .map_err(|e| anyhow!("failed to umount sandbox uts ns, {}", e))?; + let ipc_path = format!("{}/ipc", self.base_dir); + umount(&*ipc_path) + .map_err(|e| anyhow!("failed to umount sandbox ipc ns, {}", e))?; + } } let ts = time::OffsetDateTime::now_utc().unix_timestamp_nanos(); self.status = SandboxStatus::Stopped(0, ts); @@ -204,7 +227,7 @@ impl RuncSandbox { .read_to_end(&mut content) .await .map_err(Error::IO)?; - let mut sb = serde_json::from_slice::(content.as_slice()) + let sb = serde_json::from_slice::(content.as_slice()) .map_err(|e| anyhow!("failed to deserialize sandbox, {}", e))?; Ok(sb) } @@ -225,6 +248,75 @@ impl RuncSandbox { .map_err(Error::IO)?; Ok(()) } + + async fn prepare_sandbox_ns(&mut self, sandbox_pid: i32) -> Result<()> { + if !self.has_shared_pid_namespace() { + let uts_path = format!("{}/uts", self.base_dir); + { + File::create(&uts_path).await.map_err(Error::IO)?; + } + mount( + Some(&*format!("/proc/{}/ns/uts", sandbox_pid)), + &*uts_path, + None::<&str>, + MsFlags::MS_BIND, + None::<&str>, + ) + .map_err(|e| anyhow!("failed to mount sandbox uts ns, {}", e))?; + + let ipc_path = format!("{}/ipc", self.base_dir); + { + File::create(&ipc_path).await.map_err(Error::IO)?; + } + mount( + Some(&*format!("/proc/{}/ns/ipc", sandbox_pid)), + &*ipc_path, + None::<&str>, + MsFlags::MS_BIND, + None::<&str>, + ) + .map_err(|e| anyhow!("failed to mount sandbox ipc ns, {}", e))?; + + let net_path = format!("{}/net", self.base_dir); + { + File::create(&net_path).await.map_err(Error::IO)?; + } + mount( + Some(&*format!("/proc/{}/ns/net", sandbox_pid)), + &*net_path, + None::<&str>, + MsFlags::MS_BIND, + None::<&str>, + ) + .map_err(|e| anyhow!("failed to mount sandbox network ns, {}", e))?; + + kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + self.status = SandboxStatus::Running(0); + } else { + self.status = SandboxStatus::Running(sandbox_pid as u32); + } + Ok(()) + } + + fn has_shared_pid_namespace(&self) -> bool { + if let Some(conf) = &self.data.config { + let a = conf + .linux + .as_ref() + .unwrap() + .security_context + .as_ref() + .unwrap() + .namespace_options + .as_ref() + .unwrap() + .pid(); + if a == NamespaceMode::Pod { + return true; + } + } + return false; + } } #[async_trait] @@ -246,9 +338,12 @@ impl Sandbox for RuncSandbox { } async fn append_container(&mut self, id: &str, option: ContainerOption) -> Result<()> { - self.containers.insert(id.to_string(), RuncContainerData { - data: option.container - }); + self.containers.insert( + id.to_string(), + RuncContainerData { + data: option.container, + }, + ); self.dump().await?; Ok(()) } @@ -271,73 +366,3 @@ impl Sandbox for RuncSandbox { Ok(self.data.clone()) } } - -// any wasm runtime implementation should implement this function -pub async fn process_exits(task: &TaskService) { - let containers = task.containers.clone(); - let exit_signal = task.exit.clone(); - let mut s = monitor_subscribe(Topic::Pid) - .await - .expect("monitor subscribe failed"); - tokio::spawn(async move { - loop { - tokio::select! { - _ = exit_signal.wait() => { - debug!("sandbox exit, should break"); - monitor_unsubscribe(s.id).await.unwrap_or_default(); - return; - }, - res = s.rx.recv() => { - if let Some(e) = res { - if let Subject::Pid(pid) = e.subject { - debug!("receive exit event: {}", &e); - let exit_code = e.exit_code; - for (_k, cont) in containers.lock().await.iter_mut() { - // pid belongs to container init process - if cont.init.pid == pid { - // set exit for init process - cont.init.set_exited(exit_code).await; - break; - } - - // pid belongs to container common process - for (_exec_id, p) in cont.processes.iter_mut() { - // set exit for exec process - if p.pid == pid { - p.set_exited(exit_code).await; - break; - } - } - } - } - } else { - monitor_unsubscribe(s.id).await.unwrap_or_default(); - return; - } - } - } - } - }); -} - -async fn start_task_service() -> Result> { - let (tx, mut rx) = channel(128); - let factory = RuncFactory::default(); - let containers = recover().await.map_err(|e| anyhow!("failed to recover containers {}", e))?; - let task = TaskService { - factory, - containers: Arc::new(Mutex::new(containers)), - namespace: "k8s.io".to_string(), - exit: Arc::new(Default::default()), - tx: tx.clone(), - }; - - process_exits(&task).await; - - tokio::spawn(async move { - while let Some((_topic, e)) = rx.recv().await { - debug!("received event {:?}", e); - } - }); - Ok(task) -} \ No newline at end of file diff --git a/runc/src/task.rs b/runc/src/task.rs new file mode 100644 index 00000000..85acbc27 --- /dev/null +++ b/runc/src/task.rs @@ -0,0 +1,185 @@ +use std::os::fd::{AsRawFd, RawFd}; +use std::os::unix::net::UnixListener; +use std::process::exit; +use std::sync::Arc; +use std::{collections::HashMap, time::Duration}; + +use anyhow::anyhow; +use containerd_sandbox::error; +use containerd_shim::asynchronous::monitor::{monitor_subscribe, monitor_unsubscribe}; +use containerd_shim::asynchronous::task::TaskService; +use containerd_shim::asynchronous::util::{asyncify, read_spec}; +use containerd_shim::container::Container; +use containerd_shim::monitor::{Subject, Topic}; +use containerd_shim::processes::Process; +use containerd_shim::protos::shim::shim_ttrpc_async::create_task; +use containerd_shim::protos::ttrpc::asynchronous::Server; +use log::{debug, error}; +use nix::libc; +use nix::unistd::{close, fork, pipe, ForkResult}; +use signal_hook_tokio::Signals; +use tokio::sync::Mutex; +use tokio::{sync::mpsc::channel, time::sleep}; + +use crate::common::{has_shared_pid_namespace, prepare_unix_socket}; +use crate::runc::{RuncContainer, RuncFactory}; +use crate::{handle_signals, read_count}; + +pub fn fork_task_server(task_socket: &str, sandbox_parent_dir: &str) -> Result<(), anyhow::Error> { + prepare_unix_socket(task_socket)?; + + let task_listener = UnixListener::bind(task_socket)?; + let (pipe_r, pipe_w) = pipe().map_err(|e| anyhow!("failed to create pipe {}", e))?; + match unsafe { fork().map_err(|e| anyhow!("failed to fork task service {}", e))? } { + ForkResult::Parent { child: _ } => { + close(pipe_r).unwrap_or_default(); + drop(task_listener); + return Ok(()); + } + ForkResult::Child => { + close(pipe_w).unwrap_or_default(); + prctl::set_child_subreaper(true).unwrap(); + // TODO set thread count + let runtime = tokio::runtime::Runtime::new().unwrap(); + runtime.block_on(async move { + if let Err(e) = run_task_server(task_listener, pipe_r, sandbox_parent_dir).await { + error!("run task server failed {}", e); + exit(-1); + } + }); + exit(0); + } + } +} + +async fn run_task_server( + listener: UnixListener, + exit_pipe: RawFd, + sandbox_parent_dir: &str, +) -> error::Result<()> { + let task = start_task_service(sandbox_parent_dir).await?; + let containers = task.containers.clone(); + let task_service: HashMap = + create_task(Arc::new(Box::new(task))); + let mut server = Server::new().register_service(task_service); + server = server + .add_listener(listener.as_raw_fd()) + .map_err(|e| anyhow!("failed to add listener to server {:?}", e))?; + server = server.set_domain_unix(); + + server + .start() + .await + .map_err(|e| anyhow!("failed to start task server, {}", e))?; + // wait parent exit + asyncify(move || Ok(read_count(exit_pipe, 1).unwrap_or_default())) + .await + .unwrap_or_default(); + + // after parent exit, wait exit_signal so that if all containers is removed, we can exit. + loop { + if containers.lock().await.is_empty() { + break; + } + sleep(Duration::from_millis(100)).await; + } + server + .shutdown() + .await + .map_err(|e| anyhow!("failed to shutdown task server {}", e))?; + Ok(()) +} + +async fn process_exits(task: &TaskService) { + let containers = task.containers.clone(); + let exit_signal = task.exit.clone(); + let mut s = monitor_subscribe(Topic::Pid) + .await + .expect("monitor subscribe failed"); + tokio::spawn(async move { + loop { + tokio::select! { + _ = exit_signal.wait() => { + debug!("sandbox exit, should break"); + monitor_unsubscribe(s.id).await.unwrap_or_default(); + return; + }, + res = s.rx.recv() => { + if let Some(e) = res { + if let Subject::Pid(pid) = e.subject { + debug!("receive exit event: {}", &e); + let exit_code = e.exit_code; + for (_k, cont) in containers.lock().await.iter_mut() { + // pid belongs to container init process + if cont.init.pid == pid { + // kill all children process if the container has a + // private PID namespace + if should_kill_all_on_exit(&cont.bundle).await { + cont.kill(None, 9, true).await.unwrap_or_else(|e| { + error!("failed to kill init's children: {}", e) + }); + } + cont.init.set_exited(exit_code).await; + break; + } + + // pid belongs to container common process + for (_exec_id, p) in cont.processes.iter_mut() { + // set exit for exec process + if p.pid == pid { + p.set_exited(exit_code).await; + break; + } + } + } + } + } else { + monitor_unsubscribe(s.id).await.unwrap_or_default(); + return; + } + } + } + } + }); +} + +async fn start_task_service( + sandbox_parent_dir: &str, +) -> error::Result> { + tokio::spawn(async move { + let signals = Signals::new([libc::SIGTERM, libc::SIGINT, libc::SIGPIPE, libc::SIGCHLD]) + .expect("new signal failed"); + handle_signals(signals).await; + }); + let (tx, mut rx) = channel(128); + let factory = RuncFactory::new(sandbox_parent_dir); + let task = TaskService { + factory, + containers: Arc::new(Mutex::new(HashMap::new())), + namespace: "k8s.io".to_string(), + exit: Arc::new(Default::default()), + tx: tx.clone(), + }; + + process_exits(&task).await; + + tokio::spawn(async move { + while let Some((_topic, e)) = rx.recv().await { + debug!("received event {:?}", e); + } + }); + Ok(task) +} + +async fn should_kill_all_on_exit(bundle_path: &str) -> bool { + match read_spec(bundle_path).await { + Ok(spec) => has_shared_pid_namespace(&spec), + Err(e) => { + error!( + "failed to read spec when call should_kill_all_on_exit: {}", + e + ); + false + } + } +} From d0bd12930fb7f228b4f9778279e263f75d40fdf7 Mon Sep 17 00:00:00 2001 From: Abel Feng Date: Thu, 7 Dec 2023 10:40:56 +0800 Subject: [PATCH 9/9] runc: add copyright and CI scripts Signed-off-by: Abel Feng --- .github/workflows/ci.yml | 4 +- Makefile | 11 +- runc/deny.toml | 274 +++++++++++++++++++++++++++++++++++++++ runc/src/common.rs | 20 +-- runc/src/main.rs | 30 ++++- runc/src/runc.rs | 4 +- runc/src/sandbox.rs | 28 +++- runc/src/task.rs | 18 ++- 8 files changed, 360 insertions(+), 29 deletions(-) create mode 100644 runc/deny.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 994a6f0c..e0415e42 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: checks: strategy: matrix: - directories: [vmm/sandbox, vmm/task, shim, quark] + directories: [vmm/sandbox, vmm/task, shim, quark, runc] features: [--all-features] include: - directories: wasm @@ -49,7 +49,7 @@ jobs: tests: strategy: matrix: - directories: [vmm/sandbox, vmm/task, shim, quark] + directories: [vmm/sandbox, vmm/task, shim, quark, runc] features: [--all-features] include: - directories: wasm diff --git a/Makefile b/Makefile index f4986d6b..2cdfb123 100644 --- a/Makefile +++ b/Makefile @@ -42,8 +42,13 @@ bin/quark-sandboxer: @cd quark && cargo build --release @mkdir -p bin && cp quark/target/release/quark-sandboxer bin/quark-sandboxer +bin/runc-sandboxer: + @cd runc && cargo build --release + @mkdir -p bin && cp runc/target/release/runc-sandboxer bin/runc-sandboxer + wasm: bin/wasm-sandboxer quark: bin/quark-sandboxer +runc: bin/runc-sandboxer ifeq ($(HYPERVISOR), stratovirt) vmm: bin/vmm-sandboxer bin/kuasar.initrd bin/vmlinux.bin @@ -57,6 +62,7 @@ clean: @cd vmm/task && cargo clean @cd wasm && cargo clean @cd quark && cargo clean + @cd runc && cargo clean install-vmm: @install -d -m 750 ${DEST_DIR}${BIN_DIR} @@ -82,4 +88,7 @@ install-wasm: install-quark: @install -p -m 550 bin/quark-sandboxer ${DEST_DIR}${BIN_DIR}/quark-sandboxer -install: all install-vmm install-wasm install-quark +install-runc: + @install -p -m 550 bin/runc-sandboxer ${DEST_DIR}${BIN_DIR}/runc-sandboxer + +install: all install-vmm install-wasm install-quark install-runc diff --git a/runc/deny.toml b/runc/deny.toml new file mode 100644 index 00000000..1098ed45 --- /dev/null +++ b/runc/deny.toml @@ -0,0 +1,274 @@ +# This template contains all of the possible sections and their default values + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# Root options + +# If 1 or more target triples (and optionally, target_features) are specified, +# only the specified targets will be checked when running `cargo deny check`. +# This means, if a particular package is only ever used as a target specific +# dependency, such as, for example, the `nix` crate only being used via the +# `target_family = "unix"` configuration, that only having windows targets in +# this list would mean the nix crate, as well as any of its exclusive +# dependencies not shared by any other crates, would be ignored, as the target +# list here is effectively saying which targets you are building for. +targets = [ + # The triple can be any string, but only the target triples built in to + # rustc (as of 1.40) can be checked against actual config expressions + #{ triple = "x86_64-unknown-linux-musl" }, + # You can also specify which target_features you promise are enabled for a + # particular target. target_features are currently not validated against + # the actual valid features supported by the target architecture. + #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, +] +# When creating the dependency graph used as the source of truth when checks are +# executed, this field can be used to prune crates from the graph, removing them +# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate +# is pruned from the graph, all of its dependencies will also be pruned unless +# they are connected to another crate in the graph that hasn't been pruned, +# so it should be used with care. The identifiers are [Package ID Specifications] +# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) +#exclude = [] +# If true, metadata will be collected with `--all-features`. Note that this can't +# be toggled off if true, if you want to conditionally enable `--all-features` it +# is recommended to pass `--all-features` on the cmd line instead +all-features = false +# If true, metadata will be collected with `--no-default-features`. The same +# caveat with `all-features` applies +no-default-features = false +# If set, these feature will be enabled when collecting metadata. If `--features` +# is specified on the cmd line they will take precedence over this option. +#features = [] +# When outputting inclusion graphs in diagnostics that include features, this +# option can be used to specify the depth at which feature edges will be added. +# This option is included since the graphs can be quite large and the addition +# of features from the crate(s) to all of the graph roots can be far too verbose. +# This option can be overridden via `--feature-depth` on the cmd line +feature-depth = 1 + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# The path where the advisory database is cloned/fetched into +db-path = "~/.cargo/advisory-db" +# The url(s) of the advisory databases to use +db-urls = ["https://github.com/rustsec/advisory-db"] +# The lint level for security vulnerabilities +vulnerability = "warn" +# The lint level for unmaintained crates +unmaintained = "warn" +# The lint level for crates that have been yanked from their source registry +yanked = "warn" +# The lint level for crates with security notices. Note that as of +# 2019-12-17 there are no security notice advisories in +# https://github.com/rustsec/advisory-db +notice = "warn" +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + #"RUSTSEC-0000-0000", +] +# Threshold for security vulnerabilities, any vulnerability with a CVSS score +# lower than the range specified will be ignored. Note that ignored advisories +# will still output a note when they are encountered. +# * None - CVSS Score 0.0 +# * Low - CVSS Score 0.1 - 3.9 +# * Medium - CVSS Score 4.0 - 6.9 +# * High - CVSS Score 7.0 - 8.9 +# * Critical - CVSS Score 9.0 - 10.0 +#severity-threshold = + +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +# Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. +# See Git Authentication for more information about setting up git authentication. +#git-fetch-with-cli = true + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +# The lint level for crates which do not have a detectable license +unlicensed = "warn" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "MIT", + "ISC", + "Unlicense", + "BSD-3-Clause", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "BSL-1.0", + "Unicode-DFS-2016" +] +# List of explicitly disallowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +deny = [ + #"Nokia", +] +# Lint level for licenses considered copyleft +copyleft = "warn" +# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses +# * both - The license will be approved if it is both OSI-approved *AND* FSF +# * either - The license will be approved if it is either OSI-approved *OR* FSF +# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF +# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved +# * neither - This predicate is ignored and the default lint level is used +allow-osi-fsf-free = "neither" +# Lint level used when no other predicates are matched +# 1. License isn't in the allow or deny lists +# 2. License isn't copyleft +# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" +default = "deny" +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.8 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], name = "adler32", version = "*" }, +] + +# Some crates don't have (easily) machine readable licensing information, +# adding a clarification entry for it allows you to manually specify the +# licensing information +#[[licenses.clarify]] +# The name of the crate the clarification applies to +#name = "ring" +# The optional version constraint for the crate +#version = "*" +# The SPDX expression for the license requirements of the crate +#expression = "MIT AND ISC AND OpenSSL" +# One or more files in the crate's source used as the "source of truth" for +# the license expression. If the contents match, the clarification will be used +# when running the license check, otherwise the clarification will be ignored +# and the crate will be checked normally, which may produce warnings or errors +# depending on the rest of your configuration +#license-files = [ +# Each entry is a crate relative path, and the (opaque) hash of its contents +#{ path = "LICENSE", hash = 0xbd0eed23 } +#] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [ + #"https://sekretz.com/registry +] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "allow" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overriden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overriden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" +# List of crates that are allowed. Use with care! +allow = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# List of crates to deny +deny = [ + # Each entry the name of a crate and a version range. If version is + # not specified, all versions will be matched. + #{ name = "ansi_term", version = "=0.11.0" }, + # + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ name = "ansi_term", version = "=0.11.0", wrappers = [] }, +] + +# List of features to allow/deny +# Each entry the name of a crate and a version range. If version is +# not specified, all versions will be matched. +#[[bans.features]] +#name = "reqwest" +# Features to not allow +#deny = ["json"] +# Features to allow +#allow = [ +# "rustls", +# "__rustls", +# "__tls", +# "hyper-rustls", +# "rustls", +# "rustls-pemfile", +# "rustls-tls-webpki-roots", +# "tokio-rustls", +# "webpki-roots", +#] +# If true, the allowed features must exactly match the enabled feature set. If +# this is set there is no point setting `deny` +#exact = true + +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + #{ name = "ansi_term", version = "=0.11.0" }, +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite. +skip-tree = [ + #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "warn" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "warn" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] + +[sources.allow-org] +# 1 or more github.com organizations to allow git sources for +github = [""] +# 1 or more gitlab.com organizations to allow git sources for +gitlab = [""] +# 1 or more bitbucket.org organizations to allow git sources for +bitbucket = [""] diff --git a/runc/src/common.rs b/runc/src/common.rs index e876a895..fa1011f7 100644 --- a/runc/src/common.rs +++ b/runc/src/common.rs @@ -1,17 +1,17 @@ /* - Copyright The containerd Authors. +Copyright 2022 The Kuasar Authors. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ use std::{io::IoSliceMut, ops::Deref, os::unix::io::RawFd, path::Path, sync::Arc}; diff --git a/runc/src/main.rs b/runc/src/main.rs index d53d5018..60299efa 100644 --- a/runc/src/main.rs +++ b/runc/src/main.rs @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Kuasar Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use std::ffi::CString; use std::os::fd::RawFd; use std::process::exit; @@ -38,7 +54,7 @@ fn main() { let os_args: Vec<_> = std::env::args_os().collect(); // TODO avoid parse args multiple times let flags = containerd_sandbox::args::parse(&os_args[1..]).unwrap(); - let task_socket = format!("{}/task-{}.sock", flags.dir, Uuid::new_v4().to_string()); + let task_socket = format!("{}/task-{}.sock", flags.dir, Uuid::new_v4()); fork_task_server(&task_socket, &flags.dir).unwrap(); let runtime = tokio::runtime::Runtime::new().unwrap(); runtime.block_on(async move { @@ -66,7 +82,7 @@ fn fork_sandbox_parent() -> Result { close(reqw).unwrap_or_default(); close(respr).unwrap_or_default(); prctl::set_child_subreaper(true).unwrap(); - let comm = format!("[sandbox-parent]"); + let comm = "[sandbox-parent]"; let comm_cstr = CString::new(comm).unwrap(); let addr = comm_cstr.as_ptr(); set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); @@ -82,8 +98,8 @@ fn fork_sandbox_parent() -> Result { let buffer = read_count(reqr, 512).unwrap(); let id = String::from_utf8_lossy(&buffer[0..64]).to_string(); let mut zero_index = 64; - for i in 64..512 { - if buffer[i] == 0 { + for (i, &b) in buffer.iter().enumerate().take(512).skip(64) { + if b == 0 { zero_index = i; break; } @@ -151,7 +167,7 @@ fn fork_sandbox(id: &str, netns: &str) -> Result { let r = read_count(r, 4)?; resp[..].copy_from_slice(r.as_slice()); let pid = i32::from_le_bytes(resp); - return Ok(pid); + Ok(pid) } ForkResult::Child => { close(r).unwrap_or_default(); @@ -170,7 +186,7 @@ fn fork_sandbox(id: &str, netns: &str) -> Result { set_process_comm(addr as u64, comm_cstr.as_bytes_with_nul().len() as u64); if !netns.is_empty() { let netns_fd = - nix::fcntl::open(&*netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); + nix::fcntl::open(netns, OFlag::O_CLOEXEC, Mode::empty()).unwrap(); setns(netns_fd, CloneFlags::CLONE_NEWNET).unwrap(); } loop { @@ -183,7 +199,7 @@ fn fork_sandbox(id: &str, netns: &str) -> Result { } fn set_process_comm(addr: u64, len: u64) { - if let Err(_) = prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr) { + if prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr).is_err() { prctl::set_mm(PrctlMM::PR_SET_MM_ARG_END, addr + len).unwrap(); prctl::set_mm(PrctlMM::PR_SET_MM_ARG_START, addr).unwrap() } else { diff --git a/runc/src/runc.rs b/runc/src/runc.rs index ebdc9d1a..3b55f1fd 100644 --- a/runc/src/runc.rs +++ b/runc/src/runc.rs @@ -219,7 +219,7 @@ impl RuncFactory { if let Some(s) = socket { s.clean().await; } - let runtime_e = runtime_error(e, &*bundle).await; + let runtime_e = runtime_error(e, bundle).await; return Err(runtime_e); } copy_io_or_console(init, socket, pio, init.lifecycle.exit_signal.clone()).await?; @@ -474,7 +474,7 @@ impl ProcessLifecycle for RuncExecLifecycle { } else { // TODO this is kill from nix crate, it is os specific, maybe have annotated with target os kill( - Pid::from_raw(p.pid as i32), + Pid::from_raw(p.pid), nix::sys::signal::Signal::try_from(signal as i32).unwrap(), ) .map_err(Into::into) diff --git a/runc/src/sandbox.rs b/runc/src/sandbox.rs index ba66733c..b4435bb0 100644 --- a/runc/src/sandbox.rs +++ b/runc/src/sandbox.rs @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Kuasar Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use std::collections::HashMap; use std::io::Write; use std::os::fd::RawFd; @@ -94,11 +110,11 @@ impl Drop for SandboxParent { impl RuncSandboxer { pub async fn new(sandbox_parent: SandboxParent, task_address: &str) -> Result { - return Ok(Self { + Ok(Self { task_address: task_address.to_string(), sandboxes: Default::default(), sandbox_parent: Arc::new(Mutex::new(sandbox_parent)), - }); + }) } pub async fn recover(&self, dir: &str) -> Result<()> { @@ -161,13 +177,13 @@ impl Sandboxer for RuncSandboxer { let mut sandbox_parent = self.sandbox_parent.lock().await; let sandbox_pid = sandbox_parent.fork_sandbox_process(id, &sandbox.data.netns)?; sandbox.prepare_sandbox_ns(sandbox_pid).await.map_err(|e| { - kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + kill(Pid::from_raw(sandbox_pid), Signal::SIGKILL).unwrap_or_default(); e })?; sandbox.data.task_address = self.task_address.clone(); sandbox.dump().await.map_err(|e| { - kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + kill(Pid::from_raw(sandbox_pid), Signal::SIGKILL).unwrap_or_default(); e })?; Ok(()) @@ -290,7 +306,7 @@ impl RuncSandbox { ) .map_err(|e| anyhow!("failed to mount sandbox network ns, {}", e))?; - kill(Pid::from_raw(sandbox_pid as i32), Signal::SIGKILL).unwrap_or_default(); + kill(Pid::from_raw(sandbox_pid), Signal::SIGKILL).unwrap_or_default(); self.status = SandboxStatus::Running(0); } else { self.status = SandboxStatus::Running(sandbox_pid as u32); @@ -315,7 +331,7 @@ impl RuncSandbox { return true; } } - return false; + false } } diff --git a/runc/src/task.rs b/runc/src/task.rs index 85acbc27..7462b6c7 100644 --- a/runc/src/task.rs +++ b/runc/src/task.rs @@ -1,3 +1,19 @@ +/* +Copyright 2022 The Kuasar Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + use std::os::fd::{AsRawFd, RawFd}; use std::os::unix::net::UnixListener; use std::process::exit; @@ -34,7 +50,7 @@ pub fn fork_task_server(task_socket: &str, sandbox_parent_dir: &str) -> Result<( ForkResult::Parent { child: _ } => { close(pipe_r).unwrap_or_default(); drop(task_listener); - return Ok(()); + Ok(()) } ForkResult::Child => { close(pipe_w).unwrap_or_default();