]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/console.c
avoid compile warning in src/lxc/console.c
[mirror_lxc.git] / src / lxc / console.c
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>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <pty.h>
30 #include <sys/types.h>
31 #include <sys/un.h>
32
33 #include "log.h"
34 #include "conf.h"
35 #include "start.h" /* for struct lxc_handler */
36 #include "caps.h"
37 #include "commands.h"
38 #include "mainloop.h"
39 #include "af_unix.h"
40
41 lxc_log_define(lxc_console, lxc);
42
43 extern int lxc_console(const char *name, int ttynum, int *fd)
44 {
45 int ret, stopped = 0;
46 struct lxc_command command = {
47 .request = { .type = LXC_COMMAND_TTY, .data = ttynum },
48 };
49
50 ret = lxc_command(name, &command, &stopped);
51 if (ret < 0 && stopped) {
52 ERROR("'%s' is stopped", name);
53 return -1;
54 }
55
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
66 if (command.answer.ret) {
67 ERROR("console access denied: %s",
68 strerror(-command.answer.ret));
69 return -1;
70 }
71
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 }
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,
102 struct lxc_handler *handler)
103 {
104 int ttynum = request->data;
105 struct lxc_tty_info *tty_info = &handler->conf->tty_info;
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
142 static int get_default_console(char **console)
143 {
144 int fd;
145
146 if (!access("/dev/tty", F_OK)) {
147 fd = open("/dev/tty", O_RDWR);
148 if (fd > 0) {
149 close(fd);
150 *console = strdup("/dev/tty");
151 goto out;
152 }
153 }
154
155 if (!access("/dev/null", F_OK)) {
156 *console = strdup("/dev/null");
157 goto out;
158 }
159
160 ERROR("No suitable default console");
161 out:
162 return *console ? 0 : -1;
163 }
164
165 int lxc_create_console(struct lxc_conf *conf)
166 {
167 struct termios tios;
168 struct lxc_console *console = &conf->console;
169 int fd;
170
171 if (!conf->rootfs.path)
172 return 0;
173
174 if (!console->path && get_default_console(&console->path)) {
175 ERROR("failed to get default console");
176 return -1;
177 }
178
179 if (openpty(&console->master, &console->slave,
180 console->name, NULL, NULL)) {
181 SYSERROR("failed to allocate a pty");
182 return -1;
183 }
184
185 if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) {
186 SYSERROR("failed to set console master to close-on-exec");
187 goto err;
188 }
189
190 if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) {
191 SYSERROR("failed to set console slave to close-on-exec");
192 goto err;
193 }
194
195 fd = lxc_unpriv(open(console->path, O_CLOEXEC | O_RDWR | O_CREAT |
196 O_APPEND, 0600));
197 if (fd < 0) {
198 SYSERROR("failed to open '%s'", console->path);
199 goto err;
200 }
201
202 DEBUG("using '%s' as console", console->path);
203
204 console->peer = fd;
205
206 if (!isatty(console->peer))
207 return 0;
208
209 console->tios = malloc(sizeof(tios));
210 if (!console->tios) {
211 SYSERROR("failed to allocate memory");
212 goto err;
213 }
214
215 /* Get termios */
216 if (tcgetattr(console->peer, console->tios)) {
217 SYSERROR("failed to get current terminal settings");
218 goto err_free;
219 }
220
221 tios = *console->tios;
222
223 /* Remove the echo characters and signal reception, the echo
224 * will be done below with master proxying */
225 tios.c_iflag &= ~IGNBRK;
226 tios.c_iflag &= BRKINT;
227 tios.c_lflag &= ~(ECHO|ICANON|ISIG);
228 tios.c_cc[VMIN] = 1;
229 tios.c_cc[VTIME] = 0;
230
231 /* Set new attributes */
232 if (tcsetattr(console->peer, TCSAFLUSH, &tios)) {
233 ERROR("failed to set new terminal settings");
234 goto err_free;
235 }
236
237 return 0;
238
239 err_free:
240 free(console->tios);
241 err:
242 close(console->master);
243 close(console->slave);
244 return -1;
245 }
246
247 void lxc_delete_console(const struct lxc_console *console)
248 {
249 if (console->tios &&
250 tcsetattr(console->peer, TCSAFLUSH, console->tios))
251 WARN("failed to set old terminal settings");
252 close(console->master);
253 close(console->slave);
254 }
255
256 static int console_handler(int fd, void *data, struct lxc_epoll_descr *descr)
257 {
258 struct lxc_console *console = (struct lxc_console *)data;
259 char buf[1024];
260 int r;
261
262 r = read(fd, buf, sizeof(buf));
263 if (r < 0) {
264 SYSERROR("failed to read");
265 return 1;
266 }
267
268 if (!r) {
269 INFO("console client has exited");
270 lxc_mainloop_del_handler(descr, fd);
271 close(fd);
272 return 0;
273 }
274
275 /* no output for the console, do nothing */
276 if (console->peer == -1)
277 return 0;
278
279 if (console->peer == fd)
280 r = write(console->master, buf, r);
281 else
282 r = write(console->peer, buf, r);
283
284 return 0;
285 }
286
287 int lxc_console_mainloop_add(struct lxc_epoll_descr *descr,
288 struct lxc_handler *handler)
289 {
290 struct lxc_conf *conf = handler->conf;
291 struct lxc_console *console = &conf->console;
292
293 if (!conf->rootfs.path) {
294 INFO("no rootfs, no console.");
295 return 0;
296 }
297
298 if (!console->path) {
299 INFO("no console specified");
300 return 0;
301 }
302
303 if (lxc_mainloop_add_handler(descr, console->master,
304 console_handler, console)) {
305 ERROR("failed to add to mainloop console handler for '%d'",
306 console->master);
307 return -1;
308 }
309
310 if (console->peer != -1 &&
311 lxc_mainloop_add_handler(descr, console->peer,
312 console_handler, console))
313 WARN("console input disabled");
314
315 return 0;
316 }