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