├── .cargo └── config ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── README.md ├── build.rs └── src ├── api ├── exec.rs ├── macros.rs ├── mod.rs ├── signals.rs └── sys.rs ├── bin └── init │ └── main.rs ├── lib.rs └── pty └── mod.rs /.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | linux-* 2 | vmlinux* 3 | .bin 4 | /target 5 | /init* -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "adler32" 5 | version = "1.2.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 8 | 9 | [[package]] 10 | name = "aho-corasick" 11 | version = "0.7.13" 12 | source = "registry+https://github.com/rust-lang/crates.io-index" 13 | checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" 14 | dependencies = [ 15 | "memchr", 16 | ] 17 | 18 | [[package]] 19 | name = "anyhow" 20 | version = "1.0.33" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c" 23 | 24 | [[package]] 25 | name = "arc-swap" 26 | version = "0.4.7" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" 29 | 30 | [[package]] 31 | name = "atty" 32 | version = "0.2.14" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 35 | dependencies = [ 36 | "hermit-abi", 37 | "libc", 38 | "winapi 0.3.9", 39 | ] 40 | 41 | [[package]] 42 | name = "autocfg" 43 | version = "0.1.7" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" 46 | 47 | [[package]] 48 | name = "autocfg" 49 | version = "1.0.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 52 | 53 | [[package]] 54 | name = "base64" 55 | version = "0.12.3" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" 58 | 59 | [[package]] 60 | name = "bitflags" 61 | version = "1.2.1" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 64 | 65 | [[package]] 66 | name = "block-buffer" 67 | version = "0.7.3" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" 70 | dependencies = [ 71 | "block-padding", 72 | "byte-tools", 73 | "byteorder", 74 | "generic-array 0.12.3", 75 | ] 76 | 77 | [[package]] 78 | name = "block-buffer" 79 | version = "0.9.0" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" 82 | dependencies = [ 83 | "generic-array 0.14.4", 84 | ] 85 | 86 | [[package]] 87 | name = "block-padding" 88 | version = "0.1.5" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" 91 | dependencies = [ 92 | "byte-tools", 93 | ] 94 | 95 | [[package]] 96 | name = "buf_redux" 97 | version = "0.8.4" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 100 | dependencies = [ 101 | "memchr", 102 | "safemem", 103 | ] 104 | 105 | [[package]] 106 | name = "byte-tools" 107 | version = "0.3.1" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" 110 | 111 | [[package]] 112 | name = "byteorder" 113 | version = "1.3.4" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" 116 | 117 | [[package]] 118 | name = "bytes" 119 | version = "0.4.12" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" 122 | dependencies = [ 123 | "byteorder", 124 | "iovec", 125 | ] 126 | 127 | [[package]] 128 | name = "bytes" 129 | version = "0.5.6" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" 132 | 133 | [[package]] 134 | name = "cc" 135 | version = "1.0.61" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" 138 | 139 | [[package]] 140 | name = "cfg-if" 141 | version = "0.1.10" 142 | source = "registry+https://github.com/rust-lang/crates.io-index" 143 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 144 | 145 | [[package]] 146 | name = "cfg-if" 147 | version = "1.0.0" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 150 | 151 | [[package]] 152 | name = "chrono" 153 | version = "0.4.19" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 156 | dependencies = [ 157 | "libc", 158 | "num-integer", 159 | "num-traits", 160 | "time", 161 | "winapi 0.3.9", 162 | ] 163 | 164 | [[package]] 165 | name = "cloudabi" 166 | version = "0.0.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 169 | dependencies = [ 170 | "bitflags", 171 | ] 172 | 173 | [[package]] 174 | name = "cpuid-bool" 175 | version = "0.1.2" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" 178 | 179 | [[package]] 180 | name = "crc32fast" 181 | version = "1.2.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" 184 | dependencies = [ 185 | "cfg-if 0.1.10", 186 | ] 187 | 188 | [[package]] 189 | name = "digest" 190 | version = "0.8.1" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" 193 | dependencies = [ 194 | "generic-array 0.12.3", 195 | ] 196 | 197 | [[package]] 198 | name = "digest" 199 | version = "0.9.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" 202 | dependencies = [ 203 | "generic-array 0.14.4", 204 | ] 205 | 206 | [[package]] 207 | name = "dtoa" 208 | version = "0.4.6" 209 | source = "registry+https://github.com/rust-lang/crates.io-index" 210 | checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b" 211 | 212 | [[package]] 213 | name = "env_logger" 214 | version = "0.6.2" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" 217 | dependencies = [ 218 | "atty", 219 | "humantime", 220 | "log", 221 | "regex", 222 | "termcolor", 223 | ] 224 | 225 | [[package]] 226 | name = "fake-simd" 227 | version = "0.1.2" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" 230 | 231 | [[package]] 232 | name = "fly-init" 233 | version = "0.1.0" 234 | dependencies = [ 235 | "anyhow", 236 | "env_logger", 237 | "futures", 238 | "ifstructs", 239 | "ipnetwork", 240 | "libc", 241 | "log", 242 | "nix", 243 | "os_pipe", 244 | "pin-project 1.0.1", 245 | "procfs", 246 | "rlimit", 247 | "rtnetlink", 248 | "serde", 249 | "serde_derive", 250 | "serde_json", 251 | "thiserror", 252 | "tokio", 253 | "tokio-vsock", 254 | "vergen", 255 | "warp", 256 | ] 257 | 258 | [[package]] 259 | name = "fnv" 260 | version = "1.0.7" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 263 | 264 | [[package]] 265 | name = "fuchsia-cprng" 266 | version = "0.1.1" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 269 | 270 | [[package]] 271 | name = "fuchsia-zircon" 272 | version = "0.3.3" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 275 | dependencies = [ 276 | "bitflags", 277 | "fuchsia-zircon-sys", 278 | ] 279 | 280 | [[package]] 281 | name = "fuchsia-zircon-sys" 282 | version = "0.3.3" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 285 | 286 | [[package]] 287 | name = "futures" 288 | version = "0.3.6" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "5d8e3078b7b2a8a671cb7a3d17b4760e4181ea243227776ba83fd043b4ca034e" 291 | dependencies = [ 292 | "futures-channel", 293 | "futures-core", 294 | "futures-executor", 295 | "futures-io", 296 | "futures-sink", 297 | "futures-task", 298 | "futures-util", 299 | ] 300 | 301 | [[package]] 302 | name = "futures-channel" 303 | version = "0.3.6" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | checksum = "a7a4d35f7401e948629c9c3d6638fb9bf94e0b2121e96c3b428cc4e631f3eb74" 306 | dependencies = [ 307 | "futures-core", 308 | "futures-sink", 309 | ] 310 | 311 | [[package]] 312 | name = "futures-core" 313 | version = "0.3.6" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "d674eaa0056896d5ada519900dbf97ead2e46a7b6621e8160d79e2f2e1e2784b" 316 | 317 | [[package]] 318 | name = "futures-executor" 319 | version = "0.3.6" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "cc709ca1da6f66143b8c9bec8e6260181869893714e9b5a490b169b0414144ab" 322 | dependencies = [ 323 | "futures-core", 324 | "futures-task", 325 | "futures-util", 326 | ] 327 | 328 | [[package]] 329 | name = "futures-io" 330 | version = "0.3.6" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "5fc94b64bb39543b4e432f1790b6bf18e3ee3b74653c5449f63310e9a74b123c" 333 | 334 | [[package]] 335 | name = "futures-macro" 336 | version = "0.3.6" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "f57ed14da4603b2554682e9f2ff3c65d7567b53188db96cb71538217fc64581b" 339 | dependencies = [ 340 | "proc-macro-hack", 341 | "proc-macro2", 342 | "quote", 343 | "syn", 344 | ] 345 | 346 | [[package]] 347 | name = "futures-sink" 348 | version = "0.3.6" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "0d8764258ed64ebc5d9ed185cf86a95db5cac810269c5d20ececb32e0088abbd" 351 | 352 | [[package]] 353 | name = "futures-task" 354 | version = "0.3.6" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "4dd26820a9f3637f1302da8bceba3ff33adbe53464b54ca24d4e2d4f1db30f94" 357 | dependencies = [ 358 | "once_cell", 359 | ] 360 | 361 | [[package]] 362 | name = "futures-util" 363 | version = "0.3.6" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "8a894a0acddba51a2d49a6f4263b1e64b8c579ece8af50fa86503d52cd1eea34" 366 | dependencies = [ 367 | "futures-channel", 368 | "futures-core", 369 | "futures-io", 370 | "futures-macro", 371 | "futures-sink", 372 | "futures-task", 373 | "memchr", 374 | "pin-project 0.4.26", 375 | "pin-utils", 376 | "proc-macro-hack", 377 | "proc-macro-nested", 378 | "slab", 379 | ] 380 | 381 | [[package]] 382 | name = "generic-array" 383 | version = "0.12.3" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" 386 | dependencies = [ 387 | "typenum", 388 | ] 389 | 390 | [[package]] 391 | name = "generic-array" 392 | version = "0.14.4" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" 395 | dependencies = [ 396 | "typenum", 397 | "version_check", 398 | ] 399 | 400 | [[package]] 401 | name = "getrandom" 402 | version = "0.1.15" 403 | source = "registry+https://github.com/rust-lang/crates.io-index" 404 | checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" 405 | dependencies = [ 406 | "cfg-if 0.1.10", 407 | "libc", 408 | "wasi 0.9.0+wasi-snapshot-preview1", 409 | ] 410 | 411 | [[package]] 412 | name = "h2" 413 | version = "0.2.6" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "993f9e0baeed60001cf565546b0d3dbe6a6ad23f2bd31644a133c641eccf6d53" 416 | dependencies = [ 417 | "bytes 0.5.6", 418 | "fnv", 419 | "futures-core", 420 | "futures-sink", 421 | "futures-util", 422 | "http", 423 | "indexmap", 424 | "slab", 425 | "tokio", 426 | "tokio-util 0.3.1", 427 | "tracing", 428 | ] 429 | 430 | [[package]] 431 | name = "hashbrown" 432 | version = "0.9.1" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" 435 | 436 | [[package]] 437 | name = "headers" 438 | version = "0.3.2" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "ed18eb2459bf1a09ad2d6b1547840c3e5e62882fa09b9a6a20b1de8e3228848f" 441 | dependencies = [ 442 | "base64", 443 | "bitflags", 444 | "bytes 0.5.6", 445 | "headers-core", 446 | "http", 447 | "mime", 448 | "sha-1 0.8.2", 449 | "time", 450 | ] 451 | 452 | [[package]] 453 | name = "headers-core" 454 | version = "0.2.0" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" 457 | dependencies = [ 458 | "http", 459 | ] 460 | 461 | [[package]] 462 | name = "hermit-abi" 463 | version = "0.1.17" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" 466 | dependencies = [ 467 | "libc", 468 | ] 469 | 470 | [[package]] 471 | name = "hex" 472 | version = "0.4.2" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" 475 | 476 | [[package]] 477 | name = "http" 478 | version = "0.2.1" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" 481 | dependencies = [ 482 | "bytes 0.5.6", 483 | "fnv", 484 | "itoa", 485 | ] 486 | 487 | [[package]] 488 | name = "http-body" 489 | version = "0.3.1" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" 492 | dependencies = [ 493 | "bytes 0.5.6", 494 | "http", 495 | ] 496 | 497 | [[package]] 498 | name = "httparse" 499 | version = "1.3.4" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" 502 | 503 | [[package]] 504 | name = "httpdate" 505 | version = "0.3.2" 506 | source = "registry+https://github.com/rust-lang/crates.io-index" 507 | checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" 508 | 509 | [[package]] 510 | name = "humantime" 511 | version = "1.3.0" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 514 | dependencies = [ 515 | "quick-error", 516 | ] 517 | 518 | [[package]] 519 | name = "hyper" 520 | version = "0.13.8" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835" 523 | dependencies = [ 524 | "bytes 0.5.6", 525 | "futures-channel", 526 | "futures-core", 527 | "futures-util", 528 | "h2", 529 | "http", 530 | "http-body", 531 | "httparse", 532 | "httpdate", 533 | "itoa", 534 | "pin-project 0.4.26", 535 | "socket2", 536 | "tokio", 537 | "tower-service", 538 | "tracing", 539 | "want", 540 | ] 541 | 542 | [[package]] 543 | name = "idna" 544 | version = "0.2.0" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" 547 | dependencies = [ 548 | "matches", 549 | "unicode-bidi", 550 | "unicode-normalization", 551 | ] 552 | 553 | [[package]] 554 | name = "ifstructs" 555 | version = "0.1.1" 556 | source = "registry+https://github.com/rust-lang/crates.io-index" 557 | checksum = "b24d770f92a5ea876a33851b16553f21985bb83e7fe8e7e1f596ad75545e9581" 558 | dependencies = [ 559 | "cfg-if 0.1.10", 560 | "libc", 561 | ] 562 | 563 | [[package]] 564 | name = "indexmap" 565 | version = "1.6.0" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" 568 | dependencies = [ 569 | "autocfg 1.0.1", 570 | "hashbrown", 571 | ] 572 | 573 | [[package]] 574 | name = "input_buffer" 575 | version = "0.3.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "19a8a95243d5a0398cae618ec29477c6e3cb631152be5c19481f80bc71559754" 578 | dependencies = [ 579 | "bytes 0.5.6", 580 | ] 581 | 582 | [[package]] 583 | name = "iovec" 584 | version = "0.1.4" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 587 | dependencies = [ 588 | "libc", 589 | ] 590 | 591 | [[package]] 592 | name = "ipnetwork" 593 | version = "0.16.0" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "b8eca9f51da27bc908ef3dd85c21e1bbba794edaf94d7841e37356275b82d31e" 596 | dependencies = [ 597 | "serde", 598 | ] 599 | 600 | [[package]] 601 | name = "itoa" 602 | version = "0.4.6" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 605 | 606 | [[package]] 607 | name = "kernel32-sys" 608 | version = "0.2.2" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 611 | dependencies = [ 612 | "winapi 0.2.8", 613 | "winapi-build", 614 | ] 615 | 616 | [[package]] 617 | name = "lazy_static" 618 | version = "1.4.0" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 621 | 622 | [[package]] 623 | name = "libc" 624 | version = "0.2.79" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743" 627 | 628 | [[package]] 629 | name = "libflate" 630 | version = "1.0.2" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "e9bac9023e1db29c084f9f8cd9d3852e5e8fddf98fb47c4964a0ea4663d95949" 633 | dependencies = [ 634 | "adler32", 635 | "crc32fast", 636 | "libflate_lz77", 637 | "rle-decode-fast", 638 | ] 639 | 640 | [[package]] 641 | name = "libflate_lz77" 642 | version = "1.0.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b" 645 | 646 | [[package]] 647 | name = "log" 648 | version = "0.4.13" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" 651 | dependencies = [ 652 | "cfg-if 0.1.10", 653 | ] 654 | 655 | [[package]] 656 | name = "matches" 657 | version = "0.1.8" 658 | source = "registry+https://github.com/rust-lang/crates.io-index" 659 | checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" 660 | 661 | [[package]] 662 | name = "memchr" 663 | version = "2.3.3" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 666 | 667 | [[package]] 668 | name = "mime" 669 | version = "0.3.16" 670 | source = "registry+https://github.com/rust-lang/crates.io-index" 671 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 672 | 673 | [[package]] 674 | name = "mime_guess" 675 | version = "2.0.3" 676 | source = "registry+https://github.com/rust-lang/crates.io-index" 677 | checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" 678 | dependencies = [ 679 | "mime", 680 | "unicase", 681 | ] 682 | 683 | [[package]] 684 | name = "mio" 685 | version = "0.6.22" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" 688 | dependencies = [ 689 | "cfg-if 0.1.10", 690 | "fuchsia-zircon", 691 | "fuchsia-zircon-sys", 692 | "iovec", 693 | "kernel32-sys", 694 | "libc", 695 | "log", 696 | "miow 0.2.1", 697 | "net2", 698 | "slab", 699 | "winapi 0.2.8", 700 | ] 701 | 702 | [[package]] 703 | name = "mio-named-pipes" 704 | version = "0.1.7" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" 707 | dependencies = [ 708 | "log", 709 | "mio", 710 | "miow 0.3.5", 711 | "winapi 0.3.9", 712 | ] 713 | 714 | [[package]] 715 | name = "mio-uds" 716 | version = "0.6.8" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" 719 | dependencies = [ 720 | "iovec", 721 | "libc", 722 | "mio", 723 | ] 724 | 725 | [[package]] 726 | name = "miow" 727 | version = "0.2.1" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 730 | dependencies = [ 731 | "kernel32-sys", 732 | "net2", 733 | "winapi 0.2.8", 734 | "ws2_32-sys", 735 | ] 736 | 737 | [[package]] 738 | name = "miow" 739 | version = "0.3.5" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e" 742 | dependencies = [ 743 | "socket2", 744 | "winapi 0.3.9", 745 | ] 746 | 747 | [[package]] 748 | name = "multipart" 749 | version = "0.17.0" 750 | source = "registry+https://github.com/rust-lang/crates.io-index" 751 | checksum = "8209c33c951f07387a8497841122fc6f712165e3f9bda3e6be4645b58188f676" 752 | dependencies = [ 753 | "buf_redux", 754 | "httparse", 755 | "log", 756 | "mime", 757 | "mime_guess", 758 | "quick-error", 759 | "rand 0.6.5", 760 | "safemem", 761 | "tempfile", 762 | "twoway", 763 | ] 764 | 765 | [[package]] 766 | name = "net2" 767 | version = "0.2.35" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853" 770 | dependencies = [ 771 | "cfg-if 0.1.10", 772 | "libc", 773 | "winapi 0.3.9", 774 | ] 775 | 776 | [[package]] 777 | name = "netlink-packet-core" 778 | version = "0.2.1" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "255ea7f8f99e3f5c2fdae62ddd71013d31c1ea4fddd8a7339a6b0ffbfa75c749" 781 | dependencies = [ 782 | "anyhow", 783 | "byteorder", 784 | "libc", 785 | "netlink-packet-utils", 786 | ] 787 | 788 | [[package]] 789 | name = "netlink-packet-route" 790 | version = "0.5.0" 791 | source = "registry+https://github.com/rust-lang/crates.io-index" 792 | checksum = "075c2390c490b5d8f807fdc7f7c921f42a14d6f5081073fe0a9325331aa425b0" 793 | dependencies = [ 794 | "anyhow", 795 | "bitflags", 796 | "byteorder", 797 | "libc", 798 | "netlink-packet-core", 799 | "netlink-packet-utils", 800 | ] 801 | 802 | [[package]] 803 | name = "netlink-packet-utils" 804 | version = "0.3.0" 805 | source = "registry+https://github.com/rust-lang/crates.io-index" 806 | checksum = "c26039404424c9f7cc6a04b6c585da999f3471d309dd2c41cb65e0ce5c505626" 807 | dependencies = [ 808 | "anyhow", 809 | "byteorder", 810 | "paste", 811 | "thiserror", 812 | ] 813 | 814 | [[package]] 815 | name = "netlink-proto" 816 | version = "0.4.1" 817 | source = "registry+https://github.com/rust-lang/crates.io-index" 818 | checksum = "db439abcdc707c2972287b6b1ecb1fd6731549349e09e0cf2686697959e0f10a" 819 | dependencies = [ 820 | "bytes 0.5.6", 821 | "futures", 822 | "log", 823 | "netlink-packet-core", 824 | "netlink-sys", 825 | "tokio", 826 | "tokio-util 0.2.0", 827 | ] 828 | 829 | [[package]] 830 | name = "netlink-sys" 831 | version = "0.4.0" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "cc9e9df13fd91bdd4b92bea93d5d2848c8035677c60fc3fee5dabddc02c3012e" 834 | dependencies = [ 835 | "futures", 836 | "libc", 837 | "log", 838 | "mio", 839 | "tokio", 840 | ] 841 | 842 | [[package]] 843 | name = "nix" 844 | version = "0.19.1" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2" 847 | dependencies = [ 848 | "bitflags", 849 | "cc", 850 | "cfg-if 1.0.0", 851 | "libc", 852 | ] 853 | 854 | [[package]] 855 | name = "num-integer" 856 | version = "0.1.43" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" 859 | dependencies = [ 860 | "autocfg 1.0.1", 861 | "num-traits", 862 | ] 863 | 864 | [[package]] 865 | name = "num-traits" 866 | version = "0.2.12" 867 | source = "registry+https://github.com/rust-lang/crates.io-index" 868 | checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" 869 | dependencies = [ 870 | "autocfg 1.0.1", 871 | ] 872 | 873 | [[package]] 874 | name = "once_cell" 875 | version = "1.4.1" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" 878 | 879 | [[package]] 880 | name = "opaque-debug" 881 | version = "0.2.3" 882 | source = "registry+https://github.com/rust-lang/crates.io-index" 883 | checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" 884 | 885 | [[package]] 886 | name = "opaque-debug" 887 | version = "0.3.0" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 890 | 891 | [[package]] 892 | name = "os_pipe" 893 | version = "0.9.2" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" 896 | dependencies = [ 897 | "libc", 898 | "winapi 0.3.9", 899 | ] 900 | 901 | [[package]] 902 | name = "paste" 903 | version = "1.0.1" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "0520af26d4cf99643dbbe093a61507922b57232d9978d8491fdc8f7b44573c8c" 906 | 907 | [[package]] 908 | name = "percent-encoding" 909 | version = "2.1.0" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" 912 | 913 | [[package]] 914 | name = "pin-project" 915 | version = "0.4.26" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "13fbdfd6bdee3dc9be46452f86af4a4072975899cf8592466668620bebfbcc17" 918 | dependencies = [ 919 | "pin-project-internal 0.4.26", 920 | ] 921 | 922 | [[package]] 923 | name = "pin-project" 924 | version = "1.0.1" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" 927 | dependencies = [ 928 | "pin-project-internal 1.0.1", 929 | ] 930 | 931 | [[package]] 932 | name = "pin-project-internal" 933 | version = "0.4.26" 934 | source = "registry+https://github.com/rust-lang/crates.io-index" 935 | checksum = "c82fb1329f632c3552cf352d14427d57a511b1cf41db93b3a7d77906a82dcc8e" 936 | dependencies = [ 937 | "proc-macro2", 938 | "quote", 939 | "syn", 940 | ] 941 | 942 | [[package]] 943 | name = "pin-project-internal" 944 | version = "1.0.1" 945 | source = "registry+https://github.com/rust-lang/crates.io-index" 946 | checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" 947 | dependencies = [ 948 | "proc-macro2", 949 | "quote", 950 | "syn", 951 | ] 952 | 953 | [[package]] 954 | name = "pin-project-lite" 955 | version = "0.1.10" 956 | source = "registry+https://github.com/rust-lang/crates.io-index" 957 | checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95" 958 | 959 | [[package]] 960 | name = "pin-utils" 961 | version = "0.1.0" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 964 | 965 | [[package]] 966 | name = "ppv-lite86" 967 | version = "0.2.9" 968 | source = "registry+https://github.com/rust-lang/crates.io-index" 969 | checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" 970 | 971 | [[package]] 972 | name = "proc-macro-hack" 973 | version = "0.5.18" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" 976 | 977 | [[package]] 978 | name = "proc-macro-nested" 979 | version = "0.1.6" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" 982 | 983 | [[package]] 984 | name = "proc-macro2" 985 | version = "1.0.24" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" 988 | dependencies = [ 989 | "unicode-xid", 990 | ] 991 | 992 | [[package]] 993 | name = "procfs" 994 | version = "0.7.9" 995 | source = "registry+https://github.com/rust-lang/crates.io-index" 996 | checksum = "c434e93ef69c216e68e4f417c927b4f31502c3560b72cfdb6827e2321c5c6b3e" 997 | dependencies = [ 998 | "bitflags", 999 | "byteorder", 1000 | "chrono", 1001 | "hex", 1002 | "lazy_static", 1003 | "libc", 1004 | "libflate", 1005 | ] 1006 | 1007 | [[package]] 1008 | name = "quick-error" 1009 | version = "1.2.3" 1010 | source = "registry+https://github.com/rust-lang/crates.io-index" 1011 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 1012 | 1013 | [[package]] 1014 | name = "quote" 1015 | version = "1.0.7" 1016 | source = "registry+https://github.com/rust-lang/crates.io-index" 1017 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" 1018 | dependencies = [ 1019 | "proc-macro2", 1020 | ] 1021 | 1022 | [[package]] 1023 | name = "rand" 1024 | version = "0.6.5" 1025 | source = "registry+https://github.com/rust-lang/crates.io-index" 1026 | checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" 1027 | dependencies = [ 1028 | "autocfg 0.1.7", 1029 | "libc", 1030 | "rand_chacha 0.1.1", 1031 | "rand_core 0.4.2", 1032 | "rand_hc 0.1.0", 1033 | "rand_isaac", 1034 | "rand_jitter", 1035 | "rand_os", 1036 | "rand_pcg", 1037 | "rand_xorshift", 1038 | "winapi 0.3.9", 1039 | ] 1040 | 1041 | [[package]] 1042 | name = "rand" 1043 | version = "0.7.3" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 1046 | dependencies = [ 1047 | "getrandom", 1048 | "libc", 1049 | "rand_chacha 0.2.2", 1050 | "rand_core 0.5.1", 1051 | "rand_hc 0.2.0", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "rand_chacha" 1056 | version = "0.1.1" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" 1059 | dependencies = [ 1060 | "autocfg 0.1.7", 1061 | "rand_core 0.3.1", 1062 | ] 1063 | 1064 | [[package]] 1065 | name = "rand_chacha" 1066 | version = "0.2.2" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" 1069 | dependencies = [ 1070 | "ppv-lite86", 1071 | "rand_core 0.5.1", 1072 | ] 1073 | 1074 | [[package]] 1075 | name = "rand_core" 1076 | version = "0.3.1" 1077 | source = "registry+https://github.com/rust-lang/crates.io-index" 1078 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 1079 | dependencies = [ 1080 | "rand_core 0.4.2", 1081 | ] 1082 | 1083 | [[package]] 1084 | name = "rand_core" 1085 | version = "0.4.2" 1086 | source = "registry+https://github.com/rust-lang/crates.io-index" 1087 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 1088 | 1089 | [[package]] 1090 | name = "rand_core" 1091 | version = "0.5.1" 1092 | source = "registry+https://github.com/rust-lang/crates.io-index" 1093 | checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 1094 | dependencies = [ 1095 | "getrandom", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "rand_hc" 1100 | version = "0.1.0" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" 1103 | dependencies = [ 1104 | "rand_core 0.3.1", 1105 | ] 1106 | 1107 | [[package]] 1108 | name = "rand_hc" 1109 | version = "0.2.0" 1110 | source = "registry+https://github.com/rust-lang/crates.io-index" 1111 | checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 1112 | dependencies = [ 1113 | "rand_core 0.5.1", 1114 | ] 1115 | 1116 | [[package]] 1117 | name = "rand_isaac" 1118 | version = "0.1.1" 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" 1120 | checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" 1121 | dependencies = [ 1122 | "rand_core 0.3.1", 1123 | ] 1124 | 1125 | [[package]] 1126 | name = "rand_jitter" 1127 | version = "0.1.4" 1128 | source = "registry+https://github.com/rust-lang/crates.io-index" 1129 | checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" 1130 | dependencies = [ 1131 | "libc", 1132 | "rand_core 0.4.2", 1133 | "winapi 0.3.9", 1134 | ] 1135 | 1136 | [[package]] 1137 | name = "rand_os" 1138 | version = "0.1.3" 1139 | source = "registry+https://github.com/rust-lang/crates.io-index" 1140 | checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 1141 | dependencies = [ 1142 | "cloudabi", 1143 | "fuchsia-cprng", 1144 | "libc", 1145 | "rand_core 0.4.2", 1146 | "rdrand", 1147 | "winapi 0.3.9", 1148 | ] 1149 | 1150 | [[package]] 1151 | name = "rand_pcg" 1152 | version = "0.1.2" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" 1155 | dependencies = [ 1156 | "autocfg 0.1.7", 1157 | "rand_core 0.4.2", 1158 | ] 1159 | 1160 | [[package]] 1161 | name = "rand_xorshift" 1162 | version = "0.1.1" 1163 | source = "registry+https://github.com/rust-lang/crates.io-index" 1164 | checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" 1165 | dependencies = [ 1166 | "rand_core 0.3.1", 1167 | ] 1168 | 1169 | [[package]] 1170 | name = "rdrand" 1171 | version = "0.4.0" 1172 | source = "registry+https://github.com/rust-lang/crates.io-index" 1173 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 1174 | dependencies = [ 1175 | "rand_core 0.3.1", 1176 | ] 1177 | 1178 | [[package]] 1179 | name = "redox_syscall" 1180 | version = "0.1.57" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1183 | 1184 | [[package]] 1185 | name = "regex" 1186 | version = "1.3.9" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" 1189 | dependencies = [ 1190 | "aho-corasick", 1191 | "memchr", 1192 | "regex-syntax", 1193 | "thread_local", 1194 | ] 1195 | 1196 | [[package]] 1197 | name = "regex-syntax" 1198 | version = "0.6.18" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 1201 | 1202 | [[package]] 1203 | name = "remove_dir_all" 1204 | version = "0.5.3" 1205 | source = "registry+https://github.com/rust-lang/crates.io-index" 1206 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1207 | dependencies = [ 1208 | "winapi 0.3.9", 1209 | ] 1210 | 1211 | [[package]] 1212 | name = "rle-decode-fast" 1213 | version = "1.0.1" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" 1216 | 1217 | [[package]] 1218 | name = "rlimit" 1219 | version = "0.3.0" 1220 | source = "registry+https://github.com/rust-lang/crates.io-index" 1221 | checksum = "e6d26e65e82a1e2628c5f209ec12f4508fa50e644bd2f264138e60129d61eae8" 1222 | dependencies = [ 1223 | "cfg-if 0.1.10", 1224 | "libc", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "rtnetlink" 1229 | version = "0.5.0" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "887246d6c8bb58f2ee8a11b8c7e65565a918cd07edd541d903e79e67ca5532ae" 1232 | dependencies = [ 1233 | "futures", 1234 | "log", 1235 | "netlink-packet-route", 1236 | "netlink-proto", 1237 | "thiserror", 1238 | ] 1239 | 1240 | [[package]] 1241 | name = "ryu" 1242 | version = "1.0.5" 1243 | source = "registry+https://github.com/rust-lang/crates.io-index" 1244 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" 1245 | 1246 | [[package]] 1247 | name = "safemem" 1248 | version = "0.3.3" 1249 | source = "registry+https://github.com/rust-lang/crates.io-index" 1250 | checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 1251 | 1252 | [[package]] 1253 | name = "scoped-tls" 1254 | version = "1.0.0" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" 1257 | 1258 | [[package]] 1259 | name = "serde" 1260 | version = "1.0.116" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "96fe57af81d28386a513cbc6858332abc6117cfdb5999647c6444b8f43a370a5" 1263 | 1264 | [[package]] 1265 | name = "serde_derive" 1266 | version = "1.0.116" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "f630a6370fd8e457873b4bd2ffdae75408bc291ba72be773772a4c2a065d9ae8" 1269 | dependencies = [ 1270 | "proc-macro2", 1271 | "quote", 1272 | "syn", 1273 | ] 1274 | 1275 | [[package]] 1276 | name = "serde_json" 1277 | version = "1.0.58" 1278 | source = "registry+https://github.com/rust-lang/crates.io-index" 1279 | checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" 1280 | dependencies = [ 1281 | "itoa", 1282 | "ryu", 1283 | "serde", 1284 | ] 1285 | 1286 | [[package]] 1287 | name = "serde_urlencoded" 1288 | version = "0.6.1" 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" 1290 | checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" 1291 | dependencies = [ 1292 | "dtoa", 1293 | "itoa", 1294 | "serde", 1295 | "url", 1296 | ] 1297 | 1298 | [[package]] 1299 | name = "sha-1" 1300 | version = "0.8.2" 1301 | source = "registry+https://github.com/rust-lang/crates.io-index" 1302 | checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" 1303 | dependencies = [ 1304 | "block-buffer 0.7.3", 1305 | "digest 0.8.1", 1306 | "fake-simd", 1307 | "opaque-debug 0.2.3", 1308 | ] 1309 | 1310 | [[package]] 1311 | name = "sha-1" 1312 | version = "0.9.1" 1313 | source = "registry+https://github.com/rust-lang/crates.io-index" 1314 | checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" 1315 | dependencies = [ 1316 | "block-buffer 0.9.0", 1317 | "cfg-if 0.1.10", 1318 | "cpuid-bool", 1319 | "digest 0.9.0", 1320 | "opaque-debug 0.3.0", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "signal-hook-registry" 1325 | version = "1.2.1" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" 1328 | dependencies = [ 1329 | "arc-swap", 1330 | "libc", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "slab" 1335 | version = "0.4.2" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 1338 | 1339 | [[package]] 1340 | name = "socket2" 1341 | version = "0.3.15" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44" 1344 | dependencies = [ 1345 | "cfg-if 0.1.10", 1346 | "libc", 1347 | "redox_syscall", 1348 | "winapi 0.3.9", 1349 | ] 1350 | 1351 | [[package]] 1352 | name = "syn" 1353 | version = "1.0.45" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "ea9c5432ff16d6152371f808fb5a871cd67368171b09bb21b43df8e4a47a3556" 1356 | dependencies = [ 1357 | "proc-macro2", 1358 | "quote", 1359 | "unicode-xid", 1360 | ] 1361 | 1362 | [[package]] 1363 | name = "tempfile" 1364 | version = "3.1.0" 1365 | source = "registry+https://github.com/rust-lang/crates.io-index" 1366 | checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" 1367 | dependencies = [ 1368 | "cfg-if 0.1.10", 1369 | "libc", 1370 | "rand 0.7.3", 1371 | "redox_syscall", 1372 | "remove_dir_all", 1373 | "winapi 0.3.9", 1374 | ] 1375 | 1376 | [[package]] 1377 | name = "termcolor" 1378 | version = "1.1.0" 1379 | source = "registry+https://github.com/rust-lang/crates.io-index" 1380 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 1381 | dependencies = [ 1382 | "winapi-util", 1383 | ] 1384 | 1385 | [[package]] 1386 | name = "thiserror" 1387 | version = "1.0.21" 1388 | source = "registry+https://github.com/rust-lang/crates.io-index" 1389 | checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42" 1390 | dependencies = [ 1391 | "thiserror-impl", 1392 | ] 1393 | 1394 | [[package]] 1395 | name = "thiserror-impl" 1396 | version = "1.0.21" 1397 | source = "registry+https://github.com/rust-lang/crates.io-index" 1398 | checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab" 1399 | dependencies = [ 1400 | "proc-macro2", 1401 | "quote", 1402 | "syn", 1403 | ] 1404 | 1405 | [[package]] 1406 | name = "thread_local" 1407 | version = "1.0.1" 1408 | source = "registry+https://github.com/rust-lang/crates.io-index" 1409 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 1410 | dependencies = [ 1411 | "lazy_static", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "time" 1416 | version = "0.1.44" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1419 | dependencies = [ 1420 | "libc", 1421 | "wasi 0.10.0+wasi-snapshot-preview1", 1422 | "winapi 0.3.9", 1423 | ] 1424 | 1425 | [[package]] 1426 | name = "tinyvec" 1427 | version = "0.3.4" 1428 | source = "registry+https://github.com/rust-lang/crates.io-index" 1429 | checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" 1430 | 1431 | [[package]] 1432 | name = "tokio" 1433 | version = "0.2.22" 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" 1435 | checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd" 1436 | dependencies = [ 1437 | "bytes 0.5.6", 1438 | "fnv", 1439 | "futures-core", 1440 | "iovec", 1441 | "lazy_static", 1442 | "libc", 1443 | "memchr", 1444 | "mio", 1445 | "mio-named-pipes", 1446 | "mio-uds", 1447 | "pin-project-lite", 1448 | "signal-hook-registry", 1449 | "slab", 1450 | "tokio-macros", 1451 | "winapi 0.3.9", 1452 | ] 1453 | 1454 | [[package]] 1455 | name = "tokio-macros" 1456 | version = "0.2.5" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 1459 | dependencies = [ 1460 | "proc-macro2", 1461 | "quote", 1462 | "syn", 1463 | ] 1464 | 1465 | [[package]] 1466 | name = "tokio-tungstenite" 1467 | version = "0.11.0" 1468 | source = "registry+https://github.com/rust-lang/crates.io-index" 1469 | checksum = "6d9e878ad426ca286e4dcae09cbd4e1973a7f8987d97570e2469703dd7f5720c" 1470 | dependencies = [ 1471 | "futures-util", 1472 | "log", 1473 | "pin-project 0.4.26", 1474 | "tokio", 1475 | "tungstenite", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "tokio-util" 1480 | version = "0.2.0" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" 1483 | dependencies = [ 1484 | "bytes 0.5.6", 1485 | "futures-core", 1486 | "futures-sink", 1487 | "log", 1488 | "pin-project-lite", 1489 | "tokio", 1490 | ] 1491 | 1492 | [[package]] 1493 | name = "tokio-util" 1494 | version = "0.3.1" 1495 | source = "registry+https://github.com/rust-lang/crates.io-index" 1496 | checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" 1497 | dependencies = [ 1498 | "bytes 0.5.6", 1499 | "futures-core", 1500 | "futures-sink", 1501 | "log", 1502 | "pin-project-lite", 1503 | "tokio", 1504 | ] 1505 | 1506 | [[package]] 1507 | name = "tokio-vsock" 1508 | version = "0.2.2" 1509 | source = "git+https://github.com/jeromegn/tokio-vsock.git?branch=fix-io-errors#9617341210e65ffc1a0b1c2ce27411433ba99cbb" 1510 | dependencies = [ 1511 | "bytes 0.4.12", 1512 | "futures", 1513 | "iovec", 1514 | "libc", 1515 | "mio", 1516 | "nix", 1517 | "tokio", 1518 | "vsock", 1519 | ] 1520 | 1521 | [[package]] 1522 | name = "tower-service" 1523 | version = "0.3.0" 1524 | source = "registry+https://github.com/rust-lang/crates.io-index" 1525 | checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" 1526 | 1527 | [[package]] 1528 | name = "tracing" 1529 | version = "0.1.21" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" 1532 | dependencies = [ 1533 | "cfg-if 0.1.10", 1534 | "log", 1535 | "pin-project-lite", 1536 | "tracing-core", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "tracing-core" 1541 | version = "0.1.17" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" 1544 | dependencies = [ 1545 | "lazy_static", 1546 | ] 1547 | 1548 | [[package]] 1549 | name = "tracing-futures" 1550 | version = "0.2.4" 1551 | source = "registry+https://github.com/rust-lang/crates.io-index" 1552 | checksum = "ab7bb6f14721aa00656086e9335d363c5c8747bae02ebe32ea2c7dece5689b4c" 1553 | dependencies = [ 1554 | "pin-project 0.4.26", 1555 | "tracing", 1556 | ] 1557 | 1558 | [[package]] 1559 | name = "try-lock" 1560 | version = "0.2.3" 1561 | source = "registry+https://github.com/rust-lang/crates.io-index" 1562 | checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" 1563 | 1564 | [[package]] 1565 | name = "tungstenite" 1566 | version = "0.11.1" 1567 | source = "registry+https://github.com/rust-lang/crates.io-index" 1568 | checksum = "f0308d80d86700c5878b9ef6321f020f29b1bb9d5ff3cab25e75e23f3a492a23" 1569 | dependencies = [ 1570 | "base64", 1571 | "byteorder", 1572 | "bytes 0.5.6", 1573 | "http", 1574 | "httparse", 1575 | "input_buffer", 1576 | "log", 1577 | "rand 0.7.3", 1578 | "sha-1 0.9.1", 1579 | "url", 1580 | "utf-8", 1581 | ] 1582 | 1583 | [[package]] 1584 | name = "twoway" 1585 | version = "0.1.8" 1586 | source = "registry+https://github.com/rust-lang/crates.io-index" 1587 | checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" 1588 | dependencies = [ 1589 | "memchr", 1590 | ] 1591 | 1592 | [[package]] 1593 | name = "typenum" 1594 | version = "1.12.0" 1595 | source = "registry+https://github.com/rust-lang/crates.io-index" 1596 | checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" 1597 | 1598 | [[package]] 1599 | name = "unicase" 1600 | version = "2.6.0" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1603 | dependencies = [ 1604 | "version_check", 1605 | ] 1606 | 1607 | [[package]] 1608 | name = "unicode-bidi" 1609 | version = "0.3.4" 1610 | source = "registry+https://github.com/rust-lang/crates.io-index" 1611 | checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" 1612 | dependencies = [ 1613 | "matches", 1614 | ] 1615 | 1616 | [[package]] 1617 | name = "unicode-normalization" 1618 | version = "0.1.13" 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" 1620 | checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" 1621 | dependencies = [ 1622 | "tinyvec", 1623 | ] 1624 | 1625 | [[package]] 1626 | name = "unicode-xid" 1627 | version = "0.2.1" 1628 | source = "registry+https://github.com/rust-lang/crates.io-index" 1629 | checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" 1630 | 1631 | [[package]] 1632 | name = "url" 1633 | version = "2.1.1" 1634 | source = "registry+https://github.com/rust-lang/crates.io-index" 1635 | checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" 1636 | dependencies = [ 1637 | "idna", 1638 | "matches", 1639 | "percent-encoding", 1640 | ] 1641 | 1642 | [[package]] 1643 | name = "urlencoding" 1644 | version = "1.1.1" 1645 | source = "registry+https://github.com/rust-lang/crates.io-index" 1646 | checksum = "c9232eb53352b4442e40d7900465dfc534e8cb2dc8f18656fcb2ac16112b5593" 1647 | 1648 | [[package]] 1649 | name = "utf-8" 1650 | version = "0.7.5" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" 1653 | 1654 | [[package]] 1655 | name = "vergen" 1656 | version = "3.1.0" 1657 | source = "registry+https://github.com/rust-lang/crates.io-index" 1658 | checksum = "4ce50d8996df1f85af15f2cd8d33daae6e479575123ef4314a51a70a230739cb" 1659 | dependencies = [ 1660 | "bitflags", 1661 | "chrono", 1662 | ] 1663 | 1664 | [[package]] 1665 | name = "version_check" 1666 | version = "0.9.2" 1667 | source = "registry+https://github.com/rust-lang/crates.io-index" 1668 | checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" 1669 | 1670 | [[package]] 1671 | name = "vsock" 1672 | version = "0.2.2" 1673 | source = "registry+https://github.com/rust-lang/crates.io-index" 1674 | checksum = "07a39f9e24a5782c392413c51eb05f22d5f0db486f95f0ce72db5ab46a45904c" 1675 | dependencies = [ 1676 | "libc", 1677 | "nix", 1678 | ] 1679 | 1680 | [[package]] 1681 | name = "want" 1682 | version = "0.3.0" 1683 | source = "registry+https://github.com/rust-lang/crates.io-index" 1684 | checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" 1685 | dependencies = [ 1686 | "log", 1687 | "try-lock", 1688 | ] 1689 | 1690 | [[package]] 1691 | name = "warp" 1692 | version = "0.2.5" 1693 | source = "registry+https://github.com/rust-lang/crates.io-index" 1694 | checksum = "f41be6df54c97904af01aa23e613d4521eed7ab23537cede692d4058f6449407" 1695 | dependencies = [ 1696 | "bytes 0.5.6", 1697 | "futures", 1698 | "headers", 1699 | "http", 1700 | "hyper", 1701 | "log", 1702 | "mime", 1703 | "mime_guess", 1704 | "multipart", 1705 | "pin-project 0.4.26", 1706 | "scoped-tls", 1707 | "serde", 1708 | "serde_json", 1709 | "serde_urlencoded", 1710 | "tokio", 1711 | "tokio-tungstenite", 1712 | "tower-service", 1713 | "tracing", 1714 | "tracing-futures", 1715 | "urlencoding", 1716 | ] 1717 | 1718 | [[package]] 1719 | name = "wasi" 1720 | version = "0.9.0+wasi-snapshot-preview1" 1721 | source = "registry+https://github.com/rust-lang/crates.io-index" 1722 | checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 1723 | 1724 | [[package]] 1725 | name = "wasi" 1726 | version = "0.10.0+wasi-snapshot-preview1" 1727 | source = "registry+https://github.com/rust-lang/crates.io-index" 1728 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1729 | 1730 | [[package]] 1731 | name = "winapi" 1732 | version = "0.2.8" 1733 | source = "registry+https://github.com/rust-lang/crates.io-index" 1734 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 1735 | 1736 | [[package]] 1737 | name = "winapi" 1738 | version = "0.3.9" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1741 | dependencies = [ 1742 | "winapi-i686-pc-windows-gnu", 1743 | "winapi-x86_64-pc-windows-gnu", 1744 | ] 1745 | 1746 | [[package]] 1747 | name = "winapi-build" 1748 | version = "0.1.1" 1749 | source = "registry+https://github.com/rust-lang/crates.io-index" 1750 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 1751 | 1752 | [[package]] 1753 | name = "winapi-i686-pc-windows-gnu" 1754 | version = "0.4.0" 1755 | source = "registry+https://github.com/rust-lang/crates.io-index" 1756 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1757 | 1758 | [[package]] 1759 | name = "winapi-util" 1760 | version = "0.1.5" 1761 | source = "registry+https://github.com/rust-lang/crates.io-index" 1762 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1763 | dependencies = [ 1764 | "winapi 0.3.9", 1765 | ] 1766 | 1767 | [[package]] 1768 | name = "winapi-x86_64-pc-windows-gnu" 1769 | version = "0.4.0" 1770 | source = "registry+https://github.com/rust-lang/crates.io-index" 1771 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1772 | 1773 | [[package]] 1774 | name = "ws2_32-sys" 1775 | version = "0.2.1" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 1778 | dependencies = [ 1779 | "winapi 0.2.8", 1780 | "winapi-build", 1781 | ] 1782 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fly-init" 3 | version = "0.1.0" 4 | authors = ["Jerome Gravel-Niquet "] 5 | edition = "2018" 6 | build = "build.rs" 7 | 8 | [dependencies] 9 | libc = "0.2.62" 10 | nix = "0.19" 11 | tokio = { version = "0.2", default-features = false, features = [ 12 | "macros", 13 | "rt-core", 14 | "io-driver", 15 | "process", 16 | "io-util", 17 | "io-std", 18 | ] } 19 | env_logger = "0.6.2" 20 | log = "0.4.8" 21 | serde_derive = "1.0.101" 22 | serde = "1.0.101" 23 | serde_json = "1.0.40" 24 | futures = "0.3" 25 | warp = "0.2" 26 | procfs = "0.7.7" 27 | rlimit = "0.3.0" 28 | rtnetlink = "0.5.0" 29 | ipnetwork = { version = "0.16.0", features = ["serde"] } 30 | ifstructs = "0.1.1" 31 | anyhow = "1.0.33" 32 | thiserror = "1.0.21" 33 | pin-project = "1.0.1" 34 | os_pipe = "0.9.2" 35 | tokio-vsock = "0.2.2" 36 | 37 | [profile.release] 38 | lto = true 39 | 40 | [build-dependencies] 41 | vergen = "3.1.0" 42 | 43 | [patch.crates-io] 44 | tokio-vsock = { git = "https://github.com/jeromegn/tokio-vsock.git", branch = "fix-io-errors" } 45 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Fly.io 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fly Init 2 | 3 | This is a public snapshot of Fly's init code. It powers every Firecracker microvm we run for our users. 4 | 5 | It is Rust-based and we thought making it public (even in this very limited fashion) could help as a reference for people making Rust-based init programs. 6 | 7 | ## Usage 8 | 9 | Please note that our init is tailored for firecracker microvms. These instructions might not quite work and differ from what we do in production. 10 | 11 | - Build with `cargo build --release` 12 | - Create a device for the init 13 | ```bash 14 | fallocate -l 64M tmpinit 15 | mkfs.ext2 tmpinit 16 | mkdir initmount 17 | mount -o loop,noatime tmpinit initmount 18 | mkdir initmount/fly 19 | cp target/x86_64-unknown-linux-musl/release/init initmount/fly/init 20 | cp run.json initmount/fly/run.json # more on this later 21 | umount initmount 22 | ``` 23 | - Attach this device as /dev/vda 24 | - Attach your rootfs as /dev/vdb 25 | - Attach a vsock virtio device 26 | 27 | ### initrd 28 | 29 | This init should also work packaged as an initrd. However, we're not running it as such at the time of this writing. 30 | 31 | ```bash 32 | mkdir -p tmpcpio/fly 33 | cp target/x86_64-unknown-linux-musl/release/init tmpcpio/fly/init 34 | cp run.json tmpcpio/fly/run.json 35 | cd tmpcpio 36 | ls | cpio --null --create -V --format=newc -O ../initrd.cpio 37 | ``` 38 | 39 | You can then use initrd.cpio as your initrd parameter. 40 | 41 | ## `/fly/run.json` 42 | 43 | To configure out init, we're injecting a JSON file into the root device. 44 | 45 | You should be able to figure out the format by looking at [`lib.rs`](src/lib.rs) -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use vergen::{generate_cargo_keys, ConstantsFlags}; 2 | 3 | fn main() { 4 | let flags = ConstantsFlags::SHA_SHORT | ConstantsFlags::BUILD_TIMESTAMP; 5 | generate_cargo_keys(flags).expect("Unable to generate the cargo keys!"); 6 | } 7 | -------------------------------------------------------------------------------- /src/api/exec.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::os::unix::{process::ExitStatusExt}; 3 | use std::pin::Pin; 4 | use std::task::{Context, Poll}; 5 | use std::collections::HashMap; 6 | use std::sync::Arc; 7 | 8 | use warp::reject::Rejection; 9 | use futures::{SinkExt, StreamExt}; 10 | use pin_project::pin_project; 11 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 12 | use tokio::process::{ChildStdin, ChildStdout, Command}; 13 | use warp::filters::ws::Message; 14 | use tokio::sync::Mutex; 15 | 16 | use crate::pty::{PtyCommandExt, PtyMaster, PtyMasterRead, PtyMasterWrite}; 17 | 18 | 19 | #[derive(Debug, Deserialize)] 20 | pub struct ExecRequest { 21 | cmd: Vec, 22 | } 23 | 24 | #[derive(Debug, Serialize)] 25 | struct ExecResponse { 26 | exit_code: Option, 27 | exit_signal: Option, 28 | stdout: Vec, 29 | stderr: Vec, 30 | } 31 | 32 | pub async fn exec_cmd(mut exec_req: ExecRequest, envs: HashMap, waitpid_mutex: Arc>) -> Result { 33 | let full_cmd = exec_req.cmd.join(" "); 34 | debug!("exec_cmd: {}", full_cmd); 35 | let mut command = Command::new(exec_req.cmd.swap_remove(0)); 36 | for arg in exec_req.cmd.into_iter() { 37 | command.arg(arg); 38 | } 39 | 40 | command.envs(&envs); 41 | 42 | let guard = waitpid_mutex.lock().await; 43 | let output = command 44 | .output().await 45 | .map_err(|err| warp::reject::custom(ExecError::from(err)))?; 46 | 47 | drop(guard); 48 | 49 | let status = output.status; 50 | 51 | debug!("command '{}' exited with code: {}", full_cmd, status.code().map(|i| i.to_string()).unwrap_or_else(||"unknown".to_string())); 52 | 53 | Ok(warp::reply::json(&ExecResponse { 54 | exit_code: status.code(), 55 | exit_signal: status.signal(), 56 | stderr: output.stderr, 57 | stdout: output.stdout, 58 | })) 59 | } 60 | 61 | pub fn exec_ws(ws: warp::ws::Ws, envs: HashMap) -> impl warp::Reply { 62 | ws.on_upgrade(|websocket| { 63 | async move { 64 | let (mut ws_write, mut ws_read) = websocket.split(); 65 | debug!("exec ws upgrade, waiting for init client message"); 66 | let (mut command, tty) = match ws_read.next().await { 67 | Some(Ok(client_msg)) => { 68 | if client_msg.is_text() { 69 | match serde_json::from_slice(client_msg.as_bytes()) { 70 | Ok(client_msg) => match client_msg { 71 | ClientMessage::Init {command, tty} => (command, tty), 72 | _ => { 73 | error!("expecting init message, got something else"); 74 | ws_write.send(Message::close()).await.ok(); 75 | return; 76 | } 77 | }, 78 | Err(e) => { 79 | error!("error decoding client message: {}", e); 80 | return; 81 | } 82 | } 83 | } else { 84 | error!("expecting init message, got something else"); 85 | ws_write.send(Message::close()).await.ok(); 86 | return; 87 | } 88 | }, 89 | Some(Err(e)) => { 90 | error!("error reading init message: {}", e); 91 | return; 92 | } 93 | None => { 94 | return; 95 | } 96 | }; 97 | 98 | debug!("spawning process with command: {:?} (tty? {})", command, tty); 99 | 100 | if command.len() <= 0 { 101 | error!("command needs at least 1 item"); 102 | ws_write.send(Message::close()).await.ok(); 103 | return; 104 | } 105 | 106 | let mut cmd = Command::new(command.remove(0)); 107 | for arg in command.into_iter(){ 108 | cmd.arg(arg); 109 | } 110 | 111 | cmd.env_clear().envs(&envs); 112 | 113 | cmd.kill_on_drop(true); 114 | 115 | let (child_res, mut cmd_stdin, mut cmd_stdout) = if tty { 116 | let pty_master = PtyMaster::open().expect("could not create pty master"); 117 | debug!("spawning with PTY"); 118 | cmd.env("TERM", "xterm-256color"); 119 | let res = cmd.spawn_pty(&pty_master, false); 120 | let (r,w) = pty_master.split().expect("could not split pty into read/write halves"); 121 | (res, Stdin::Tty(w), Stdout::Tty(r)) 122 | } else { 123 | debug!("spawning non-TTY"); 124 | cmd.stdin(std::process::Stdio::null()); 125 | cmd.stdout(std::process::Stdio::piped()); 126 | let mut child = match cmd.spawn() { 127 | Ok(c) => c, 128 | Err(e) => { 129 | error!("error spawning child process: {}", e); 130 | ws_write.send(Message::close()).await.ok(); 131 | return; 132 | } 133 | }; 134 | let stdin = child.stdin.take().map(|s| Stdin::Normal(s)).unwrap_or(Stdin::DevNull); 135 | let stdout = child.stdout.take().map(|s| Stdout::Normal(s)).unwrap(); 136 | (Ok(child), stdin, stdout) 137 | }; 138 | 139 | let mut child = match child_res { 140 | Ok(c) => c, 141 | Err(e) => { 142 | error!("error spawning child process: {}", e); 143 | ws_write.send(Message::close()).await.ok(); 144 | return; 145 | } 146 | }; 147 | 148 | let mut child_exited = false; 149 | let mut exit_loop_i = 10; 150 | 151 | loop { 152 | let mut buf = [0; 65536]; 153 | let from_cmd = cmd_stdout.read(&mut buf); 154 | tokio::pin!(from_cmd); 155 | 156 | let from_ws = ws_read.next(); 157 | tokio::pin!(from_ws); 158 | 159 | tokio::select! { 160 | maybe_msg = &mut from_ws => match maybe_msg { 161 | Some(Ok(msg)) => { 162 | trace!("GOT A MESSAGE: {:?}", msg); 163 | if msg.is_binary() { 164 | match cmd_stdin.write_all(msg.as_bytes()).await { 165 | Ok(_) => trace!("wrote it to the cmd!"), 166 | Err(e) => debug!("error writing to cmd stdin: {}", e), 167 | } 168 | } else if msg.is_text() { 169 | match serde_json::from_slice(msg.as_bytes()) { 170 | Ok(client_msg) => match client_msg { 171 | ClientMessage::Resize {cols, rows} => if tty { 172 | debug!("got resize message, cols: {}, rows: {}", cols, rows); 173 | if let Stdin::Tty(pty) = &mut cmd_stdin { 174 | match pty.resize(cols, rows) { 175 | Ok(_) => debug!("resized pty correctly!"), 176 | Err(e) => error!("error resizing pty: {}", e), 177 | } 178 | } 179 | }, 180 | _ => {} 181 | }, 182 | Err(e) => error!("error deserializing ws text message: {}", e) 183 | } 184 | } else if msg.is_close() { 185 | debug!("closing exec ws, reason: {}", msg.to_str().unwrap_or("unknown")); 186 | break; 187 | } 188 | }, 189 | Some(Err(e)) => { 190 | error!("error reading from ws: {}", e); 191 | break; 192 | } 193 | None => { 194 | debug!("no more ws messages"); 195 | break; 196 | } 197 | }, 198 | read_res = &mut from_cmd => match read_res { 199 | Ok(nread) => { 200 | trace!("read {} bytes from the cmd stdout!", nread); 201 | if nread == 0 { 202 | debug!("end of stdout stream"); 203 | break; 204 | } 205 | match ws_write.send(Message::binary(&buf[..nread])).await { 206 | Ok(_) => trace!("sent message to ws!"), 207 | Err(e) => { 208 | error!("error writing to ws: {}", e); 209 | break; 210 | } 211 | } 212 | if child_exited { 213 | debug!("child had exited, breaking loop"); 214 | break; 215 | } 216 | }, 217 | Err(e) => { 218 | error!("error reading from cmd stdout: {}", e); 219 | break; 220 | } 221 | }, 222 | status = &mut child => { 223 | if child_exited { 224 | exit_loop_i -= 1; 225 | if exit_loop_i <= 0 { 226 | break; 227 | } 228 | continue; 229 | } 230 | child_exited = true; 231 | let msg = match status { 232 | Ok(es) => { 233 | debug!("child exited {:?}", es); 234 | ServerMessage::Exit{code: es.code(), signal: es.signal()} 235 | 236 | }, 237 | Err(e) => { 238 | debug!("child exited w/ error {}", e); 239 | ServerMessage::Error{message: e.to_string()} 240 | } 241 | }; 242 | 243 | match serde_json::to_string(&msg) { 244 | Ok(s) => match ws_write.send(Message::text(s)).await { 245 | Ok(_) => debug!("wrote exit code to websocket"), 246 | Err(e) => debug!("error writing exit code to websocket: {}", e), 247 | } 248 | Err(e) => { 249 | debug!("error encoding exit message: {}", e); 250 | } 251 | } 252 | 253 | if let Err(e) = cmd_stdin.flush().await { 254 | error!("error flushing cmd stdin: {}", e); 255 | } else { 256 | debug!("flushed stdin"); 257 | } 258 | 259 | if let Err(e) = cmd_stdin.shutdown().await { 260 | error!("error shutting down cmd stdin: {}", e); 261 | } else { 262 | debug!("shutdown stdin"); 263 | } 264 | } 265 | } 266 | } 267 | 268 | match ws_write.send(Message::close()).await { 269 | Ok(_) => debug!("successfully sent close message"), 270 | Err(e) => debug!("error sending exec close message: {}", e), 271 | } 272 | 273 | } 274 | }) 275 | } 276 | 277 | #[derive(Debug)] 278 | pub enum ExecError { 279 | Io(io::Error), 280 | } 281 | 282 | impl From for ExecError { 283 | fn from(e: io::Error) -> Self { 284 | ExecError::Io(e) 285 | } 286 | } 287 | 288 | impl warp::reject::Reject for ExecError {} 289 | 290 | #[derive(Deserialize)] 291 | #[serde(untagged)] 292 | enum ClientMessage { 293 | Resize { cols: u16, rows: u16 }, 294 | Init { command: Vec, tty: bool }, 295 | } 296 | 297 | #[derive(Serialize)] 298 | enum ServerMessage { 299 | Exit { 300 | code: Option, 301 | signal: Option, 302 | }, 303 | Error { 304 | message: String, 305 | }, 306 | } 307 | 308 | #[pin_project(project = StdinProj)] 309 | enum Stdin { 310 | DevNull, 311 | Normal(#[pin]ChildStdin), 312 | Tty(#[pin]PtyMasterWrite), 313 | } 314 | 315 | impl AsyncWrite for Stdin { 316 | fn poll_write( 317 | self: Pin<&mut Self>, 318 | cx: &mut Context, 319 | buf: &[u8], 320 | ) -> Poll> { 321 | match self.project() { 322 | StdinProj::DevNull => Poll::Ready(Ok(buf.len())), 323 | StdinProj::Normal(stdin) => stdin.poll_write(cx, buf), 324 | StdinProj::Tty(stdin) => stdin.poll_write(cx, buf), 325 | } 326 | } 327 | 328 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 329 | match self.project() { 330 | StdinProj::DevNull => Poll::Ready(Ok(())), 331 | StdinProj::Normal(stdin) => stdin.poll_flush(cx), 332 | StdinProj::Tty(stdin) => stdin.poll_flush(cx), 333 | } 334 | } 335 | 336 | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 337 | match self.project() { 338 | StdinProj::DevNull => Poll::Ready(Ok(())), 339 | StdinProj::Normal(stdin) => stdin.poll_shutdown(cx), 340 | StdinProj::Tty(stdin) => stdin.poll_shutdown(cx), 341 | } 342 | } 343 | } 344 | 345 | 346 | #[pin_project(project = StdoutProj)] 347 | enum Stdout { 348 | Normal(#[pin]ChildStdout), 349 | Tty(#[pin]PtyMasterRead), 350 | } 351 | 352 | impl AsyncRead for Stdout { 353 | fn poll_read( 354 | self: Pin<&mut Self>, 355 | cx: &mut Context, 356 | buf: &mut [u8], 357 | ) -> Poll> { 358 | match self.project() { 359 | StdoutProj::Normal(stdout) => stdout.poll_read(cx, buf), 360 | StdoutProj::Tty(stdout) => stdout.poll_read(cx, buf), 361 | } 362 | } 363 | } -------------------------------------------------------------------------------- /src/api/macros.rs: -------------------------------------------------------------------------------- 1 | /// Helper to combine multiple filters together with Filter::or, possibly boxing the types in 2 | /// the process. This greatly helps the build times for `ipfs-http`. 3 | /// Source: https://github.com/seanmonstar/warp/issues/619#issuecomment-662716377 4 | #[macro_export] 5 | macro_rules! combine { 6 | ($x:expr $(,)?) => { boxed_on_debug!($x) }; 7 | ($($x:expr),+ $(,)?) => { 8 | combine!(@internal ; $($x),+; $($x),+) 9 | }; 10 | (@internal $($left:expr),*; $head:expr, $($tail:expr),+; $a:expr $(,$b:expr)?) => { 11 | (combine!($($left,)* $head)).or(combine!($($tail),+)) 12 | }; 13 | (@internal $($left:expr),*; $head:expr, $($tail:expr),+; $a:expr, $b:expr, $($more:expr),+) => { 14 | combine!(@internal $($left,)* $head; $($tail),+; $($more),+) 15 | }; 16 | } 17 | 18 | #[macro_export] 19 | #[cfg(debug_assertions)] 20 | macro_rules! boxed_on_debug { 21 | ($x:expr) => { 22 | $x.boxed() 23 | }; 24 | } 25 | 26 | #[macro_export] 27 | #[cfg(not(debug_assertions))] 28 | macro_rules! boxed_on_debug { 29 | ($x:expr) => { 30 | $x 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /src/api/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::error::Error as StdError; 3 | use std::sync::Arc; 4 | 5 | use futures::channel::oneshot; 6 | use futures::{Future, FutureExt, TryStream}; 7 | use nix::sys::signal::Signal; 8 | use tokio::io::{AsyncRead, AsyncWrite}; 9 | use tokio::sync::{mpsc, Mutex}; 10 | use warp::Filter; 11 | 12 | pub mod exec; 13 | #[macro_use] 14 | pub mod macros; 15 | pub mod signals; 16 | pub mod sys; 17 | 18 | pub enum ApiReply { 19 | Ok(A), 20 | Err(B), 21 | } 22 | 23 | impl warp::Reply for ApiReply 24 | where 25 | A: warp::Reply, 26 | B: warp::Reply, 27 | { 28 | fn into_response(self) -> warp::reply::Response { 29 | let mut res = match self { 30 | ApiReply::Ok(a) => a.into_response(), 31 | ApiReply::Err(b) => b.into_response(), 32 | }; 33 | let headers = res.headers_mut(); 34 | headers.insert( 35 | "fly-init-version", 36 | warp::http::header::HeaderValue::from_str(env!("VERGEN_SHA_SHORT")).unwrap(), 37 | ); 38 | res 39 | } 40 | } 41 | 42 | #[derive(Debug, Deserialize, Serialize, Clone)] 43 | pub struct ErrorMessage { 44 | message: String, 45 | } 46 | 47 | #[derive(Debug, Deserialize, Serialize, Clone)] 48 | pub struct ExitCode { 49 | pub code: i32, 50 | pub oom_killed: bool, 51 | } 52 | 53 | pub fn server( 54 | envs: HashMap, 55 | waitpid_mutex: Arc>, 56 | incoming: I, 57 | ) -> ( 58 | impl Future, 59 | oneshot::Sender<(i32, bool)>, 60 | mpsc::Receiver, 61 | ) 62 | where 63 | I: TryStream + Send + 'static, 64 | I::Ok: AsyncRead + AsyncWrite + Send + 'static + Unpin, 65 | I::Error: Into>, 66 | { 67 | let v1 = warp::path("v1"); 68 | 69 | let status_show = v1.and(warp::path("status")); 70 | let get_status = warp::get().and(status_show).map(status); 71 | 72 | let sysinfo_index = v1.and(warp::path("sysinfo")); 73 | 74 | let get_sysinfo = warp::get().and(sysinfo_index).map(sys::list_sysinfo); 75 | 76 | let kill_signal_path = v1.and(warp::path("signals")); 77 | 78 | let json_body = warp::body::content_length_limit(1024 * 16).and(warp::body::json()); 79 | 80 | let (tx_sig, rx_sig) = mpsc::channel(1); 81 | 82 | let post_signals = warp::post() 83 | .and(kill_signal_path) 84 | .and(warp::any().map(move || tx_sig.clone())) 85 | .and(json_body) 86 | .and_then(signals::send_kill_signal); 87 | 88 | let (tx, rx) = oneshot::channel::<(i32, bool)>(); 89 | 90 | let rx = rx.shared(); 91 | 92 | let get_exit_code = warp::get() 93 | .and(v1.and(warp::path("exit_code"))) 94 | .and(warp::any().map(move || rx.clone())) 95 | .and_then( 96 | |rx: futures::future::Shared>| async move { 97 | debug!("Received request for exit code"); 98 | match rx.await { 99 | Ok((code, oom_killed)) => Ok(warp::reply::json(&ExitCode { code, oom_killed })), 100 | Err(_e) => Err(warp::reject::not_found()), 101 | } 102 | }, 103 | ); 104 | 105 | let env_filter = warp::any().map(move || envs.clone()); 106 | 107 | let post_exec = warp::post() 108 | .and(v1.and(warp::path("exec"))) 109 | .and(warp::body::content_length_limit(1024 * 16).and(warp::body::json())) 110 | .and(env_filter.clone()) 111 | .and(warp::any().map(move || waitpid_mutex.clone())) 112 | .and_then(exec::exec_cmd); 113 | 114 | let ws_exec = v1 115 | .and(warp::path("ws")) 116 | .and(warp::path("exec")) 117 | .and(warp::ws()) 118 | .and(env_filter.clone()) 119 | .map(exec::exec_ws); 120 | 121 | ( 122 | warp::serve(combine!( 123 | get_status, 124 | get_exit_code, 125 | post_signals, 126 | get_sysinfo, 127 | post_exec, 128 | ws_exec, 129 | )) 130 | .serve_incoming(incoming), 131 | tx, 132 | rx_sig, 133 | ) 134 | } 135 | 136 | pub fn status() -> impl warp::Reply { 137 | warp::reply::json(&serde_json::json!({"ok": true})) 138 | } 139 | -------------------------------------------------------------------------------- /src/api/signals.rs: -------------------------------------------------------------------------------- 1 | use std::convert::{Infallible, TryFrom}; 2 | 3 | use nix::sys::signal::Signal; 4 | use tokio::sync::mpsc; 5 | use warp::http::StatusCode; 6 | 7 | use super::{ApiReply, ErrorMessage}; 8 | 9 | #[derive(Debug, Deserialize, Serialize, Clone)] 10 | pub struct KillSignal { 11 | signal: i32, 12 | } 13 | 14 | #[derive(Debug, Deserialize, Serialize, Clone)] 15 | pub struct OkReply { 16 | ok: bool, 17 | } 18 | 19 | pub async fn send_kill_signal( 20 | mut tx_sig: mpsc::Sender, 21 | kill_signal: KillSignal, 22 | ) -> Result { 23 | { 24 | match Signal::try_from(kill_signal.signal) { 25 | Ok(sig) => { 26 | if let Err(e) = tx_sig.send(sig).await { 27 | return Ok(ApiReply::Err(warp::reply::with_status( 28 | warp::reply::json(&ErrorMessage { 29 | message: format!("{}", e), 30 | }), 31 | StatusCode::INTERNAL_SERVER_ERROR, 32 | ))); 33 | } 34 | Ok(ApiReply::Ok(warp::reply::json(&OkReply { ok: true }))) 35 | } 36 | Err(e) => { 37 | error!("Received unknown signal {}", kill_signal.signal); 38 | Ok(ApiReply::Err(warp::reply::with_status( 39 | warp::reply::json(&ErrorMessage { 40 | message: format!("{}", e), 41 | }), 42 | StatusCode::BAD_REQUEST, 43 | ))) 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/api/sys.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fs::File; 3 | use std::io::Read; 4 | 5 | use super::{ApiReply, ErrorMessage}; 6 | use anyhow::Error; 7 | use warp::http::StatusCode; 8 | 9 | #[derive(Debug, Serialize, Clone)] 10 | pub struct SysInfo { 11 | memory: Memory, 12 | load_average: [f32; 3], 13 | cpus: HashMap, 14 | disks: Vec, 15 | net: Vec, 16 | filefd: FileFd, 17 | } 18 | 19 | #[derive(Debug, Serialize, Clone)] 20 | pub struct FileFd { 21 | allocated: usize, 22 | maximum: usize, 23 | } 24 | 25 | #[derive(Debug, Serialize, Clone)] 26 | pub struct Cpu { 27 | user: f32, 28 | nice: f32, 29 | system: f32, 30 | idle: f32, 31 | iowait: Option, 32 | irq: Option, 33 | softirq: Option, 34 | steal: Option, 35 | guest: Option, 36 | guest_nice: Option, 37 | } 38 | 39 | #[derive(Debug, Serialize, Clone)] 40 | pub struct Memory { 41 | mem_total: u64, 42 | mem_free: u64, 43 | mem_available: Option, 44 | buffers: u64, 45 | cached: u64, 46 | swap_cached: u64, 47 | active: u64, 48 | inactive: u64, 49 | swap_total: u64, 50 | swap_free: u64, 51 | dirty: u64, 52 | writeback: u64, 53 | slab: u64, 54 | shmem: Option, 55 | vmalloc_total: u64, 56 | vmalloc_used: u64, 57 | vmalloc_chunk: u64, 58 | } 59 | 60 | #[derive(Debug, Serialize, Clone)] 61 | pub struct NetworkDevice { 62 | name: String, 63 | recv_bytes: u64, 64 | recv_packets: u64, 65 | recv_errs: u64, 66 | recv_drop: u64, 67 | recv_fifo: u64, 68 | recv_frame: u64, 69 | recv_compressed: u64, 70 | recv_multicast: u64, 71 | sent_bytes: u64, 72 | sent_packets: u64, 73 | sent_errs: u64, 74 | sent_drop: u64, 75 | sent_fifo: u64, 76 | sent_colls: u64, 77 | sent_carrier: u64, 78 | sent_compressed: u64, 79 | } 80 | 81 | fn collect_sysinfo() -> Result { 82 | let meminfo = procfs::Meminfo::new()?; 83 | let load_avg = procfs::LoadAverage::new()?; 84 | let kernel = procfs::KernelStats::new()?; 85 | 86 | let mut file = File::open("/proc/sys/fs/file-nr")?; 87 | let mut raw_filefd = String::new(); 88 | file.read_to_string(&mut raw_filefd)?; 89 | 90 | let mut splitted_fd = raw_filefd.split_whitespace(); 91 | 92 | let filefd = FileFd { 93 | allocated: splitted_fd.next().unwrap_or("0").trim().parse()?, 94 | maximum: splitted_fd.skip(1).next().unwrap_or("0").trim().parse()?, 95 | }; 96 | 97 | Ok(SysInfo { 98 | filefd, 99 | memory: Memory { 100 | mem_total: meminfo.mem_total, 101 | mem_free: meminfo.mem_free, 102 | mem_available: meminfo.mem_available, 103 | buffers: meminfo.buffers, 104 | cached: meminfo.cached, 105 | swap_cached: meminfo.swap_cached, 106 | active: meminfo.active, 107 | inactive: meminfo.inactive, 108 | swap_total: meminfo.swap_total, 109 | swap_free: meminfo.swap_free, 110 | dirty: meminfo.dirty, 111 | writeback: meminfo.writeback, 112 | slab: meminfo.slab, 113 | shmem: meminfo.shmem, 114 | vmalloc_total: meminfo.vmalloc_total, 115 | vmalloc_used: meminfo.vmalloc_used, 116 | vmalloc_chunk: meminfo.vmalloc_chunk, 117 | }, 118 | load_average: [load_avg.one, load_avg.five, load_avg.fifteen], 119 | cpus: kernel 120 | .cpu_time 121 | .iter() 122 | .enumerate() 123 | .map(|(i, ct)| { 124 | ( 125 | i, 126 | Cpu { 127 | user: ct.user, 128 | nice: ct.nice, 129 | system: ct.system, 130 | idle: ct.idle, 131 | iowait: ct.iowait, 132 | irq: ct.irq, 133 | softirq: ct.softirq, 134 | steal: ct.steal, 135 | guest: ct.guest, 136 | guest_nice: ct.guest_nice, 137 | }, 138 | ) 139 | }) 140 | .collect(), 141 | disks: DiskStat::get_all()?, 142 | net: procfs::net::dev_status()? 143 | .into_iter() 144 | .filter(|(_, n)| n.name != "lo") 145 | .map(|(_, n)| NetworkDevice { 146 | name: n.name, 147 | recv_bytes: n.recv_bytes, 148 | recv_packets: n.recv_packets, 149 | recv_errs: n.recv_errs, 150 | recv_drop: n.recv_drop, 151 | recv_fifo: n.recv_fifo, 152 | recv_frame: n.recv_frame, 153 | recv_compressed: n.recv_compressed, 154 | recv_multicast: n.recv_multicast, 155 | sent_bytes: n.sent_bytes, 156 | sent_packets: n.sent_packets, 157 | sent_errs: n.sent_errs, 158 | sent_drop: n.sent_drop, 159 | sent_fifo: n.sent_fifo, 160 | sent_colls: n.sent_colls, 161 | sent_carrier: n.sent_carrier, 162 | sent_compressed: n.sent_compressed, 163 | }) 164 | .collect(), 165 | }) 166 | } 167 | 168 | pub fn list_sysinfo() -> impl warp::Reply { 169 | let res = collect_sysinfo(); 170 | 171 | debug!("sysinfo: {:?}", res); 172 | 173 | match res { 174 | Ok(s) => ApiReply::Ok(warp::reply::with_status( 175 | warp::reply::json(&s), 176 | StatusCode::OK, 177 | )), 178 | Err(e) => ApiReply::Err(warp::reply::with_status( 179 | warp::reply::json(&ErrorMessage { 180 | message: format!("{}", e), 181 | }), 182 | StatusCode::INTERNAL_SERVER_ERROR, 183 | )), 184 | } 185 | } 186 | 187 | #[derive(Debug, Clone, Serialize)] 188 | pub struct DiskStat { 189 | name: String, 190 | reads_completed: u64, 191 | reads_merged: u64, 192 | sectors_read: u64, 193 | time_reading: u64, 194 | writes_completed: u64, 195 | writes_merged: u64, 196 | sectors_written: u64, 197 | // in ms 198 | time_writing: u64, 199 | io_in_progress: u64, 200 | // in ms 201 | time_io: u64, 202 | // in ms 203 | time_io_weighted: u64, 204 | } 205 | 206 | impl DiskStat { 207 | fn get_all() -> Result, Error> { 208 | use std::io::{BufRead, BufReader}; 209 | let reader = BufReader::new(std::fs::File::open("/proc/diskstats")?); 210 | let mut disks = vec![]; 211 | for line in reader.lines() { 212 | if let Some(ds) = DiskStat::from_line(&line?)? { 213 | disks.push(ds); 214 | } 215 | } 216 | Ok(disks) 217 | } 218 | 219 | fn from_line(line: &str) -> Result, Error> { 220 | let mut split = line.trim_start().split_whitespace(); 221 | let name = split 222 | .nth(2) 223 | .ok_or_else(|| StringError::from("name missing".to_owned()))? 224 | .to_string(); 225 | 226 | if !name.starts_with("vd") { 227 | return Ok(None); 228 | } 229 | 230 | Ok(Some(DiskStat { 231 | name, 232 | reads_completed: split 233 | .next() 234 | .ok_or_else(|| StringError::from("reads_completed missing".to_owned()))? 235 | .parse()?, 236 | reads_merged: split 237 | .next() 238 | .ok_or_else(|| StringError::from("reads_merged missing".to_owned()))? 239 | .parse()?, 240 | sectors_read: split 241 | .next() 242 | .ok_or_else(|| StringError::from("sectors_read missing".to_owned()))? 243 | .parse()?, 244 | time_reading: split 245 | .next() 246 | .ok_or_else(|| StringError::from("time_reading missing".to_owned()))? 247 | .parse()?, 248 | writes_completed: split 249 | .next() 250 | .ok_or_else(|| StringError::from("writes_completed missing".to_owned()))? 251 | .parse()?, 252 | writes_merged: split 253 | .next() 254 | .ok_or_else(|| StringError::from("writes_merged missing".to_owned()))? 255 | .parse()?, 256 | sectors_written: split 257 | .next() 258 | .ok_or_else(|| StringError::from("sectors_written missing".to_owned()))? 259 | .parse()?, 260 | time_writing: split 261 | .next() 262 | .ok_or_else(|| StringError::from("time_writing missing".to_owned()))? 263 | .parse()?, 264 | io_in_progress: split 265 | .next() 266 | .ok_or_else(|| StringError::from("io_in_progress missing".to_owned()))? 267 | .parse()?, 268 | time_io: split 269 | .next() 270 | .ok_or_else(|| StringError::from("time_io missing".to_owned()))? 271 | .parse()?, 272 | time_io_weighted: split 273 | .next() 274 | .ok_or_else(|| StringError::from("time_io_weighted missing".to_owned()))? 275 | .parse()?, 276 | })) 277 | } 278 | } 279 | 280 | pub struct StringError(String); 281 | 282 | impl From for StringError { 283 | fn from(f: String) -> StringError { 284 | StringError(f) 285 | } 286 | } 287 | 288 | impl<'a> From<&'a str> for StringError { 289 | fn from(f: &str) -> StringError { 290 | StringError(f.into()) 291 | } 292 | } 293 | 294 | use std::fmt; 295 | 296 | impl fmt::Debug for StringError { 297 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 298 | fmt::Debug::fmt(&self.0, f) 299 | } 300 | } 301 | 302 | impl fmt::Display for StringError { 303 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 304 | fmt::Display::fmt(&self.0, f) 305 | } 306 | } 307 | 308 | impl std::error::Error for StringError {} 309 | -------------------------------------------------------------------------------- /src/bin/init/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | use std::env; 5 | use std::fs::{self, File}; 6 | use std::io::{self, BufReader, Write}; 7 | use std::os::unix::io::{AsRawFd, FromRawFd}; 8 | use std::os::unix::process::ExitStatusExt; 9 | use std::sync::Arc; 10 | use std::time::Duration; 11 | use std::{collections::HashMap, convert::TryFrom}; 12 | 13 | use anyhow::Error; 14 | use futures::{StreamExt, TryStreamExt}; 15 | use ifstructs::ifreq; 16 | use ipnetwork::IpNetwork; 17 | use libc::pid_t; 18 | use nix::errno::Errno; 19 | use nix::ioctl_write_ptr_bad; 20 | use nix::mount::{mount as nix_mount, umount, umount2, MntFlags, MsFlags}; 21 | use nix::sys::socket::SockAddr; 22 | use nix::sys::{ 23 | self, 24 | stat::Mode, 25 | wait::{waitpid, WaitPidFlag, WaitStatus}, 26 | }; 27 | use nix::unistd::{ 28 | chdir as nix_chdir, chown, chroot as nix_chroot, close, fchown, mkdir as nix_mkdir, 29 | sethostname, symlinkat, sync, Gid, Group, Uid, User, 30 | }; 31 | use nix::NixPath; 32 | use os_pipe::pipe; 33 | use sys::socket::{AddressFamily, SockFlag, SockType}; 34 | use tokio::io::AsyncBufReadExt; 35 | use tokio::process::Command; 36 | use tokio::sync::Mutex; 37 | use tokio_vsock::VsockListener; 38 | 39 | use fly_init::{ImageConfig, RunConfig}; 40 | 41 | #[derive(Debug, thiserror::Error)] 42 | enum InitError { 43 | #[error(transparent)] 44 | Config(#[from] ConfigError), 45 | 46 | #[error("couldn't mount {} onto {}, because: {}", source, target, error)] 47 | Mount { 48 | source: String, 49 | target: String, 50 | #[source] 51 | error: nix::Error, 52 | }, 53 | 54 | #[error("couldn't mkdir {}, because: {}", path, error)] 55 | Mkdir { 56 | path: String, 57 | #[source] 58 | error: nix::Error, 59 | }, 60 | 61 | #[error("couldn't chroot to {}, because: {}", path, error)] 62 | Chroot { 63 | path: String, 64 | #[source] 65 | error: nix::Error, 66 | }, 67 | 68 | #[error("couldn't chdir to {}, because: {}", path, error)] 69 | Chdir { 70 | path: String, 71 | #[source] 72 | error: nix::Error, 73 | }, 74 | 75 | #[error(r#"couldn't find user "{}""#, 0)] 76 | UserNotFound(String), 77 | #[error(r#"couldn't find group "{}""#, 0)] 78 | GroupNotFound(String), 79 | 80 | #[error("an unhandled error occurred: {}", 0)] 81 | UnhandledNixError(#[from] nix::Error), 82 | 83 | #[error("an unhandled IO error occurred: {}", 0)] 84 | UnhandledIoError(#[from] io::Error), 85 | 86 | #[error("an unhandled netlink error occurred: {}", 0)] 87 | UnhandledNetlinkError(#[from] rtnetlink::Error), 88 | 89 | #[error("an unhandled error occurred: {}", 0)] 90 | UnhandledError(#[from] Error), 91 | } 92 | 93 | #[derive(Debug, thiserror::Error)] 94 | enum ConfigError { 95 | #[error("error reading fly json config: {}", 0)] 96 | Read(#[from] io::Error), 97 | #[error("error parsing fly json config: {}", 0)] 98 | Parse(#[from] serde_json::Error), 99 | } 100 | 101 | const SIOCETHTOOL: u32 = 0x8946; 102 | const IFA_F_NODAD: u8 = 0x02; 103 | 104 | //const ETHTOOL_GRXCSUM: u32 = 0x00000014; 105 | const ETHTOOL_SRXCSUM: u32 = 0x00000015; 106 | //const ETHTOOL_GTXCSUM: u32 = 0x00000016; 107 | const ETHTOOL_STXCSUM: u32 = 0x00000017; 108 | 109 | #[repr(C)] 110 | pub struct EthtoolValue { 111 | cmd: u32, 112 | value: u32, 113 | } 114 | 115 | ioctl_write_ptr_bad!(ethtoolset, SIOCETHTOOL, ifreq); 116 | 117 | pub fn ethtool_set(name: &str, cmd: u32, value: u32) -> nix::Result<()> { 118 | let mut ifres = ifreq::from_name(name); 119 | if let Ok(ref mut ifr) = ifres { 120 | let mut ev = EthtoolValue { 121 | cmd: cmd, 122 | value: value, 123 | }; 124 | 125 | ifr.ifr_ifru.ifr_data = (&mut ev as *mut EthtoolValue).cast::<_>(); 126 | 127 | let sfd = sys::socket::socket( 128 | AddressFamily::Netlink, 129 | SockType::Raw, 130 | SockFlag::empty(), 131 | None, 132 | )?; 133 | 134 | let res = unsafe { ethtoolset(sfd, ifr) }; 135 | 136 | close(sfd)?; 137 | 138 | match res { 139 | Ok(_) => { 140 | return Ok(()); 141 | } 142 | Err(v) => { 143 | return Err(v); 144 | } 145 | } 146 | } 147 | 148 | return Err(nix::Error::invalid_argument()); 149 | } 150 | 151 | pub fn log_init() { 152 | // default to "info" level, just for this bin 153 | let level = env::var("LOG_FILTER").unwrap_or_else(|_| "init=info".into()); 154 | 155 | env_logger::builder() 156 | .parse_filters(&level) 157 | .write_style(env_logger::WriteStyle::Never) 158 | .default_format_level(false) 159 | .default_format_module_path(false) 160 | .default_format_timestamp(false) 161 | .init(); 162 | } 163 | 164 | #[tokio::main] 165 | async fn main() -> Result<(), InitError> { 166 | log_init(); 167 | 168 | // can't put these as const unfortunately... 169 | let chmod_0755: Mode = 170 | Mode::S_IRWXU | Mode::S_IRGRP | Mode::S_IXGRP | Mode::S_IROTH | Mode::S_IXOTH; 171 | let chmod_0555: Mode = Mode::S_IRUSR 172 | | Mode::S_IXUSR 173 | | Mode::S_IRGRP 174 | | Mode::S_IXGRP 175 | | Mode::S_IROTH 176 | | Mode::S_IXOTH; 177 | let chmod_1777: Mode = Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO | Mode::S_ISVTX; 178 | // let chmod_0777 = Mode::S_IRWXU | Mode::S_IRWXG | Mode::S_IRWXO; 179 | let common_mnt_flags: MsFlags = MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID; 180 | 181 | info!("Starting init (commit: {})...", env!("VERGEN_SHA_SHORT")); 182 | 183 | let conf_reader = BufReader::new(File::open("/fly/run.json").map_err(ConfigError::from)?); 184 | let conf: RunConfig = serde_json::from_reader(conf_reader).map_err(ConfigError::from)?; 185 | trace!("run conf: {:?}", conf); 186 | 187 | debug!("Mounting /dev"); 188 | mkdir("/dev", chmod_0755).ok(); 189 | mount( 190 | Some("devtmpfs"), 191 | "/dev", 192 | Some("devtmpfs"), 193 | MsFlags::MS_NOSUID, 194 | Some("mode=0755"), 195 | )?; 196 | 197 | mkdir("/newroot", chmod_0755)?; 198 | 199 | let root_device = if let Some(rd) = conf.root_device { 200 | rd 201 | } else { 202 | "/dev/vdb".to_owned() 203 | }; 204 | 205 | debug!("Mounting newroot fs"); 206 | mount::<_, _, _, [u8]>( 207 | Some(root_device.as_str()), 208 | "/newroot", 209 | Some("ext4"), 210 | MsFlags::MS_RELATIME, 211 | None, 212 | )?; 213 | 214 | // Move /dev so we don't have to re-mount it 215 | debug!("Mounting (move) /dev"); 216 | mkdir("/newroot/dev", chmod_0755).ok(); 217 | mount::<_, _, [u8], [u8]>(Some("/dev"), "/newroot/dev", None, MsFlags::MS_MOVE, None)?; 218 | 219 | // Saving some space 220 | debug!("Removing /fly"); 221 | fs::remove_dir_all("/fly")?; 222 | 223 | // Our own hacky switch_root 224 | debug!("Switching root"); 225 | // Change directory to the new root 226 | chdir("/newroot")?; 227 | // Mount the new root over / 228 | mount::<_, _, [u8], [u8]>(Some("."), "/", None, MsFlags::MS_MOVE, None)?; 229 | // Change root to the current directory (new root) 230 | chroot(".")?; 231 | // Change directory to / 232 | chdir("/")?; 233 | 234 | debug!("Mounting /dev/pts"); 235 | mkdir("/dev/pts", chmod_0755).ok(); 236 | mount( 237 | Some("devpts"), 238 | "/dev/pts", 239 | Some("devpts"), 240 | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_NOATIME, 241 | Some("mode=0620,gid=5,ptmxmode=666"), 242 | )?; 243 | 244 | debug!("Mounting /dev/mqueue"); 245 | mkdir("/dev/mqueue", chmod_0755).ok(); 246 | mount::<_, _, _, [u8]>( 247 | Some("mqueue"), 248 | "/dev/mqueue", 249 | Some("mqueue"), 250 | common_mnt_flags, 251 | None, 252 | )?; 253 | 254 | debug!("Mounting /dev/shm"); 255 | mkdir("/dev/shm", chmod_1777).ok(); 256 | mount::<_, _, _, [u8]>( 257 | Some("shm"), 258 | "/dev/shm", 259 | Some("tmpfs"), 260 | MsFlags::MS_NOSUID | MsFlags::MS_NODEV, 261 | None, 262 | )?; 263 | 264 | debug!("Mounting /dev/hugepages"); 265 | mkdir("/dev/hugepages", chmod_0755).ok(); 266 | mount( 267 | Some("hugetlbfs"), 268 | "/dev/hugepages", 269 | Some("hugetlbfs"), 270 | MsFlags::MS_RELATIME, 271 | Some("pagesize=2M"), 272 | )?; 273 | 274 | debug!("Mounting /proc"); 275 | mkdir("/proc", chmod_0555).ok(); 276 | mount::<_, _, _, [u8]>(Some("proc"), "/proc", Some("proc"), common_mnt_flags, None)?; 277 | mount::<_, _, _, [u8]>( 278 | Some("binfmt_misc"), 279 | "/proc/sys/fs/binfmt_misc", 280 | Some("binfmt_misc"), 281 | common_mnt_flags | MsFlags::MS_RELATIME, 282 | None, 283 | )?; 284 | 285 | debug!("Mounting /sys"); 286 | mkdir("/sys", chmod_0555).ok(); 287 | mount::<_, _, _, [u8]>(Some("sys"), "/sys", Some("sysfs"), common_mnt_flags, None)?; 288 | 289 | debug!("Mounting /run"); 290 | mkdir("/run", chmod_0755).ok(); 291 | mount( 292 | Some("run"), 293 | "/run", 294 | Some("tmpfs"), 295 | MsFlags::MS_NOSUID | MsFlags::MS_NODEV, 296 | Some("mode=0755"), 297 | )?; 298 | mkdir("/run/lock", Mode::all()).ok(); 299 | 300 | symlinkat("/proc/self/fd", None, "/dev/fd").ok(); 301 | symlinkat("/proc/self/fd/0", None, "/dev/stdin").ok(); 302 | symlinkat("/proc/self/fd/1", None, "/dev/stdout").ok(); 303 | symlinkat("/proc/self/fd/2", None, "/dev/stderr").ok(); 304 | 305 | mkdir("/root", Mode::S_IRWXU).ok(); 306 | 307 | let common_cgroup_mnt_flags = 308 | MsFlags::MS_NODEV | MsFlags::MS_NOEXEC | MsFlags::MS_NOSUID | MsFlags::MS_RELATIME; 309 | 310 | debug!("Mounting cgroup"); 311 | mount( 312 | Some("tmpfs"), 313 | "/sys/fs/cgroup", 314 | Some("tmpfs"), 315 | MsFlags::MS_NOSUID | MsFlags::MS_NOEXEC | MsFlags::MS_NODEV, // | MsFlags::MS_RDONLY, 316 | Some("mode=755"), 317 | )?; 318 | 319 | debug!("Mounting cgroup2"); 320 | mkdir("/sys/fs/cgroup/unified", chmod_0555)?; 321 | mount( 322 | Some("cgroup2"), 323 | "/sys/fs/cgroup/unified", 324 | Some("cgroup2"), 325 | common_mnt_flags | MsFlags::MS_RELATIME, 326 | Some("nsdelegate"), 327 | )?; 328 | 329 | debug!("Mounting /sys/fs/cgroup/net_cls,net_prio"); 330 | mkdir("/sys/fs/cgroup/net_cls,net_prio", chmod_0555)?; 331 | mount( 332 | Some("cgroup"), 333 | "/sys/fs/cgroup/net_cls,net_prio", 334 | Some("cgroup"), 335 | common_cgroup_mnt_flags, 336 | Some("net_cls,net_prio"), 337 | )?; 338 | 339 | debug!("Mounting /sys/fs/cgroup/hugetlb"); 340 | mkdir("/sys/fs/cgroup/hugetlb", chmod_0555)?; 341 | mount( 342 | Some("cgroup"), 343 | "/sys/fs/cgroup/hugetlb", 344 | Some("cgroup"), 345 | common_cgroup_mnt_flags, 346 | Some("hugetlb"), 347 | )?; 348 | 349 | debug!("Mounting /sys/fs/cgroup/pids"); 350 | mkdir("/sys/fs/cgroup/pids", chmod_0555)?; 351 | mount( 352 | Some("cgroup"), 353 | "/sys/fs/cgroup/pids", 354 | Some("cgroup"), 355 | common_cgroup_mnt_flags, 356 | Some("pids"), 357 | )?; 358 | 359 | debug!("Mounting /sys/fs/cgroup/freezer"); 360 | mkdir("/sys/fs/cgroup/freezer", chmod_0555)?; 361 | mount( 362 | Some("cgroup"), 363 | "/sys/fs/cgroup/freezer", 364 | Some("cgroup"), 365 | common_cgroup_mnt_flags, 366 | Some("freezer"), 367 | )?; 368 | 369 | debug!("Mounting /sys/fs/cgroup/cpu,cpuacct"); 370 | mkdir("/sys/fs/cgroup/cpu,cpuacct", chmod_0555)?; 371 | mount( 372 | Some("cgroup"), 373 | "/sys/fs/cgroup/cpu,cpuacct", 374 | Some("cgroup"), 375 | common_cgroup_mnt_flags, 376 | Some("cpu,cpuacct"), 377 | )?; 378 | 379 | debug!("Mounting /sys/fs/cgroup/devices"); 380 | mkdir("/sys/fs/cgroup/devices", chmod_0555)?; 381 | mount( 382 | Some("cgroup"), 383 | "/sys/fs/cgroup/devices", 384 | Some("cgroup"), 385 | common_cgroup_mnt_flags, 386 | Some("devices"), 387 | )?; 388 | 389 | debug!("Mounting /sys/fs/cgroup/blkio"); 390 | mkdir("/sys/fs/cgroup/blkio", chmod_0555)?; 391 | mount( 392 | Some("cgroup"), 393 | "/sys/fs/cgroup/blkio", 394 | Some("cgroup"), 395 | common_cgroup_mnt_flags, 396 | Some("blkio"), 397 | )?; 398 | 399 | debug!("Mounting cgroup/memory"); 400 | mkdir("/sys/fs/cgroup/memory", chmod_0555)?; 401 | mount( 402 | Some("cgroup"), 403 | "/sys/fs/cgroup/memory", 404 | Some("cgroup"), 405 | common_cgroup_mnt_flags, 406 | Some("memory"), 407 | )?; 408 | 409 | debug!("Mounting /sys/fs/cgroup/perf_event"); 410 | mkdir("/sys/fs/cgroup/perf_event", chmod_0555)?; 411 | mount( 412 | Some("cgroup"), 413 | "/sys/fs/cgroup/perf_event", 414 | Some("cgroup"), 415 | common_cgroup_mnt_flags, 416 | Some("perf_event"), 417 | )?; 418 | 419 | debug!("Mounting /sys/fs/cgroup/cpuset"); 420 | mkdir("/sys/fs/cgroup/cpuset", chmod_0555)?; 421 | mount( 422 | Some("cgroup"), 423 | "/sys/fs/cgroup/cpuset", 424 | Some("cgroup"), 425 | common_cgroup_mnt_flags, 426 | Some("cpuset"), 427 | )?; 428 | 429 | rlimit::setrlimit(rlimit::Resource::NOFILE, 10240, 10240).ok(); 430 | 431 | let image_conf = conf 432 | .image_config 433 | .clone() 434 | .unwrap_or_else(|| ImageConfig::default()); 435 | 436 | let user = if let Some(user_override) = conf.user_override { 437 | user_override 438 | } else if let Some(user) = image_conf.user { 439 | user 440 | } else { 441 | "root".to_owned() 442 | }; 443 | 444 | let mut user_split = user.split(":"); 445 | 446 | let user = user_split 447 | .next() 448 | .expect("no user defined, this should not happen, please contact support!"); 449 | let group = user_split.next(); 450 | 451 | debug!("searching for user '{}", user); 452 | 453 | let (uid, mut gid, home_dir) = match User::from_name(user) { 454 | Ok(Some(u)) => (u.uid, u.gid, u.dir), 455 | Ok(None) => { 456 | if let Ok(uid) = user.parse::() { 457 | match User::from_uid(Uid::from_raw(uid)) { 458 | Ok(Some(u)) => (u.uid, u.gid, u.dir), 459 | _ => (Uid::from_raw(uid), Gid::from_raw(uid), "/".into()), 460 | } 461 | } else { 462 | return Err(InitError::UserNotFound(user.into()).into()); 463 | } 464 | } 465 | Err(e) => { 466 | if user != "root" { 467 | return Err(InitError::UserNotFound(user.into()).into()); 468 | } 469 | debug!("error getting user '{}' by name => {}", user, e); 470 | match User::from_name("root") { 471 | Ok(Some(u)) => (u.uid, u.gid, u.dir), 472 | _ => (Uid::from_raw(0), Gid::from_raw(0), "/root".into()), 473 | } 474 | } 475 | }; 476 | 477 | if let Some(group) = group { 478 | debug!("searching for group '{}'", group); 479 | match Group::from_name(group) { 480 | Err(_e) => { 481 | return Err(InitError::GroupNotFound(group.into()).into()); 482 | } 483 | Ok(Some(g)) => gid = g.gid, 484 | Ok(None) => { 485 | if let Ok(raw_gid) = group.parse::() { 486 | gid = Gid::from_raw(raw_gid); 487 | } else { 488 | return Err(InitError::GroupNotFound(group.into()).into()); 489 | } 490 | } 491 | } 492 | } 493 | 494 | let envs = image_conf.env.clone().unwrap_or_else(|| vec![]); 495 | 496 | let mut envs: HashMap = envs 497 | .iter() 498 | .map(|e| { 499 | let mut splitted = e.splitn(2, "="); 500 | ( 501 | splitted.next().unwrap().to_owned(), 502 | splitted.next().unwrap().to_owned(), 503 | ) 504 | }) 505 | .collect(); 506 | 507 | if let Some(ref extras) = conf.extra_env { 508 | envs.extend(extras.clone()); 509 | } 510 | 511 | // if we have a PATH, set it on the OS to be able to find argv[0] 512 | if let Some(p) = envs.get("PATH") { 513 | if p != "" { 514 | env::set_var("PATH", p); 515 | } 516 | } 517 | 518 | envs.entry("HOME".to_owned()) 519 | .or_insert(home_dir.to_string_lossy().into_owned()); 520 | 521 | let incoming = VsockListener::bind(&SockAddr::new_vsock(3, 10000))?.incoming(); 522 | 523 | let waitpid_mutex = Arc::new(Mutex::new(())); 524 | 525 | let (api, tx, mut rx_sig) = 526 | fly_init::api::server(envs.clone(), waitpid_mutex.clone(), incoming); 527 | tokio::spawn(api); 528 | 529 | if let Some(ref mounts) = conf.mounts { 530 | for m in mounts { 531 | info!("Mounting {} at {}", m.device_path, m.mount_path); 532 | 533 | if let Err(e) = nix_mkdir(m.mount_path.as_str(), chmod_0755) { 534 | if let Some(nix::errno::Errno::EEXIST) = e.as_errno() { 535 | warn!("directory {} already exists", m.mount_path); 536 | } else { 537 | panic!("could not create directory {}: {}", m.mount_path, e); 538 | } 539 | } 540 | 541 | mount::<_, _, _, [u8]>( 542 | Some(m.device_path.as_str()), 543 | m.mount_path.as_str(), 544 | Some("ext4"), 545 | MsFlags::MS_RELATIME, 546 | None, 547 | )?; 548 | 549 | if let Err(e) = chown(m.mount_path.as_str(), Some(uid), Some(gid)) { 550 | warn!( 551 | "could not chown directory {} with uid {} and gid {}: {}", 552 | m.mount_path, uid, gid, e 553 | ); 554 | }; 555 | } 556 | } 557 | 558 | // command argv 559 | let mut argv: Vec = vec![]; 560 | 561 | match conf.exec_override { 562 | Some(ref ovrd) => argv = ovrd.clone(), 563 | None => { 564 | match image_conf.entrypoint { 565 | Some(ref entry) => { 566 | argv = entry.clone(); 567 | } 568 | _ => {} 569 | }; 570 | match conf.cmd_override { 571 | Some(ref ovrd) => { 572 | argv.push(ovrd.clone()); 573 | } 574 | None => match image_conf.cmd { 575 | Some(ref c) => { 576 | argv.append(&mut c.clone()); 577 | } 578 | _ => {} 579 | }, 580 | }; 581 | } 582 | }; 583 | 584 | match sethostname(&conf.hostname) { 585 | Err(e) => warn!("error setting hostname: {}", e), 586 | Ok(_) => {} 587 | }; 588 | 589 | mkdir("/etc", chmod_0755).ok(); 590 | 591 | // Some programs might prefer this 592 | fs::write("/etc/hostname", conf.hostname).ok(); 593 | 594 | if let Some(ref etc_hosts) = conf.etc_hosts { 595 | debug!("Populating /etc/hosts"); 596 | let mut f = fs::OpenOptions::new() 597 | .append(true) 598 | .create(true) 599 | .open("/etc/hosts")?; 600 | 601 | for entry in etc_hosts { 602 | if let Some(ref desc) = entry.desc { 603 | write!(&mut f, "\n# {}\n{}\t{}\n", desc, entry.ip, entry.host).ok(); 604 | } else { 605 | write!(&mut f, "\n{}\t{}\n", entry.ip, entry.host).ok(); 606 | } 607 | } 608 | } 609 | 610 | if let Some(ref etc_resolv) = conf.etc_resolv { 611 | debug!("Populating /etc/resolv.conf"); 612 | let mut f = fs::OpenOptions::new() 613 | .create(true) 614 | .write(true) 615 | .truncate(true) 616 | .open("/etc/resolv.conf")?; 617 | 618 | for ns in etc_resolv.nameservers.iter() { 619 | write!(&mut f, "\nnameserver\t{}", ns).ok(); 620 | } 621 | 622 | write!(&mut f, "\n").ok(); 623 | } 624 | 625 | let (connection, handle, _) = rtnetlink::new_connection().unwrap(); 626 | tokio::spawn(connection); 627 | 628 | debug!("netlink: getting lo link"); 629 | let lo = handle 630 | .link() 631 | .get() 632 | .set_name_filter("lo".into()) 633 | .execute() 634 | .try_next() 635 | .await? 636 | .expect("no lo link found"); 637 | 638 | debug!("netlink: setting lo link \"up\""); 639 | handle.link().set(lo.header.index).up().execute().await?; 640 | 641 | debug!("netlink: getting eth0 link"); 642 | let eth0 = handle 643 | .link() 644 | .get() 645 | .set_name_filter("eth0".into()) 646 | .execute() 647 | .try_next() 648 | .await? 649 | .expect("no eth0 link found"); 650 | 651 | debug!("netlink: setting eth0 link \"up\""); 652 | handle 653 | .link() 654 | .set(eth0.header.index) 655 | .up() 656 | .mtu(1420) 657 | .execute() 658 | .await?; 659 | 660 | let _ = ethtool_set("eth0", ETHTOOL_SRXCSUM, 0); 661 | ethtool_set("eth0", ETHTOOL_STXCSUM, 0)?; 662 | 663 | if let Some(ref ip_configs) = conf.ip_configs { 664 | let address = handle.address(); 665 | let route = handle.route(); 666 | 667 | for ipc in ip_configs { 668 | debug!("netlink: adding ip {}/{}", ipc.ip.ip(), ipc.mask); 669 | let mut addr_req = address.add(eth0.header.index, ipc.ip.ip(), ipc.mask); 670 | addr_req.message_mut().header.flags |= IFA_F_NODAD; 671 | addr_req.execute().await?; 672 | 673 | if let IpNetwork::V4(ipn) = ipc.ip { 674 | if ipc.mask < 30 { 675 | let ipint: u32 = ipn.ip().into(); 676 | let nextip: std::net::Ipv4Addr = (ipint + 1).into(); 677 | 678 | address 679 | .add(eth0.header.index, std::net::IpAddr::V4(nextip), ipc.mask) 680 | .execute() 681 | .await?; 682 | } 683 | } 684 | 685 | debug!("netlink: adding default route via {}", ipc.gateway); 686 | match ipc.gateway { 687 | IpNetwork::V4(gateway) => { 688 | route.add_v4().gateway(gateway.ip()).execute().await?; 689 | } 690 | IpNetwork::V6(gateway) => { 691 | if ipc.mask != 112 { 692 | route.add_v6().gateway(gateway.ip()).execute().await?; 693 | } 694 | } 695 | } 696 | } 697 | } 698 | 699 | info!("Running: `{}` as {}", argv.join(" "), user); 700 | 701 | let (reader, writer) = pipe().expect("could not create read/write pipe for process"); 702 | let writer_clone = writer 703 | .try_clone() 704 | .expect("could not clone pipe writer for process"); 705 | 706 | fchown(reader.as_raw_fd(), Some(uid), Some(gid)).expect("could not fchown pipe reader"); 707 | fchown(writer.as_raw_fd(), Some(uid), Some(gid)).expect("could not fchown pipe writer"); 708 | 709 | let mut command = Command::new(argv.remove(0)); 710 | command 711 | .args(&argv) 712 | .envs(&envs) 713 | .stdout(writer) 714 | .stderr(writer_clone); 715 | command.uid(uid.as_raw()).gid(gid.as_raw()); 716 | 717 | if let Some(ref wd) = image_conf.working_dir { 718 | if wd != "" { 719 | debug!("Setting current dir on command to: {}", wd); 720 | command.current_dir(&wd); 721 | } 722 | } 723 | 724 | let child = command.spawn()?; 725 | let pid = child.id() as pid_t; 726 | debug!("child pid: {}", pid); 727 | 728 | let mut stdouterr: tokio::fs::File = 729 | unsafe { std::fs::File::from_raw_fd(reader.as_raw_fd()) }.into(); 730 | 731 | tokio::spawn(async move { 732 | let mut own_stdout = tokio::io::stdout(); 733 | if let Err(e) = tokio::io::copy(&mut stdouterr, &mut own_stdout).await { 734 | debug!("stdout/err copy ended with error: {}", e); 735 | }; 736 | }); 737 | 738 | let mut exit_status = -1; 739 | 740 | loop { 741 | // wait for a signal for 1 second! 742 | let mut deadline = tokio::time::delay_for(Duration::from_secs(1)); 743 | let sig_fut = rx_sig.recv(); 744 | tokio::pin!(sig_fut); 745 | tokio::select! { 746 | _ = &mut deadline => { 747 | // deadline expired 748 | }, 749 | maybe_sig = &mut sig_fut => match maybe_sig { 750 | Some(sig) => { 751 | info!("Sending signal {} to main child process w/ PID {}", sig, pid); 752 | match nix::sys::signal::kill(nix::unistd::Pid::from_raw(pid), sig) { 753 | Ok(_) => { 754 | debug!("kill signal sent successfully"); 755 | }, 756 | Err(e) => { 757 | error!("error signaling ({}) main child process: {}", sig, e); 758 | } 759 | } 760 | }, 761 | None => { 762 | debug!("all signal senders dropped! that should not happen!"); 763 | } 764 | } 765 | } 766 | 767 | // ward off the zombies! this will also detect if the main child has exited 768 | let child_exited = { 769 | let _guard = waitpid_mutex.lock().await; 770 | reap_zombies(pid, &mut exit_status) 771 | }; 772 | 773 | if child_exited { 774 | break; 775 | } 776 | } 777 | 778 | // match child.await { 779 | // Ok(status) => { 780 | // if let Some(sig) = status.signal() { 781 | // info!( 782 | // "Program exited with code: {:?} signal: {} ({})", 783 | // status.code(), 784 | // nix::sys::signal::Signal::try_from(sig) 785 | // .map(|s| s.to_string()) 786 | // .unwrap_or_else(|_| sig.to_string()), 787 | // sig 788 | // ); 789 | // } else if let Some(code) = status.code() { 790 | // info!("Program exited with code: {}", code); 791 | // exit_status = code; 792 | // } else { 793 | // info!("Program exited with an unknown code and was not signaled"); 794 | // } 795 | // } 796 | // Err(e) => { 797 | // debug!("error waiting for main child to exit: {}", e) 798 | // } 799 | // } 800 | 801 | let mut oom_killed = false; 802 | match tokio::fs::File::open("/dev/kmsg").await { 803 | Err(e) => error!("error opening /dev/kmsg: {}", e), 804 | Ok(f) => { 805 | let bf = tokio::io::BufReader::new(f); 806 | let mut lines = bf.lines(); 807 | 808 | let matcher = format!("Killed process {}", pid); 809 | trace!("attempting to match '{}' from kernel logs", matcher); 810 | 811 | loop { 812 | let mut delay = tokio::time::delay_for(Duration::from_millis(10)); 813 | tokio::select! { 814 | _ = &mut delay => { 815 | trace!("timed out waiting for OOM message"); 816 | break; 817 | } 818 | line = lines.next() => match line { 819 | Some(Ok(line)) => { 820 | if line.contains(&matcher) { 821 | info!("Process appears to have been OOM killed!"); 822 | oom_killed = true; 823 | break; 824 | } 825 | }, 826 | _ => { 827 | break; 828 | } 829 | } 830 | } 831 | } 832 | } 833 | } 834 | 835 | tx.send((exit_status, oom_killed)).ok(); 836 | 837 | if let Some(mounts) = conf.mounts { 838 | for m in mounts { 839 | info!("Umounting {} from {}", m.device_path, m.mount_path); 840 | 841 | let mut attempts = 5; 842 | 843 | loop { 844 | debug!("Attempting umount"); 845 | if let Err(e) = umount(m.mount_path.as_str()) { 846 | attempts -= 1; 847 | if attempts > 0 { 848 | error!("error umounting {}: {}, retrying in a bit", m.mount_path, e); 849 | tokio::time::delay_for(Duration::from_millis(750)).await; 850 | continue; 851 | } else { 852 | if let Err(e) = umount2(m.mount_path.as_str(), MntFlags::MNT_DETACH) { 853 | error!("error lazy umounting {}: {}, not retrying", m.mount_path, e); 854 | } else { 855 | debug!("Syncing after lazy umount..."); 856 | sync(); 857 | } 858 | break; 859 | } 860 | } else { 861 | debug!( 862 | "Successfully umounted {} from {}", 863 | m.device_path, m.mount_path 864 | ); 865 | } 866 | 867 | break; 868 | } 869 | } 870 | } 871 | 872 | tokio::time::delay_for(Duration::from_secs(1)).await; 873 | 874 | debug!("exiting after delay"); 875 | 876 | exit_cleanly().map_err(InitError::from) 877 | } 878 | 879 | fn exit_cleanly() -> nix::Result<()> { 880 | nix::sys::reboot::reboot(nix::sys::reboot::RebootMode::RB_AUTOBOOT).map(|_| {}) 881 | } 882 | 883 | fn reap_zombies(pid: i32, exit_status: &mut i32) -> bool { 884 | let mut child_exited = false; 885 | loop { 886 | match waitpid(None, Some(WaitPidFlag::WNOHANG)) { 887 | Ok(status) => { 888 | if Some(pid) == status.pid().map(nix::unistd::Pid::as_raw) { 889 | // main process pid exited 890 | child_exited = true; 891 | } 892 | match status { 893 | WaitStatus::Exited(child_pid, exit_code) => { 894 | if child_pid.as_raw() == pid { 895 | info!("Main child exited normally with code: {}", exit_code); 896 | *exit_status = exit_code; 897 | } else { 898 | warn!( 899 | "Reaped child process with pid: {}, exit code: {}", 900 | child_pid, exit_code 901 | ) 902 | } 903 | } 904 | WaitStatus::Signaled(child_pid, signal, core_dumped) => { 905 | if child_pid.as_raw() == pid { 906 | info!( 907 | "Main child exited with signal (with signal '{}', core dumped? {})", 908 | signal, core_dumped 909 | ); 910 | *exit_status = 128 + (signal as i32); 911 | } else { 912 | warn!( 913 | "Reaped child process with pid: {} and signal: {}, core dumped? {}", 914 | child_pid, signal, core_dumped 915 | ) 916 | } 917 | } 918 | WaitStatus::Stopped(child_pid, signal) => { 919 | debug!( 920 | "waitpid Stopped: surprising (pid: {}, signal: {})", 921 | child_pid, signal 922 | ); 923 | } 924 | WaitStatus::PtraceEvent(child_pid, signal, event) => { 925 | debug!( 926 | "waitpid PtraceEvent: interesting (pid: {}, signal: {}, event: {})", 927 | child_pid, signal, event 928 | ); 929 | } 930 | WaitStatus::PtraceSyscall(child_pid) => { 931 | debug!("waitpid PtraceSyscall: unfathomable (pid: {})", child_pid); 932 | } 933 | WaitStatus::Continued(child_pid) => { 934 | debug!("waitpid Continue: not supposed to! (pid: {})", child_pid); 935 | } 936 | WaitStatus::StillAlive => { 937 | trace!("no more children to reap"); 938 | break; 939 | } 940 | } 941 | } 942 | Err(e) => match e { 943 | nix::Error::Sys(Errno::ECHILD) => { 944 | debug!("no child to wait"); 945 | break; 946 | } 947 | nix::Error::Sys(Errno::EINTR) => { 948 | debug!("got EINTR waiting for pids, continuing..."); 949 | continue; 950 | } 951 | _ => { 952 | debug!("error calling waitpid: {}", e); 953 | // TODO: return an error? handle it? 954 | return false; 955 | } 956 | }, 957 | } 958 | } 959 | child_exited 960 | } 961 | 962 | fn mount( 963 | source: Option<&P1>, 964 | target: &P2, 965 | fstype: Option<&P3>, 966 | flags: MsFlags, 967 | data: Option<&P4>, 968 | ) -> Result<(), InitError> { 969 | nix_mount(source, target, fstype, flags, data).map_err(|error| InitError::Mount { 970 | source: source 971 | .map(|p| { 972 | p.with_nix_path(|cs| { 973 | cs.to_owned() 974 | .into_string() 975 | .ok() 976 | .unwrap_or_else(|| String::new()) 977 | }) 978 | .unwrap_or_else(|_| String::new()) 979 | }) 980 | .unwrap_or_else(|| String::new()), 981 | target: target 982 | .with_nix_path(|cs| { 983 | cs.to_owned() 984 | .into_string() 985 | .ok() 986 | .unwrap_or_else(|| String::new()) 987 | }) 988 | .unwrap_or_else(|_| String::new()), 989 | error, 990 | }) 991 | } 992 | 993 | fn chdir(path: &P) -> Result<(), InitError> { 994 | nix_chdir(path).map_err(|error| InitError::Chdir { 995 | path: path 996 | .with_nix_path(|cs| { 997 | cs.to_owned() 998 | .into_string() 999 | .ok() 1000 | .unwrap_or_else(|| String::new()) 1001 | }) 1002 | .unwrap_or_else(|_| String::new()), 1003 | error, 1004 | }) 1005 | } 1006 | 1007 | fn mkdir(path: &P, mode: Mode) -> Result<(), InitError> { 1008 | nix_mkdir(path, mode).map_err(|error| InitError::Mkdir { 1009 | path: path 1010 | .with_nix_path(|cs| { 1011 | cs.to_owned() 1012 | .into_string() 1013 | .ok() 1014 | .unwrap_or_else(|| String::new()) 1015 | }) 1016 | .unwrap_or_else(|_| String::new()), 1017 | error, 1018 | }) 1019 | } 1020 | 1021 | fn chroot(path: &P) -> Result<(), InitError> { 1022 | nix_chroot(path).map_err(|error| InitError::Chroot { 1023 | path: path 1024 | .with_nix_path(|cs| { 1025 | cs.to_owned() 1026 | .into_string() 1027 | .ok() 1028 | .unwrap_or_else(|| String::new()) 1029 | }) 1030 | .unwrap_or_else(|_| String::new()), 1031 | error, 1032 | }) 1033 | } 1034 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | 4 | #[macro_use] 5 | extern crate log; 6 | 7 | use std::collections::HashMap; 8 | 9 | use ipnetwork::IpNetwork; 10 | 11 | pub mod api; 12 | pub mod pty; 13 | 14 | #[derive(Serialize, Deserialize, Debug, Default)] 15 | #[serde(rename_all = "PascalCase")] 16 | pub struct RunConfig { 17 | pub image_config: Option, 18 | pub exec_override: Option>, 19 | pub extra_env: Option>, 20 | pub user_override: Option, 21 | pub cmd_override: Option, 22 | #[serde(rename = "IPConfigs")] 23 | pub ip_configs: Option>, 24 | #[serde(default)] 25 | pub tty: bool, 26 | pub hostname: String, 27 | pub mounts: Option>, 28 | 29 | pub root_device: Option, 30 | 31 | pub etc_resolv: Option, 32 | pub etc_hosts: Option>, 33 | } 34 | 35 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 36 | #[serde(rename_all = "PascalCase")] 37 | pub struct ImageConfig { 38 | pub entrypoint: Option>, 39 | pub cmd: Option>, 40 | pub env: Option>, 41 | pub working_dir: Option, 42 | pub user: Option, 43 | } 44 | 45 | #[derive(Serialize, Deserialize, Debug, Clone)] 46 | #[serde(rename_all = "PascalCase")] 47 | pub struct IPConfig { 48 | pub gateway: IpNetwork, 49 | #[serde(rename = "IP")] 50 | pub ip: IpNetwork, 51 | pub mask: u8, 52 | } 53 | 54 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 55 | #[serde(rename_all = "PascalCase")] 56 | pub struct Mount { 57 | pub mount_path: String, 58 | pub device_path: String, 59 | } 60 | 61 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 62 | #[serde(rename_all = "PascalCase")] 63 | pub struct EtcHost { 64 | pub host: String, 65 | #[serde(rename = "IP")] 66 | pub ip: String, 67 | pub desc: Option, 68 | } 69 | 70 | #[derive(Serialize, Deserialize, Debug, Default, Clone)] 71 | #[serde(rename_all = "PascalCase")] 72 | pub struct EtcResolv { 73 | pub nameservers: Vec, 74 | } 75 | -------------------------------------------------------------------------------- /src/pty/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File as StdFile; 2 | use std::io; 3 | use std::os::unix::io::{AsRawFd, RawFd}; 4 | use std::pin::Pin; 5 | use std::task::{Context, Poll}; 6 | 7 | use tokio::fs::File as TokioFile; 8 | use tokio::io::{AsyncRead, AsyncWrite}; 9 | use tokio::process::Command; 10 | 11 | /* 12 | * PtyMaster 13 | */ 14 | pub struct PtyMaster { 15 | file: StdFile, 16 | } 17 | 18 | impl PtyMaster { 19 | pub fn open() -> Result { 20 | use std::os::unix::io::FromRawFd as _; 21 | 22 | let file = unsafe { 23 | let fd = libc::posix_openpt(libc::O_RDWR | libc::O_NOCTTY); 24 | 25 | if fd < 0 { 26 | return Err(io::Error::last_os_error()); 27 | } 28 | 29 | if libc::grantpt(fd) != 0 { 30 | return Err(io::Error::last_os_error()); 31 | } 32 | 33 | if libc::unlockpt(fd) != 0 { 34 | return Err(io::Error::last_os_error()); 35 | } 36 | 37 | StdFile::from_raw_fd(fd) 38 | }; 39 | 40 | Ok(Self { file }) 41 | } 42 | 43 | pub fn open_slave(&self) -> Result { 44 | use std::os::unix::io::{AsRawFd as _, FromRawFd as _}; 45 | 46 | let file = unsafe { 47 | let mut buf: [libc::c_char; 512] = [0; 512]; 48 | 49 | #[cfg(not(any(target_os = "macos", target_os = "freebsd")))] 50 | { 51 | if libc::ptsname_r(self.file.as_raw_fd(), buf.as_mut_ptr(), buf.len()) != 0 { 52 | return Err(io::Error::last_os_error()); 53 | } 54 | } 55 | 56 | #[cfg(any(target_os = "macos", target_os = "freebsd"))] 57 | { 58 | let st = libc::ptsname(self.file.as_raw_fd()); 59 | if st.is_null() { 60 | return Err(io::Error::last_os_error()); 61 | } 62 | libc::strncpy(buf.as_mut_ptr(), st, buf.len()); 63 | } 64 | 65 | let fd = libc::open(buf.as_ptr(), libc::O_RDWR | libc::O_NOCTTY); 66 | 67 | if fd < 0 { 68 | return Err(io::Error::last_os_error()); 69 | } 70 | 71 | StdFile::from_raw_fd(fd) 72 | }; 73 | 74 | Ok(file) 75 | } 76 | 77 | pub fn split(self) -> Result<(PtyMasterRead, PtyMasterWrite), io::Error> { 78 | Ok(( 79 | PtyMasterRead { 80 | file: TokioFile::from_std(self.file.try_clone()?), 81 | }, 82 | PtyMasterWrite { 83 | file: TokioFile::from_std(self.file), 84 | }, 85 | )) 86 | } 87 | } 88 | 89 | /* 90 | * PtyMasterRead 91 | */ 92 | pub struct PtyMasterRead { 93 | file: TokioFile, 94 | } 95 | 96 | impl PtyMasterRead { 97 | fn get_file(self: Pin<&mut Self>) -> Pin<&mut TokioFile> { 98 | unsafe { self.map_unchecked_mut(|s| &mut s.file) } 99 | } 100 | } 101 | 102 | impl AsyncRead for PtyMasterRead { 103 | fn poll_read( 104 | self: Pin<&mut Self>, 105 | cx: &mut Context, 106 | buf: &mut [u8], 107 | ) -> Poll> { 108 | self.get_file().poll_read(cx, buf) 109 | } 110 | } 111 | 112 | impl AsRawFd for PtyMaster { 113 | fn as_raw_fd(&self) -> RawFd { 114 | self.file.as_raw_fd() 115 | } 116 | } 117 | 118 | /* 119 | * PtyMasterWrite 120 | */ 121 | pub struct PtyMasterWrite { 122 | file: TokioFile, 123 | } 124 | 125 | impl PtyMasterWrite { 126 | fn get_file(self: Pin<&mut Self>) -> Pin<&mut TokioFile> { 127 | unsafe { self.map_unchecked_mut(|s| &mut s.file) } 128 | } 129 | 130 | pub fn resize(&self, cols: libc::c_ushort, rows: libc::c_ushort) -> Result<(), io::Error> { 131 | let winsz = libc::winsize { 132 | ws_row: rows, 133 | ws_col: cols, 134 | ws_xpixel: 0, 135 | ws_ypixel: 0, 136 | }; 137 | 138 | if unsafe { libc::ioctl(self.file.as_raw_fd(), libc::TIOCSWINSZ.into(), &winsz) } != 0 { 139 | return Err(io::Error::last_os_error()); 140 | } 141 | 142 | Ok(()) 143 | } 144 | } 145 | 146 | impl AsyncWrite for PtyMasterWrite { 147 | fn poll_write( 148 | self: Pin<&mut Self>, 149 | cx: &mut Context, 150 | buf: &[u8], 151 | ) -> Poll> { 152 | self.get_file().poll_write(cx, buf) 153 | } 154 | 155 | fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 156 | self.get_file().poll_flush(cx) 157 | } 158 | 159 | fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 160 | self.get_file().poll_shutdown(cx) 161 | } 162 | } 163 | 164 | pub trait PtyCommandExt { 165 | fn spawn_pty( 166 | &mut self, 167 | pty_master: &PtyMaster, 168 | raw: bool, 169 | ) -> Result; 170 | } 171 | 172 | impl PtyCommandExt for Command { 173 | fn spawn_pty( 174 | &mut self, 175 | pty_master: &PtyMaster, 176 | raw: bool, 177 | ) -> Result { 178 | let master_fd = pty_master.as_raw_fd(); 179 | let slave = pty_master.open_slave()?; 180 | let slave_fd = slave.as_raw_fd(); 181 | 182 | self.stdin(slave.try_clone()?); 183 | self.stdout(slave.try_clone()?); 184 | self.stderr(slave.try_clone()?); 185 | 186 | debug!("set stdin, stdout and stderr"); 187 | 188 | unsafe { 189 | self.pre_exec(move || { 190 | if raw { 191 | let mut attrs: libc::termios = std::mem::zeroed(); 192 | 193 | if libc::tcgetattr(slave_fd, &mut attrs as _) != 0 { 194 | return Err(io::Error::last_os_error()); 195 | } 196 | 197 | libc::cfmakeraw(&mut attrs as _); 198 | 199 | if libc::tcsetattr(slave_fd, libc::TCSANOW, &attrs as _) != 0 { 200 | return Err(io::Error::last_os_error()); 201 | } 202 | } 203 | 204 | // This is OK even though we don't own master since this process is 205 | // about to become something totally different anyway. 206 | if libc::close(master_fd) != 0 { 207 | return Err(io::Error::last_os_error()); 208 | } 209 | 210 | if libc::setsid() < 0 { 211 | return Err(io::Error::last_os_error()); 212 | } 213 | 214 | if libc::ioctl(0, libc::TIOCSCTTY.into(), 1) != 0 { 215 | return Err(io::Error::last_os_error()); 216 | } 217 | 218 | Ok(()) 219 | }); 220 | } 221 | 222 | self.spawn() 223 | } 224 | } 225 | --------------------------------------------------------------------------------