jail/seccomp: add support for aarch64
[project/procd.git] / jail / console.c
1 /*
2 * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #ifndef _GNU_SOURCE
15 #define _GNU_SOURCE
16 #endif
17
18 #include <stdlib.h>
19 #include <fcntl.h>
20 #include <libubox/ustream.h>
21 #include <libubus.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <termios.h>
30
31 static inline int setup_tios(int fd, struct termios *oldtios)
32 {
33 struct termios newtios;
34
35 if (!isatty(fd)) {
36 return -1;
37 }
38
39 /* Get current termios */
40 if (tcgetattr(fd, oldtios))
41 return -1;
42
43 newtios = *oldtios;
44
45 /* We use the same settings that ssh does. */
46 newtios.c_iflag |= IGNPAR;
47 newtios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
48 newtios.c_lflag &= ~(TOSTOP | ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
49 newtios.c_oflag &= ~ONLCR;
50 newtios.c_oflag |= OPOST;
51 newtios.c_cc[VMIN] = 1;
52 newtios.c_cc[VTIME] = 0;
53
54 /* Set new attributes */
55 if (tcsetattr(fd, TCSAFLUSH, &newtios))
56 return -1;
57
58 return 0;
59 }
60
61
62
63 #define OPT_ARGS "i:c:"
64
65 static struct ustream_fd cufd;
66 static struct ustream_fd lufd;
67
68 static void usage()
69 {
70 fprintf(stderr, "ujail-console -c <container> [-i <instance>]\n");
71 exit(1);
72 }
73
74 static void client_cb(struct ustream *s, int bytes)
75 {
76 char *buf;
77 int len, rv;
78
79 do {
80 buf = ustream_get_read_buf(s, &len);
81 if (!buf)
82 break;
83
84 rv = ustream_write(&lufd.stream, buf, len, false);
85
86 if (rv > 0)
87 ustream_consume(s, rv);
88
89 if (rv <= len)
90 break;
91 } while(1);
92 }
93
94 static void local_cb(struct ustream *s, int bytes)
95 {
96 char *buf;
97 int len, rv;
98
99 do {
100 buf = ustream_get_read_buf(s, &len);
101 if (!buf)
102 break;
103
104 if ((len > 0) && (buf[0] == 2))
105 uloop_end();
106
107 rv = ustream_write(&cufd.stream, buf, len, false);
108
109 if (rv > 0)
110 ustream_consume(s, rv);
111
112 if (rv <= len)
113 break;
114 } while(1);
115 }
116
117 int main(int argc, char **argv)
118 {
119 struct ubus_context *ctx;
120 uint32_t id;
121 static struct blob_buf req;
122 char *container_name = NULL, *instance_name = NULL;
123 int client_fd, server_fd, tty_fd;
124 struct termios oldtermios;
125 int ch;
126
127 while ((ch = getopt(argc, argv, OPT_ARGS)) != -1) {
128 switch (ch) {
129 case 'i':
130 instance_name = optarg;
131 break;
132 case 'c':
133 container_name = optarg;
134 break;
135 default:
136 usage();
137 }
138 }
139
140 if (!container_name)
141 usage();
142
143 ctx = ubus_connect(NULL);
144 if (!ctx) {
145 fprintf(stderr, "can't connect to ubus!\n");
146 return -1;
147 }
148
149 /* open pseudo-terminal pair */
150 client_fd = posix_openpt(O_RDWR | O_NOCTTY);
151 if (client_fd < 0) {
152 fprintf(stderr, "can't create virtual console!\n");
153 ubus_free(ctx);
154 return -1;
155 }
156 setup_tios(client_fd, &oldtermios);
157 grantpt(client_fd);
158 unlockpt(client_fd);
159 server_fd = open(ptsname(client_fd), O_RDWR | O_NOCTTY);
160 if (server_fd < 0) {
161 fprintf(stderr, "can't open virtual console!\n");
162 close(client_fd);
163 ubus_free(ctx);
164 return -1;
165 }
166
167 setup_tios(server_fd, &oldtermios);
168 tty_fd = open("/dev/tty", O_RDWR);
169 setup_tios(tty_fd, &oldtermios);
170
171 /* register server-side with procd */
172 blob_buf_init(&req, 0);
173 blobmsg_add_string(&req, "name", container_name);
174 if (instance_name)
175 blobmsg_add_string(&req, "instance", instance_name);
176
177 if (ubus_lookup_id(ctx, "container", &id) ||
178 ubus_invoke_fd(ctx, id, "console_attach", req.head, NULL, NULL, 3000, server_fd)) {
179 fprintf(stderr, "ubus request failed\n");
180 close(server_fd);
181 close(client_fd);
182 blob_buf_free(&req);
183 ubus_free(ctx);
184 return -2;
185 }
186
187 close(server_fd);
188 blob_buf_free(&req);
189 ubus_free(ctx);
190
191 uloop_init();
192
193 /* forward between stdio and client_fd until detach is requested */
194 lufd.stream.notify_read = local_cb;
195 ustream_fd_init(&lufd, tty_fd);
196
197 cufd.stream.notify_read = client_cb;
198 /* ToDo: handle remote close and other events */
199 // cufd.stream.notify_state = client_state_cb;
200 ustream_fd_init(&cufd, client_fd);
201
202 fprintf(stderr, "attaching to jail console. press [CTRL]+[B] to exit.\n");
203 close(0);
204 close(1);
205 close(2);
206 uloop_run();
207
208 tcsetattr(tty_fd, TCSAFLUSH, &oldtermios);
209 ustream_free(&lufd.stream);
210 ustream_free(&cufd.stream);
211 close(client_fd);
212
213 return 0;
214 }