]>
Commit | Line | Data |
---|---|---|
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 | 55 | static 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 |
70 | static int elevated_privileges = 0; |
71 | static signed long new_personality = -1; | |
fc763ab7 | 72 | static int namespace_flags = -1; |
7a0b0b56 | 73 | static int remount_sys_proc = 0; |
799f96fd | 74 | static lxc_attach_env_policy_t env_policy = LXC_ATTACH_KEEP_ENV; |
3d5e9f48 CS |
75 | static char **extra_env = NULL; |
76 | static ssize_t extra_env_size = 0; | |
77 | static char **extra_keep = NULL; | |
78 | static ssize_t extra_keep_size = 0; | |
79 | ||
80 | static 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 | |
108 | static 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 |
191 | static struct lxc_arguments my_args = { |
192 | .progname = "lxc-attach", | |
193 | .help = "\ | |
03027ad9 | 194 | --name=NAME [-- COMMAND]\n\ |
81c75799 | 195 | \n\ |
03027ad9 | 196 | Execute the specified COMMAND - enter the container NAME\n\ |
81c75799 DL |
197 | \n\ |
198 | Options :\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 |
244 | struct 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. */ |
252 | static 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 | 264 | static 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 | 279 | static 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 | ||
372 | err3: | |
373 | lxc_mainloop_close(&descr); | |
374 | err2: | |
b9a24c4f | 375 | if (ts && ts->sigfd != -1) |
0519b5cc | 376 | lxc_console_signal_fini(ts); |
5eacdc3d CB |
377 | err1: |
378 | lxc_console_delete(&conf->console); | |
379 | ||
380 | return ret; | |
381 | } | |
382 | ||
c87524b7 CB |
383 | static 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 |
395 | int 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); |
509 | out: | |
510 | lxc_container_put(c); | |
511 | if (ret >= 0) | |
512 | exit(wexit); | |
5eacdc3d | 513 | exit(EXIT_FAILURE); |
81c75799 | 514 | } |