]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/tools/lxc_autostart.c
spelling: container
[mirror_lxc.git] / src / lxc / tools / lxc_autostart.c
CommitLineData
a6adab20
SG
1/* lxc_autostart
2 *
3 * Copyright © 2013 Stéphane Graber <stgraber@ubuntu.com>
4 * Copyright © 2013 Canonical Ltd.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
d38dd64a
CB
21#ifndef _GNU_SOURCE
22#define _GNU_SOURCE 1
23#endif
a6993015
CB
24#include <stdio.h>
25#include <stdlib.h>
a6adab20
SG
26#include <string.h>
27#include <unistd.h>
28
29#include <lxc/lxccontainer.h>
30
31#include "arguments.h"
d38dd64a 32#include "config.h"
1cd7db65 33#include "list.h"
34#include "log.h"
35#include "utils.h"
36
37lxc_log_define(lxc_autostart, lxc);
a6adab20 38
015f0dd7
MW
39static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list);
40
41struct lxc_list *cmd_groups_list = NULL;
6ea518f6 42
1cd7db65 43static int my_parser(struct lxc_arguments *args, int c, char *arg)
a6adab20
SG
44{
45 switch (c) {
63610340
CB
46 case 'k':
47 args->hardstop = 1;
48 break;
49 case 'L':
50 args->list = 1;
51 break;
52 case 'r':
53 args->reboot = 1;
54 break;
55 case 's':
56 args->shutdown = 1;
57 break;
58 case 'a':
59 args->all = 1;
60 break;
61 case 'A':
62 args->ignore_auto = 1;
63 break;
64 case 'g':
65 cmd_groups_list = accumulate_list(arg, ",", cmd_groups_list);
66 break;
67 case 't':
68 if (lxc_safe_long(arg, &args->timeout) < 0)
69 return -1;
70 break;
a6adab20
SG
71 }
72 return 0;
73}
74
75static const struct option my_longopts[] = {
76 {"kill", no_argument, 0, 'k'},
77 {"list", no_argument, 0, 'L'},
78 {"reboot", no_argument, 0, 'r'},
79 {"shutdown", no_argument, 0, 's'},
80 {"all", no_argument, 0, 'a'},
e582991f 81 {"ignore-auto", no_argument, 0, 'A'},
a6adab20
SG
82 {"groups", required_argument, 0, 'g'},
83 {"timeout", required_argument, 0, 't'},
84 {"help", no_argument, 0, 'h'},
85 LXC_COMMON_OPTIONS
86};
87
88static struct lxc_arguments my_args = {
89 .progname = "lxc-autostart",
90 .help = "\
91\n\
92lxc-autostart managed auto-started containers\n\
93\n\
94Options:\n\
95 -k, --kill kill the containers instead of starting them\n\
96 -L, --list list all affected containers and wait delay\n\
97 -r, --reboot reboot the containers instead of starting them\n\
98 -s, --shutdown shutdown the containers instead of starting them\n\
99\n\
100 -a, --all list all auto-started containers (ignore groups)\n\
e582991f 101 -A, --ignore-auto ignore lxc.start.auto and select all matching containers\n\
a6adab20
SG
102 -g, --groups list of groups (comma separated) to select\n\
103 -t, --timeout=T wait T seconds before hard-stopping\n",
104 .options = my_longopts,
105 .parser = my_parser,
106 .checker = NULL,
a771fe18 107 .timeout = 60,
a6adab20
SG
108};
109
1cd7db65 110static int list_contains_entry(char *str_ptr, struct lxc_list *p1) {
015f0dd7
MW
111 struct lxc_list *it1;
112
113 /*
114 * If the entry is NULL or the empty string and the list
115 * is NULL, we have a match
116 */
54d47131 117 if (!p1 && (!str_ptr || !*str_ptr))
a6adab20
SG
118 return 1;
119
120 if (!p1)
121 return 0;
122
a6adab20 123 lxc_list_for_each(it1, p1) {
54d47131 124 if (strncmp(it1->elem, str_ptr, strlen(it1->elem)) == 0)
1cd7db65 125 return 1;
a6adab20
SG
126 }
127
128 return 0;
129}
130
a6993015
CB
131/* This is a variation of get_list below it. This version allows two additional
132 * features. If a list is passed to it, it adds to it. It allows for empty
133 * entries (i.e. "group1,,group2") generating and empty list entry.
015f0dd7 134 */
a6993015
CB
135static struct lxc_list *accumulate_list(char *input, char *delimiter,
136 struct lxc_list *str_list)
137{
015f0dd7
MW
138 char *workstr = NULL;
139 char *workptr = NULL;
140 char *next_ptr = NULL;
141 struct lxc_list *worklist;
142 struct lxc_list *workstr_list;
143
144 workstr = strdup(input);
a6993015 145 if (!workstr)
015f0dd7 146 return NULL;
015f0dd7
MW
147
148 workstr_list = str_list;
a6993015 149 if (!workstr_list) {
015f0dd7 150 workstr_list = malloc(sizeof(*workstr_list));
1cd7db65 151 if (!workstr_list) {
152 free(workstr);
153 return NULL;
154 }
155
015f0dd7
MW
156 lxc_list_init(workstr_list);
157 }
158
159 for (workptr = workstr; workptr; workptr = next_ptr) {
a6993015
CB
160 /* We can't use strtok_r here because it collapses multiple
161 * delimiters into 1 making empty fields impossible...
015f0dd7 162 */
a6993015
CB
163 next_ptr = strchr(workptr, *delimiter);
164 if (next_ptr)
015f0dd7 165 *next_ptr++ = '\0';
015f0dd7 166
a6993015
CB
167 /* At this point, we'd like to check to see if this group is
168 * already contained in the list and ignore it if it is... This
169 * also helps us with any corner cases where a string begins or
170 * ends with a delimiter.
015f0dd7 171 */
a6993015
CB
172 if (list_contains_entry(workptr, workstr_list)) {
173 if (*workptr)
1cd7db65 174 ERROR("Duplicate group \"%s\" in list - ignoring", workptr);
a6993015 175 else
1cd7db65 176 ERROR("Duplicate NULL group in list - ignoring");
015f0dd7
MW
177 } else {
178 worklist = malloc(sizeof(*worklist));
179 if (!worklist)
180 break;
181
182 worklist->elem = strdup(workptr);
183 if (!worklist->elem) {
184 free(worklist);
185 break;
186 }
187
188 lxc_list_add_tail(workstr_list, worklist);
189 }
190 }
191
192 free(workstr);
193
194 return workstr_list;
195}
196
a6993015
CB
197static struct lxc_list *get_list(char *input, char *delimiter)
198{
a6adab20 199 char *workstr = NULL;
a6adab20
SG
200 char *token = NULL;
201 struct lxc_list *worklist;
202 struct lxc_list *workstr_list;
203
204 workstr_list = malloc(sizeof(*workstr_list));
1cd7db65 205 if (!workstr_list)
206 return NULL;
207
a6adab20
SG
208 lxc_list_init(workstr_list);
209
210 workstr = strdup(input);
f75b4de0
SG
211 if (!workstr) {
212 free(workstr_list);
a6adab20 213 return NULL;
f75b4de0 214 }
a6adab20 215
7de8e0a9 216 lxc_iterate_parts(token, workstr, delimiter) {
a6adab20
SG
217 worklist = malloc(sizeof(*worklist));
218 if (!worklist)
219 break;
220
221 worklist->elem = strdup(token);
222 if (!worklist->elem) {
223 free(worklist);
224 break;
225 }
226
227 lxc_list_add_tail(workstr_list, worklist);
228 }
229
230 free(workstr);
231
232 return workstr_list;
233}
234
a6993015
CB
235static struct lxc_list *get_config_list(struct lxc_container *c, char *key)
236{
a6adab20 237 int len = 0;
a6993015 238 char *value = NULL;
a6adab20
SG
239 struct lxc_list *config_list = NULL;
240
241 len = c->get_config_item(c, key, NULL, 0);
242 if (len < 0)
243 return NULL;
244
a6993015 245 value = (char *)malloc(sizeof(char) * len + 1);
1cd7db65 246 if (!value)
a6adab20
SG
247 return NULL;
248
249 if (c->get_config_item(c, key, value, len + 1) != len) {
250 free(value);
251 return NULL;
252 }
253
254 if (strlen(value) == 0) {
255 free(value);
256 return NULL;
257 }
258
259 config_list = get_list(value, "\n");
260 free(value);
261
262 return config_list;
263}
264
a6993015
CB
265static int get_config_integer(struct lxc_container *c, char *key)
266{
267 int len = 0, ret = 0;
268 char *value = NULL;
a6adab20
SG
269
270 len = c->get_config_item(c, key, NULL, 0);
271 if (len < 0)
272 return 0;
273
a6993015 274 value = (char *)malloc(sizeof(char) * len + 1);
1cd7db65 275 if (!value)
a6adab20
SG
276 return 0;
277
278 if (c->get_config_item(c, key, value, len + 1) != len) {
279 free(value);
280 return 0;
281 }
282
63610340 283 if (lxc_safe_int(value, &ret) < 0)
5f514cee 284 printf("Could not parse config item.\n");
63610340 285
a6adab20
SG
286 free(value);
287
288 return ret;
289}
290
a6993015
CB
291static int cmporder(const void *p1, const void *p2)
292{
a6adab20
SG
293 struct lxc_container *c1 = *(struct lxc_container **)p1;
294 struct lxc_container *c2 = *(struct lxc_container **)p2;
295
296 int c1_order = get_config_integer(c1, "lxc.start.order");
297 int c2_order = get_config_integer(c2, "lxc.start.order");
298
299 if (c1_order == c2_order)
1cd7db65 300 return strncmp(c1->name, c2->name, strlen(c1->name));
a6993015
CB
301
302 return (c1_order - c2_order);
a6adab20
SG
303}
304
a6993015
CB
305static int toss_list(struct lxc_list *c_groups_list)
306{
015f0dd7
MW
307 struct lxc_list *it, *next;
308
a6993015
CB
309 if (!c_groups_list)
310 return 1;
311
312 lxc_list_for_each_safe(it, c_groups_list, next) {
313 lxc_list_del(it);
314 free(it->elem);
315 free(it);
015f0dd7 316 }
a6993015 317 free(c_groups_list);
015f0dd7
MW
318
319 return 1;
320}
321
a6adab20
SG
322int main(int argc, char *argv[])
323{
e3e70db3 324 int count = 0, failed = 0, i = 0, ret = 0;
a6993015 325 struct lxc_list *cmd_group;
a6adab20 326 struct lxc_container **containers = NULL;
015f0dd7 327 struct lxc_list **c_groups_lists = NULL;
73b910a3 328 struct lxc_log log;
a6adab20
SG
329
330 if (lxc_arguments_parse(&my_args, argc, argv))
b52b0595 331 exit(EXIT_FAILURE);
a6adab20 332
f6d79ec1
CB
333 /* Only create log if explicitly instructed */
334 if (my_args.log_file || my_args.log_priority) {
335 log.name = my_args.name;
336 log.file = my_args.log_file;
337 log.level = my_args.log_priority;
338 log.prefix = my_args.progname;
339 log.quiet = my_args.quiet;
340 log.lxcpath = my_args.lxcpath[0];
341
342 if (lxc_log_init(&log))
343 exit(EXIT_FAILURE);
344 }
6ea518f6 345
f44b73e1 346 count = list_defined_containers(my_args.lxcpath[0], NULL, &containers);
a6adab20
SG
347
348 if (count < 0)
b52b0595 349 exit(EXIT_FAILURE);
a6adab20 350
a6993015
CB
351 /* Allocate an array for our container group lists */
352 if (!my_args.all)
015f0dd7 353 c_groups_lists = calloc( count, sizeof( struct lxc_list * ) );
a6adab20 354
015f0dd7 355 qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
a6adab20 356
a6993015 357 if (cmd_groups_list && my_args.all)
54d47131 358 ERROR("Specifying -a (all) with -g (groups) doesn't make sense. All option overrides");
a6adab20 359
a6993015
CB
360 /* We need a default cmd_groups_list even for the -a
361 * case in order to force a pass through the loop for
362 * the NULL group. This, someday, could be taken from
363 * a config file somewhere...
364 */
365 if (!cmd_groups_list)
015f0dd7 366 cmd_groups_list = accumulate_list( "" , ",", NULL );
a6adab20 367
015f0dd7 368 lxc_list_for_each(cmd_group, cmd_groups_list) {
a6993015
CB
369 /* Because we may take several passes through the container list
370 * We'll switch on if the container pointer is NULL and if we
371 * process a container (run it or decide to ignore it) and call
372 * lxc_container_put then we'll NULL it out and not check it
373 * again.
015f0dd7
MW
374 */
375 for (i = 0; i < count; i++) {
376 struct lxc_container *c = containers[i];
a6adab20 377
015f0dd7
MW
378 if (!c)
379 /* Skip - must have been already processed */
380 continue;
a6adab20 381
a6993015
CB
382 /* We haven't loaded the container groups yet so these
383 * next two checks don't need to free them if they fail.
384 * They'll fail on the first pass.
015f0dd7
MW
385 */
386 if (!c->may_control(c)) {
387 /* We're done with this container */
54d47131 388 if (lxc_container_put(c) > 0)
015f0dd7 389 containers[i] = NULL;
1cd7db65 390
015f0dd7 391 continue;
a6adab20
SG
392 }
393
015f0dd7
MW
394 if (!my_args.ignore_auto &&
395 get_config_integer(c, "lxc.start.auto") != 1) {
396 /* We're done with this container */
1cd7db65 397 if (lxc_container_put(c) > 0)
015f0dd7 398 containers[i] = NULL;
1cd7db65 399
a6adab20
SG
400 continue;
401 }
a6adab20 402
015f0dd7
MW
403 if (!my_args.all) {
404 /* Filter by group */
1cd7db65 405 if (!c_groups_lists[i]) {
015f0dd7
MW
406 /* Now we're loading up a container's groups */
407 c_groups_lists[i] = get_config_list(c, "lxc.group");
408 }
409
410 ret = list_contains_entry(cmd_group->elem, c_groups_lists[i]);
1cd7db65 411 if (ret == 0) {
a6993015
CB
412 /* Not in the target group this pass so
413 * leave in the list for subsequent
414 * passes.
415 */
015f0dd7
MW
416 continue;
417 }
418 }
419
6f3fd27f 420 /* We have a candidate container to process */
015f0dd7
MW
421 c->want_daemonize(c, 1);
422
423 if (my_args.shutdown) {
424 /* Shutdown the container */
425 if (c->is_running(c)) {
76e484a7 426 if (my_args.list) {
015f0dd7 427 printf("%s\n", c->name);
76e484a7
SG
428 fflush(stdout);
429 }
015f0dd7
MW
430 else {
431 if (!c->shutdown(c, my_args.timeout)) {
1cd7db65 432 if (!c->stop(c))
433 ERROR("Error shutting down container: %s", c->name);
b8ac2750
SG
434 }
435 }
a6adab20 436 }
a6993015 437 } else if (my_args.hardstop) {
015f0dd7
MW
438 /* Kill the container */
439 if (c->is_running(c)) {
76e484a7 440 if (my_args.list) {
015f0dd7 441 printf("%s\n", c->name);
76e484a7
SG
442 fflush(stdout);
443 }
015f0dd7 444 else {
1cd7db65 445 if (!c->stop(c))
446 ERROR("Error killing container: %s", c->name);
015f0dd7 447 }
a6adab20 448 }
a6993015 449 } else if (my_args.reboot) {
015f0dd7
MW
450 /* Reboot the container */
451 if (c->is_running(c)) {
76e484a7 452 if (my_args.list) {
015f0dd7
MW
453 printf("%s %d\n", c->name,
454 get_config_integer(c, "lxc.start.delay"));
76e484a7
SG
455 fflush(stdout);
456 }
015f0dd7 457 else {
1cd7db65 458 if (!c->reboot(c))
459 ERROR("Error rebooting container: %s", c->name);
015f0dd7
MW
460 else
461 sleep(get_config_integer(c, "lxc.start.delay"));
462 }
a6adab20 463 }
a6993015 464 } else {
015f0dd7
MW
465 /* Start the container */
466 if (!c->is_running(c)) {
76e484a7 467 if (my_args.list) {
015f0dd7
MW
468 printf("%s %d\n", c->name,
469 get_config_integer(c, "lxc.start.delay"));
76e484a7
SG
470 fflush(stdout);
471 }
015f0dd7 472 else {
1cd7db65 473 if (!c->start(c, 0, NULL))
474 ERROR("Error starting container: %s", c->name);
015f0dd7
MW
475 else
476 sleep(get_config_integer(c, "lxc.start.delay"));
477 }
a6adab20
SG
478 }
479 }
a6adab20 480
015f0dd7
MW
481 /*
482 * If we get this far and we haven't hit any skip "continue"
483 * then we're done with this container... We can dump any
484 * c_groups_list and the container itself.
485 */
1cd7db65 486 if (lxc_container_put(c) > 0)
015f0dd7 487 containers[i] = NULL;
1cd7db65 488
489 if (c_groups_lists) {
015f0dd7
MW
490 toss_list(c_groups_lists[i]);
491 c_groups_lists[i] = NULL;
492 }
493 }
a6adab20 494
a6adab20
SG
495 }
496
e3e70db3
TH
497 /* clean up any lingering detritus, if container exists here
498 * then it must have failed to start.
499 */
500 failed = 0;
015f0dd7 501 for (i = 0; i < count; i++) {
e3e70db3
TH
502 if (containers[i]) {
503 failed++;
015f0dd7 504 lxc_container_put(containers[i]);
e3e70db3 505 }
a6993015 506 if (c_groups_lists && c_groups_lists[i])
015f0dd7 507 toss_list(c_groups_lists[i]);
015f0dd7
MW
508 }
509
f10fad2f 510 free(c_groups_lists);
1cd7db65 511 toss_list(cmd_groups_list);
a6adab20
SG
512 free(containers);
513
e3e70db3
TH
514 if (failed == count)
515 exit(1); /* Total failure */
516 else if (failed > 0)
517 exit(2); /* Partial failure */
518
b52b0595 519 exit(EXIT_SUCCESS);
a6adab20 520}