Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.clang-format
.nvimrc
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ ifndef SHELLCHECK
endif
shellcheck -a -x \
vmctl cmd/* common/* lib/qemu/*

31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
209 changes: 141 additions & 68 deletions contrib/nix/vmctl_init.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include "limits.h"
#include <unistd.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

Expand All @@ -11,97 +13,168 @@
#include <stdio.h>
#include <stdarg.h>

char const * const indent_str = " ";
static void info(unsigned indent, char const *fmt, ...) {
while(0 != indent--)
fprintf(stdout, "%s", indent_str);
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);
va_list args;
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);

fprintf(stdout, "\n");
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, "VMCTL image-less INIT started ... \n");
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;
const char *arg_str;
char ***arg_vals;
int *count;
};

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;
}
}
}
// 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 < 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;
}
}
}
}

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(char *path)
{
if (path[0] == '\0')
return -1;

int mount_nix_store() {
int ret;
// This must match the tag used in virtiofsd
const char *source = "nixstore";
const char *target = "/nix/store";
for(int idx = 1, path_end =0; idx < PATH_MAX; ++idx) {
if (path[idx] != '/' && path[idx] != '\0')
continue;

if (ensure_dir("/nix") || ensure_dir("/nix/store"))
return 1;
if (path[idx] == '\0')
path_end = 1;
else
path[idx] = '\0';

if (mount(source, target, "virtiofs", 0, "") == -1) {
info(1, "Error: mounting source (%s) at target (%s)", source, target);
return 1;
}
if (mkdir(path, 0755) == -1 && errno != EEXIST)
goto err;

info(1, "Mounted %s at %s\n", source, target);
if (path_end)
return 0;
else
path[idx] = '/';
}

err:
info(1, "Error: creating the path (%s)", path);
return -1;

}

return 0;
int read_until(const char *str, char c)
{
int idx;
for(idx = 0 ; str[idx] != '\0' && str[idx] != c; ++idx)
;
return idx;
}

int main(int argc, char *argv[]) {
int ret;
char *init_path = NULL;
struct ArgOpt opts[] = {
{"--init-path", &init_path},
};
/* 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, "VMCTL image-less INIT started ... \n");
prnt_args(argc, argv);
info(1, "Mount string: %s", mount_str);
do {
idx = read_until(strs[i], ',');
if (idx == 0)
break;

parse_args(argc, argv, opts, ARRAY_SIZE(opts));
if (init_path == NULL) {
info(1, "Error: missing --init-path arg\n");
return -1;
}
if(strs[i][idx] == '\0')
end = 1;
else {
strs[i][idx] = '\0';
strs[i+1] = strs[i] + idx + 1;
}

if(mount_nix_store())
return 1;
if (end)
break;

info(1, "Attempting to exec: %s\n", init_path);
execv(init_path, argv);
info(1, "Error: execv failed");
i++;
} while (i < 3);

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", strs[0], strs[1]);

return 0;
}

return -1;
int main(int argc, char *argv[])
{
int ret;
char **mount_strs = NULL, **init_paths = NULL;
int mounts_count = 0, init_paths_count = 0;
struct ArgOpt opts[] = {
{"--init-path", &init_paths, &init_paths_count},
{"--mount", &mount_strs, &mounts_count},
};

prnt_args(argc, argv);
parse_args(argc, argv, opts, ARRAY_SIZE(opts));
if (init_paths_count != 1) {
info(1, "Error: %s --init-path arg",
init_paths_count == 0 ? "missing" : "too many");
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_paths[0]);
execv(init_paths[0], argv);
info(1, "Error: execv failed");

return -1;
}
16 changes: 12 additions & 4 deletions lib/qemu/nix
Original file line number Diff line number Diff line change
Expand Up @@ -103,19 +103,27 @@ 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}"
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}"
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"
}
14 changes: 12 additions & 2 deletions lib/qemu/share
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -63,6 +63,9 @@ qemu_share_add() {
'--tag' )
local tag="$2"; shift 2;
;;
'--vm-dir' )
local vm_dir="$2"; shift 2;
;;
'--' )
shift; break
;;
Expand Down Expand Up @@ -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
}