Skip to content

Commit 6a3e3a1

Browse files
committed
examples: Add a multiport virtio console example
Add an example which shows the new multiport console functionality with multiple terminals. The example uses tmux to create the the terminals in the background. Signed-off-by: Matej Hrica <[email protected]>
1 parent 20b0c06 commit 6a3e3a1

File tree

3 files changed

+224
-2
lines changed

3 files changed

+224
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ __pycache__
99
/libkrun.pc
1010
init/init
1111
examples/chroot_vm
12+
examples/consoles
1213
test-prefix

examples/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ROOTFS_DIR = rootfs_$(ROOTFS_DISTRO)
1414

1515
.PHONY: clean rootfs
1616

17-
EXAMPLES := chroot_vm external_kernel
17+
EXAMPLES := chroot_vm external_kernel consoles
1818
ifeq ($(SEV),1)
1919
EXAMPLES := launch-tee
2020
endif
@@ -53,6 +53,9 @@ ifeq ($(OS),Darwin)
5353
codesign --entitlements chroot_vm.entitlements --force -s - $@
5454
endif
5555

56+
consoles: consoles.c
57+
gcc -o $@ $< $(CFLAGS) $(LDFLAGS_$(ARCH)_$(OS))
58+
5659
nitro: nitro.c
5760
gcc -o $@ $< $(CFLAGS) $(LDFLAGS_nitro)
5861

@@ -64,4 +67,4 @@ rootfs:
6467
podman rm libkrun_chroot_vm
6568

6669
clean:
67-
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi external_kernel nitro
70+
rm -rf chroot_vm $(ROOTFS_DIR) launch-tee boot_efi external_kernel nitro consoles

examples/consoles.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
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

Comments
 (0)