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