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