]>
Commit | Line | Data |
---|---|---|
b0a33c1e | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
7 | * Daniel Lezcano <dlezcano at fr.ibm.com> | |
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 | |
21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
22 | */ | |
23 | ||
24 | #include <stdio.h> | |
e0dc0de7 | 25 | #include <stdlib.h> |
b0a33c1e | 26 | #include <unistd.h> |
63376d7d | 27 | #include <fcntl.h> |
b0a33c1e | 28 | #include <errno.h> |
63376d7d | 29 | #include <pty.h> |
b0a33c1e | 30 | #include <sys/types.h> |
31 | #include <sys/un.h> | |
32 | ||
00b3c2e2 CLG |
33 | #include <lxc/log.h> |
34 | #include <lxc/conf.h> | |
35 | #include <lxc/start.h> /* for struct lxc_handler */ | |
36 | ||
96fa1ff0 | 37 | #include "commands.h" |
63376d7d | 38 | #include "mainloop.h" |
724e753c | 39 | #include "af_unix.h" |
36eb9bde CLG |
40 | |
41 | lxc_log_define(lxc_console, lxc); | |
42 | ||
96fa1ff0 MN |
43 | extern int lxc_console(const char *name, int ttynum, int *fd) |
44 | { | |
d97b36f8 | 45 | int ret, stopped = 0; |
96fa1ff0 MN |
46 | struct lxc_command command = { |
47 | .request = { .type = LXC_COMMAND_TTY, .data = ttynum }, | |
48 | }; | |
49 | ||
d97b36f8 DL |
50 | ret = lxc_command(name, &command, &stopped); |
51 | if (ret < 0 && stopped) { | |
52 | ERROR("'%s' is stopped", name); | |
53 | return -1; | |
54 | } | |
55 | ||
96fa1ff0 MN |
56 | if (ret < 0) { |
57 | ERROR("failed to send command"); | |
58 | return -1; | |
59 | } | |
60 | ||
61 | if (!ret) { | |
62 | ERROR("console denied by '%s'", name); | |
63 | return -1; | |
64 | } | |
65 | ||
3cc5de36 MN |
66 | if (command.answer.ret) { |
67 | ERROR("console access denied: %s", | |
68 | strerror(-command.answer.ret)); | |
69 | return -1; | |
70 | } | |
71 | ||
96fa1ff0 MN |
72 | *fd = command.answer.fd; |
73 | if (*fd <0) { | |
74 | ERROR("unable to allocate fd for tty %d", ttynum); | |
75 | return -1; | |
76 | } | |
77 | ||
78 | INFO("tty %d allocated", ttynum); | |
79 | return 0; | |
80 | } | |
724e753c MN |
81 | |
82 | /*---------------------------------------------------------------------------- | |
83 | * functions used by lxc-start mainloop | |
84 | * to handle above command request. | |
85 | *--------------------------------------------------------------------------*/ | |
86 | extern void lxc_console_remove_fd(int fd, struct lxc_tty_info *tty_info) | |
87 | { | |
88 | int i; | |
89 | ||
90 | for (i = 0; i < tty_info->nbtty; i++) { | |
91 | ||
92 | if (tty_info->pty_info[i].busy != fd) | |
93 | continue; | |
94 | ||
95 | tty_info->pty_info[i].busy = 0; | |
96 | } | |
97 | ||
98 | return; | |
99 | } | |
100 | ||
101 | extern int lxc_console_callback(int fd, struct lxc_request *request, | |
63376d7d | 102 | struct lxc_handler *handler) |
724e753c MN |
103 | { |
104 | int ttynum = request->data; | |
fae349da | 105 | struct lxc_tty_info *tty_info = &handler->conf->tty_info; |
724e753c MN |
106 | |
107 | if (ttynum > 0) { | |
108 | if (ttynum > tty_info->nbtty) | |
109 | goto out_close; | |
110 | ||
111 | if (tty_info->pty_info[ttynum - 1].busy) | |
112 | goto out_close; | |
113 | ||
114 | goto out_send; | |
115 | } | |
116 | ||
117 | /* fixup index tty1 => [0] */ | |
118 | for (ttynum = 1; | |
119 | ttynum <= tty_info->nbtty && tty_info->pty_info[ttynum - 1].busy; | |
120 | ttynum++); | |
121 | ||
122 | /* we didn't find any available slot for tty */ | |
123 | if (ttynum > tty_info->nbtty) | |
124 | goto out_close; | |
125 | ||
126 | out_send: | |
127 | if (lxc_af_unix_send_fd(fd, tty_info->pty_info[ttynum - 1].master, | |
128 | &ttynum, sizeof(ttynum)) < 0) { | |
129 | ERROR("failed to send tty to client"); | |
130 | goto out_close; | |
131 | } | |
132 | ||
133 | tty_info->pty_info[ttynum - 1].busy = fd; | |
134 | ||
135 | return 0; | |
136 | ||
137 | out_close: | |
138 | /* the close fd and related cleanup will be done by caller */ | |
139 | return 1; | |
140 | } | |
141 | ||
1560f6c9 | 142 | int lxc_create_console(struct lxc_conf *conf) |
63376d7d | 143 | { |
e0dc0de7 | 144 | struct termios tios; |
1560f6c9 DL |
145 | struct lxc_console *console = &conf->console; |
146 | ||
147 | if (!conf->rootfs) | |
148 | return 0; | |
e0dc0de7 | 149 | |
63376d7d DL |
150 | if (openpty(&console->master, &console->slave, |
151 | console->name, NULL, NULL)) { | |
152 | SYSERROR("failed to allocate a pty"); | |
153 | return -1; | |
154 | } | |
155 | ||
156 | if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) { | |
157 | SYSERROR("failed to set console master to close-on-exec"); | |
158 | goto err; | |
159 | } | |
160 | ||
161 | if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) { | |
162 | SYSERROR("failed to set console slave to close-on-exec"); | |
163 | goto err; | |
164 | } | |
165 | ||
e0dc0de7 DL |
166 | if (!isatty(console->peer)) |
167 | return 0; | |
168 | ||
169 | console->tios = malloc(sizeof(tios)); | |
170 | if (!console->tios) { | |
171 | SYSERROR("failed to allocate memory"); | |
172 | goto err; | |
173 | } | |
174 | ||
175 | /* Get termios */ | |
176 | if (tcgetattr(console->peer, console->tios)) { | |
177 | SYSERROR("failed to get current terminal settings"); | |
178 | goto err_free; | |
179 | } | |
180 | ||
181 | tios = *console->tios; | |
182 | ||
183 | /* Remove the echo characters and signal reception, the echo | |
184 | * will be done below with master proxying */ | |
185 | tios.c_iflag &= ~IGNBRK; | |
186 | tios.c_iflag &= BRKINT; | |
187 | tios.c_lflag &= ~(ECHO|ICANON|ISIG); | |
188 | tios.c_cc[VMIN] = 1; | |
189 | tios.c_cc[VTIME] = 0; | |
190 | ||
191 | /* Set new attributes */ | |
192 | if (tcsetattr(console->peer, TCSAFLUSH, &tios)) { | |
193 | ERROR("failed to set new terminal settings"); | |
194 | goto err_free; | |
195 | } | |
196 | ||
63376d7d | 197 | return 0; |
e0dc0de7 DL |
198 | |
199 | err_free: | |
200 | free(console->tios); | |
63376d7d DL |
201 | err: |
202 | close(console->master); | |
203 | close(console->slave); | |
204 | return -1; | |
205 | } | |
206 | ||
207 | void lxc_delete_console(const struct lxc_console *console) | |
208 | { | |
e0dc0de7 DL |
209 | if (console->tios && |
210 | tcsetattr(console->peer, TCSAFLUSH, console->tios)) | |
211 | WARN("failed to set old terminal settings"); | |
63376d7d DL |
212 | close(console->master); |
213 | close(console->slave); | |
214 | } | |
215 | ||
216 | static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr) | |
217 | { | |
218 | struct lxc_console *console = (struct lxc_console *)data; | |
219 | char buf[1024]; | |
220 | int r; | |
221 | ||
222 | r = read(fd, buf, sizeof(buf)); | |
223 | if (r < 0) { | |
224 | SYSERROR("failed to read"); | |
225 | return 1; | |
226 | } | |
227 | ||
228 | /* no output for the console, do nothing */ | |
229 | if (console->peer == -1) | |
230 | return 0; | |
231 | ||
232 | if (console->peer == fd) | |
233 | write(console->master, buf, r); | |
234 | else | |
235 | write(console->peer, buf, r); | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | int lxc_console_mainloop_add(struct lxc_epoll_descr *descr, | |
241 | struct lxc_handler *handler) | |
242 | { | |
243 | struct lxc_conf *conf = handler->conf; | |
244 | struct lxc_console *console = &conf->console; | |
245 | ||
1560f6c9 DL |
246 | if (!conf->rootfs) { |
247 | INFO("no rootfs, no console."); | |
248 | return 0; | |
249 | } | |
250 | ||
63376d7d DL |
251 | if (lxc_mainloop_add_handler(descr, console->master, |
252 | console_handler, console)) { | |
253 | ERROR("failed to add to mainloop console handler for '%d'", | |
254 | console->master); | |
255 | return -1; | |
256 | } | |
257 | ||
258 | if (console->peer != -1 && | |
259 | lxc_mainloop_add_handler(descr, console->peer, | |
260 | console_handler, console)) | |
261 | WARN("console input disabled"); | |
262 | ||
263 | return 0; | |
264 | } |