From 4a209bc69084f24bc93e231f65f41e573225c5bf Mon Sep 17 00:00:00 2001 From: Teoh Han Hui Date: Thu, 25 Apr 2024 21:04:50 +0800 Subject: [PATCH] Rewrite it in Rust --- .editorconfig | 44 +++ .gitattributes | 9 + .gitignore | 1 + Cargo.lock | 443 ++++++++++++++++++++++++ Cargo.toml | 19 + crates/krun-guest/Cargo.toml | 16 + crates/krun-guest/src/bin/krun-guest.rs | 35 ++ crates/krun-guest/src/cli_options.rs | 52 +++ crates/krun-guest/src/fex.rs | 27 ++ crates/krun-guest/src/lib.rs | 6 + crates/krun-guest/src/mount.rs | 54 +++ crates/krun-guest/src/net.rs | 35 ++ crates/krun-guest/src/sommelier.rs | 30 ++ crates/krun-guest/src/user.rs | 57 +++ crates/krun/Cargo.toml | 17 + crates/krun/src/bin/krun.rs | 253 ++++++++++++++ crates/krun/src/cli_options.rs | 57 +++ crates/krun/src/lib.rs | 3 + crates/krun/src/net.rs | 111 ++++++ crates/krun/src/utils.rs | 22 ++ crates/libkrun/Cargo.toml | 11 + crates/libkrun/build.rs | 16 + crates/libkrun/src/lib.rs | 5 + crates/libkrun/wrapper.h | 1 + rustfmt.toml | 18 + 25 files changed, 1342 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/krun-guest/Cargo.toml create mode 100644 crates/krun-guest/src/bin/krun-guest.rs create mode 100644 crates/krun-guest/src/cli_options.rs create mode 100644 crates/krun-guest/src/fex.rs create mode 100644 crates/krun-guest/src/lib.rs create mode 100644 crates/krun-guest/src/mount.rs create mode 100644 crates/krun-guest/src/net.rs create mode 100644 crates/krun-guest/src/sommelier.rs create mode 100644 crates/krun-guest/src/user.rs create mode 100644 crates/krun/Cargo.toml create mode 100644 crates/krun/src/bin/krun.rs create mode 100644 crates/krun/src/cli_options.rs create mode 100644 crates/krun/src/lib.rs create mode 100644 crates/krun/src/net.rs create mode 100644 crates/krun/src/utils.rs create mode 100644 crates/libkrun/Cargo.toml create mode 100644 crates/libkrun/build.rs create mode 100644 crates/libkrun/src/lib.rs create mode 100644 crates/libkrun/wrapper.h create mode 100644 rustfmt.toml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d62e5ad --- /dev/null +++ b/.editorconfig @@ -0,0 +1,44 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.rs] +indent_style = space +indent_size = 4 + +[*.sh] +indent_style = tab +indent_size = 4 + +[*.toml] +indent_style = space +indent_size = 4 + +[*.{yaml,yml}] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = false + +[.github/{actions,workflows}/**/*.yml] +indent_style = space +indent_size = 2 + +[Cargo.toml] +indent_style = space +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2ff72ae --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* text=auto eol=lf + +*.md text eol=lf +*.rs text eol=lf +*.sh text eol=lf +*.toml text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +Cargo.lock text eol=lf merge=binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0a5501d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,443 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bpaf" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567fc5f0a754100df11b167b2a247b2366fc1ac18e9b776a07659be00878f681" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "krun" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec", + "bpaf", + "libkrun", + "nix", + "rustix", +] + +[[package]] +name = "krun_guest" +version = "0.1.0" +dependencies = [ + "anyhow", + "bpaf", + "nix", + "rustix", + "tempfile", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libkrun" +version = "1.8.1" +dependencies = [ + "bindgen", +] + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "prettyplease" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..63f5d49 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] +resolver = "2" +members = ["crates/*"] + +[workspace.package] +version = "0.1.0" +authors = ["Sergio Lopez ", "Teoh Han Hui "] +edition = "2021" +rust-version = "1.77.0" +description = "Run programs from your system in a microVM" +repository = "https://github.com/slp/krun" +license = "MIT" + +[workspace.dependencies] +anyhow = "1.0.82" +bindgen = "0.69.4" +bpaf = "0.9.11" +nix = "0.28.0" +rustix = "0.38.34" diff --git a/crates/krun-guest/Cargo.toml b/crates/krun-guest/Cargo.toml new file mode 100644 index 0000000..cd446bf --- /dev/null +++ b/crates/krun-guest/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "krun_guest" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +description = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +bpaf = { workspace = true } +nix = { workspace = true, features = ["process", "user"] } +rustix = { workspace = true, features = ["fs", "mount", "net", "process", "stdio", "system"] } +tempfile = "3.10.1" diff --git a/crates/krun-guest/src/bin/krun-guest.rs b/crates/krun-guest/src/bin/krun-guest.rs new file mode 100644 index 0000000..bc9872c --- /dev/null +++ b/crates/krun-guest/src/bin/krun-guest.rs @@ -0,0 +1,35 @@ +use std::{os::unix::process::CommandExt as _, process::Command}; + +use anyhow::{Context, Result}; +use krun_guest::{ + cli_options::options, fex::setup_fex, mount::mount_filesystems, net::configure_network, + sommelier::exec_sommelier, user::setup_user, +}; + +fn main() -> Result<()> { + let options = options().run(); + + if let Err(err) = mount_filesystems() { + eprintln!("Couldn't mount filesystems, bailing out"); + Err(err)?; + } + + setup_fex()?; + + configure_network()?; + + if let Err(err) = setup_user(options.username, options.uid, options.gid) { + eprintln!("Couldn't set up user, bailing out"); + Err(err)?; + } + + // Will not return if successful. + exec_sommelier(&options.command, &options.command_args) + .context("Failed to execute sommelier")?; + + // Fallback option if sommelier is not present. + let err = Command::new(&options.command) + .args(options.command_args) + .exec(); + Err(err).with_context(|| format!("Failed to exec {}", options.command))? +} diff --git a/crates/krun-guest/src/cli_options.rs b/crates/krun-guest/src/cli_options.rs new file mode 100644 index 0000000..05d64b8 --- /dev/null +++ b/crates/krun-guest/src/cli_options.rs @@ -0,0 +1,52 @@ +use anyhow::Context; +use bpaf::{any, construct, positional, OptionParser, Parser}; +use nix::{ + libc::{gid_t, uid_t}, + unistd::{Gid, Uid}, +}; + +#[derive(Clone, Debug)] +pub struct Options { + pub username: String, + pub uid: Uid, + pub gid: Gid, + pub command: String, + pub command_args: Vec, +} + +pub fn options() -> OptionParser { + let username = positional("USER"); + let uid = positional::("UID").parse(|s| { + s.parse::() + .context("Failed to parse UID") + .map(|uid| uid.into()) + }); + let gid = positional::("GID").parse(|s| { + s.parse::() + .context("Failed to parse GID") + .map(|gid| gid.into()) + }); + let command = positional("COMMAND"); + let command_args = + any::("COMMAND_ARGS", |arg| (arg != "--help").then_some(arg)).many(); + + construct!(Options { + // positionals + username, + uid, + gid, + command, + command_args, + }) + .to_options() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_options() { + options().check_invariants(false) + } +} diff --git a/crates/krun-guest/src/fex.rs b/crates/krun-guest/src/fex.rs new file mode 100644 index 0000000..7d9449c --- /dev/null +++ b/crates/krun-guest/src/fex.rs @@ -0,0 +1,27 @@ +use std::{fs::OpenOptions, io::Write, path::Path}; + +use anyhow::{Context, Result}; + +const FEX_X86_BINFMT_MISC_RULE: &str = ":FEX-x86:M:0:\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x03\\x00:\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\x00\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/FEXInterpreter:POCF"; +const FEX_X86_64_BINFMT_MISC_RULE: &str = ":FEX-x86_64:M:0:\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x3e\\x00:\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\x00\\x00\\x00\\x00\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff:/usr/bin/FEXInterpreter:POCF"; + +pub fn setup_fex() -> Result<()> { + if !Path::new("/usr/bin/FEXInterpreter") + .try_exists() + .context("Failed to check existence of FEXInterpreter")? + { + return Ok(()); + } + + let mut file = OpenOptions::new() + .write(true) + .open("/proc/sys/fs/binfmt_misc/register") + .context("Failed to open binfmt_misc/register for writing")?; + + file.write_all(FEX_X86_BINFMT_MISC_RULE.as_bytes()) + .context("Failed to register FEX-x86 binfmt_misc rule")?; + file.write_all(FEX_X86_64_BINFMT_MISC_RULE.as_bytes()) + .context("Failed to register FEX-x86_64 binfmt_misc rule")?; + + Ok(()) +} diff --git a/crates/krun-guest/src/lib.rs b/crates/krun-guest/src/lib.rs new file mode 100644 index 0000000..4452084 --- /dev/null +++ b/crates/krun-guest/src/lib.rs @@ -0,0 +1,6 @@ +pub mod cli_options; +pub mod fex; +pub mod mount; +pub mod net; +pub mod sommelier; +pub mod user; diff --git a/crates/krun-guest/src/mount.rs b/crates/krun-guest/src/mount.rs new file mode 100644 index 0000000..84c15f7 --- /dev/null +++ b/crates/krun-guest/src/mount.rs @@ -0,0 +1,54 @@ +use std::{fs::OpenOptions, os::fd::AsFd}; + +use anyhow::{Context, Result}; +use rustix::{ + fs::CWD, + mount::{mount2, move_mount, open_tree, MountFlags, MoveMountFlags, OpenTreeFlags}, +}; + +pub fn mount_filesystems() -> Result<()> { + mount2( + Some("tmpfs"), + "/var/run", + Some("tmpfs"), + MountFlags::NOEXEC | MountFlags::NOSUID | MountFlags::RELATIME, + None, + ) + .context("Failed to mount /var/run")?; + + let _ = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open("/tmp/resolv.conf") + .context("Failed to create /tmp/resolv.conf")?; + + { + let fd = open_tree( + CWD, + "/tmp/resolv.conf", + OpenTreeFlags::OPEN_TREE_CLONE | OpenTreeFlags::OPEN_TREE_CLOEXEC, + ) + .context("Failed to open_tree /tmp/resolv.conf")?; + + move_mount( + fd.as_fd(), + "", + CWD, + "/etc/resolv.conf", + MoveMountFlags::MOVE_MOUNT_F_EMPTY_PATH, + ) + .context("Failed to move_mount /etc/resolv.conf")?; + } + + mount2( + Some("binfmt_misc"), + "/proc/sys/fs/binfmt_misc", + Some("binfmt_misc"), + MountFlags::NOEXEC | MountFlags::NOSUID | MountFlags::RELATIME, + None, + ) + .context("Failed to mount binfmt_misc")?; + + Ok(()) +} diff --git a/crates/krun-guest/src/net.rs b/crates/krun-guest/src/net.rs new file mode 100644 index 0000000..c640ffb --- /dev/null +++ b/crates/krun-guest/src/net.rs @@ -0,0 +1,35 @@ +use std::{fs, os::unix::process::ExitStatusExt, process::Command}; + +use anyhow::{anyhow, Context, Result}; +use rustix::system::sethostname; + +pub fn configure_network() -> Result<()> { + { + let hostname = + fs::read_to_string("/etc/hostname").context("Failed to read /etc/hostname")?; + let hostname = if let Some((hostname, _)) = hostname.split_once('\n') { + hostname.to_owned() + } else { + hostname + }; + sethostname(hostname.as_bytes()).context("Failed to set hostname")?; + } + + let output = Command::new("/sbin/dhclient") + .output() + .context("Failed to execute dhclient as child process")?; + if !output.status.success() { + Err(anyhow!(match output.status.code() { + Some(code) => format!("dhclient process exited with status code: {code}"), + None => format!( + "dhclient process terminated by signal: {}", + output + .status + .signal() + .expect("either one of status code or signal should be set") + ), + }))?; + } + + Ok(()) +} diff --git a/crates/krun-guest/src/sommelier.rs b/crates/krun-guest/src/sommelier.rs new file mode 100644 index 0000000..c6cc189 --- /dev/null +++ b/crates/krun-guest/src/sommelier.rs @@ -0,0 +1,30 @@ +use std::{os::unix::process::CommandExt as _, path::Path, process::Command}; + +use anyhow::{Context, Result}; + +pub fn exec_sommelier(command: &String, command_args: &[String]) -> Result<()> { + if !Path::new("/usr/bin/sommelier") + .try_exists() + .context("Failed to check existence of sommelier")? + { + return Ok(()); + } + + let gl_env = std::env::var("LIBGL_DRIVERS_PATH").ok(); + + let mut cmd = Command::new("/usr/bin/sommelier"); + cmd.args(["--virtgpu-channel", "-X", "--glamor"]); + + if let Some(gl_env) = gl_env { + cmd.arg(format!("--xwayland-gl-driver-path={}", gl_env)); + } + + println!("arg: {command}"); + for arg in command_args { + println!("arg: {arg}"); + } + cmd.arg(command).args(command_args); + + let err = cmd.exec(); + Err(err).context("Failed to exec sommelier")? +} diff --git a/crates/krun-guest/src/user.rs b/crates/krun-guest/src/user.rs new file mode 100644 index 0000000..5710aea --- /dev/null +++ b/crates/krun-guest/src/user.rs @@ -0,0 +1,57 @@ +use std::{ + fs::{read_dir, Permissions}, + os::unix::fs::{chown, PermissionsExt as _}, + path::PathBuf, +}; + +use anyhow::{Context, Result}; +use nix::unistd::{setgid, setuid, Gid, Uid}; + +pub fn setup_user(username: String, uid: Uid, gid: Gid) -> Result<()> { + setup_directories(uid, gid)?; + + setgid(gid).context("Failed to setgid")?; + setuid(uid).context("Failed to setuid")?; + + { + let path = tempfile::Builder::new() + .prefix(&format!("krun-run-{uid}-")) + .permissions(Permissions::from_mode(0o755)) + .tempdir() + .context("Failed to create temp dir for XDG_RUNTIME_DIR")? + .into_path(); + // SAFETY: safe if and only if `krun-guest` program is not multithreaded + // See https://doc.rust-lang.org/std/env/fn.set_var.html#safety + std::env::set_var("XDG_RUNTIME_DIR", path); + } + + { + let path: PathBuf = ["/home", &username].iter().collect(); + // SAFETY: safe if and only if `krun-guest` program is not multithreaded + // See https://doc.rust-lang.org/std/env/fn.set_var.html#safety + std::env::set_var("HOME", path); + } + + // TODO - only do this if running on Asahi Linux. + // SAFETY: safe if and only if `krun-guest` program is not multithreaded + // See https://doc.rust-lang.org/std/env/fn.set_var.html#safety + std::env::set_var("MESA_LOADER_DRIVER_OVERRIDE", "asahi"); + + Ok(()) +} + +fn setup_directories(uid: Uid, gid: Gid) -> Result<()> { + for dir in ["/dev/dri", "/dev/snd"] { + let dir_iter = read_dir(dir).with_context(|| format!("Failed to read directory {dir}"))?; + + for entry in dir_iter { + let path = entry + .with_context(|| format!("Failed to read directory entry in {dir}"))? + .path(); + chown(&path, Some(uid.into()), Some(gid.into())) + .with_context(|| format!("Failed to chown {path:?}"))?; + } + } + + Ok(()) +} diff --git a/crates/krun/Cargo.toml b/crates/krun/Cargo.toml new file mode 100644 index 0000000..83b438b --- /dev/null +++ b/crates/krun/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "krun" +version = { workspace = true } +authors = { workspace = true } +edition = { workspace = true } +rust-version = { workspace = true } +description = { workspace = true } +repository = { workspace = true } +license = { workspace = true } + +[dependencies] +anyhow = { workspace = true } +arrayvec = "0.7.4" +bpaf = { workspace = true } +libkrun = { path = "../libkrun" } +nix = { workspace = true, features = ["process", "user"] } +rustix = { workspace = true, features = ["fs", "mount", "net", "process", "stdio", "system"] } diff --git a/crates/krun/src/bin/krun.rs b/crates/krun/src/bin/krun.rs new file mode 100644 index 0000000..f4cd527 --- /dev/null +++ b/crates/krun/src/bin/krun.rs @@ -0,0 +1,253 @@ +use std::{ + ffi::{c_char, CString}, + os::fd::{IntoRawFd, OwnedFd}, +}; + +use anyhow::{anyhow, Context, Result}; +use krun::{ + cli_options::options, + net::{connect_to_passt, start_passt, NetMode}, +}; +use libkrun::{ + krun_create_ctx, krun_set_exec, krun_set_gpu_options, krun_set_log_level, krun_set_passt_fd, + krun_set_root, krun_set_vm_config, krun_set_workdir, krun_start_enter, VIRGLRENDERER_DRM, + VIRGLRENDERER_THREAD_SYNC, VIRGLRENDERER_USE_ASYNC_FENCE_CB, VIRGLRENDERER_USE_EGL, +}; +use nix::unistd::User; +use rustix::{ + io::Errno, + process::{geteuid, getgid, getrlimit, getuid, setrlimit, Resource}, +}; + +fn main() -> Result<()> { + if getuid().as_raw() == 0 || geteuid().as_raw() == 0 { + println!("Running as root is not supported as it may break your system"); + return Err(anyhow!("real user ID or effective user ID is 0")); + } + + let options = options().fallback_to_usage().run(); + + { + // Set the log level to "off". + // + // SAFETY: safe as no pointers involved + let err = unsafe { krun_set_log_level(0) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).context("Failed to configure log level"); + } + } + + let ctx_id = { + // Create the configuration context. + // + // SAFETY: safe as no pointers involved + let ctx_id = unsafe { krun_create_ctx() }; + if ctx_id < 0 { + let err = Errno::from_raw_os_error(-ctx_id); + return Err(err).context("Failed to create configuration context"); + } + ctx_id as u32 + }; + + { + // Configure the number of vCPUs (4) and the amount of RAM (4096 MiB). + // + // SAFETY: safe as no pointers involved + let err = unsafe { krun_set_vm_config(ctx_id, 4, 4096) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err) + .context("Failed to configure the number of vCPUs and/or the amount of RAM"); + } + } + + { + // Raise RLIMIT_NOFILE to the maximum allowed to create some room for virtio-fs + let mut rlim = getrlimit(Resource::Nofile); + rlim.current = rlim.maximum; + setrlimit(Resource::Nofile, rlim).context("Failed to raise RLIMIT_NOFILE")?; + } + + { + // SAFETY: safe as `root_path` is a pointer to a C-string literal + let err = unsafe { krun_set_root(ctx_id, c"/".as_ptr()) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).context("Failed to configure root path"); + } + } + + { + let virgl_flags = VIRGLRENDERER_USE_EGL + | VIRGLRENDERER_DRM + | VIRGLRENDERER_THREAD_SYNC + | VIRGLRENDERER_USE_ASYNC_FENCE_CB; + // SAFETY: safe as no pointers involved + let err = unsafe { krun_set_gpu_options(ctx_id, virgl_flags) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).context("Failed to configure gpu"); + } + } + + if options.net == NetMode::PASST { + let passt_fd: OwnedFd = if let Some(passt_socket) = options.passt_socket { + connect_to_passt(passt_socket) + .context("Failed to connect to passt")? + .into() + } else { + start_passt().context("Failed to start passt")? + }; + // SAFETY: safe as `passt_fd` is an `OwnedFd` and consumed to prevent closing on drop + // See https://doc.rust-lang.org/std/io/index.html#io-safety + let err = unsafe { krun_set_passt_fd(ctx_id, passt_fd.into_raw_fd()) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).context("Failed to configure net mode"); + } + } + + let username = std::env::var("USER").context("Failed to get username from environment")?; + let user = User::from_name(&username); + let Ok(Some(user)) = user else { + let err = if let Err(err) = user { + anyhow!(err) + } else { + anyhow!("requested entry not found") + }; + return Err(err) + .with_context(|| format!("Failed to get user `{username}` from user database")); + }; + let workdir_path = CString::new( + user.dir + .to_str() + .expect("workdir_path should not contain invalid UTF-8"), + ) + .expect("workdir_path should not contain NUL character"); + + { + // Set the working directory to the user's home directory, just for the sake of + // completeness. + // + // SAFETY: safe as `workdir_path` is a pointer to a `CString` with long enough lifetime + let err = unsafe { krun_set_workdir(ctx_id, workdir_path.as_ptr()) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).with_context(|| { + format!( + "Failed to configure `{}` as working directory", + workdir_path + .into_string() + .expect("workdir_path should not contain invalid UTF-8") + ) + }); + } + } + + let krun_path = std::env::current_exe().and_then(|p| p.canonicalize()); + let krun_path = krun_path.context("Failed to get path of current running executable")?; + let krun_guest_path = krun_path.with_file_name(format!( + "{}-guest", + krun_path + .file_name() + .expect("krun_path should end with a file name") + .to_str() + .expect("krun file name should not contain invalid UTF-8") + )); + let krun_guest_path = CString::new( + krun_guest_path + .to_str() + .expect("krun_guest_path should not contain invalid UTF-8"), + ) + .expect("krun_guest_path should not contain NUL character"); + + let mut krun_guest_args: Vec = vec![ + CString::new(username).expect("username should not contain NUL character"), + CString::new(format!("{}", getuid().as_raw())) + .expect("uid should not contain NUL character"), + CString::new(format!("{}", getgid().as_raw())) + .expect("gid should not contain NUL character"), + ]; + krun_guest_args.push( + CString::new(options.command) + .context("Failed to process command as it contains NUL character")?, + ); + let command_argc = options.command_args.len(); + for arg in options.command_args { + let s = CString::new(arg) + .context("Failed to process command arg as it contains NUL character")?; + krun_guest_args.push(s); + } + let krun_guest_args: Vec<*const c_char> = { + const KRUN_GUEST_ARGS_FIXED: usize = 4; + // SAFETY: all pointers must be stored in the same allocation + // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety + let mut vec = Vec::with_capacity(KRUN_GUEST_ARGS_FIXED + command_argc + 1); + for s in &krun_guest_args { + vec.push(s.as_ptr()); + } + vec.push(std::ptr::null()); + vec + }; + + let [ld_env, gl_env] = ["LD_LIBRARY_PATH", "LIBGL_DRIVERS_PATH"] + .map(|key| std::env::var(key).map_or(None, |value| Some((key, value)))); + let mut env: Vec = vec![]; + if let (Some(ld_env), Some(gl_env)) = (ld_env, gl_env) { + for (key, value) in [ld_env, gl_env] { + let s = CString::new(format!("{key}={value}")).with_context(|| { + format!("Failed to process {key} env var as it contains NUL character") + })?; + env.push(s); + } + } + let env: Vec<*const c_char> = { + // SAFETY: all pointers must be stored in the same allocation + // See https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety + let mut vec = Vec::with_capacity(env.len() + 1); + for s in &env { + vec.push(s.as_ptr()); + } + vec.push(std::ptr::null()); + vec + }; + + { + // Specify the path of the binary to be executed in the isolated context, relative to + // the root path. + // + // SAFETY: safe as + // * `krun_guest_path` is a pointer to a `CString` with long enough lifetime + // * `krun_guest_args` is a pointer to a `Vec` of pointers to `CString`s all with long + // enough lifetime + // * `env` is a pointer to a `Vec` of pointers to `CString`s all with long enough lifetime + let err = unsafe { + krun_set_exec( + ctx_id, + krun_guest_path.as_ptr(), + krun_guest_args.as_ptr(), + env.as_ptr(), + ) + }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err) + .context("Failed to configure the parameters for the executable to be run"); + } + } + + { + // Start and enter the microVM. Unless there is some error while creating the microVM + // this function never returns. + // + // SAFETY: safe as no pointers involved + let err = unsafe { krun_start_enter(ctx_id) }; + if err < 0 { + let err = Errno::from_raw_os_error(-err); + return Err(err).context("Failed to create the microVM"); + } + } + + unreachable!("`krun_start_enter` should never return"); +} diff --git a/crates/krun/src/cli_options.rs b/crates/krun/src/cli_options.rs new file mode 100644 index 0000000..a406866 --- /dev/null +++ b/crates/krun/src/cli_options.rs @@ -0,0 +1,57 @@ +use std::path::PathBuf; + +use anyhow::anyhow; +use bpaf::{any, construct, long, positional, OptionParser, Parser}; + +use crate::net::NetMode; + +#[derive(Clone, Debug)] +pub struct Options { + pub net: NetMode, + pub passt_socket: Option, + pub command: String, + pub command_args: Vec, +} + +pub fn options() -> OptionParser { + let net = long("net") + .help( + "Set network mode + NET_MODE can be either TSI (default) or PASST", + ) + .argument::("NET_MODE") + .fallback("TSI".to_owned()) + .display_fallback() + .parse(|s| match &*s.to_ascii_uppercase() { + "PASST" => Ok(NetMode::PASST), + "TSI" => Ok(NetMode::TSI), + _ => Err(anyhow!("unknown mode {s}")), + }); + let passt_socket = long("passt-socket") + .help("Instead of starting passt, connect to passt socket at PATH") + .argument("PATH") + .optional(); + let command = positional("COMMAND").help("the command you want to execute in the vm"); + let command_args = any::("COMMAND_ARGS", |arg| (arg != "--help").then_some(arg)) + .help("arguments of COMMAND") + .many(); + + construct!(Options { + net, + passt_socket, + // positionals + command, + command_args, + }) + .to_options() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn check_options() { + options().check_invariants(false) + } +} diff --git a/crates/krun/src/lib.rs b/crates/krun/src/lib.rs new file mode 100644 index 0000000..7e4db97 --- /dev/null +++ b/crates/krun/src/lib.rs @@ -0,0 +1,3 @@ +pub mod cli_options; +pub mod net; +pub mod utils; diff --git a/crates/krun/src/net.rs b/crates/krun/src/net.rs new file mode 100644 index 0000000..247d2cb --- /dev/null +++ b/crates/krun/src/net.rs @@ -0,0 +1,111 @@ +use std::{ + ffi::CString, + fmt::Write, + os::{ + fd::{AsRawFd, OwnedFd}, + unix::{fs::PermissionsExt as _, net::UnixStream}, + }, + path::{Path, PathBuf}, + process::exit, +}; + +use anyhow::{Context, Result}; +use arrayvec::ArrayString; +use nix::unistd::{execv, fork, write, ForkResult}; +use rustix::net::{socketpair, AddressFamily, SocketFlags, SocketType}; + +use crate::utils::print_error; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum NetMode { + PASST = 0, + TSI, +} + +/// Connects to an existing `passt` socket. +pub fn connect_to_passt

(passt_socket_path: P) -> Result +where + P: AsRef, +{ + Ok(UnixStream::connect(passt_socket_path)?) +} + +/// Starts `passt`. +/// +/// This does [fork(2)] and [exec(3)]. +/// +/// [exec(3)]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html +/// [fork(2)]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html +pub fn start_passt() -> Result { + let (parent_fd, child_fd) = socketpair( + AddressFamily::UNIX, + SocketType::STREAM, + SocketFlags::empty(), + None, + )?; + + let mut passt_path = PathBuf::from("passt"); + // Search for passt in PATH. + if let Ok(p) = std::env::var("PATH") { + for search_path in std::env::split_paths(&p) { + let pb = search_path.join("passt"); + if !pb.is_file() { + continue; + } + let Ok(metadata) = std::fs::metadata(&pb) else { + continue; + }; + if metadata.permissions().mode() & 0o111 != 0 { + passt_path = pb.canonicalize()?; + } + } + } + let passt_path = CString::new( + passt_path + .to_str() + .expect("passt_path should not contain invalid UTF-8"), + ) + .expect("passt_path should not contain NUL character"); + + // SAFETY: safe as `child_fd` is an `OwnedFd` and never dropped (as `execv` never returns) + // See https://doc.rust-lang.org/std/io/index.html#io-safety + let child_fd_str = CString::new(format!("{}", child_fd.as_raw_fd())) + .expect("child_fd string should not contain NUL character"); + + match unsafe { fork() } { + Ok(ForkResult::Parent { child: _, .. }) => { + drop(child_fd); + Ok(parent_fd) + }, + Ok(ForkResult::Child) => { + // SAFETY: child must only call async-signal-safe functions + // See https://docs.rs/nix/latest/nix/unistd/fn.fork.html#safety + + drop(parent_fd); + + { + const MAX_DIGITS: usize = 10; + const MAX_REST: usize = 20; + let mut s = ArrayString::<{ MAX_DIGITS + MAX_REST + 1 }>::new(); + // SAFETY: unsafe to use `print!` (or `unwrap`) here + // `write!` is safe here as `fmt::Write` impl for `ArrayString` does not allocate + writeln!(&mut s, "passing fd {} to passt", child_fd.as_raw_fd()).ok(); + write(std::io::stdout(), s.as_bytes()).ok(); + } + + // SAFETY: safe if and only if the `krun` program is not multithreaded + // See https://github.com/nix-rust/nix/issues/555#issuecomment-294422144 + if let Err(err) = execv( + &passt_path, + &[&*passt_path, c"-q", c"-f", c"--fd", &child_fd_str], + ) { + print_error(err); + // SAFETY: unsafe to `return` + exit(-1); + } + + unreachable!("`exec` should never return"); + }, + Err(err) => Err(err).context("Failed to fork child process"), + } +} diff --git a/crates/krun/src/utils.rs b/crates/krun/src/utils.rs new file mode 100644 index 0000000..491c873 --- /dev/null +++ b/crates/krun/src/utils.rs @@ -0,0 +1,22 @@ +use std::fmt::Write; + +use arrayvec::ArrayString; +use nix::{errno::Errno, unistd::write}; + +/// Prints an error in an [async-signal-safe] way. +/// +/// The output should be similar to [perror(3)]. +/// +/// [async-signal-safe]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03_03 +/// [perror(3)]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html +pub fn print_error(err: Errno) { + const MAX_CHARS: usize = 15; + let mut s = ArrayString::::new(); + // SAFETY: unsafe to use `print!` (or `unwrap`) here + // `write!` is safe here as `fmt::Write` impl for `ArrayString` does not allocate + write!(&mut s, "{err:?}").ok(); + write(std::io::stderr(), s.as_bytes()).ok(); + write(std::io::stderr(), ": ".as_bytes()).ok(); + write(std::io::stderr(), err.desc().as_bytes()).ok(); + write(std::io::stderr(), "\n".as_bytes()).ok(); +} diff --git a/crates/libkrun/Cargo.toml b/crates/libkrun/Cargo.toml new file mode 100644 index 0000000..b171295 --- /dev/null +++ b/crates/libkrun/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "libkrun" +version = "1.8.1" +edition = { workspace = true } +rust-version = { workspace = true } +repository = "https://github.com/containers/libkrun" +license = "Apache-2.0" +publish = false + +[build-dependencies] +bindgen = { workspace = true } diff --git a/crates/libkrun/build.rs b/crates/libkrun/build.rs new file mode 100644 index 0000000..c670f2b --- /dev/null +++ b/crates/libkrun/build.rs @@ -0,0 +1,16 @@ +use std::{env, path::PathBuf}; + +fn main() { + println!("cargo::rerun-if-changed=wrapper.h"); + println!("cargo::rustc-link-lib=krun"); + + let bindings = bindgen::Builder::default() + .header("wrapper.h") + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/crates/libkrun/src/lib.rs b/crates/libkrun/src/lib.rs new file mode 100644 index 0000000..32aa412 --- /dev/null +++ b/crates/libkrun/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/crates/libkrun/wrapper.h b/crates/libkrun/wrapper.h new file mode 100644 index 0000000..fba0181 --- /dev/null +++ b/crates/libkrun/wrapper.h @@ -0,0 +1 @@ +#include diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..bba0995 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,18 @@ +edition = "2021" + +# empty_item_single_line = false +# error_on_line_overflow = true +# format_code_in_doc_comments = true +# format_strings = true +# group_imports = "StdExternalCrate" +# imports_granularity = "Crate" +# imports_layout = "HorizontalVertical" +match_block_trailing_comma = true +newline_style = "Unix" +# normalize_comments = true +# normalize_doc_attributes = true +# overflow_delimited_expr = true +# reorder_impl_items = true +use_field_init_shorthand = true +use_try_shorthand = true +# wrap_comments = true