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