]>
Commit | Line | Data |
---|---|---|
05f05512 | 1 | /* |
2 | * lxc: linux Container library | |
3 | * | |
4 | * (C) Copyright IBM Corp. 2007, 2008 | |
5 | * | |
6 | * Authors: | |
9afe19d6 | 7 | * Daniel Lezcano <daniel.lezcano at free.fr> |
05f05512 | 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 |
05f05512 | 22 | */ |
23 | ||
eab15c1e | 24 | #define _GNU_SOURCE |
05f05512 | 25 | #include <errno.h> |
a6f151a7 | 26 | #include <getopt.h> |
8559e703 | 27 | #include <libgen.h> |
b467714b | 28 | #include <pthread.h> |
a6f151a7 CB |
29 | #include <signal.h> |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <string.h> | |
33 | #include <unistd.h> | |
0e322d22 | 34 | #include <sys/stat.h> |
05f05512 | 35 | #include <sys/types.h> |
36 | #include <sys/wait.h> | |
5ee606bc | 37 | #include <ctype.h> |
00b3c2e2 | 38 | |
1574fc24 | 39 | #include <lxc/lxccontainer.h> |
1194822f | 40 | #include <lxc/version.h> |
1574fc24 | 41 | |
05cda563 | 42 | #include "error.h" |
4295c5de | 43 | #include "initutils.h" |
a6f151a7 | 44 | #include "log.h" |
0059379f | 45 | #include "namespace.h" |
1933b53f | 46 | #include "parse.h" |
5ee606bc R |
47 | |
48 | /* option keys for long only options */ | |
49 | #define OPT_USAGE 0x1000 | |
0059379f | 50 | #define OPT_VERSION OPT_USAGE - 1 |
8559e703 | 51 | |
c379af4c TA |
52 | #define QUOTE(macro) #macro |
53 | #define QUOTEVAL(macro) QUOTE(macro) | |
54 | ||
8559e703 | 55 | lxc_log_define(lxc_init, lxc); |
05f05512 | 56 | |
a8aa709c | 57 | static sig_atomic_t was_interrupted = 0; |
e4b3fe58 | 58 | |
a81bad13 | 59 | static void interrupt_handler(int sig) |
05f05512 | 60 | { |
a81bad13 RW |
61 | if (!was_interrupted) |
62 | was_interrupted = sig; | |
63 | } | |
e4b3fe58 | 64 | |
5ee606bc | 65 | static struct option long_options[] = { |
0059379f | 66 | { "name", required_argument, 0, 'n' }, |
78c16484 | 67 | { "help", no_argument, 0, 'h' }, |
0059379f CB |
68 | { "usage", no_argument, 0, OPT_USAGE }, |
69 | { "version", no_argument, 0, OPT_VERSION }, | |
70 | { "quiet", no_argument, 0, 'q' }, | |
71 | { "logfile", required_argument, 0, 'o' }, | |
72 | { "logpriority", required_argument, 0, 'l' }, | |
73 | { "lxcpath", required_argument, 0, 'P' }, | |
5ee606bc R |
74 | { 0, 0, 0, 0 } |
75 | }; | |
76 | static char short_options[] = "n:hqo:l:P:"; | |
77 | ||
78 | struct arguments { | |
79 | const struct option *options; | |
80 | const char *shortopts; | |
81 | ||
82 | const char *name; | |
83 | char *log_file; | |
84 | char *log_priority; | |
d51dde8a | 85 | bool quiet; |
5ee606bc R |
86 | const char *lxcpath; |
87 | ||
88 | /* remaining arguments */ | |
89 | char *const *argv; | |
90 | int argc; | |
a6f151a7 CB |
91 | }; |
92 | ||
5ee606bc R |
93 | static int arguments_parse(struct arguments *my_args, int argc, |
94 | char *const argv[]); | |
e0b0b533 | 95 | |
5ee606bc R |
96 | static struct arguments my_args = { |
97 | .options = long_options, | |
98 | .shortopts = short_options | |
a6f151a7 CB |
99 | }; |
100 | ||
d76e3e1a TA |
101 | static void prevent_forking(void) |
102 | { | |
103 | FILE *f; | |
1933b53f CB |
104 | size_t len = 0; |
105 | char *line = NULL; | |
106 | char path[MAXPATHLEN]; | |
d76e3e1a TA |
107 | |
108 | f = fopen("/proc/self/cgroup", "r"); | |
1933b53f | 109 | if (!f) |
d76e3e1a | 110 | return; |
d76e3e1a | 111 | |
1933b53f | 112 | while (getline(&line, &len, f) != -1) { |
d6d727af | 113 | int fd, ret; |
1933b53f | 114 | char *p, *p2; |
d76e3e1a | 115 | |
1933b53f CB |
116 | p = strchr(line, ':'); |
117 | if (!p) | |
118 | continue; | |
119 | p++; | |
120 | p2 = strchr(p, ':'); | |
121 | if (!p2) | |
122 | continue; | |
123 | *p2 = '\0'; | |
c379af4c | 124 | |
1933b53f CB |
125 | /* This is a cgroup v2 entry. Skip it. */ |
126 | if ((p2 - p) == 0) | |
127 | continue; | |
d76e3e1a | 128 | |
1933b53f | 129 | if (strcmp(p, "pids") != 0) |
d76e3e1a | 130 | continue; |
1933b53f CB |
131 | p2++; |
132 | ||
133 | p2 += lxc_char_left_gc(p2, strlen(p2)); | |
134 | p2[lxc_char_right_gc(p2, strlen(p2))] = '\0'; | |
d76e3e1a | 135 | |
1933b53f CB |
136 | ret = snprintf(path, sizeof(path), |
137 | "/sys/fs/cgroup/pids/%s/pids.max", p2); | |
c379af4c TA |
138 | if (ret < 0 || (size_t)ret >= sizeof(path)) { |
139 | ERROR("Failed to create string"); | |
1933b53f | 140 | goto on_error; |
d76e3e1a TA |
141 | } |
142 | ||
1933b53f | 143 | fd = open(path, O_WRONLY); |
d76e3e1a | 144 | if (fd < 0) { |
1933b53f CB |
145 | SYSERROR("Failed to open \"%s\"", path); |
146 | goto on_error; | |
d76e3e1a TA |
147 | } |
148 | ||
d6d727af CB |
149 | ret = write(fd, "1", 1); |
150 | if (ret != 1) | |
1933b53f | 151 | SYSERROR("Failed to write to \"%s\"", path); |
d76e3e1a TA |
152 | |
153 | close(fd); | |
154 | break; | |
155 | } | |
156 | ||
1933b53f | 157 | on_error: |
1933b53f | 158 | free(line); |
d76e3e1a TA |
159 | fclose(f); |
160 | } | |
161 | ||
162 | static void kill_children(pid_t pid) | |
163 | { | |
164 | FILE *f; | |
165 | char path[PATH_MAX]; | |
166 | int ret; | |
167 | ||
168 | ret = snprintf(path, sizeof(path), "/proc/%d/task/%d/children", pid, pid); | |
c379af4c TA |
169 | if (ret < 0 || (size_t)ret >= sizeof(path)) { |
170 | ERROR("Failed to create string"); | |
d76e3e1a TA |
171 | return; |
172 | } | |
173 | ||
174 | f = fopen(path, "r"); | |
175 | if (!f) { | |
c379af4c | 176 | SYSERROR("Failed to open %s", path); |
d76e3e1a TA |
177 | return; |
178 | } | |
179 | ||
180 | while (!feof(f)) { | |
181 | pid_t pid; | |
182 | ||
183 | if (fscanf(f, "%d ", &pid) != 1) { | |
c379af4c | 184 | ERROR("Failed to retrieve pid"); |
d76e3e1a TA |
185 | fclose(f); |
186 | return; | |
187 | } | |
188 | ||
189 | kill_children(pid); | |
190 | kill(pid, SIGKILL); | |
191 | } | |
192 | ||
193 | fclose(f); | |
194 | } | |
195 | ||
58fb9c8e TA |
196 | static void remove_self(void) |
197 | { | |
4d078b3c | 198 | int ret; |
58fb9c8e | 199 | ssize_t n; |
4d078b3c | 200 | char path[MAXPATHLEN] = {0}; |
58fb9c8e TA |
201 | |
202 | n = readlink("/proc/self/exe", path, sizeof(path)); | |
4d078b3c | 203 | if (n < 0 || n >= MAXPATHLEN) { |
58fb9c8e TA |
204 | SYSERROR("Failed to readlink \"/proc/self/exe\""); |
205 | return; | |
206 | } | |
4d078b3c | 207 | path[n] = '\0'; |
58fb9c8e | 208 | |
4d078b3c CB |
209 | ret = umount2(path, MNT_DETACH); |
210 | if (ret < 0) { | |
58fb9c8e TA |
211 | SYSERROR("Failed to unmount \"%s\"", path); |
212 | return; | |
213 | } | |
214 | ||
4d078b3c CB |
215 | ret = unlink(path); |
216 | if (ret < 0) { | |
58fb9c8e TA |
217 | SYSERROR("Failed to unlink \"%s\"", path); |
218 | return; | |
219 | } | |
220 | } | |
221 | ||
a81bad13 RW |
222 | int main(int argc, char *argv[]) |
223 | { | |
a6f151a7 | 224 | int i, ret; |
f6d9b7d5 | 225 | pid_t pid, sid; |
16affc24 | 226 | struct sigaction act; |
73b910a3 | 227 | struct lxc_log log; |
a6f151a7 | 228 | sigset_t mask, omask; |
4f4530fa | 229 | int have_status = 0, exit_with = 1, shutdown = 0; |
05f05512 | 230 | |
5ee606bc | 231 | if (arguments_parse(&my_args, argc, argv)) |
a6f151a7 | 232 | exit(EXIT_FAILURE); |
05f05512 | 233 | |
5ee606bc | 234 | log.prefix = "lxc-init"; |
a6f151a7 CB |
235 | log.name = my_args.name; |
236 | log.file = my_args.log_file; | |
237 | log.level = my_args.log_priority; | |
a6f151a7 | 238 | log.quiet = my_args.quiet; |
5ee606bc | 239 | log.lxcpath = my_args.lxcpath; |
73b910a3 | 240 | |
a6f151a7 CB |
241 | ret = lxc_log_init(&log); |
242 | if (ret < 0) | |
e0b0b533 | 243 | exit(EXIT_FAILURE); |
6edbfc86 | 244 | lxc_log_options_no_override(); |
8559e703 | 245 | |
a6f151a7 CB |
246 | if (!my_args.argc) { |
247 | ERROR("Please specify a command to execute"); | |
e0b0b533 | 248 | exit(EXIT_FAILURE); |
05f05512 | 249 | } |
250 | ||
a6f151a7 CB |
251 | /* Mask all the signals so we are safe to install a signal handler and |
252 | * to fork. | |
6f0a4200 | 253 | */ |
a6f151a7 CB |
254 | ret = sigfillset(&mask); |
255 | if (ret < 0) | |
0291b5fa | 256 | exit(EXIT_FAILURE); |
e4b3fe58 | 257 | |
a6f151a7 CB |
258 | ret = sigdelset(&mask, SIGILL); |
259 | if (ret < 0) | |
16affc24 | 260 | exit(EXIT_FAILURE); |
a6f151a7 CB |
261 | |
262 | ret = sigdelset(&mask, SIGSEGV); | |
263 | if (ret < 0) | |
264 | exit(EXIT_FAILURE); | |
265 | ||
266 | ret = sigdelset(&mask, SIGBUS); | |
267 | if (ret < 0) | |
268 | exit(EXIT_FAILURE); | |
269 | ||
b467714b | 270 | ret = pthread_sigmask(SIG_SETMASK, &mask, &omask); |
a6f151a7 CB |
271 | if (ret < 0) |
272 | exit(EXIT_FAILURE); | |
273 | ||
274 | ret = sigfillset(&act.sa_mask); | |
275 | if (ret < 0) | |
276 | exit(EXIT_FAILURE); | |
277 | ||
278 | ret = sigdelset(&act.sa_mask, SIGILL); | |
279 | if (ret < 0) | |
280 | exit(EXIT_FAILURE); | |
281 | ||
282 | ret = sigdelset(&act.sa_mask, SIGSEGV); | |
283 | if (ret < 0) | |
284 | exit(EXIT_FAILURE); | |
285 | ||
286 | ret = sigdelset(&act.sa_mask, SIGBUS); | |
287 | if (ret < 0) | |
288 | exit(EXIT_FAILURE); | |
289 | ||
290 | ret = sigdelset(&act.sa_mask, SIGSTOP); | |
291 | if (ret < 0) | |
292 | exit(EXIT_FAILURE); | |
293 | ||
294 | ret = sigdelset(&act.sa_mask, SIGKILL); | |
295 | if (ret < 0) | |
296 | exit(EXIT_FAILURE); | |
297 | ||
16affc24 LW |
298 | act.sa_flags = 0; |
299 | act.sa_handler = interrupt_handler; | |
e4b3fe58 | 300 | |
16affc24 | 301 | for (i = 1; i < NSIG; i++) { |
a6f151a7 CB |
302 | /* Exclude some signals: ILL, SEGV and BUS are likely to reveal |
303 | * a bug and we want a core. STOP and KILL cannot be handled | |
304 | * anyway: they're here for documentation. 32 and 33 are not | |
305 | * defined. | |
5781a74a | 306 | */ |
a6f151a7 CB |
307 | if (i == SIGILL || i == SIGSEGV || i == SIGBUS || |
308 | i == SIGSTOP || i == SIGKILL || i == 32 || i == 33) | |
5781a74a JX |
309 | continue; |
310 | ||
a6f151a7 CB |
311 | ret = sigaction(i, &act, NULL); |
312 | if (ret < 0) { | |
313 | if (errno == EINVAL) | |
314 | continue; | |
315 | ||
316 | SYSERROR("Failed to change signal action"); | |
0291b5fa QH |
317 | exit(EXIT_FAILURE); |
318 | } | |
e4b3fe58 | 319 | } |
320 | ||
58fb9c8e TA |
321 | remove_self(); |
322 | ||
05f05512 | 323 | pid = fork(); |
05f05512 | 324 | if (pid < 0) |
e0b0b533 | 325 | exit(EXIT_FAILURE); |
05f05512 | 326 | |
327 | if (!pid) { | |
6f0a4200 | 328 | /* restore default signal handlers */ |
a6f151a7 CB |
329 | for (i = 1; i < NSIG; i++) { |
330 | sighandler_t sigerr; | |
11c69d5e CB |
331 | |
332 | if (i == SIGILL || i == SIGSEGV || i == SIGBUS || | |
333 | i == SIGSTOP || i == SIGKILL || i == 32 || i == 33) | |
334 | continue; | |
335 | ||
a6f151a7 CB |
336 | sigerr = signal(i, SIG_DFL); |
337 | if (sigerr == SIG_ERR) { | |
338 | DEBUG("%s - Failed to reset to default action " | |
339 | "for signal \"%d\": %d", strerror(errno), | |
340 | i, pid); | |
341 | } | |
342 | } | |
6f0a4200 | 343 | |
b467714b | 344 | ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); |
a6f151a7 | 345 | if (ret < 0) { |
1bfb1200 | 346 | SYSERROR("Failed to set signal mask"); |
0291b5fa QH |
347 | exit(EXIT_FAILURE); |
348 | } | |
e4b3fe58 | 349 | |
f6d9b7d5 CB |
350 | sid = setsid(); |
351 | if (sid < 0) | |
352 | DEBUG("Failed to make child session leader"); | |
353 | ||
0cf42edd JC |
354 | if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0) |
355 | DEBUG("Failed to set controlling terminal"); | |
356 | ||
a6f151a7 | 357 | NOTICE("Exec'ing \"%s\"", my_args.argv[0]); |
72439b9f | 358 | |
a6f151a7 CB |
359 | ret = execvp(my_args.argv[0], my_args.argv); |
360 | ERROR("%s - Failed to exec \"%s\"", strerror(errno), my_args.argv[0]); | |
8130d864 | 361 | exit(ret); |
05f05512 | 362 | } |
363 | ||
a6f151a7 CB |
364 | INFO("Attempting to set proc title to \"init\""); |
365 | setproctitle("init"); | |
366 | ||
367 | /* Let's process the signals now. */ | |
368 | ret = sigdelset(&omask, SIGALRM); | |
369 | if (ret < 0) | |
370 | exit(EXIT_FAILURE); | |
371 | ||
b467714b | 372 | ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); |
a6f151a7 | 373 | if (ret < 0) { |
1bfb1200 | 374 | SYSERROR("Failed to set signal mask"); |
0291b5fa QH |
375 | exit(EXIT_FAILURE); |
376 | } | |
e4b3fe58 | 377 | |
a6f151a7 CB |
378 | /* No need of other inherited fds but stderr. */ |
379 | close(STDIN_FILENO); | |
380 | close(STDOUT_FILENO); | |
affaa6da | 381 | |
05f05512 | 382 | for (;;) { |
383 | int status; | |
b0ed5e64 MN |
384 | pid_t waited_pid; |
385 | ||
6fd1668e | 386 | switch (was_interrupted) { |
6fd1668e | 387 | case 0: |
186dfb16 TA |
388 | /* Some applications send SIGHUP in order to get init to reload |
389 | * its configuration. We don't want to forward this onto the | |
390 | * application itself, because it probably isn't expecting this | |
391 | * signal since it was expecting init to do something with it. | |
392 | * | |
393 | * Instead, let's explicitly ignore it here. The actual | |
394 | * terminal case is handled in the monitor's handler, which | |
395 | * sends this task a SIGTERM in the case of a SIGHUP, which is | |
396 | * what we want. | |
397 | */ | |
398 | case SIGHUP: | |
6fd1668e | 399 | break; |
5c7f03ae | 400 | case SIGPWR: |
6fd1668e | 401 | case SIGTERM: |
6f0a4200 | 402 | if (!shutdown) { |
0059379f | 403 | pid_t mypid = lxc_raw_getpid(); |
c379af4c | 404 | |
6f0a4200 | 405 | shutdown = 1; |
d76e3e1a | 406 | prevent_forking(); |
c379af4c TA |
407 | if (mypid != 1) { |
408 | kill_children(mypid); | |
d76e3e1a TA |
409 | } else { |
410 | ret = kill(-1, SIGTERM); | |
411 | if (ret < 0) | |
412 | DEBUG("%s - Failed to send SIGTERM to " | |
413 | "all children", strerror(errno)); | |
414 | } | |
415 | alarm(1); | |
416 | } | |
417 | break; | |
c379af4c | 418 | case SIGALRM: { |
0059379f | 419 | pid_t mypid = lxc_raw_getpid(); |
c379af4c | 420 | |
d76e3e1a | 421 | prevent_forking(); |
c379af4c TA |
422 | if (mypid != 1) { |
423 | kill_children(mypid); | |
d76e3e1a | 424 | } else { |
60e324aa | 425 | ret = kill(-1, SIGKILL); |
a6f151a7 CB |
426 | if (ret < 0) |
427 | DEBUG("%s - Failed to send SIGTERM to " | |
428 | "all children", strerror(errno)); | |
6f0a4200 DL |
429 | } |
430 | break; | |
c379af4c | 431 | } |
6fd1668e | 432 | default: |
a6f151a7 CB |
433 | ret = kill(pid, was_interrupted); |
434 | if (ret < 0) | |
435 | DEBUG("%s - Failed to send signal \"%d\" to " | |
436 | "%d", strerror(errno), was_interrupted, pid); | |
6fd1668e | 437 | break; |
e4b3fe58 | 438 | } |
a6f151a7 | 439 | ret = EXIT_SUCCESS; |
e4b3fe58 | 440 | |
6fd1668e | 441 | was_interrupted = 0; |
b0ed5e64 MN |
442 | waited_pid = wait(&status); |
443 | if (waited_pid < 0) { | |
05f05512 | 444 | if (errno == ECHILD) |
b0ed5e64 | 445 | goto out; |
a6f151a7 | 446 | |
05f05512 | 447 | if (errno == EINTR) |
448 | continue; | |
6f0a4200 | 449 | |
a6f151a7 CB |
450 | ERROR("%s - Failed to wait on child %d", |
451 | strerror(errno), pid); | |
b0ed5e64 | 452 | goto out; |
05f05512 | 453 | } |
37c3dfc9 | 454 | |
a6f151a7 | 455 | /* Reset timer each time a process exited. */ |
6f0a4200 DL |
456 | if (shutdown) |
457 | alarm(1); | |
458 | ||
a6f151a7 CB |
459 | /* Keep the exit code of the started application (not wrapped |
460 | * pid) and continue to wait for the end of the orphan group. | |
37c3dfc9 | 461 | */ |
fe19f236 | 462 | if (waited_pid == pid && !have_status) { |
4f4530fa | 463 | exit_with = lxc_error_set_and_log(waited_pid, status); |
fe19f236 DE |
464 | have_status = 1; |
465 | } | |
05f05512 | 466 | } |
b0ed5e64 | 467 | out: |
a6f151a7 | 468 | if (ret < 0) |
13bc2fd2 | 469 | exit(EXIT_FAILURE); |
4f4530fa | 470 | exit(exit_with); |
05f05512 | 471 | } |
5ee606bc | 472 | |
5ee606bc R |
473 | static void print_usage(const struct option longopts[]) |
474 | ||
475 | { | |
d51dde8a R |
476 | fprintf(stderr, "Usage: lxc-init [-n|--name=NAME] [-h|--help] [--usage] [--version] \n\ |
477 | [-q|--quiet] [-o|--logfile=LOGFILE] [-l|--logpriority=LOGPRIORITY] [-P|--lxcpath=LXCPATH]\n"); | |
5ee606bc R |
478 | exit(0); |
479 | } | |
480 | ||
481 | static void print_version() | |
482 | { | |
5f98011c | 483 | printf("%s\n", LXC_VERSION); |
5ee606bc R |
484 | exit(0); |
485 | } | |
486 | ||
d51dde8a | 487 | static void print_help() |
5ee606bc R |
488 | { |
489 | fprintf(stderr, "\ | |
490 | Usage: lxc-init --name=NAME -- COMMAND\n\ | |
491 | \n\ | |
492 | lxc-init start a COMMAND as PID 2 inside a container\n\ | |
493 | \n\ | |
494 | Options :\n\ | |
495 | -n, --name=NAME NAME of the container\n\ | |
496 | -o, --logfile=FILE Output log to FILE instead of stderr\n\ | |
497 | -l, --logpriority=LEVEL Set log priority to LEVEL\n\ | |
498 | -q, --quiet Don't produce any output\n\ | |
499 | -P, --lxcpath=PATH Use specified container path\n\ | |
500 | -?, --help Give this help list\n\ | |
501 | --usage Give a short usage message\n\ | |
502 | --version Print the version number\n\ | |
503 | \n\ | |
504 | Mandatory or optional arguments to long options are also mandatory or optional\n\ | |
505 | for any corresponding short options.\n\ | |
506 | \n\ | |
507 | See the lxc-init man page for further information.\n\n"); | |
508 | ||
5ee606bc R |
509 | } |
510 | ||
511 | static int arguments_parse(struct arguments *args, int argc, | |
512 | char *const argv[]) | |
513 | { | |
514 | while (true) { | |
515 | int c; | |
516 | int index = 0; | |
517 | ||
518 | c = getopt_long(argc, argv, args->shortopts, args->options, &index); | |
519 | if (c == -1) | |
520 | break; | |
521 | switch (c) { | |
522 | case 'n': | |
523 | args->name = optarg; | |
524 | break; | |
525 | case 'o': | |
526 | args->log_file = optarg; | |
527 | break; | |
528 | case 'l': | |
529 | args->log_priority = optarg; | |
530 | break; | |
531 | case 'q': | |
d51dde8a | 532 | args->quiet = true; |
5ee606bc R |
533 | break; |
534 | case 'P': | |
535 | remove_trailing_slashes(optarg); | |
536 | args->lxcpath = optarg; | |
537 | break; | |
538 | case OPT_USAGE: | |
539 | print_usage(args->options); | |
540 | case OPT_VERSION: | |
541 | print_version(); | |
542 | case '?': | |
d51dde8a R |
543 | print_help(); |
544 | exit(EXIT_FAILURE); | |
5ee606bc | 545 | case 'h': |
d51dde8a R |
546 | print_help(); |
547 | exit(EXIT_SUCCESS); | |
5ee606bc R |
548 | } |
549 | } | |
550 | ||
551 | /* | |
552 | * Reclaim the remaining command arguments | |
553 | */ | |
554 | args->argv = &argv[optind]; | |
555 | args->argc = argc - optind; | |
556 | ||
557 | /* If no lxcpath was given, use default */ | |
558 | if (!args->lxcpath) { | |
559 | args->lxcpath = lxc_global_config_value("lxc.lxcpath"); | |
560 | } | |
561 | ||
562 | /* Check the command options */ | |
563 | if (!args->name) { | |
564 | if(!args->quiet) | |
565 | fprintf(stderr, "lxc-init: missing container name, use --name option\n"); | |
566 | return -1; | |
567 | } | |
568 | ||
569 | return 0; | |
570 | } |