|  | 
|  | 1 | +#include <errno.h> | 
|  | 2 | +#include <fcntl.h> | 
|  | 3 | +#include <stdio.h> | 
|  | 4 | +#include <stdlib.h> | 
|  | 5 | +#include <string.h> | 
|  | 6 | +#include <unistd.h> | 
|  | 7 | +#include <stdarg.h> | 
|  | 8 | +#include <sys/wait.h> | 
|  | 9 | +#include <sys/stat.h> | 
|  | 10 | + | 
|  | 11 | +#include <libkrun.h> | 
|  | 12 | + | 
|  | 13 | +static int cmd_output(char *output, size_t output_size, const char *prog, ...) | 
|  | 14 | +{ | 
|  | 15 | +    va_list args; | 
|  | 16 | +    const char *argv[32]; | 
|  | 17 | +    int argc = 0; | 
|  | 18 | +    int pipe_fds[2] = { -1, -1 }; | 
|  | 19 | + | 
|  | 20 | +    argv[argc++] = prog; | 
|  | 21 | +    va_start(args, prog); | 
|  | 22 | +    while (argc < 31) { | 
|  | 23 | +        const char *arg = va_arg(args, const char *); | 
|  | 24 | +        argv[argc++] = arg; | 
|  | 25 | +        if (arg == NULL) break; | 
|  | 26 | +    } | 
|  | 27 | +    va_end(args); | 
|  | 28 | +    argv[argc] = NULL; | 
|  | 29 | + | 
|  | 30 | +    if (output && output_size > 0) { | 
|  | 31 | +        if (pipe(pipe_fds) < 0) return -1; | 
|  | 32 | +    } | 
|  | 33 | + | 
|  | 34 | +    pid_t pid = fork(); | 
|  | 35 | +    if (pid < 0) return -1; | 
|  | 36 | +    if (pid == 0) { | 
|  | 37 | +        if (pipe_fds[0] >= 0) { | 
|  | 38 | +            close(pipe_fds[0]); | 
|  | 39 | +            dup2(pipe_fds[1], STDOUT_FILENO); | 
|  | 40 | +            close(pipe_fds[1]); | 
|  | 41 | +        } | 
|  | 42 | +        execvp(prog, (char *const *)argv); | 
|  | 43 | +        _exit(127); | 
|  | 44 | +    } | 
|  | 45 | + | 
|  | 46 | +    if (pipe_fds[0] >= 0) { | 
|  | 47 | +        close(pipe_fds[1]); | 
|  | 48 | +        ssize_t n = read(pipe_fds[0], output, output_size - 1); | 
|  | 49 | +        close(pipe_fds[0]); | 
|  | 50 | +        if (n < 0) n = 0; | 
|  | 51 | +        output[n] = '\0'; | 
|  | 52 | +    } | 
|  | 53 | + | 
|  | 54 | +    int status; | 
|  | 55 | +    if (waitpid(pid, &status, 0) < 0) return -1; | 
|  | 56 | +    if (!WIFEXITED(status)) return -1; | 
|  | 57 | +    return WEXITSTATUS(status); | 
|  | 58 | +} | 
|  | 59 | + | 
|  | 60 | +#define cmd(...) ({ char _d[1]; cmd_output(_d, 0, __VA_ARGS__); }) | 
|  | 61 | + | 
|  | 62 | +static int create_tmux_tty(const char *session_name) | 
|  | 63 | +{ | 
|  | 64 | +    char tty_path[256]; | 
|  | 65 | +    char wait_cmd[128]; | 
|  | 66 | +     | 
|  | 67 | +    snprintf(wait_cmd, sizeof(wait_cmd), "tail --pid=%d -f /dev/null", (int)getpid()); | 
|  | 68 | +    if (cmd("tmux", "new-session", "-d", "-s", session_name, "sh", "-c", wait_cmd, NULL) != 0) | 
|  | 69 | +        return -1; | 
|  | 70 | + | 
|  | 71 | +    // Hook up tmux to send us SIGWINCH signal on resize | 
|  | 72 | +    char hook_cmd[128]; | 
|  | 73 | +    snprintf(hook_cmd, sizeof(hook_cmd), "run-shell 'kill -WINCH %d'", (int)getpid()); | 
|  | 74 | +    cmd("tmux", "set-hook", "-g", "client-resized", hook_cmd, NULL); | 
|  | 75 | + | 
|  | 76 | +    if (cmd_output(tty_path, sizeof(tty_path), "tmux", "display-message", "-p", "-t", session_name, "#{pane_tty}", NULL) != 0) | 
|  | 77 | +        return -1; | 
|  | 78 | +    tty_path[strcspn(tty_path, "\n")] = '\0'; | 
|  | 79 | + | 
|  | 80 | +    int fd = open(tty_path, O_RDWR); | 
|  | 81 | +    if (fd < 0) return -1; | 
|  | 82 | +    return fd; | 
|  | 83 | +} | 
|  | 84 | + | 
|  | 85 | +static int mkfifo_if_needed(const char *path) | 
|  | 86 | +{ | 
|  | 87 | +    if (mkfifo(path, 0666) < 0) { | 
|  | 88 | +        if (errno != EEXIST) return -1; | 
|  | 89 | +    } | 
|  | 90 | +    return 0; | 
|  | 91 | +} | 
|  | 92 | + | 
|  | 93 | + | 
|  | 94 | +static int create_fifo_inout(const char *fifo_in, const char *fifo_out, int *input_fd, int *output_fd) | 
|  | 95 | +{ | 
|  | 96 | +    if (mkfifo_if_needed(fifo_in) < 0) return -1; | 
|  | 97 | +    if (mkfifo_if_needed(fifo_out) < 0) return -1; | 
|  | 98 | + | 
|  | 99 | +    int in_fd = open(fifo_in, O_RDONLY | O_NONBLOCK); | 
|  | 100 | +    if (in_fd < 0) return -1; | 
|  | 101 | + | 
|  | 102 | +    int out_fd = open(fifo_out, O_RDWR | O_NONBLOCK); | 
|  | 103 | +    if (out_fd < 0) { close(in_fd); return -1; } | 
|  | 104 | + | 
|  | 105 | +    *input_fd = in_fd; | 
|  | 106 | +    *output_fd = out_fd; | 
|  | 107 | +    return 0; | 
|  | 108 | +} | 
|  | 109 | + | 
|  | 110 | +int main(int argc, char *const argv[]) | 
|  | 111 | +{ | 
|  | 112 | +    if (argc < 3) { | 
|  | 113 | +        fprintf(stderr, "Usage: %s ROOT_DIR COMMAND [ARGS...]\n", argv[0]); | 
|  | 114 | +        return 1; | 
|  | 115 | +    } | 
|  | 116 | + | 
|  | 117 | +    const char *root_dir = argv[1]; | 
|  | 118 | +    const char *command = argv[2]; | 
|  | 119 | +    const char *const *command_args = (argc > 3) ? (const char *const *)&argv[3] : NULL; | 
|  | 120 | +    const char *const envp[] = { 0 }; | 
|  | 121 | + | 
|  | 122 | +    krun_set_log_level(KRUN_LOG_LEVEL_WARN); | 
|  | 123 | + | 
|  | 124 | +    int err; | 
|  | 125 | +    int ctx_id = krun_create_ctx(); | 
|  | 126 | +    if (ctx_id < 0) { errno = -ctx_id; perror("krun_create_ctx"); return 1; } | 
|  | 127 | + | 
|  | 128 | +    if ((err = krun_disable_implicit_console(ctx_id))) { | 
|  | 129 | +        errno = -err; | 
|  | 130 | +        perror("krun_disable_implicit_console"); | 
|  | 131 | +        return 1; | 
|  | 132 | +    } | 
|  | 133 | + | 
|  | 134 | +    int console_id = krun_add_virtio_console_multiport(ctx_id); | 
|  | 135 | +    if (console_id < 0) { | 
|  | 136 | +        errno = -console_id; | 
|  | 137 | +        perror("krun_add_virtio_console_multiport"); | 
|  | 138 | +        return 1; | 
|  | 139 | +    } | 
|  | 140 | + | 
|  | 141 | +    /* Configure console ports - edit this section to add/remove ports */ | 
|  | 142 | +    { | 
|  | 143 | +         | 
|  | 144 | +        // You could also use the controlling terminal of this process in the guest:  | 
|  | 145 | +        /*  | 
|  | 146 | +        if ((err = krun_add_console_port_tty(ctx_id, console_id, "host_tty", open("/dev/tty", O_RDWR)))) { | 
|  | 147 | +            errno = -err;  | 
|  | 148 | +            perror("port host_tty");  | 
|  | 149 | +            return 1; | 
|  | 150 | +        } | 
|  | 151 | +        */ | 
|  | 152 | + | 
|  | 153 | +        int num_consoles = 3; | 
|  | 154 | +        for (int i = 0; i < num_consoles; i++) { | 
|  | 155 | +            char session_name[64]; | 
|  | 156 | +            char port_name[64]; | 
|  | 157 | +            snprintf(session_name, sizeof(session_name), "krun-console-%d", i + 1); | 
|  | 158 | +            snprintf(port_name, sizeof(port_name), "console-%d", i + 1); | 
|  | 159 | + | 
|  | 160 | +            int tmux_fd = create_tmux_tty(session_name); | 
|  | 161 | +            if (tmux_fd < 0) { | 
|  | 162 | +                perror("create_tmux_tty"); | 
|  | 163 | +                return 1; | 
|  | 164 | +            } | 
|  | 165 | +            if ((err = krun_add_console_port_tty(ctx_id, console_id, port_name, tmux_fd))) { | 
|  | 166 | +                errno = -err; | 
|  | 167 | +                perror("krun_add_console_port_tty"); | 
|  | 168 | +                return 1; | 
|  | 169 | +            } | 
|  | 170 | +        } | 
|  | 171 | + | 
|  | 172 | +        int in_fd, out_fd; | 
|  | 173 | +        if (create_fifo_inout("/tmp/consoles_example_in", "/tmp/consoles_example_out", &in_fd, &out_fd) < 0) { | 
|  | 174 | +            perror("create_fifo_inout"); | 
|  | 175 | +            return 1; | 
|  | 176 | +        } | 
|  | 177 | +        if ((err = krun_add_console_port_inout(ctx_id, console_id, "fifo_inout", in_fd, out_fd))) { | 
|  | 178 | +            errno = -err; | 
|  | 179 | +            perror("krun_add_console_port_inout"); | 
|  | 180 | +            return 1; | 
|  | 181 | +        } | 
|  | 182 | + | 
|  | 183 | +        fprintf(stderr, "\n=== Console ports configured ===\n"); | 
|  | 184 | +        for (int i = 0; i < num_consoles; i++) { | 
|  | 185 | +            fprintf(stderr, "  console-%d: tmux attach -t krun-console-%d\n", i + 1, i + 1); | 
|  | 186 | +        } | 
|  | 187 | +        fprintf(stderr, "  fifo_inout: /tmp/consoles_example_in (host->guest)\n"); | 
|  | 188 | +        fprintf(stderr, "  fifo_inout: /tmp/consoles_example_out (guest->host)\n"); | 
|  | 189 | +        fprintf(stderr, "================================\n\n"); | 
|  | 190 | +    } | 
|  | 191 | + | 
|  | 192 | +    if ((err = krun_set_vm_config(ctx_id, 4, 4096))) { | 
|  | 193 | +        errno = -err; | 
|  | 194 | +        perror("krun_set_vm_config"); | 
|  | 195 | +        return 1; | 
|  | 196 | +    } | 
|  | 197 | + | 
|  | 198 | +    if ((err = krun_set_root(ctx_id, root_dir))) { | 
|  | 199 | +        errno = -err; | 
|  | 200 | +        perror("krun_set_root"); | 
|  | 201 | +        return 1; | 
|  | 202 | +    } | 
|  | 203 | + | 
|  | 204 | +    if ((err = krun_set_exec(ctx_id, command, command_args, envp))) { | 
|  | 205 | +        errno = -err; | 
|  | 206 | +        perror("krun_set_exec"); | 
|  | 207 | +        return 1; | 
|  | 208 | +    } | 
|  | 209 | + | 
|  | 210 | +    if ((err = krun_start_enter(ctx_id))) { | 
|  | 211 | +        errno = -err; | 
|  | 212 | +        perror("krun_start_enter"); | 
|  | 213 | +        return 1; | 
|  | 214 | +    } | 
|  | 215 | +    return 0; | 
|  | 216 | +} | 
|  | 217 | + | 
|  | 218 | + | 
0 commit comments