Something went wrong. Try again.
libkrun v1.19.4 fork adding PVH boot primitives (start_info + protected-mode vCPU state) for faster, kernel-direct x86_64 microVM startup.
Something went wrong. Try again.
C
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321/* * This is an example implementing chroot-like functionality with libkrun. * * It executes the requested command (relative to NEWROOT) inside a fresh * Virtual Machine created and managed by libkrun. */
#include <errno.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/socket.h>#include <sys/un.h>#include <unistd.h>#include <libkrun.h>#include <getopt.h>#include <stdbool.h>#include <assert.h>#include <pthread.h>
#define MAX_ARGS_LEN 4096#ifndef MAX_PATH#define MAX_PATH 4096#endif
enum net_mode{ NET_MODE_PASST = 0, NET_MODE_TSI,};
#if defined(__x86_64__)#define KERNEL_FORMAT KRUN_KERNEL_FORMAT_ELF#else#define KERNEL_FORMAT KRUN_KERNEL_FORMAT_RAW#endif
static void print_help(char *const name){ fprintf(stderr, "Usage: %s [OPTIONS] KERNEL\n" "OPTIONS: \n" " -b --boot-disk Path to a boot disk in raw format\n" " -c --kernel-cmdline Kernel command line\n" " -d --data-disk Path to a data disk in raw format\n" " -h --help Show help\n" " -i --initrd Path to initramfs\n" " -n --nested Enabled nested virtualization\n" " --net=NET_MODE Set network mode\n" " --passt-socket=PATH Connect to passt socket at PATH" "\n" "NET_MODE can be either TSI (default) or PASST\n" "\n"#if defined(__x86_64__) "KERNEL: path to the kernel image in ELF format\n",#else "KERNEL: path to the kernel image in RAW format\n",#endif name);}
static const struct option long_options[] = { {"boot-disk", required_argument, NULL, 'b'}, {"kernel-cmdline", required_argument, NULL, 'c'}, {"data-disk", required_argument, NULL, 'd'}, {"initrd-path", required_argument, NULL, 'i'}, {"nested", no_argument, NULL, 'n'}, {"help", no_argument, NULL, 'h'}, {"passt-socket", required_argument, NULL, 'P'}, {NULL, 0, NULL, 0}};
struct cmdline{ bool show_help; enum net_mode net_mode; char const *boot_disk; char const *data_disk; char const *passt_socket_path; char const *kernel_path; char const *kernel_cmdline; char const *initrd_path; bool nested;};
bool parse_cmdline(int argc, char *const argv[], struct cmdline *cmdline){ assert(cmdline != NULL);
// set the defaults *cmdline = (struct cmdline){ .show_help = false, .net_mode = NET_MODE_TSI, .passt_socket_path = "/tmp/network.sock", .boot_disk = NULL, .data_disk = NULL, .kernel_path = NULL, .kernel_cmdline = NULL, .initrd_path = NULL, .nested = false, };
int option_index = 0; int c; // the '+' in optstring is a GNU extension that disables permutating argv while ((c = getopt_long(argc, argv, "+hb:c:d:i:n", long_options, &option_index)) != -1) { switch (c) { case 'b': cmdline->boot_disk = optarg; break; case 'c': cmdline->kernel_cmdline = optarg; break; case 'd': cmdline->data_disk = optarg; break; case 'h': cmdline->show_help = true; return true; case 'i': cmdline->initrd_path = optarg; break; case 'n': cmdline->nested = true; break; case 'P': cmdline->passt_socket_path = optarg; break; case '?': return false; default: fprintf(stderr, "internal argument parsing error (returned character code 0x%x)\n", c); return false; } }
if (optind <= argc - 1) { cmdline->kernel_path = argv[optind]; return true; }
if (optind == argc) { fprintf(stderr, "Missing KERNEL argument\n"); }
return false;}
int start_passt(){ int socket_fds[2]; const int PARENT = 0; const int CHILD = 1;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds) < 0) { perror("Failed to create passt socket fd"); return -1; }
int pid = fork(); if (pid < 0) { perror("fork"); return -1; }
if (pid == 0) { // child if (close(socket_fds[PARENT]) < 0) { perror("close PARENT"); }
char fd_as_str[16]; snprintf(fd_as_str, sizeof(fd_as_str), "%d", socket_fds[CHILD]);
printf("passing fd %s to passt", fd_as_str);
if (execlp("passt", "passt", "-f", "--fd", fd_as_str, NULL) < 0) { perror("execlp"); return -1; } } else { // parent if (close(socket_fds[CHILD]) < 0) { perror("close CHILD"); }
return socket_fds[PARENT]; }}
int main(int argc, char *const argv[]){ int ctx_id; int err; pthread_t thread; struct cmdline cmdline;
if (!parse_cmdline(argc, argv, &cmdline)) { putchar('\n'); print_help(argv[0]); return -1; }
if (cmdline.show_help) { print_help(argv[0]); return 0; }
// Set the log level to "off". err = krun_set_log_level(0); if (err) { errno = -err; perror("Error configuring log level"); return -1; }
// Create the configuration context. ctx_id = krun_create_ctx(); if (ctx_id < 0) { errno = -ctx_id; perror("Error creating configuration context"); return -1; }
// Configure the number of vCPUs (2) and the amount of RAM (2048 MiB). if (err = krun_set_vm_config(ctx_id, 2, 2048)) { errno = -err; perror("Error configuring the number of vCPUs and/or the amount of RAM"); return -1; }
if (cmdline.boot_disk) { if (err = krun_add_disk(ctx_id, "boot", cmdline.boot_disk, 0)) { errno = -err, perror("Error configuring boot disk"); return -1; } } if (cmdline.data_disk) { if (err = krun_add_disk(ctx_id, "data", cmdline.data_disk, 0)) { errno = -err, perror("Error configuring data disk"); return -1; } }
if (cmdline.net_mode == NET_MODE_PASST) { uint8_t mac[] = {0x5a, 0x94, 0xef, 0xe4, 0x0c, 0xee}; if (cmdline.passt_socket_path != NULL) { if (err = krun_add_net_unixstream(ctx_id, cmdline.passt_socket_path, -1, &mac[0], COMPAT_NET_FEATURES, 0)) { errno = -err; perror("Error configuring net mode"); return -1; } } else { int passt_fd = start_passt();
if (passt_fd < 0) { return -1; }
if (err = krun_add_net_unixstream(ctx_id, NULL, passt_fd, &mac[0], COMPAT_NET_FEATURES, 0)) { errno = -err; perror("Error configuring net mode"); return -1; } } }
fprintf(stderr, "kernel_path: %s\n", cmdline.kernel_path); fprintf(stderr, "kernel_cmdline: %s\n", cmdline.kernel_cmdline); fflush(stderr);
if (err = krun_set_kernel(ctx_id, cmdline.kernel_path, KERNEL_FORMAT, cmdline.initrd_path, cmdline.kernel_cmdline)) { errno = -err; perror("Error configuring kernel"); return -1; }
fprintf(stderr, "nested=%d\n", cmdline.nested); if (err = krun_set_nested_virt(ctx_id, cmdline.nested)) { errno = -err; perror("Error configuring nested virtualization"); return -1; }
// Start and enter the microVM. Unless there is some error while creating the microVM // this function never returns. if (err = krun_start_enter(ctx_id)) { errno = -err; perror("Error creating the microVM"); return -1; }
// Not reached. return 0;}