]>
Commit | Line | Data |
---|---|---|
b0a33c1e | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
b0a33c1e | 8 | * |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, write to the Free Software | |
250b1eec | 21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
b0a33c1e | 22 | */ |
23 | ||
b5159817 DE |
24 | #include <assert.h> |
25 | #include <signal.h> | |
b0a33c1e | 26 | #include <stdio.h> |
e0dc0de7 | 27 | #include <stdlib.h> |
b0a33c1e | 28 | #include <unistd.h> |
63376d7d | 29 | #include <fcntl.h> |
b0a33c1e | 30 | #include <errno.h> |
31 | #include <sys/types.h> | |
8173e600 | 32 | #include <termios.h> |
b0a33c1e | 33 | |
948955a2 | 34 | #include <lxc/lxccontainer.h> |
f2363e38 | 35 | |
00dbc43e DL |
36 | #include "log.h" |
37 | #include "conf.h" | |
e827ff7e | 38 | #include "config.h" |
00dbc43e DL |
39 | #include "start.h" /* for struct lxc_handler */ |
40 | #include "caps.h" | |
96fa1ff0 | 41 | #include "commands.h" |
63376d7d | 42 | #include "mainloop.h" |
724e753c | 43 | #include "af_unix.h" |
b5159817 DE |
44 | #include "lxclock.h" |
45 | #include "utils.h" | |
36eb9bde | 46 | |
e827ff7e SG |
47 | #if HAVE_PTY_H |
48 | #include <pty.h> | |
49 | #else | |
50 | #include <../include/openpty.h> | |
51 | #endif | |
52 | ||
36eb9bde CLG |
53 | lxc_log_define(lxc_console, lxc); |
54 | ||
b5159817 | 55 | static struct lxc_list lxc_ttys; |
724e753c | 56 | |
b5159817 DE |
57 | typedef void (*sighandler_t)(int); |
58 | struct lxc_tty_state | |
59 | { | |
60 | struct lxc_list node; | |
61 | int stdinfd; | |
62 | int stdoutfd; | |
63 | int masterfd; | |
64 | int escape; | |
65 | int saw_escape; | |
66 | const char *winch_proxy; | |
67 | const char *winch_proxy_lxcpath; | |
68 | int sigfd; | |
69 | sigset_t oldmask; | |
70 | }; | |
71 | ||
72 | __attribute__((constructor)) | |
73 | void lxc_console_init(void) | |
74 | { | |
75 | lxc_list_init(&lxc_ttys); | |
76 | } | |
724e753c | 77 | |
b5159817 DE |
78 | /* lxc_console_winsz: propagte winsz from one terminal to another |
79 | * | |
80 | * @srcfd : terminal to get size from (typically a slave pty) | |
81 | * @dstfd : terminal to set size on (typically a master pty) | |
82 | */ | |
83 | static void lxc_console_winsz(int srcfd, int dstfd) | |
84 | { | |
85 | struct winsize wsz; | |
86 | if (isatty(srcfd) && ioctl(srcfd, TIOCGWINSZ, &wsz) == 0) { | |
87 | DEBUG("set winsz dstfd:%d cols:%d rows:%d", dstfd, | |
88 | wsz.ws_col, wsz.ws_row); | |
89 | ioctl(dstfd, TIOCSWINSZ, &wsz); | |
724e753c | 90 | } |
b5159817 | 91 | } |
724e753c | 92 | |
b5159817 DE |
93 | static void lxc_console_winch(struct lxc_tty_state *ts) |
94 | { | |
95 | lxc_console_winsz(ts->stdinfd, ts->masterfd); | |
96 | if (ts->winch_proxy) { | |
97 | lxc_cmd_console_winch(ts->winch_proxy, | |
98 | ts->winch_proxy_lxcpath); | |
99 | } | |
724e753c MN |
100 | } |
101 | ||
b5159817 | 102 | void lxc_console_sigwinch(int sig) |
cd453b38 | 103 | { |
025ed0f3 SH |
104 | struct lxc_list *it; |
105 | struct lxc_tty_state *ts; | |
cd453b38 | 106 | |
025ed0f3 SH |
107 | lxc_list_for_each(it, &lxc_ttys) { |
108 | ts = it->elem; | |
109 | lxc_console_winch(ts); | |
cd453b38 | 110 | } |
b5159817 | 111 | } |
cd453b38 | 112 | |
84c92abd | 113 | static int lxc_console_cb_sigwinch_fd(int fd, uint32_t events, void *cbdata, |
b5159817 DE |
114 | struct lxc_epoll_descr *descr) |
115 | { | |
116 | struct signalfd_siginfo siginfo; | |
117 | struct lxc_tty_state *ts = cbdata; | |
118 | ||
9de2ebe9 | 119 | if (read(fd, &siginfo, sizeof(siginfo)) < sizeof(siginfo)) { |
b5159817 DE |
120 | ERROR("failed to read signal info"); |
121 | return -1; | |
cd453b38 DL |
122 | } |
123 | ||
b5159817 DE |
124 | lxc_console_winch(ts); |
125 | return 0; | |
126 | } | |
127 | ||
128 | /* | |
129 | * lxc_console_sigwinch_init: install SIGWINCH handler | |
130 | * | |
131 | * @srcfd : src for winsz in SIGWINCH handler | |
132 | * @dstfd : dst for winsz in SIGWINCH handler | |
133 | * | |
134 | * Returns lxc_tty_state structure on success or NULL on failure. The sigfd | |
135 | * member of the returned lxc_tty_state can be select()/poll()ed/epoll()ed | |
136 | * on (ie added to a mainloop) for SIGWINCH. | |
137 | * | |
138 | * Must be called with process_lock held to protect the lxc_ttys list, or | |
139 | * from a non-threaded context. | |
140 | * | |
141 | * Note that SIGWINCH isn't installed as a classic asychronous handler, | |
142 | * rather signalfd(2) is used so that we can handle the signal when we're | |
143 | * ready for it. This avoids deadlocks since a signal handler | |
144 | * (ie lxc_console_sigwinch()) would need to take the thread mutex to | |
145 | * prevent lxc_ttys list corruption, but using the fd we can provide the | |
146 | * tty_state needed to the callback (lxc_console_cb_sigwinch_fd()). | |
147 | */ | |
148 | static struct lxc_tty_state *lxc_console_sigwinch_init(int srcfd, int dstfd) | |
149 | { | |
150 | sigset_t mask; | |
151 | struct lxc_tty_state *ts; | |
152 | ||
153 | ts = malloc(sizeof(*ts)); | |
154 | if (!ts) | |
155 | return NULL; | |
156 | ||
157 | memset(ts, 0, sizeof(*ts)); | |
158 | ts->stdinfd = srcfd; | |
159 | ts->masterfd = dstfd; | |
160 | ts->sigfd = -1; | |
161 | ||
162 | /* add tty to list to be scanned at SIGWINCH time */ | |
163 | lxc_list_add_elem(&ts->node, ts); | |
164 | lxc_list_add_tail(&lxc_ttys, &ts->node); | |
165 | ||
166 | sigemptyset(&mask); | |
167 | sigaddset(&mask, SIGWINCH); | |
168 | if (sigprocmask(SIG_BLOCK, &mask, &ts->oldmask)) { | |
169 | SYSERROR("failed to block SIGWINCH"); | |
170 | goto err1; | |
171 | } | |
172 | ||
173 | ts->sigfd = signalfd(-1, &mask, 0); | |
174 | if (ts->sigfd < 0) { | |
175 | SYSERROR("failed to get signalfd"); | |
176 | goto err2; | |
177 | } | |
178 | ||
179 | DEBUG("%d got SIGWINCH fd %d", getpid(), ts->sigfd); | |
180 | goto out; | |
181 | ||
182 | err2: | |
183 | sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); | |
184 | err1: | |
185 | lxc_list_del(&ts->node); | |
186 | free(ts); | |
187 | ts = NULL; | |
cd453b38 | 188 | out: |
b5159817 | 189 | return ts; |
cd453b38 DL |
190 | } |
191 | ||
b5159817 DE |
192 | /* |
193 | * lxc_console_sigwinch_fini: uninstall SIGWINCH handler | |
194 | * | |
195 | * @ts : the lxc_tty_state returned by lxc_console_sigwinch_init | |
196 | * | |
197 | * Restore the saved signal handler that was in effect at the time | |
198 | * lxc_console_sigwinch_init() was called. | |
199 | * | |
200 | * Must be called with process_lock held to protect the lxc_ttys list, or | |
201 | * from a non-threaded context. | |
202 | */ | |
203 | static void lxc_console_sigwinch_fini(struct lxc_tty_state *ts) | |
63376d7d | 204 | { |
025ed0f3 | 205 | if (ts->sigfd >= 0) { |
b5159817 | 206 | close(ts->sigfd); |
025ed0f3 | 207 | } |
b5159817 DE |
208 | lxc_list_del(&ts->node); |
209 | sigprocmask(SIG_SETMASK, &ts->oldmask, NULL); | |
210 | free(ts); | |
211 | } | |
1560f6c9 | 212 | |
84c92abd | 213 | static int lxc_console_cb_con(int fd, uint32_t events, void *data, |
b5159817 DE |
214 | struct lxc_epoll_descr *descr) |
215 | { | |
216 | struct lxc_console *console = (struct lxc_console *)data; | |
217 | char buf[1024]; | |
218 | int r,w; | |
e0dc0de7 | 219 | |
b5159817 DE |
220 | w = r = read(fd, buf, sizeof(buf)); |
221 | if (r < 0) { | |
222 | SYSERROR("failed to read"); | |
223 | return 1; | |
cd453b38 | 224 | } |
f78a1f32 | 225 | |
b5159817 DE |
226 | if (!r) { |
227 | INFO("console client on fd %d has exited", fd); | |
228 | lxc_mainloop_del_handler(descr, fd); | |
229 | close(fd); | |
dff21ef0 | 230 | return 0; |
33fcb7a0 | 231 | } |
63376d7d | 232 | |
b5159817 DE |
233 | if (fd == console->peer) |
234 | w = write(console->master, buf, r); | |
235 | ||
236 | if (fd == console->master) { | |
237 | if (console->log_fd >= 0) | |
238 | w = write(console->log_fd, buf, r); | |
239 | ||
240 | if (console->peer >= 0) | |
241 | w = write(console->peer, buf, r); | |
63376d7d DL |
242 | } |
243 | ||
b5159817 DE |
244 | if (w != r) |
245 | WARN("console short write r:%d w:%d", r, w); | |
246 | return 0; | |
247 | } | |
248 | ||
249 | static void lxc_console_mainloop_add_peer(struct lxc_console *console) | |
250 | { | |
251 | if (console->peer >= 0) { | |
252 | if (lxc_mainloop_add_handler(console->descr, console->peer, | |
253 | lxc_console_cb_con, console)) | |
254 | WARN("console peer not added to mainloop"); | |
63376d7d DL |
255 | } |
256 | ||
b5159817 DE |
257 | if (console->tty_state) { |
258 | if (lxc_mainloop_add_handler(console->descr, | |
259 | console->tty_state->sigfd, | |
260 | lxc_console_cb_sigwinch_fd, | |
261 | console->tty_state)) { | |
262 | WARN("failed to add to mainloop SIGWINCH handler for '%d'", | |
263 | console->tty_state->sigfd); | |
596a818d | 264 | } |
b5159817 DE |
265 | } |
266 | } | |
267 | ||
268 | int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, | |
269 | struct lxc_handler *handler) | |
270 | { | |
271 | struct lxc_conf *conf = handler->conf; | |
272 | struct lxc_console *console = &conf->console; | |
273 | ||
37903589 SH |
274 | if (conf->is_execute) { |
275 | INFO("no console for lxc-execute."); | |
276 | return 0; | |
277 | } | |
278 | ||
b5159817 DE |
279 | if (!conf->rootfs.path) { |
280 | INFO("no rootfs, no console."); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | if (console->master < 0) { | |
285 | INFO("no console"); | |
286 | return 0; | |
596a818d DE |
287 | } |
288 | ||
b5159817 DE |
289 | if (lxc_mainloop_add_handler(descr, console->master, |
290 | lxc_console_cb_con, console)) { | |
291 | ERROR("failed to add to mainloop console handler for '%d'", | |
292 | console->master); | |
293 | return -1; | |
28a4b0e5 DL |
294 | } |
295 | ||
b5159817 DE |
296 | /* we cache the descr so that we can add an fd to it when someone |
297 | * does attach to it in lxc_console_allocate() | |
298 | */ | |
299 | console->descr = descr; | |
300 | lxc_console_mainloop_add_peer(console); | |
cd453b38 | 301 | |
b5159817 DE |
302 | return 0; |
303 | } | |
28a4b0e5 | 304 | |
b5159817 DE |
305 | static int setup_tios(int fd, struct termios *oldtios) |
306 | { | |
307 | struct termios newtios; | |
e0dc0de7 | 308 | |
b5159817 DE |
309 | if (!isatty(fd)) { |
310 | ERROR("'%d' is not a tty", fd); | |
311 | return -1; | |
e0dc0de7 DL |
312 | } |
313 | ||
b5159817 DE |
314 | /* Get current termios */ |
315 | if (tcgetattr(fd, oldtios)) { | |
e0dc0de7 | 316 | SYSERROR("failed to get current terminal settings"); |
b5159817 | 317 | return -1; |
e0dc0de7 DL |
318 | } |
319 | ||
b5159817 | 320 | newtios = *oldtios; |
e0dc0de7 DL |
321 | |
322 | /* Remove the echo characters and signal reception, the echo | |
b5159817 DE |
323 | * will be done with master proxying */ |
324 | newtios.c_iflag &= ~IGNBRK; | |
325 | newtios.c_iflag &= BRKINT; | |
326 | newtios.c_lflag &= ~(ECHO|ICANON|ISIG); | |
327 | newtios.c_cc[VMIN] = 1; | |
328 | newtios.c_cc[VTIME] = 0; | |
e0dc0de7 DL |
329 | |
330 | /* Set new attributes */ | |
b5159817 | 331 | if (tcsetattr(fd, TCSAFLUSH, &newtios)) { |
e0dc0de7 | 332 | ERROR("failed to set new terminal settings"); |
b5159817 | 333 | return -1; |
e0dc0de7 DL |
334 | } |
335 | ||
63376d7d | 336 | return 0; |
b5159817 | 337 | } |
e0dc0de7 | 338 | |
b5159817 DE |
339 | static void lxc_console_peer_proxy_free(struct lxc_console *console) |
340 | { | |
341 | if (console->tty_state) { | |
342 | lxc_console_sigwinch_fini(console->tty_state); | |
343 | console->tty_state = NULL; | |
344 | } | |
345 | close(console->peerpty.master); | |
346 | close(console->peerpty.slave); | |
347 | console->peerpty.master = -1; | |
348 | console->peerpty.slave = -1; | |
349 | console->peerpty.busy = -1; | |
350 | console->peerpty.name[0] = '\0'; | |
596a818d | 351 | console->peer = -1; |
b5159817 | 352 | } |
596a818d | 353 | |
b5159817 DE |
354 | static int lxc_console_peer_proxy_alloc(struct lxc_console *console, int sockfd) |
355 | { | |
356 | struct termios oldtermio; | |
357 | struct lxc_tty_state *ts; | |
025ed0f3 | 358 | int ret; |
b5159817 DE |
359 | |
360 | if (console->master < 0) { | |
361 | ERROR("console not set up"); | |
362 | return -1; | |
363 | } | |
364 | if (console->peerpty.busy != -1 || console->peer != -1) { | |
365 | NOTICE("console already in use"); | |
366 | return -1; | |
367 | } | |
368 | if (console->tty_state) { | |
369 | ERROR("console already has tty_state"); | |
370 | return -1; | |
596a818d DE |
371 | } |
372 | ||
b5159817 DE |
373 | /* this is the proxy pty that will be given to the client, and that |
374 | * the real pty master will send to / recv from | |
375 | */ | |
025ed0f3 SH |
376 | process_lock(); |
377 | ret = openpty(&console->peerpty.master, &console->peerpty.slave, | |
378 | console->peerpty.name, NULL, NULL); | |
379 | process_unlock(); | |
380 | if (ret) { | |
b5159817 DE |
381 | SYSERROR("failed to create proxy pty"); |
382 | return -1; | |
383 | } | |
596a818d | 384 | |
b5159817 DE |
385 | if (setup_tios(console->peerpty.slave, &oldtermio) < 0) |
386 | goto err1; | |
387 | ||
388 | ts = lxc_console_sigwinch_init(console->peerpty.master, console->master); | |
389 | if (!ts) | |
390 | goto err1; | |
391 | ||
392 | console->tty_state = ts; | |
393 | console->peer = console->peerpty.slave; | |
394 | console->peerpty.busy = sockfd; | |
395 | lxc_console_mainloop_add_peer(console); | |
396 | ||
397 | DEBUG("%d %s peermaster:%d sockfd:%d", getpid(), __FUNCTION__, console->peerpty.master, sockfd); | |
398 | return 0; | |
399 | ||
400 | err1: | |
401 | lxc_console_peer_proxy_free(console); | |
63376d7d DL |
402 | return -1; |
403 | } | |
404 | ||
b5159817 DE |
405 | /* lxc_console_allocate: allocate the console or a tty |
406 | * | |
407 | * @conf : the configuration of the container to allocate from | |
408 | * @sockfd : the socket fd whose remote side when closed, will be an | |
409 | * indication that the console or tty is no longer in use | |
410 | * @ttyreq : the tty requested to be opened, -1 for any, 0 for the console | |
411 | */ | |
412 | int lxc_console_allocate(struct lxc_conf *conf, int sockfd, int *ttyreq) | |
413 | { | |
414 | int masterfd = -1, ttynum; | |
415 | struct lxc_tty_info *tty_info = &conf->tty_info; | |
416 | struct lxc_console *console = &conf->console; | |
417 | ||
b5159817 DE |
418 | if (*ttyreq == 0) { |
419 | if (lxc_console_peer_proxy_alloc(console, sockfd) < 0) | |
420 | goto out; | |
421 | masterfd = console->peerpty.master; | |
422 | goto out; | |
423 | } | |
424 | ||
425 | if (*ttyreq > 0) { | |
426 | if (*ttyreq > tty_info->nbtty) | |
427 | goto out; | |
428 | ||
429 | if (tty_info->pty_info[*ttyreq - 1].busy) | |
430 | goto out; | |
431 | ||
432 | /* the requested tty is available */ | |
433 | ttynum = *ttyreq; | |
434 | goto out_tty; | |
435 | } | |
436 | ||
437 | /* search for next available tty, fixup index tty1 => [0] */ | |
438 | for (ttynum = 1; | |
439 | ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; | |
440 | ttynum++); | |
441 | ||
442 | /* we didn't find any available slot for tty */ | |
443 | if (ttynum > tty_info->nbtty) | |
444 | goto out; | |
445 | ||
446 | *ttyreq = ttynum; | |
447 | ||
448 | out_tty: | |
449 | tty_info->pty_info[ttynum - 1].busy = sockfd; | |
450 | masterfd = tty_info->pty_info[ttynum - 1].master; | |
451 | out: | |
b5159817 DE |
452 | return masterfd; |
453 | } | |
454 | ||
455 | /* lxc_console_free: mark the console or a tty as unallocated, free any | |
456 | * resources allocated by lxc_console_allocate(). | |
457 | * | |
458 | * @conf : the configuration of the container whose tty was closed | |
459 | * @fd : the socket fd whose remote side was closed, which indicated | |
460 | * the console or tty is no longer in use. this is used to match | |
461 | * which console/tty is being freed. | |
462 | */ | |
463 | void lxc_console_free(struct lxc_conf *conf, int fd) | |
63376d7d | 464 | { |
b5159817 DE |
465 | int i; |
466 | struct lxc_tty_info *tty_info = &conf->tty_info; | |
467 | struct lxc_console *console = &conf->console; | |
468 | ||
b5159817 DE |
469 | for (i = 0; i < tty_info->nbtty; i++) { |
470 | if (tty_info->pty_info[i].busy == fd) | |
471 | tty_info->pty_info[i].busy = 0; | |
472 | } | |
473 | ||
474 | if (console->peerpty.busy == fd) { | |
475 | lxc_mainloop_del_handler(console->descr, console->peerpty.slave); | |
476 | lxc_console_peer_proxy_free(console); | |
477 | } | |
b5159817 DE |
478 | } |
479 | ||
480 | static void lxc_console_peer_default(struct lxc_console *console) | |
481 | { | |
482 | struct lxc_tty_state *ts; | |
483 | const char *path = console->path; | |
484 | ||
485 | /* if no console was given, try current controlling terminal, there | |
486 | * won't be one if we were started as a daemon (-d) | |
487 | */ | |
488 | if (!path && !access("/dev/tty", F_OK)) { | |
489 | int fd; | |
490 | fd = open("/dev/tty", O_RDWR); | |
491 | if (fd >= 0) { | |
492 | close(fd); | |
493 | path = "/dev/tty"; | |
494 | } | |
495 | } | |
496 | ||
497 | if (!path) | |
498 | goto out; | |
499 | ||
500 | DEBUG("opening %s for console peer", path); | |
501 | console->peer = lxc_unpriv(open(path, O_CLOEXEC | O_RDWR | O_CREAT | | |
502 | O_APPEND, 0600)); | |
503 | if (console->peer < 0) | |
504 | goto out; | |
505 | ||
506 | DEBUG("using '%s' as console", path); | |
507 | ||
508 | if (!isatty(console->peer)) | |
0d9acb99 | 509 | goto err1; |
b5159817 DE |
510 | |
511 | ts = lxc_console_sigwinch_init(console->peer, console->master); | |
512 | if (!ts) | |
513 | WARN("Unable to install SIGWINCH"); | |
514 | console->tty_state = ts; | |
515 | ||
516 | lxc_console_winsz(console->peer, console->master); | |
517 | ||
518 | console->tios = malloc(sizeof(*console->tios)); | |
519 | if (!console->tios) { | |
520 | SYSERROR("failed to allocate memory"); | |
521 | goto err1; | |
522 | } | |
523 | ||
524 | if (setup_tios(console->peer, console->tios) < 0) | |
525 | goto err2; | |
526 | ||
527 | return; | |
528 | ||
529 | err2: | |
530 | free(console->tios); | |
531 | console->tios = NULL; | |
532 | err1: | |
533 | close(console->peer); | |
534 | console->peer = -1; | |
535 | out: | |
536 | DEBUG("no console peer"); | |
537 | } | |
538 | ||
539 | void lxc_console_delete(struct lxc_console *console) | |
540 | { | |
541 | if (console->tios && console->peer >= 0 && | |
e0dc0de7 DL |
542 | tcsetattr(console->peer, TCSAFLUSH, console->tios)) |
543 | WARN("failed to set old terminal settings"); | |
596a818d DE |
544 | free(console->tios); |
545 | console->tios = NULL; | |
546 | ||
547 | close(console->peer); | |
025ed0f3 SH |
548 | close(console->master); |
549 | close(console->slave); | |
550 | if (console->log_fd >= 0) | |
596a818d | 551 | close(console->log_fd); |
596a818d | 552 | |
025ed0f3 | 553 | console->peer = -1; |
596a818d | 554 | console->master = -1; |
596a818d | 555 | console->slave = -1; |
025ed0f3 | 556 | console->log_fd = -1; |
63376d7d DL |
557 | } |
558 | ||
b5159817 | 559 | int lxc_console_create(struct lxc_conf *conf) |
63376d7d | 560 | { |
b5159817 | 561 | struct lxc_console *console = &conf->console; |
025ed0f3 | 562 | int ret; |
63376d7d | 563 | |
37903589 SH |
564 | if (conf->is_execute) { |
565 | INFO("no console for lxc-execute."); | |
566 | return 0; | |
567 | } | |
568 | ||
b5159817 DE |
569 | if (!conf->rootfs.path) |
570 | return 0; | |
571 | ||
572 | if (console->path && !strcmp(console->path, "none")) | |
573 | return 0; | |
574 | ||
025ed0f3 SH |
575 | process_lock(); |
576 | ret = openpty(&console->master, &console->slave, | |
577 | console->name, NULL, NULL); | |
578 | process_unlock(); | |
579 | if (ret) { | |
b5159817 DE |
580 | SYSERROR("failed to allocate a pty"); |
581 | return -1; | |
582 | } | |
583 | ||
584 | if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) { | |
585 | SYSERROR("failed to set console master to close-on-exec"); | |
586 | goto err; | |
587 | } | |
588 | ||
589 | if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) { | |
590 | SYSERROR("failed to set console slave to close-on-exec"); | |
591 | goto err; | |
592 | } | |
593 | ||
594 | lxc_console_peer_default(console); | |
595 | ||
596 | if (console->log_path) { | |
597 | console->log_fd = lxc_unpriv(open(console->log_path, | |
598 | O_CLOEXEC | O_RDWR | | |
599 | O_CREAT | O_APPEND, 0600)); | |
600 | if (console->log_fd < 0) { | |
601 | SYSERROR("failed to open '%s'", console->log_path); | |
602 | goto err; | |
603 | } | |
604 | DEBUG("using '%s' as console log", console->log_path); | |
605 | } | |
606 | ||
607 | return 0; | |
608 | ||
609 | err: | |
610 | lxc_console_delete(console); | |
611 | return -1; | |
612 | } | |
613 | ||
0d9acb99 DE |
614 | int lxc_console_set_stdfds(struct lxc_handler *handler) |
615 | { | |
616 | struct lxc_conf *conf = handler->conf; | |
617 | struct lxc_console *console = &conf->console; | |
618 | ||
619 | if (console->slave < 0) | |
620 | return 0; | |
b5159817 | 621 | |
0d9acb99 DE |
622 | if (dup2(console->slave, 0) < 0 || |
623 | dup2(console->slave, 1) < 0 || | |
624 | dup2(console->slave, 2) < 0) | |
625 | { | |
626 | SYSERROR("failed to dup console"); | |
627 | return -1; | |
628 | } | |
629 | return 0; | |
630 | } | |
b5159817 | 631 | |
84c92abd | 632 | static int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata, |
b5159817 DE |
633 | struct lxc_epoll_descr *descr) |
634 | { | |
635 | struct lxc_tty_state *ts = cbdata; | |
636 | char c; | |
637 | ||
638 | assert(fd == ts->stdinfd); | |
639 | if (read(ts->stdinfd, &c, 1) < 0) { | |
63376d7d DL |
640 | SYSERROR("failed to read"); |
641 | return 1; | |
642 | } | |
643 | ||
b5159817 DE |
644 | /* we want to exit the console with Ctrl+a q */ |
645 | if (c == ts->escape && !ts->saw_escape) { | |
646 | ts->saw_escape = 1; | |
5c294060 DL |
647 | return 0; |
648 | } | |
649 | ||
b5159817 DE |
650 | if (c == 'q' && ts->saw_escape) |
651 | return 1; | |
63376d7d | 652 | |
b5159817 DE |
653 | ts->saw_escape = 0; |
654 | if (write(ts->masterfd, &c, 1) < 0) { | |
655 | SYSERROR("failed to write"); | |
656 | return 1; | |
596a818d | 657 | } |
b5159817 | 658 | |
63376d7d DL |
659 | return 0; |
660 | } | |
661 | ||
84c92abd | 662 | static int lxc_console_cb_tty_master(int fd, uint32_t events, void *cbdata, |
b5159817 | 663 | struct lxc_epoll_descr *descr) |
63376d7d | 664 | { |
b5159817 DE |
665 | struct lxc_tty_state *ts = cbdata; |
666 | char buf[1024]; | |
667 | int r,w; | |
63376d7d | 668 | |
b5159817 DE |
669 | assert(fd == ts->masterfd); |
670 | r = read(fd, buf, sizeof(buf)); | |
671 | if (r < 0) { | |
672 | SYSERROR("failed to read"); | |
673 | return 1; | |
1560f6c9 DL |
674 | } |
675 | ||
b5159817 DE |
676 | w = write(ts->stdoutfd, buf, r); |
677 | if (w < 0 || w != r) { | |
678 | SYSERROR("failed to write"); | |
679 | return 1; | |
f78a1f32 DL |
680 | } |
681 | ||
b5159817 DE |
682 | return 0; |
683 | } | |
684 | ||
685 | int lxc_console_getfd(struct lxc_container *c, int *ttynum, int *masterfd) | |
686 | { | |
687 | return lxc_cmd_console(c->name, ttynum, masterfd, c->config_path); | |
688 | } | |
689 | ||
690 | int lxc_console(struct lxc_container *c, int ttynum, | |
691 | int stdinfd, int stdoutfd, int stderrfd, | |
692 | int escape) | |
693 | { | |
694 | int ret, ttyfd, masterfd; | |
695 | struct lxc_epoll_descr descr; | |
696 | struct termios oldtios; | |
697 | struct lxc_tty_state *ts; | |
698 | ||
699 | if (!isatty(stdinfd)) { | |
700 | ERROR("stdin is not a tty"); | |
701 | return -1; | |
dff21ef0 DL |
702 | } |
703 | ||
b5159817 DE |
704 | ret = setup_tios(stdinfd, &oldtios); |
705 | if (ret) { | |
706 | ERROR("failed to setup tios"); | |
63376d7d DL |
707 | return -1; |
708 | } | |
709 | ||
b5159817 DE |
710 | ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); |
711 | if (ttyfd < 0) { | |
712 | ret = ttyfd; | |
713 | goto err1; | |
714 | } | |
63376d7d | 715 | |
b5159817 DE |
716 | fprintf(stderr, "\n" |
717 | "Connected to tty %1$d\n" | |
718 | "Type <Ctrl+%2$c q> to exit the console, " | |
719 | "<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n", | |
720 | ttynum, 'a' + escape - 1); | |
721 | ||
722 | ret = setsid(); | |
723 | if (ret) | |
724 | INFO("already group leader"); | |
725 | ||
726 | ts = lxc_console_sigwinch_init(stdinfd, masterfd); | |
727 | if (!ts) { | |
728 | ret = -1; | |
729 | goto err2; | |
730 | } | |
731 | ts->escape = escape; | |
732 | ts->winch_proxy = c->name; | |
733 | ts->winch_proxy_lxcpath = c->config_path; | |
734 | ||
735 | lxc_console_winsz(stdinfd, masterfd); | |
736 | lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); | |
737 | ||
738 | ret = lxc_mainloop_open(&descr); | |
739 | if (ret) { | |
740 | ERROR("failed to create mainloop"); | |
741 | goto err3; | |
742 | } | |
743 | ||
744 | ret = lxc_mainloop_add_handler(&descr, ts->sigfd, | |
745 | lxc_console_cb_sigwinch_fd, ts); | |
746 | if (ret) { | |
747 | ERROR("failed to add handler for SIGWINCH fd"); | |
748 | goto err4; | |
749 | } | |
750 | ||
751 | ret = lxc_mainloop_add_handler(&descr, ts->stdinfd, | |
752 | lxc_console_cb_tty_stdin, ts); | |
753 | if (ret) { | |
754 | ERROR("failed to add handler for stdinfd"); | |
755 | goto err4; | |
756 | } | |
757 | ||
758 | ret = lxc_mainloop_add_handler(&descr, ts->masterfd, | |
759 | lxc_console_cb_tty_master, ts); | |
760 | if (ret) { | |
761 | ERROR("failed to add handler for masterfd"); | |
762 | goto err4; | |
763 | } | |
764 | ||
025ed0f3 | 765 | ret = lxc_mainloop(&descr, -1); |
b5159817 DE |
766 | if (ret) { |
767 | ERROR("mainloop returned an error"); | |
768 | goto err4; | |
769 | } | |
770 | ||
771 | ret = 0; | |
772 | ||
773 | err4: | |
774 | lxc_mainloop_close(&descr); | |
775 | err3: | |
776 | lxc_console_sigwinch_fini(ts); | |
777 | err2: | |
778 | close(masterfd); | |
779 | close(ttyfd); | |
780 | err1: | |
781 | tcsetattr(stdinfd, TCSAFLUSH, &oldtios); | |
b5159817 DE |
782 | |
783 | return ret; | |
63376d7d | 784 | } |