]>
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) { | |
7874d81a | 338 | SYSDEBUG("Failed to reset to default action " |
339 | "for signal \"%d\": %d", i, pid); | |
a6f151a7 CB |
340 | } |
341 | } | |
6f0a4200 | 342 | |
b467714b | 343 | ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); |
a6f151a7 | 344 | if (ret < 0) { |
1bfb1200 | 345 | SYSERROR("Failed to set signal mask"); |
0291b5fa QH |
346 | exit(EXIT_FAILURE); |
347 | } | |
e4b3fe58 | 348 | |
f6d9b7d5 CB |
349 | sid = setsid(); |
350 | if (sid < 0) | |
351 | DEBUG("Failed to make child session leader"); | |
352 | ||
0cf42edd JC |
353 | if (ioctl(STDIN_FILENO, TIOCSCTTY, 0) < 0) |
354 | DEBUG("Failed to set controlling terminal"); | |
355 | ||
a6f151a7 | 356 | NOTICE("Exec'ing \"%s\"", my_args.argv[0]); |
72439b9f | 357 | |
a6f151a7 | 358 | ret = execvp(my_args.argv[0], my_args.argv); |
6d1400b5 | 359 | SYSERROR("Failed to exec \"%s\"", my_args.argv[0]); |
8130d864 | 360 | exit(ret); |
05f05512 | 361 | } |
362 | ||
a6f151a7 CB |
363 | INFO("Attempting to set proc title to \"init\""); |
364 | setproctitle("init"); | |
365 | ||
366 | /* Let's process the signals now. */ | |
367 | ret = sigdelset(&omask, SIGALRM); | |
368 | if (ret < 0) | |
369 | exit(EXIT_FAILURE); | |
370 | ||
b467714b | 371 | ret = pthread_sigmask(SIG_SETMASK, &omask, NULL); |
a6f151a7 | 372 | if (ret < 0) { |
1bfb1200 | 373 | SYSERROR("Failed to set signal mask"); |
0291b5fa QH |
374 | exit(EXIT_FAILURE); |
375 | } | |
e4b3fe58 | 376 | |
a6f151a7 CB |
377 | /* No need of other inherited fds but stderr. */ |
378 | close(STDIN_FILENO); | |
379 | close(STDOUT_FILENO); | |
affaa6da | 380 | |
05f05512 | 381 | for (;;) { |
382 | int status; | |
b0ed5e64 MN |
383 | pid_t waited_pid; |
384 | ||
6fd1668e | 385 | switch (was_interrupted) { |
6fd1668e | 386 | case 0: |
186dfb16 TA |
387 | /* Some applications send SIGHUP in order to get init to reload |
388 | * its configuration. We don't want to forward this onto the | |
389 | * application itself, because it probably isn't expecting this | |
390 | * signal since it was expecting init to do something with it. | |
391 | * | |
392 | * Instead, let's explicitly ignore it here. The actual | |
393 | * terminal case is handled in the monitor's handler, which | |
394 | * sends this task a SIGTERM in the case of a SIGHUP, which is | |
395 | * what we want. | |
396 | */ | |
397 | case SIGHUP: | |
6fd1668e | 398 | break; |
5c7f03ae | 399 | case SIGPWR: |
6fd1668e | 400 | case SIGTERM: |
6f0a4200 | 401 | if (!shutdown) { |
0059379f | 402 | pid_t mypid = lxc_raw_getpid(); |
c379af4c | 403 | |
6f0a4200 | 404 | shutdown = 1; |
d76e3e1a | 405 | prevent_forking(); |
c379af4c TA |
406 | if (mypid != 1) { |
407 | kill_children(mypid); | |
d76e3e1a TA |
408 | } else { |
409 | ret = kill(-1, SIGTERM); | |
410 | if (ret < 0) | |
7874d81a | 411 | SYSDEBUG("Failed to send SIGTERM to all children"); |
d76e3e1a TA |
412 | } |
413 | alarm(1); | |
414 | } | |
415 | break; | |
c379af4c | 416 | case SIGALRM: { |
0059379f | 417 | pid_t mypid = lxc_raw_getpid(); |
c379af4c | 418 | |
d76e3e1a | 419 | prevent_forking(); |
c379af4c TA |
420 | if (mypid != 1) { |
421 | kill_children(mypid); | |
d76e3e1a | 422 | } else { |
60e324aa | 423 | ret = kill(-1, SIGKILL); |
a6f151a7 | 424 | if (ret < 0) |
7874d81a | 425 | SYSDEBUG("Failed to send SIGTERM to all children"); |
6f0a4200 DL |
426 | } |
427 | break; | |
c379af4c | 428 | } |
6fd1668e | 429 | default: |
a6f151a7 CB |
430 | ret = kill(pid, was_interrupted); |
431 | if (ret < 0) | |
7874d81a | 432 | SYSDEBUG("Failed to send signal \"%d\" to %d", was_interrupted, pid); |
6fd1668e | 433 | break; |
e4b3fe58 | 434 | } |
a6f151a7 | 435 | ret = EXIT_SUCCESS; |
e4b3fe58 | 436 | |
6fd1668e | 437 | was_interrupted = 0; |
b0ed5e64 MN |
438 | waited_pid = wait(&status); |
439 | if (waited_pid < 0) { | |
05f05512 | 440 | if (errno == ECHILD) |
b0ed5e64 | 441 | goto out; |
a6f151a7 | 442 | |
05f05512 | 443 | if (errno == EINTR) |
444 | continue; | |
6f0a4200 | 445 | |
6d1400b5 | 446 | SYSERROR("Failed to wait on child %d", pid); |
b0ed5e64 | 447 | goto out; |
05f05512 | 448 | } |
37c3dfc9 | 449 | |
a6f151a7 | 450 | /* Reset timer each time a process exited. */ |
6f0a4200 DL |
451 | if (shutdown) |
452 | alarm(1); | |
453 | ||
a6f151a7 CB |
454 | /* Keep the exit code of the started application (not wrapped |
455 | * pid) and continue to wait for the end of the orphan group. | |
37c3dfc9 | 456 | */ |
fe19f236 | 457 | if (waited_pid == pid && !have_status) { |
4f4530fa | 458 | exit_with = lxc_error_set_and_log(waited_pid, status); |
fe19f236 DE |
459 | have_status = 1; |
460 | } | |
05f05512 | 461 | } |
b0ed5e64 | 462 | out: |
a6f151a7 | 463 | if (ret < 0) |
13bc2fd2 | 464 | exit(EXIT_FAILURE); |
4f4530fa | 465 | exit(exit_with); |
05f05512 | 466 | } |
5ee606bc | 467 | |
5ee606bc R |
468 | static void print_usage(const struct option longopts[]) |
469 | ||
470 | { | |
d51dde8a R |
471 | fprintf(stderr, "Usage: lxc-init [-n|--name=NAME] [-h|--help] [--usage] [--version] \n\ |
472 | [-q|--quiet] [-o|--logfile=LOGFILE] [-l|--logpriority=LOGPRIORITY] [-P|--lxcpath=LXCPATH]\n"); | |
5ee606bc R |
473 | exit(0); |
474 | } | |
475 | ||
476 | static void print_version() | |
477 | { | |
5f98011c | 478 | printf("%s\n", LXC_VERSION); |
5ee606bc R |
479 | exit(0); |
480 | } | |
481 | ||
d51dde8a | 482 | static void print_help() |
5ee606bc R |
483 | { |
484 | fprintf(stderr, "\ | |
485 | Usage: lxc-init --name=NAME -- COMMAND\n\ | |
486 | \n\ | |
487 | lxc-init start a COMMAND as PID 2 inside a container\n\ | |
488 | \n\ | |
489 | Options :\n\ | |
490 | -n, --name=NAME NAME of the container\n\ | |
491 | -o, --logfile=FILE Output log to FILE instead of stderr\n\ | |
492 | -l, --logpriority=LEVEL Set log priority to LEVEL\n\ | |
493 | -q, --quiet Don't produce any output\n\ | |
494 | -P, --lxcpath=PATH Use specified container path\n\ | |
495 | -?, --help Give this help list\n\ | |
496 | --usage Give a short usage message\n\ | |
497 | --version Print the version number\n\ | |
498 | \n\ | |
499 | Mandatory or optional arguments to long options are also mandatory or optional\n\ | |
500 | for any corresponding short options.\n\ | |
501 | \n\ | |
502 | See the lxc-init man page for further information.\n\n"); | |
503 | ||
5ee606bc R |
504 | } |
505 | ||
506 | static int arguments_parse(struct arguments *args, int argc, | |
507 | char *const argv[]) | |
508 | { | |
509 | while (true) { | |
510 | int c; | |
511 | int index = 0; | |
512 | ||
513 | c = getopt_long(argc, argv, args->shortopts, args->options, &index); | |
514 | if (c == -1) | |
515 | break; | |
516 | switch (c) { | |
517 | case 'n': | |
518 | args->name = optarg; | |
519 | break; | |
520 | case 'o': | |
521 | args->log_file = optarg; | |
522 | break; | |
523 | case 'l': | |
524 | args->log_priority = optarg; | |
525 | break; | |
526 | case 'q': | |
d51dde8a | 527 | args->quiet = true; |
5ee606bc R |
528 | break; |
529 | case 'P': | |
530 | remove_trailing_slashes(optarg); | |
531 | args->lxcpath = optarg; | |
532 | break; | |
533 | case OPT_USAGE: | |
534 | print_usage(args->options); | |
535 | case OPT_VERSION: | |
536 | print_version(); | |
537 | case '?': | |
d51dde8a R |
538 | print_help(); |
539 | exit(EXIT_FAILURE); | |
5ee606bc | 540 | case 'h': |
d51dde8a R |
541 | print_help(); |
542 | exit(EXIT_SUCCESS); | |
5ee606bc R |
543 | } |
544 | } | |
545 | ||
546 | /* | |
547 | * Reclaim the remaining command arguments | |
548 | */ | |
549 | args->argv = &argv[optind]; | |
550 | args->argc = argc - optind; | |
551 | ||
552 | /* If no lxcpath was given, use default */ | |
553 | if (!args->lxcpath) { | |
554 | args->lxcpath = lxc_global_config_value("lxc.lxcpath"); | |
555 | } | |
556 | ||
557 | /* Check the command options */ | |
558 | if (!args->name) { | |
559 | if(!args->quiet) | |
560 | fprintf(stderr, "lxc-init: missing container name, use --name option\n"); | |
561 | return -1; | |
562 | } | |
563 | ||
564 | return 0; | |
565 | } |