From 526847b909cefa590114ac60984f4acf4137cd1b Mon Sep 17 00:00:00 2001 From: Daniel Gomez Date: Tue, 10 Jun 2025 13:27:44 +0200 Subject: [PATCH 1/4] vmctl_init: format C file according to the Linux Kernel formatting Signed-off-by: Daniel Gomez Signed-off-by: Joel Granados --- Makefile | 1 + contrib/nix/vmctl_init.c | 153 +++++++++++++++++++++------------------ 2 files changed, 84 insertions(+), 70 deletions(-) diff --git a/Makefile b/Makefile index 28323a2..bb63579 100644 --- a/Makefile +++ b/Makefile @@ -7,3 +7,4 @@ ifndef SHELLCHECK endif shellcheck -a -x \ vmctl cmd/* common/* lib/qemu/* + diff --git a/contrib/nix/vmctl_init.c b/contrib/nix/vmctl_init.c index bdaffef..b32d4bd 100644 --- a/contrib/nix/vmctl_init.c +++ b/contrib/nix/vmctl_init.c @@ -11,97 +11,110 @@ #include #include -char const * const indent_str = " "; -static void info(unsigned indent, char const *fmt, ...) { - while(0 != indent--) - fprintf(stdout, "%s", indent_str); - - va_list args; - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); - - fprintf(stdout, "\n"); +char const *const indent_str = " "; +static void info(unsigned indent, char const *fmt, ...) +{ + while (0 != indent--) + fprintf(stdout, "%s", indent_str); + + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + + fprintf(stdout, "\n"); } -static void prnt_args(int argc, char *argv[]) { - info(1, "Arguments :\n"); - info(2, "argc = %d\n", argc); - info(2, "argv:\n"); - for (int i = 0; i < argc; i++) - info(3, "argv[%d] = %s\n", i, argv[i]); +static void prnt_args(int argc, char *argv[]) +{ + info(1, "Arguments :\n"); + info(2, "argc = %d\n", argc); + info(2, "argv:\n"); + for (int i = 0; i < argc; i++) + info(3, "argv[%d] = %s\n", i, argv[i]); } #else -static inline void info(unsigned indent, char const *fmt, ...) {} -static inline void prnt_args(int argc, char *argv[]) {} +static inline void info(unsigned indent, char const *fmt, ...) +{ +} +static inline void prnt_args(int argc, char *argv[]) +{ +} #endif -struct ArgOpt{ - const char *arg_str; - char **arg_val; +struct ArgOpt { + const char *arg_str; + char **arg_val; }; -void parse_args(int const argc, char *argv[], struct ArgOpt *opts, int const opts_count) { - for (int i = 1; i < argc; i++) { - for (int j = 0; j < opts_count; j++) { - if (strcmp(argv[i], opts[j].arg_str) == 0 && i + 1 < argc) { - *opts[j].arg_val = argv[i + 1]; - i++; - break; - } - } - } +void parse_args(int const argc, char *argv[], struct ArgOpt *opts, + int const opts_count) +{ + for (int i = 1; i < argc; i++) { + for (int j = 0; j < opts_count; j++) { + if (strcmp(argv[i], opts[j].arg_str) == 0 && + i + 1 < argc) { + *opts[j].arg_val = argv[i + 1]; + i++; + break; + } + } + } } -int ensure_dir(const char *path) { - if (mkdir(path, 0755) == -1 && errno != EEXIST) { - info(1, "Error: creating the path (%s)", path); - return -1; - } - return 0; +int ensure_dir(const char *path) +{ + if (mkdir(path, 0755) == -1 && errno != EEXIST) { + info(1, "Error: creating the path (%s)", path); + return -1; + } + return 0; } -int mount_nix_store() { - int ret; - // This must match the tag used in virtiofsd - const char *source = "nixstore"; - const char *target = "/nix/store"; +int mount_nix_store() +{ + int ret; + // This must match the tag used in virtiofsd + const char *source = "nixstore"; + const char *target = "/nix/store"; - if (ensure_dir("/nix") || ensure_dir("/nix/store")) - return 1; + if (ensure_dir("/nix") || ensure_dir("/nix/store")) + return 1; - if (mount(source, target, "virtiofs", 0, "") == -1) { - info(1, "Error: mounting source (%s) at target (%s)", source, target); - return 1; - } + if (mount(source, target, "virtiofs", 0, "") == -1) { + info(1, "Error: mounting source (%s) at target (%s)", source, + target); + return 1; + } - info(1, "Mounted %s at %s\n", source, target); + info(1, "Mounted %s at %s\n", source, target); - return 0; + return 0; } -int main(int argc, char *argv[]) { - int ret; - char *init_path = NULL; - struct ArgOpt opts[] = { - {"--init-path", &init_path}, - }; +int main(int argc, char *argv[]) +{ + int ret; + char *init_path = NULL; + struct ArgOpt opts[] = { + { "--init-path", &init_path }, + }; - info(1, "VMCTL image-less INIT started ... \n"); - prnt_args(argc, argv); + info(1, "VMCTL image-less INIT started ... \n"); + prnt_args(argc, argv); - parse_args(argc, argv, opts, ARRAY_SIZE(opts)); - if (init_path == NULL) { - info(1, "Error: missing --init-path arg\n"); - return -1; - } + parse_args(argc, argv, opts, ARRAY_SIZE(opts)); + if (init_path == NULL) { + info(1, "Error: missing --init-path arg\n"); + return -1; + } - if(mount_nix_store()) - return 1; + if (mount_nix_store()) + return 1; - info(1, "Attempting to exec: %s\n", init_path); - execv(init_path, argv); - info(1, "Error: execv failed"); + info(1, "Attempting to exec: %s\n", init_path); + execv(init_path, argv); + info(1, "Error: execv failed"); - return -1; + return -1; } From a2c5c8cbf56c02d28df69fe949884acfa5356703 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 17 Jun 2025 20:00:18 +0200 Subject: [PATCH 2/4] Add .gitignore Signed-off-by: Joel Granados --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7cd612a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.clang-format +.nvimrc From 7bfc853c3d7025df2cbecb8cceffba068e213f2d Mon Sep 17 00:00:00 2001 From: Daniel Gomez Date: Tue, 10 Jun 2025 13:27:45 +0200 Subject: [PATCH 3/4] nixvm: add --mount argument to init The init executable now can parse arguments that describe a mount: init --mount NAME,PATH,TYPE. Signed-off-by: Joel Granados --- contrib/nix/vmctl_init.c | 128 ++++++++++++++++++++++++++++----------- lib/qemu/nix | 4 +- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/contrib/nix/vmctl_init.c b/contrib/nix/vmctl_init.c index b32d4bd..2d35d3b 100644 --- a/contrib/nix/vmctl_init.c +++ b/contrib/nix/vmctl_init.c @@ -1,8 +1,10 @@ +#include "limits.h" #include #include #include #include #include +#include #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) @@ -26,6 +28,7 @@ static void info(unsigned indent, char const *fmt, ...) } static void prnt_args(int argc, char *argv[]) { + info(1, "VMCTL image-less INIT started ... \n"); info(1, "Arguments :\n"); info(2, "argc = %d\n", argc); info(2, "argv:\n"); @@ -42,19 +45,22 @@ static inline void prnt_args(int argc, char *argv[]) } #endif -struct ArgOpt { +struct ArgOpt{ const char *arg_str; - char **arg_val; + char ***arg_vals; + int *count; }; -void parse_args(int const argc, char *argv[], struct ArgOpt *opts, - int const opts_count) -{ +// Parse function to match arguments and store their values +void parse_args(int argc, char *argv[], struct ArgOpt *options, int option_count) { for (int i = 1; i < argc; i++) { - for (int j = 0; j < opts_count; j++) { - if (strcmp(argv[i], opts[j].arg_str) == 0 && - i + 1 < argc) { - *opts[j].arg_val = argv[i + 1]; + for (int j = 0; j < option_count; j++) { + if (strcmp(argv[i], options[j].arg_str) == 0 && i + 1 < argc) { + (*options[j].count)++; + *options[j].arg_vals = + realloc(*options[j].arg_vals, + (*options[j].count) * sizeof(char *)); + (*options[j].arg_vals)[(*options[j].count) - 1] = argv[i + 1]; i++; break; } @@ -62,32 +68,81 @@ void parse_args(int const argc, char *argv[], struct ArgOpt *opts, } } -int ensure_dir(const char *path) +int ensure_dir(char *path) { - if (mkdir(path, 0755) == -1 && errno != EEXIST) { - info(1, "Error: creating the path (%s)", path); + if (path[0] == '\0') return -1; + + for(int idx = 1, path_end =0; idx < PATH_MAX; ++idx) { + if (path[idx] != '/' && path[idx] != '\0') + continue; + + if (path[idx] == '\0') + path_end = 1; + else + path[idx] = '\0'; + + if (mkdir(path, 0755) == -1 && errno != EEXIST) + goto err; + + if (path_end) + return 0; + else + path[idx] = '/'; } - return 0; + +err: + info(1, "Error: creating the path (%s)", path); + return -1; + } -int mount_nix_store() +int read_until(const char *str, char c) { - int ret; - // This must match the tag used in virtiofsd - const char *source = "nixstore"; - const char *target = "/nix/store"; + int idx; + for(idx = 0 ; str[idx] != '\0' && str[idx] != c; ++idx) + ; + return idx; +} - if (ensure_dir("/nix") || ensure_dir("/nix/store")) - return 1; +/* mount_str: mount str (SHARE_NAME;MOUNT_PATH;MOUNT_TYPE) */ +int mount_from_str(char *mount_str) +{ + int idx, i = 0, end = 0; + char *strs[3] = {mount_str, NULL, NULL}; + + info(1, "Mount string: %s", mount_str); + do { + idx = read_until(strs[i], ','); + if (idx == 0) + break; + + if(strs[i][idx] == '\0') + end = 1; + else { + strs[i][idx] = '\0'; + strs[i+1] = strs[i] + idx + 1; + } + + if (end) + break; + + i++; + } while (i < 3); - if (mount(source, target, "virtiofs", 0, "") == -1) { - info(1, "Error: mounting source (%s) at target (%s)", source, - target); + if (strs[1] == NULL || strs[2] == NULL) + return -1; + + if (ensure_dir(strs[1])) + return -1; + + if (mount(strs[0], strs[1], strs[2], 0, "") == -1) { + info(1, "Error: mounting %s at %s. errno %d", + strs[0], strs[1], errno); return 1; } - info(1, "Mounted %s at %s\n", source, target); + info(1, "Mounted %s at %s\n", strs[0], strs[1]); return 0; } @@ -95,25 +150,30 @@ int mount_nix_store() int main(int argc, char *argv[]) { int ret; - char *init_path = NULL; + char **mount_strs = NULL, **init_paths = NULL; + int mounts_count = 0, init_paths_count = 0; struct ArgOpt opts[] = { - { "--init-path", &init_path }, + {"--init-path", &init_paths, &init_paths_count}, + {"--mount", &mount_strs, &mounts_count}, }; - info(1, "VMCTL image-less INIT started ... \n"); prnt_args(argc, argv); - parse_args(argc, argv, opts, ARRAY_SIZE(opts)); - if (init_path == NULL) { - info(1, "Error: missing --init-path arg\n"); + if (init_paths_count != 1) { + info(1, "Error: %s --init-path arg", + init_paths_count == 0 ? "missing" : "too many"); return -1; } - if (mount_nix_store()) - return 1; + for (int i = 0; i < mounts_count; ++i) { + if (mount_from_str(mount_strs[i])) { + info(1, "Error: Could not mount"); + return -1; + } + } - info(1, "Attempting to exec: %s\n", init_path); - execv(init_path, argv); + info(1, "Attempting to exec: %s\n", init_paths[0]); + execv(init_paths[0], argv); info(1, "Error: execv failed"); return -1; diff --git a/lib/qemu/nix b/lib/qemu/nix index c52c74f..c0e9696 100644 --- a/lib/qemu/nix +++ b/lib/qemu/nix @@ -109,7 +109,9 @@ qemu_nix_add_noimgnix () { if [[ -v GUEST_KERNEL_APPEND_EXTRA ]]; then cmd_line="${cmd_line} ${GUEST_KERNEL_APPEND_EXTRA}" fi - cmd_line="${cmd_line} ${cmd_line_extra} -- --init-path ${nix_init_path}" + cmd_line="${cmd_line} ${cmd_line_extra}" + cmd_line="${cmd_line} -- --init-path ${nix_init_path}" + cmd_line="${cmd_line} --mount nixstore,/nix/store,virtiofs" QEMU_PARAMS+=("-kernel" "${kernel_dir}/${GUEST_KERNEL_IMAGE}") QEMU_PARAMS+=("-append" "${cmd_line}") From 0ee05742c28877d6d47777e21a0ec64d4237475a Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Thu, 19 Jun 2025 23:24:44 +0200 Subject: [PATCH 4/4] nixvm: automatically mount when --vm-dir is used changed the share arg from viofs to virtiofs as it is closer to the actual name. Signed-off-by: Joel Granados --- README.md | 31 +++++++++++++++++++++++++++++++ lib/qemu/nix | 14 ++++++++++---- lib/qemu/share | 14 ++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1b556a2..2f8d63b 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,37 @@ add the following text to your configruation file (usually /etc/nix/nix.conf): experimental-features = nix-command flakes +### Mounting directories + +This is useful when selftests or modules are needed within the VM. + +1. Early mount: Add a `qemu_share_add` to your vmctl config + + ```sh + qemu_share_add \ + --shared-dir "/path/to/modules/lib/modules" \ + --tag "modules" \ + --share-type "virtiofs" \ + --vm-dir "/lib/modules" + ``` + + If you want to share but not mount, remove the --vm-dir arg. + +2. Systemd mount: Add a "service" to your nix configuration: + + ```nix + systemd.services.mount-user-virtiofs = { + description = "Mount virtiofsd tag 'selftests' to /usr/lib/kselftests"; + after = [ "local-fs.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "/run/current-system/sw/bin/mount -t virtiofs user /usr/lib/kselftests"; + RemainAfterExit = true; + }; + }; + ``` + ## Prep boot img The base configruation `*-base.conf` will look for a base image in diff --git a/lib/qemu/nix b/lib/qemu/nix index c0e9696..200e1e1 100644 --- a/lib/qemu/nix +++ b/lib/qemu/nix @@ -103,6 +103,11 @@ qemu_nix_add_noimgnix () { _qemu_nix_create_nix_init "${VMSTATE}" + qemu_share_add --shared-dir "${NIX_STORE_PATH}" \ + --tag "nixstore" \ + --share-type "virtiofs" \ + --vm-dir "/nix/store" + local nix_init_path="" nix_init_path="$(readlink "${dst_path}")/init" local cmd_line="root=/dev/ram0 console=${GUEST_KERNEL_CONSOLE}" @@ -111,13 +116,14 @@ qemu_nix_add_noimgnix () { fi cmd_line="${cmd_line} ${cmd_line_extra}" cmd_line="${cmd_line} -- --init-path ${nix_init_path}" - cmd_line="${cmd_line} --mount nixstore,/nix/store,virtiofs" + if [[ -v NIX_MOUNTS ]]; then + for nmount in "${NIX_MOUNTS[@]}"; do + cmd_line="${cmd_line} --mount ${nmount}" + done + fi QEMU_PARAMS+=("-kernel" "${kernel_dir}/${GUEST_KERNEL_IMAGE}") QEMU_PARAMS+=("-append" "${cmd_line}") QEMU_PARAMS+=("-initrd" "${VMSTATE}/init.img") - qemu_share_add --shared-dir "${NIX_STORE_PATH}" \ - --tag "nixstore" \ - --share-type "viofs" } diff --git a/lib/qemu/share b/lib/qemu/share index 6f644a3..4cb375f 100644 --- a/lib/qemu/share +++ b/lib/qemu/share @@ -43,7 +43,7 @@ qemu_share_add_9p() { } qemu_share_add() { - local long="shared-dir:,tag:,share-type:" + local long="shared-dir:,tag:,share-type:,vm-dir:" if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then exit 1 @@ -63,6 +63,9 @@ qemu_share_add() { '--tag' ) local tag="$2"; shift 2; ;; + '--vm-dir' ) + local vm_dir="$2"; shift 2; + ;; '--' ) shift; break ;; @@ -90,10 +93,17 @@ qemu_share_add() { if [[ "${share_type}" == "9p" ]]; then qemu_share_add_9p "${shared_dir}" "${tag}" - elif [[ "${share_type}" == "viofs" ]]; then + elif [[ "${share_type}" == "virtiofs" ]]; then qemu_share_add_virtiofsd "${shared_dir}" "${tag}" else _fatal 1 "${FUNCNAME[0]}: Unknown directory share type (${share_type})" fi + + if [[ -v vm_dir ]]; then + if [[ ! -v NIX_STORE_PATH ]]; then + _log "vm-dir ignored! it's only active in NIX" + fi + NIX_MOUNTS+=("${tag},${vm_dir},${share_type}") + fi }