2 * lxc: linux Container library
4 * (C) Copyright IBM Corp. 2007, 2008
7 * Daniel Lezcano <daniel.lezcano at free.fr>
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.
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.
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
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <sys/epoll.h>
32 #include <sys/types.h>
34 #include <lxc/lxccontainer.h>
45 #include "start.h" /* for struct lxc_handler */
51 #include <../include/openpty.h>
54 #define LXC_CONSOLE_BUFFER_SIZE 1024
56 lxc_log_define(console
, lxc
);
58 static struct lxc_list lxc_ttys
;
60 typedef void (*sighandler_t
)(int);
62 __attribute__((constructor
)) void lxc_console_init(void)
64 lxc_list_init(&lxc_ttys
);
67 void lxc_console_winsz(int srcfd
, int dstfd
)
75 ret
= ioctl(srcfd
, TIOCGWINSZ
, &wsz
);
77 WARN("Failed to get window size");
81 ret
= ioctl(dstfd
, TIOCSWINSZ
, &wsz
);
83 WARN("Failed to set window size");
85 DEBUG("Set window size to %d columns and %d rows", wsz
.ws_col
,
91 static void lxc_console_winch(struct lxc_tty_state
*ts
)
93 lxc_console_winsz(ts
->stdinfd
, ts
->masterfd
);
96 lxc_cmd_console_winch(ts
->winch_proxy
, ts
->winch_proxy_lxcpath
);
99 void lxc_console_sigwinch(int sig
)
102 struct lxc_tty_state
*ts
;
104 lxc_list_for_each(it
, &lxc_ttys
) {
106 lxc_console_winch(ts
);
110 int lxc_console_cb_signal_fd(int fd
, uint32_t events
, void *cbdata
,
111 struct lxc_epoll_descr
*descr
)
114 struct signalfd_siginfo siginfo
;
115 struct lxc_tty_state
*ts
= cbdata
;
117 ret
= read(fd
, &siginfo
, sizeof(siginfo
));
118 if (ret
< 0 || (size_t)ret
< sizeof(siginfo
)) {
119 ERROR("Failed to read signal info");
123 if (siginfo
.ssi_signo
== SIGTERM
) {
124 DEBUG("Received SIGTERM. Detaching from the console");
125 return LXC_MAINLOOP_CLOSE
;
128 if (siginfo
.ssi_signo
== SIGWINCH
)
129 lxc_console_winch(ts
);
134 struct lxc_tty_state
*lxc_console_signal_init(int srcfd
, int dstfd
)
139 struct lxc_tty_state
*ts
;
141 ts
= malloc(sizeof(*ts
));
145 memset(ts
, 0, sizeof(*ts
));
147 ts
->masterfd
= dstfd
;
152 istty
= isatty(srcfd
) == 1;
154 INFO("fd %d does not refer to a tty device", srcfd
);
156 /* Add tty to list to be scanned at SIGWINCH time. */
157 lxc_list_add_elem(&ts
->node
, ts
);
158 lxc_list_add_tail(&lxc_ttys
, &ts
->node
);
159 sigaddset(&mask
, SIGWINCH
);
162 /* Exit the mainloop cleanly on SIGTERM. */
163 sigaddset(&mask
, SIGTERM
);
165 ret
= sigprocmask(SIG_BLOCK
, &mask
, &ts
->oldmask
);
167 WARN("Failed to block signals");
171 ts
->sigfd
= signalfd(-1, &mask
, 0);
173 WARN("Failed to create signal fd");
174 sigprocmask(SIG_SETMASK
, &ts
->oldmask
, NULL
);
178 DEBUG("Created signal fd %d", ts
->sigfd
);
182 ERROR("Failed to create signal fd");
183 if (ts
->sigfd
>= 0) {
188 lxc_list_del(&ts
->node
);
192 void lxc_console_signal_fini(struct lxc_tty_state
*ts
)
194 if (ts
->sigfd
>= 0) {
197 if (sigprocmask(SIG_SETMASK
, &ts
->oldmask
, NULL
) < 0)
198 WARN("%s - Failed to restore signal mask", strerror(errno
));
201 if (isatty(ts
->stdinfd
))
202 lxc_list_del(&ts
->node
);
207 int lxc_console_cb_con(int fd
, uint32_t events
, void *data
,
208 struct lxc_epoll_descr
*descr
)
210 struct lxc_console
*console
= (struct lxc_console
*)data
;
211 char buf
[LXC_CONSOLE_BUFFER_SIZE
];
212 int r
, w
, w_log
, w_rbuf
;
214 w
= r
= lxc_read_nointr(fd
, buf
, sizeof(buf
));
216 INFO("Console client on fd %d has exited", fd
);
217 lxc_mainloop_del_handler(descr
, fd
);
218 if (fd
== console
->peer
) {
219 if (console
->tty_state
) {
220 lxc_console_signal_fini(console
->tty_state
);
221 console
->tty_state
= NULL
;
228 return LXC_MAINLOOP_CLOSE
;
231 if (fd
== console
->peer
)
232 w
= lxc_write_nointr(console
->master
, buf
, r
);
235 if (fd
== console
->master
) {
236 /* write to peer first */
237 if (console
->peer
>= 0)
238 w
= lxc_write_nointr(console
->peer
, buf
, r
);
240 /* write to console ringbuffer */
241 if (console
->buffer_size
> 0)
242 w_rbuf
= lxc_ringbuf_write(&console
->ringbuf
, buf
, r
);
244 /* write to console log */
245 if (console
->log_fd
>= 0)
246 w_log
= lxc_write_nointr(console
->log_fd
, buf
, r
);
250 WARN("Console short write r:%d != w:%d", r
, w
);
253 TRACE("%s - Failed to write %d bytes to console ringbuffer",
254 strerror(-w_rbuf
), r
);
257 TRACE("Failed to write %d bytes to console log", r
);
262 static int lxc_console_mainloop_add_peer(struct lxc_console
*console
)
266 if (console
->peer
>= 0) {
267 ret
= lxc_mainloop_add_handler(console
->descr
, console
->peer
,
268 lxc_console_cb_con
, console
);
270 WARN("Failed to add console peer handler to mainloop");
275 if (!console
->tty_state
|| console
->tty_state
->sigfd
< 0)
278 ret
= lxc_mainloop_add_handler(console
->descr
, console
->tty_state
->sigfd
,
279 lxc_console_cb_signal_fd
, console
->tty_state
);
281 WARN("Failed to add signal handler to mainloop");
288 extern int lxc_console_mainloop_add(struct lxc_epoll_descr
*descr
,
289 struct lxc_conf
*conf
)
292 struct lxc_console
*console
= &conf
->console
;
294 if (!conf
->rootfs
.path
) {
295 INFO("no rootfs, no console.");
299 if (console
->master
< 0) {
304 if (lxc_mainloop_add_handler(descr
, console
->master
,
305 lxc_console_cb_con
, console
)) {
306 ERROR("failed to add to mainloop console handler for '%d'",
311 /* we cache the descr so that we can add an fd to it when someone
312 * does attach to it in lxc_console_allocate()
314 console
->descr
= descr
;
315 ret
= lxc_console_mainloop_add_peer(console
);
322 int lxc_setup_tios(int fd
, struct termios
*oldtios
)
324 struct termios newtios
;
327 ERROR("'%d' is not a tty", fd
);
331 /* Get current termios */
332 if (tcgetattr(fd
, oldtios
)) {
333 SYSERROR("failed to get current terminal settings");
337 /* ensure we don't end up in an endless loop:
338 * The kernel might fire SIGTTOU while an
339 * ioctl() in tcsetattr() is executed. When the ioctl()
340 * is resumed and retries, the signal handler interrupts it again.
342 signal (SIGTTIN
, SIG_IGN
);
343 signal (SIGTTOU
, SIG_IGN
);
347 /* We use the same settings that ssh does. */
348 newtios
.c_iflag
|= IGNPAR
;
349 newtios
.c_iflag
&= ~(ISTRIP
| INLCR
| IGNCR
| ICRNL
| IXON
| IXANY
| IXOFF
);
351 newtios
.c_iflag
&= ~IUCLC
;
353 newtios
.c_lflag
&= ~(TOSTOP
| ISIG
| ICANON
| ECHO
| ECHOE
| ECHOK
| ECHONL
);
355 newtios
.c_lflag
&= ~IEXTEN
;
357 newtios
.c_oflag
&= ~OPOST
;
358 newtios
.c_cc
[VMIN
] = 1;
359 newtios
.c_cc
[VTIME
] = 0;
361 /* Set new attributes. */
362 if (tcsetattr(fd
, TCSAFLUSH
, &newtios
)) {
363 ERROR("failed to set new terminal settings");
370 static void lxc_console_peer_proxy_free(struct lxc_console
*console
)
372 if (console
->tty_state
) {
373 lxc_console_signal_fini(console
->tty_state
);
374 console
->tty_state
= NULL
;
376 close(console
->peerpty
.master
);
377 close(console
->peerpty
.slave
);
378 console
->peerpty
.master
= -1;
379 console
->peerpty
.slave
= -1;
380 console
->peerpty
.busy
= -1;
381 console
->peerpty
.name
[0] = '\0';
385 static int lxc_console_peer_proxy_alloc(struct lxc_console
*console
, int sockfd
)
387 struct termios oldtermio
;
388 struct lxc_tty_state
*ts
;
391 if (console
->master
< 0) {
392 ERROR("console not set up");
395 if (console
->peerpty
.busy
!= -1 || console
->peer
!= -1) {
396 NOTICE("console already in use");
399 if (console
->tty_state
) {
400 ERROR("console already has tty_state");
404 /* this is the proxy pty that will be given to the client, and that
405 * the real pty master will send to / recv from
408 ret
= openpty(&console
->peerpty
.master
, &console
->peerpty
.slave
,
409 console
->peerpty
.name
, NULL
, NULL
);
412 SYSERROR("failed to create proxy pty");
416 if (lxc_setup_tios(console
->peerpty
.slave
, &oldtermio
) < 0)
419 ts
= lxc_console_signal_init(console
->peerpty
.master
, console
->master
);
423 console
->tty_state
= ts
;
424 console
->peer
= console
->peerpty
.slave
;
425 console
->peerpty
.busy
= sockfd
;
426 ret
= lxc_console_mainloop_add_peer(console
);
430 DEBUG("%d %s peermaster:%d sockfd:%d", lxc_raw_getpid(), __FUNCTION__
, console
->peerpty
.master
, sockfd
);
434 lxc_console_peer_proxy_free(console
);
438 int lxc_console_allocate(struct lxc_conf
*conf
, int sockfd
, int *ttyreq
)
440 int masterfd
= -1, ttynum
;
441 struct lxc_tty_info
*tty_info
= &conf
->tty_info
;
442 struct lxc_console
*console
= &conf
->console
;
445 if (lxc_console_peer_proxy_alloc(console
, sockfd
) < 0)
447 masterfd
= console
->peerpty
.master
;
452 if (*ttyreq
> tty_info
->nbtty
)
455 if (tty_info
->pty_info
[*ttyreq
- 1].busy
)
458 /* the requested tty is available */
463 /* search for next available tty, fixup index tty1 => [0] */
464 for (ttynum
= 1; ttynum
<= tty_info
->nbtty
&& tty_info
->pty_info
[ttynum
- 1].busy
; ttynum
++)
467 /* we didn't find any available slot for tty */
468 if (ttynum
> tty_info
->nbtty
)
474 tty_info
->pty_info
[ttynum
- 1].busy
= sockfd
;
475 masterfd
= tty_info
->pty_info
[ttynum
- 1].master
;
480 void lxc_console_free(struct lxc_conf
*conf
, int fd
)
483 struct lxc_tty_info
*tty_info
= &conf
->tty_info
;
484 struct lxc_console
*console
= &conf
->console
;
486 for (i
= 0; i
< tty_info
->nbtty
; i
++) {
487 if (tty_info
->pty_info
[i
].busy
== fd
)
488 tty_info
->pty_info
[i
].busy
= 0;
491 if (console
->peerpty
.busy
== fd
) {
492 lxc_mainloop_del_handler(console
->descr
, console
->peerpty
.slave
);
493 lxc_console_peer_proxy_free(console
);
497 static int lxc_console_peer_default(struct lxc_console
*console
)
499 struct lxc_tty_state
*ts
;
500 const char *path
= console
->path
;
504 /* If no console was given, try current controlling terminal, there
505 * won't be one if we were started as a daemon (-d).
507 if (!path
&& !access("/dev/tty", F_OK
)) {
508 fd
= open("/dev/tty", O_RDWR
);
517 DEBUG("process does not have a controlling terminal");
521 console
->peer
= lxc_unpriv(open(path
, O_RDWR
| O_CLOEXEC
));
522 if (console
->peer
< 0) {
523 ERROR("Failed to open \"%s\": %s", path
, strerror(errno
));
526 DEBUG("using \"%s\" as peer tty device", path
);
528 if (!isatty(console
->peer
)) {
529 ERROR("file descriptor for file \"%s\" does not refer to a tty device", path
);
533 ts
= lxc_console_signal_init(console
->peer
, console
->master
);
534 console
->tty_state
= ts
;
536 WARN("Failed to install signal handler");
540 lxc_console_winsz(console
->peer
, console
->master
);
542 console
->tios
= malloc(sizeof(*console
->tios
));
543 if (!console
->tios
) {
544 SYSERROR("failed to allocate memory");
548 if (lxc_setup_tios(console
->peer
, console
->tios
) < 0)
555 console
->tios
= NULL
;
558 close(console
->peer
);
566 int lxc_console_write_ringbuffer(struct lxc_console
*console
)
571 struct lxc_ringbuf
*buf
= &console
->ringbuf
;
573 if (!console
->buffer_log_file
)
576 used
= lxc_ringbuf_used(buf
);
580 r_addr
= lxc_ringbuf_get_read_addr(buf
);
581 ret
= lxc_write_nointr(console
->buffer_log_file_fd
, r_addr
, used
);
588 void lxc_console_delete(struct lxc_console
*console
)
592 ret
= lxc_console_write_ringbuffer(console
);
594 WARN("Failed to write console log to disk");
596 if (console
->tios
&& console
->peer
>= 0) {
597 ret
= tcsetattr(console
->peer
, TCSAFLUSH
, console
->tios
);
599 WARN("%s - Failed to set old terminal settings", strerror(errno
));
602 console
->tios
= NULL
;
604 if (console
->peer
>= 0)
605 close(console
->peer
);
608 if (console
->master
>= 0)
609 close(console
->master
);
610 console
->master
= -1;
612 if (console
->slave
>= 0)
613 close(console
->slave
);
616 if (console
->log_fd
>= 0)
617 close(console
->log_fd
);
618 console
->log_fd
= -1;
620 if (console
->buffer_log_file_fd
>= 0)
621 close(console
->buffer_log_file_fd
);
622 console
->buffer_log_file_fd
= -1;
625 /* This is the console ringbuffer log file. Please note that the console
626 * ringbuffer log file is (implementation wise not content wise) independent of
627 * the console log file.
629 static int lxc_console_create_ringbuf_log_file(struct lxc_console
*console
)
631 if (!console
->buffer_log_file
)
634 console
->buffer_log_file_fd
= lxc_unpriv(open(console
->buffer_log_file
,
635 O_CLOEXEC
| O_RDWR
| O_CREAT
| O_TRUNC
, 0600));
636 if (console
->buffer_log_file_fd
< 0) {
637 SYSERROR("Failed to open console ringbuffer log file \"%s\"",
638 console
->buffer_log_file
);
642 DEBUG("Using \"%s\" as console ringbuffer log file", console
->buffer_log_file
);
647 * Note that this function needs to run before the mainloop starts. Since we
648 * register a handler for the console's masterfd when we create the mainloop
649 * the console handler needs to see an allocated ringbuffer.
651 static int lxc_console_create_ringbuf(struct lxc_console
*console
)
654 struct lxc_ringbuf
*buf
= &console
->ringbuf
;
655 uint64_t size
= console
->buffer_size
;
657 /* no ringbuffer previously allocated and no ringbuffer requested */
658 if (!buf
->addr
&& size
<= 0)
661 /* ringbuffer allocated but no new ringbuffer requested */
662 if (buf
->addr
&& size
<= 0) {
663 lxc_ringbuf_release(buf
);
668 TRACE("Deallocated console ringbuffer");
675 /* check wether the requested size for the ringbuffer has changed */
676 if (buf
->addr
&& buf
->size
!= size
) {
677 TRACE("Console ringbuffer size changed from %" PRIu64
678 " to %" PRIu64
" bytes. Deallocating console ringbuffer",
680 lxc_ringbuf_release(buf
);
683 ret
= lxc_ringbuf_create(buf
, size
);
685 ERROR("Failed to setup %" PRIu64
" byte console ringbuffer", size
);
689 TRACE("Allocated %" PRIu64
" byte console ringbuffer", size
);
694 * This is the console log file. Please note that the console log file is
695 * (implementation wise not content wise) independent of the console ringbuffer.
697 int lxc_console_create_log_file(struct lxc_console
*console
)
699 if (!console
->log_path
)
702 console
->log_fd
= lxc_unpriv(open(console
->log_path
, O_CLOEXEC
| O_RDWR
| O_CREAT
| O_APPEND
, 0600));
703 if (console
->log_fd
< 0) {
704 SYSERROR("Failed to open console log file \"%s\"", console
->log_path
);
708 DEBUG("Using \"%s\" as console log file", console
->log_path
);
712 int lxc_pty_create(struct lxc_console
*console
)
714 int ret
, saved_errno
;
717 ret
= openpty(&console
->master
, &console
->slave
, console
->name
, NULL
,
722 ERROR("%s - Failed to allocate a pty", strerror(saved_errno
));
726 ret
= fcntl(console
->master
, F_SETFD
, FD_CLOEXEC
);
728 SYSERROR("Failed to set FD_CLOEXEC flag on console master");
732 ret
= fcntl(console
->slave
, F_SETFD
, FD_CLOEXEC
);
734 SYSERROR("Failed to set FD_CLOEXEC flag on console slave");
738 ret
= lxc_console_peer_default(console
);
740 ERROR("Failed to allocate a peer pty device");
747 lxc_console_delete(console
);
751 int lxc_console_create(struct lxc_conf
*conf
)
754 struct lxc_console
*console
= &conf
->console
;
756 if (!conf
->rootfs
.path
) {
757 INFO("Container does not have a rootfs. The console will be "
758 "shared with the host");
762 if (console
->path
&& !strcmp(console
->path
, "none")) {
763 INFO("No console was requested");
767 ret
= lxc_pty_create(console
);
771 /* create console log file */
772 ret
= lxc_console_create_log_file(console
);
776 /* create console ringbuffer */
777 ret
= lxc_console_create_ringbuf(console
);
781 /* create console ringbuffer log file */
782 ret
= lxc_console_create_ringbuf_log_file(console
);
789 lxc_console_delete(console
);
793 int lxc_console_set_stdfds(int fd
)
798 if (isatty(STDIN_FILENO
))
799 if (dup2(fd
, STDIN_FILENO
) < 0) {
800 SYSERROR("failed to duplicate stdin.");
804 if (isatty(STDOUT_FILENO
))
805 if (dup2(fd
, STDOUT_FILENO
) < 0) {
806 SYSERROR("failed to duplicate stdout.");
810 if (isatty(STDERR_FILENO
))
811 if (dup2(fd
, STDERR_FILENO
) < 0) {
812 SYSERROR("failed to duplicate stderr.");
819 int lxc_console_cb_tty_stdin(int fd
, uint32_t events
, void *cbdata
,
820 struct lxc_epoll_descr
*descr
)
822 struct lxc_tty_state
*ts
= cbdata
;
825 if (fd
!= ts
->stdinfd
)
826 return LXC_MAINLOOP_CLOSE
;
828 if (lxc_read_nointr(ts
->stdinfd
, &c
, 1) <= 0)
829 return LXC_MAINLOOP_CLOSE
;
831 if (ts
->escape
>= 1) {
832 /* we want to exit the console with Ctrl+a q */
833 if (c
== ts
->escape
&& !ts
->saw_escape
) {
838 if (c
== 'q' && ts
->saw_escape
)
839 return LXC_MAINLOOP_CLOSE
;
844 if (lxc_write_nointr(ts
->masterfd
, &c
, 1) <= 0)
845 return LXC_MAINLOOP_CLOSE
;
850 int lxc_console_cb_tty_master(int fd
, uint32_t events
, void *cbdata
,
851 struct lxc_epoll_descr
*descr
)
853 struct lxc_tty_state
*ts
= cbdata
;
854 char buf
[LXC_CONSOLE_BUFFER_SIZE
];
857 if (fd
!= ts
->masterfd
)
858 return LXC_MAINLOOP_CLOSE
;
860 r
= lxc_read_nointr(fd
, buf
, sizeof(buf
));
862 return LXC_MAINLOOP_CLOSE
;
864 w
= lxc_write_nointr(ts
->stdoutfd
, buf
, r
);
866 return LXC_MAINLOOP_CLOSE
;
868 SYSERROR("Failed to write");
875 int lxc_console_getfd(struct lxc_container
*c
, int *ttynum
, int *masterfd
)
877 return lxc_cmd_console(c
->name
, ttynum
, masterfd
, c
->config_path
);
880 int lxc_console(struct lxc_container
*c
, int ttynum
,
881 int stdinfd
, int stdoutfd
, int stderrfd
,
884 int ret
, ttyfd
, masterfd
;
885 struct lxc_epoll_descr descr
;
886 struct termios oldtios
;
887 struct lxc_tty_state
*ts
;
890 ttyfd
= lxc_cmd_console(c
->name
, &ttynum
, &masterfd
, c
->config_path
);
896 TRACE("Process is already group leader");
898 ts
= lxc_console_signal_init(stdinfd
, masterfd
);
904 ts
->winch_proxy
= c
->name
;
905 ts
->winch_proxy_lxcpath
= c
->config_path
;
906 ts
->stdoutfd
= stdoutfd
;
908 istty
= isatty(stdinfd
);
910 lxc_console_winsz(stdinfd
, masterfd
);
911 lxc_cmd_console_winch(ts
->winch_proxy
, ts
->winch_proxy_lxcpath
);
913 INFO("File descriptor %d does not refer to a tty device", stdinfd
);
916 ret
= lxc_mainloop_open(&descr
);
918 ERROR("Failed to create mainloop");
922 if (ts
->sigfd
!= -1) {
923 ret
= lxc_mainloop_add_handler(&descr
, ts
->sigfd
,
924 lxc_console_cb_signal_fd
, ts
);
926 ERROR("Failed to add signal handler to mainloop");
931 ret
= lxc_mainloop_add_handler(&descr
, ts
->stdinfd
,
932 lxc_console_cb_tty_stdin
, ts
);
934 ERROR("Failed to add stdin handler");
938 ret
= lxc_mainloop_add_handler(&descr
, ts
->masterfd
,
939 lxc_console_cb_tty_master
, ts
);
941 ERROR("Failed to add master handler");
945 if (ts
->escape
>= 1) {
948 "Connected to tty %1$d\n"
949 "Type <Ctrl+%2$c q> to exit the console, "
950 "<Ctrl+%2$c Ctrl+%2$c> to enter Ctrl+%2$c itself\n",
951 ttynum
, 'a' + escape
- 1);
955 ret
= lxc_setup_tios(stdinfd
, &oldtios
);
960 ret
= lxc_mainloop(&descr
, -1);
962 ERROR("The mainloop returned an error");
970 istty
= tcsetattr(stdinfd
, TCSAFLUSH
, &oldtios
);
972 WARN("%s - Failed to restore terminal properties",
977 lxc_mainloop_close(&descr
);
980 lxc_console_signal_fini(ts
);