]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_attach.c
console: add lxc_pty_map_ids()
[mirror_lxc.git] / src / lxc / tools / lxc_attach.c
CommitLineData
81c75799
DL
1/*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2010
5 *
6 * Authors:
9afe19d6 7 * Daniel Lezcano <daniel.lezcano at free.fr>
81c75799
DL
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
81c75799
DL
22 */
23
c87524b7
CB
24#include "config.h"
25
5eacdc3d
CB
26#include <errno.h>
27#include <fcntl.h>
28#include <stdio.h>
3d5e9f48 29#include <stdlib.h>
5eacdc3d
CB
30#include <sys/ioctl.h>
31#include <sys/types.h>
32#include <sys/wait.h>
33#include <termios.h>
34#include <unistd.h>
35
36#include <lxc/lxccontainer.h>
7adff31c 37
99d50954 38#include "attach.h"
81c75799 39#include "arguments.h"
5eacdc3d 40#include "caps.h"
5e5129d7 41#include "conf.h"
cb014488 42#include "confile.h"
5eacdc3d 43#include "console.h"
9c4693b8 44#include "log.h"
5eacdc3d
CB
45#include "list.h"
46#include "mainloop.h"
9c4693b8 47#include "utils.h"
6ff05e18 48
5eacdc3d
CB
49#if HAVE_PTY_H
50#include <pty.h>
51#else
52#include <../include/openpty.h>
53#endif
54
81c75799 55static const struct option my_longopts[] = {
4d69b293 56 {"elevated-privileges", optional_argument, 0, 'e'},
cb014488 57 {"arch", required_argument, 0, 'a'},
e13eeea2 58 {"namespaces", required_argument, 0, 's'},
7a0b0b56 59 {"remount-sys-proc", no_argument, 0, 'R'},
799f96fd
CS
60 /* TODO: decide upon short option names */
61 {"clear-env", no_argument, 0, 500},
62 {"keep-env", no_argument, 0, 501},
3d5e9f48
CS
63 {"keep-var", required_argument, 0, 502},
64 {"set-var", required_argument, 0, 'v'},
da41561c 65 {"pty-log", required_argument, 0, 'L'},
a7ae6ce4 66 {"rcfile", required_argument, 0, 'f'},
81c75799
DL
67 LXC_COMMON_OPTIONS
68};
69
cb014488
CS
70static int elevated_privileges = 0;
71static signed long new_personality = -1;
fc763ab7 72static int namespace_flags = -1;
7a0b0b56 73static int remount_sys_proc = 0;
799f96fd 74static lxc_attach_env_policy_t env_policy = LXC_ATTACH_KEEP_ENV;
3d5e9f48
CS
75static char **extra_env = NULL;
76static ssize_t extra_env_size = 0;
77static char **extra_keep = NULL;
78static ssize_t extra_keep_size = 0;
79
80static int add_to_simple_array(char ***array, ssize_t *capacity, char *value)
81{
82 ssize_t count = 0;
83
97bc2422
CB
84 if (!array)
85 return -1;
10f98e99 86
3d5e9f48
CS
87 if (*array)
88 for (; (*array)[count]; count++);
89
90 /* we have to reallocate */
91 if (count >= *capacity - 1) {
92 ssize_t new_capacity = ((count + 1) / 32 + 1) * 32;
93 char **new_array = realloc((void*)*array, sizeof(char *) * new_capacity);
94 if (!new_array)
95 return -1;
96 memset(&new_array[count], 0, sizeof(char*)*(new_capacity - count));
97 *array = new_array;
98 *capacity = new_capacity;
99 }
100
97bc2422
CB
101 if (!(*array))
102 return -1;
10f98e99
SG
103
104 (*array)[count] = value;
3d5e9f48
CS
105 return 0;
106}
cb014488
CS
107
108static int my_parser(struct lxc_arguments* args, int c, char* arg)
109{
9420e0c2
CB
110 char **it;
111 char *del;
e13eeea2
CS
112 int ret;
113
cb014488 114 switch (c) {
4d69b293
NK
115 case 'e':
116 ret = lxc_fill_elevated_privileges(arg, &elevated_privileges);
117 if (ret)
118 return -1;
119 break;
7a0b0b56 120 case 'R': remount_sys_proc = 1; break;
cb014488
CS
121 case 'a':
122 new_personality = lxc_config_parse_arch(arg);
123 if (new_personality < 0) {
124 lxc_error(args, "invalid architecture specified: %s", arg);
125 return -1;
126 }
127 break;
e13eeea2
CS
128 case 's':
129 namespace_flags = 0;
9420e0c2
CB
130
131 /* The identifiers for namespaces used with lxc-attach as given
132 * on the manpage do not align with the standard identifiers.
133 * This affects network, mount, and uts namespaces. The standard
134 * identifiers are: "mnt", "uts", and "net" whereas lxc-attach
135 * uses "MOUNT", "UTSNAME", and "NETWORK". So let's use some
136 * cheap memmove()s to replace them by their standard
137 * identifiers. Let's illustrate this with an example:
138 * Assume the string:
139 *
140 * "IPC|MOUNT|PID"
141 *
142 * then we memmove()
143 *
281f36af 144 * dest: del + 1 == OUNT|PID
9420e0c2
CB
145 * src: del + 3 == NT|PID
146 */
147 while ((del = strstr(arg, "MOUNT")))
148 memmove(del + 1, del + 3, strlen(del) - 2);
149
150 for (it = (char *[]){"NETWORK", "UTSNAME", NULL}; it && *it; it++)
151 while ((del = strstr(arg, *it)))
152 memmove(del + 3, del + 7, strlen(del) - 6);
153
e13eeea2
CS
154 ret = lxc_fill_namespace_flags(arg, &namespace_flags);
155 if (ret)
156 return -1;
157 /* -s implies -e */
4d69b293 158 lxc_fill_elevated_privileges(NULL, &elevated_privileges);
e13eeea2 159 break;
d028235d
SG
160 case 500: /* clear-env */
161 env_policy = LXC_ATTACH_CLEAR_ENV;
162 break;
163 case 501: /* keep-env */
164 env_policy = LXC_ATTACH_KEEP_ENV;
165 break;
3d5e9f48
CS
166 case 502: /* keep-var */
167 ret = add_to_simple_array(&extra_keep, &extra_keep_size, arg);
168 if (ret < 0) {
169 lxc_error(args, "memory allocation error");
170 return -1;
171 }
172 break;
173 case 'v':
174 ret = add_to_simple_array(&extra_env, &extra_env_size, arg);
175 if (ret < 0) {
176 lxc_error(args, "memory allocation error");
177 return -1;
178 }
179 break;
da41561c
CB
180 case 'L':
181 args->console_log = arg;
182 break;
2c34c8f2
CB
183 case 'f':
184 args->rcfile = arg;
185 break;
cb014488
CS
186 }
187
188 return 0;
189}
190
81c75799
DL
191static struct lxc_arguments my_args = {
192 .progname = "lxc-attach",
193 .help = "\
03027ad9 194--name=NAME [-- COMMAND]\n\
81c75799 195\n\
03027ad9 196Execute the specified COMMAND - enter the container NAME\n\
81c75799
DL
197\n\
198Options :\n\
5e8757ed 199 -n, --name=NAME NAME of the container\n\
4d69b293
NK
200 -e, --elevated-privileges=PRIVILEGES\n\
201 Use elevated privileges instead of those of the\n\
202 container. If you don't specify privileges to be\n\
203 elevated as OR'd list: CAP, CGROUP and LSM (capabilities,\n\
204 cgroup and restrictions, respectively) then all of them\n\
205 will be elevated.\n\
157aa271 206 WARNING: This may leak privileges into the container.\n\
cb014488
CS
207 Use with care.\n\
208 -a, --arch=ARCH Use ARCH for program instead of container's own\n\
e13eeea2
CS
209 architecture.\n\
210 -s, --namespaces=FLAGS\n\
211 Don't attach to all the namespaces of the container\n\
212 but just to the following OR'd list of flags:\n\
4d69b293
NK
213 MOUNT, PID, UTSNAME, IPC, USER or NETWORK.\n\
214 WARNING: Using -s implies -e with all privileges\n\
215 elevated, it may therefore leak privileges into the\n\
216 container. Use with care.\n\
7a0b0b56
CS
217 -R, --remount-sys-proc\n\
218 Remount /sys and /proc if not attaching to the\n\
219 mount namespace when using -s in order to properly\n\
220 reflect the correct namespace context. See the\n\
799f96fd 221 lxc-attach(1) manual page for details.\n\
3d5e9f48 222 --clear-env Clear all environment variables before attaching.\n\
799f96fd
CS
223 The attached shell/program will start with only\n\
224 container=lxc set.\n\
7be2c5ef 225 --keep-env Keep all current environment variables. This\n\
799f96fd 226 is the current default behaviour, but is likely to\n\
3d5e9f48 227 change in the future.\n\
da41561c 228 -L, --pty-log=FILE\n\
b609774d 229 Log pty output to FILE\n\
3d5e9f48
CS
230 -v, --set-var Set an additional variable that is seen by the\n\
231 attached program in the container. May be specified\n\
232 multiple times.\n\
233 --keep-var Keep an additional environment variable. Only\n\
234 applicable if --clear-env is specified. May be used\n\
a7ae6ce4
WB
235 multiple times.\n\
236 -f, --rcfile=FILE\n\
237 Load configuration file FILE\n\
238",
81c75799 239 .options = my_longopts,
cb014488 240 .parser = my_parser,
81c75799
DL
241 .checker = NULL,
242};
243
5eacdc3d
CB
244struct wrapargs {
245 lxc_attach_options_t *options;
246 lxc_attach_command_t *command;
5eacdc3d
CB
247 struct lxc_console *console;
248 int ptyfd;
249};
250
9bd91876
CB
251/* Minimalistic login_tty() implementation. */
252static int login_pty(int fd)
253{
254 setsid();
255 if (ioctl(fd, TIOCSCTTY, NULL) < 0)
256 return -1;
257 if (lxc_console_set_stdfds(fd) < 0)
258 return -1;
259 if (fd > STDERR_FILENO)
260 close(fd);
261 return 0;
262}
263
478dda76 264static int get_pty_on_host_callback(void *p)
5eacdc3d
CB
265{
266 struct wrapargs *wrap = p;
267
268 close(wrap->console->master);
9bd91876 269 if (login_pty(wrap->console->slave) < 0)
5eacdc3d
CB
270 return -1;
271
272 if (wrap->command->program)
273 lxc_attach_run_command(wrap->command);
274 else
275 lxc_attach_run_shell(NULL);
5eacdc3d
CB
276 return -1;
277}
278
478dda76 279static int get_pty_on_host(struct lxc_container *c, struct wrapargs *wrap, int *pid)
5eacdc3d 280{
5eacdc3d
CB
281 struct lxc_epoll_descr descr;
282 struct lxc_conf *conf;
283 struct lxc_tty_state *ts;
5e5129d7
CB
284 int ret = -1;
285 struct wrapargs *args = wrap;
5eacdc3d 286
5eacdc3d 287 if (!isatty(args->ptyfd)) {
f0ccfa13 288 fprintf(stderr, "Standard file descriptor does not refer to a pty\n");
5eacdc3d
CB
289 return -1;
290 }
291
5e5129d7
CB
292 if (c->lxc_conf) {
293 conf = c->lxc_conf;
294 } else {
295 /* If the container is not defined and the user didn't specify a
296 * config file to load we will simply init a dummy config here.
297 */
298 conf = lxc_conf_init();
299 if (!conf) {
f0ccfa13 300 fprintf(stderr, "Failed to allocate dummy config file for the container\n");
5e5129d7
CB
301 return -1;
302 }
303
304 /* We also need a dummy rootfs path otherwise
305 * lxc_console_create() will not let us create a console. Note,
306 * I don't want this change to make it into
307 * lxc_console_create()'s since this function will only be
308 * responsible for proper /dev/{console,tty<n>} devices.
309 * lxc-attach is just abusing it to also handle the pty case
310 * because it is very similar. However, with LXC 3.0 lxc-attach
311 * will need to move away from using lxc_console_create() since
312 * this is actually an internal symbol and we only want the
313 * tools to use the API with LXC 3.0.
314 */
315 conf->rootfs.path = strdup("dummy");
316 if (!conf->rootfs.path)
317 return -1;
318 }
a9d02bb9 319 free(conf->console.log_path);
da41561c
CB
320 if (my_args.console_log)
321 conf->console.log_path = strdup(my_args.console_log);
322 else
323 conf->console.log_path = NULL;
a9d02bb9
CB
324
325 /* In the case of lxc-attach our peer pty will always be the current
326 * controlling terminal. We clear whatever was set by the user for
6f18b9c4
CB
327 * lxc.console.path here and set it NULL. lxc_console_peer_default()
328 * will then try to open /dev/tty. If the process doesn't have a
329 * controlling terminal we should still proceed.
330 */
a9d02bb9 331 free(conf->console.path);
6f18b9c4 332 conf->console.path = NULL;
a9d02bb9 333
5eacdc3d
CB
334 /* Create pty on the host. */
335 if (lxc_console_create(conf) < 0)
336 return -1;
337 ts = conf->console.tty_state;
a9d02bb9 338 conf->console.descr = &descr;
5eacdc3d
CB
339
340 /* Shift ttys to container. */
7cfeddd7
CB
341 ret = lxc_pty_map_ids(conf, &conf->console);
342 if (ret < 0) {
f0ccfa13 343 fprintf(stderr, "Failed to shift tty into container\n");
5eacdc3d
CB
344 goto err1;
345 }
346
347 /* Send wrapper function on its way. */
348 wrap->console = &conf->console;
478dda76 349 if (c->attach(c, get_pty_on_host_callback, wrap, wrap->options, pid) < 0)
5eacdc3d
CB
350 goto err1;
351 close(conf->console.slave); /* Close slave side. */
bc9724f7 352 conf->console.slave = -1;
5eacdc3d
CB
353
354 ret = lxc_mainloop_open(&descr);
355 if (ret) {
f0ccfa13 356 fprintf(stderr, "failed to create mainloop\n");
5eacdc3d
CB
357 goto err2;
358 }
359
30a33fbd 360 if (lxc_console_mainloop_add(&descr, &conf->console) < 0) {
f0ccfa13 361 fprintf(stderr, "Failed to add handlers to lxc mainloop.\n");
5eacdc3d 362 goto err3;
3e6580ec 363 }
5eacdc3d
CB
364
365 ret = lxc_mainloop(&descr, -1);
366 if (ret) {
f0ccfa13 367 fprintf(stderr, "mainloop returned an error\n");
5eacdc3d
CB
368 goto err3;
369 }
370 ret = 0;
371
372err3:
373 lxc_mainloop_close(&descr);
374err2:
b9a24c4f 375 if (ts && ts->sigfd != -1)
0519b5cc 376 lxc_console_signal_fini(ts);
5eacdc3d
CB
377err1:
378 lxc_console_delete(&conf->console);
379
380 return ret;
381}
382
c87524b7
CB
383static int stdfd_is_pty(void)
384{
385 if (isatty(STDIN_FILENO))
386 return STDIN_FILENO;
387 if (isatty(STDOUT_FILENO))
388 return STDOUT_FILENO;
389 if (isatty(STDERR_FILENO))
390 return STDERR_FILENO;
391
392 return -1;
393}
394
5eacdc3d
CB
395int main(int argc, char *argv[])
396{
c87524b7 397 int ret = -1, r;
478dda76 398 int wexit = 0;
73b910a3 399 struct lxc_log log;
9c4693b8
CS
400 pid_t pid;
401 lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
5eacdc3d 402 lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL};
81c75799 403
c87524b7
CB
404 r = lxc_caps_init();
405 if (r)
5eacdc3d 406 exit(EXIT_FAILURE);
c7029344 407
c87524b7
CB
408 r = lxc_arguments_parse(&my_args, argc, argv);
409 if (r)
5eacdc3d 410 exit(EXIT_FAILURE);
81c75799 411
f5abd74d
SG
412 if (!my_args.log_file)
413 my_args.log_file = "none";
414
73b910a3 415 log.name = my_args.name;
416 log.file = my_args.log_file;
4b73005c 417 log.level = my_args.log_priority;
73b910a3 418 log.prefix = my_args.progname;
419 log.quiet = my_args.quiet;
420 log.lxcpath = my_args.lxcpath[0];
421 r = lxc_log_init(&log);
c87524b7 422 if (r)
5eacdc3d 423 exit(EXIT_FAILURE);
6edbfc86 424 lxc_log_options_no_override();
81c75799 425
5eacdc3d 426 if (geteuid()) {
37180208 427 if (access(my_args.lxcpath[0], O_RDONLY) < 0) {
5eacdc3d
CB
428 if (!my_args.quiet)
429 fprintf(stderr, "You lack access to %s\n", my_args.lxcpath[0]);
9bd91876 430 exit(EXIT_FAILURE);
5eacdc3d
CB
431 }
432 }
433
70952c01
CB
434 /* REMOVE IN LXC 3.0 */
435 setenv("LXC_UPDATE_CONFIG_FORMAT", "1", 0);
436
5eacdc3d
CB
437 struct lxc_container *c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
438 if (!c)
439 exit(EXIT_FAILURE);
440
a7ae6ce4
WB
441 if (my_args.rcfile) {
442 c->clear_config(c);
443 if (!c->load_config(c, my_args.rcfile)) {
f0ccfa13 444 fprintf(stderr, "Failed to load rcfile\n");
a7ae6ce4
WB
445 lxc_container_put(c);
446 exit(EXIT_FAILURE);
447 }
6118210e
WB
448 c->configfile = strdup(my_args.rcfile);
449 if (!c->configfile) {
f0ccfa13 450 fprintf(stderr, "Out of memory setting new config filename\n");
6118210e
WB
451 lxc_container_put(c);
452 exit(EXIT_FAILURE);
453 }
a7ae6ce4
WB
454 }
455
5eacdc3d
CB
456 if (!c->may_control(c)) {
457 fprintf(stderr, "Insufficent privileges to control %s\n", c->name);
458 lxc_container_put(c);
459 exit(EXIT_FAILURE);
460 }
461
9c4693b8
CS
462 if (remount_sys_proc)
463 attach_options.attach_flags |= LXC_ATTACH_REMOUNT_PROC_SYS;
464 if (elevated_privileges)
4d69b293 465 attach_options.attach_flags &= ~(elevated_privileges);
9c4693b8
CS
466 attach_options.namespaces = namespace_flags;
467 attach_options.personality = new_personality;
468 attach_options.env_policy = env_policy;
3d5e9f48
CS
469 attach_options.extra_env_vars = extra_env;
470 attach_options.extra_keep_env = extra_keep;
81c75799 471
5eacdc3d 472 if (my_args.argc > 0) {
a9d02bb9
CB
473 command.program = my_args.argv[0];
474 command.argv = (char**)my_args.argv;
5eacdc3d
CB
475 }
476
c87524b7
CB
477 struct wrapargs wrap = (struct wrapargs){
478 .command = &command,
5e5129d7 479 .options = &attach_options
c87524b7
CB
480 };
481
482 wrap.ptyfd = stdfd_is_pty();
483 if (wrap.ptyfd >= 0) {
484 if ((!isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) && my_args.console_log) {
485 fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
486 goto out;
487 }
478dda76 488 ret = get_pty_on_host(c, &wrap, &pid);
9c4693b8 489 } else {
c87524b7
CB
490 if (my_args.console_log) {
491 fprintf(stderr, "-L/--pty-log can only be used when stdout and stderr refer to a pty.\n");
492 goto out;
493 }
a9d02bb9 494 if (command.program)
5eacdc3d
CB
495 ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
496 else
497 ret = c->attach(c, lxc_attach_run_shell, NULL, &attach_options, &pid);
c8f7c563
CS
498 }
499
9c4693b8 500 if (ret < 0)
478dda76 501 goto out;
2b30b861 502
9c4693b8
CS
503 ret = lxc_wait_for_pid_status(pid);
504 if (ret < 0)
478dda76 505 goto out;
cb014488 506
9c4693b8 507 if (WIFEXITED(ret))
478dda76
CB
508 wexit = WEXITSTATUS(ret);
509out:
510 lxc_container_put(c);
511 if (ret >= 0)
512 exit(wexit);
5eacdc3d 513 exit(EXIT_FAILURE);
81c75799 514}