]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_autostart.c
1209897ec81ba99b882c43efc1f0a2864867e20e
[mirror_lxc.git] / src / lxc / tools / lxc_autostart.c
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
21 #ifndef _GNU_SOURCE
22 #define _GNU_SOURCE 1
23 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <lxc/lxccontainer.h>
30
31 #include "arguments.h"
32 #include "config.h"
33 #include "list.h"
34 #include "log.h"
35 #include "utils.h"
36
37 lxc_log_define(lxc_autostart, lxc);
38
39 static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list);
40
41 struct lxc_list *cmd_groups_list = NULL;
42
43 static int my_parser(struct lxc_arguments *args, int c, char *arg)
44 {
45 switch (c) {
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;
71 }
72 return 0;
73 }
74
75 static 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'},
81 {"ignore-auto", no_argument, 0, 'A'},
82 {"groups", required_argument, 0, 'g'},
83 {"timeout", required_argument, 0, 't'},
84 {"help", no_argument, 0, 'h'},
85 LXC_COMMON_OPTIONS
86 };
87
88 static struct lxc_arguments my_args = {
89 .progname = "lxc-autostart",
90 .help = "\
91 \n\
92 lxc-autostart managed auto-started containers\n\
93 \n\
94 Options:\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\
101 -A, --ignore-auto ignore lxc.start.auto and select all matching containers\n\
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,
107 .timeout = 60,
108 };
109
110 static int list_contains_entry(char *str_ptr, struct lxc_list *p1) {
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 */
117 if (!p1 && (!str_ptr || !*str_ptr))
118 return 1;
119
120 if (!p1)
121 return 0;
122
123 lxc_list_for_each(it1, p1) {
124 if (strncmp(it1->elem, str_ptr, strlen(it1->elem)) == 0)
125 return 1;
126 }
127
128 return 0;
129 }
130
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.
134 */
135 static struct lxc_list *accumulate_list(char *input, char *delimiter,
136 struct lxc_list *str_list)
137 {
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);
145 if (!workstr)
146 return NULL;
147
148 workstr_list = str_list;
149 if (!workstr_list) {
150 workstr_list = malloc(sizeof(*workstr_list));
151 if (!workstr_list) {
152 free(workstr);
153 return NULL;
154 }
155
156 lxc_list_init(workstr_list);
157 }
158
159 for (workptr = workstr; workptr; workptr = next_ptr) {
160 /* We can't use strtok_r here because it collapses multiple
161 * delimiters into 1 making empty fields impossible...
162 */
163 next_ptr = strchr(workptr, *delimiter);
164 if (next_ptr)
165 *next_ptr++ = '\0';
166
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.
171 */
172 if (list_contains_entry(workptr, workstr_list)) {
173 if (*workptr)
174 ERROR("Duplicate group \"%s\" in list - ignoring", workptr);
175 else
176 ERROR("Duplicate NULL group in list - ignoring");
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
197 static struct lxc_list *get_list(char *input, char *delimiter)
198 {
199 char *workstr = NULL;
200 char *token = NULL;
201 struct lxc_list *worklist;
202 struct lxc_list *workstr_list;
203
204 workstr_list = malloc(sizeof(*workstr_list));
205 if (!workstr_list)
206 return NULL;
207
208 lxc_list_init(workstr_list);
209
210 workstr = strdup(input);
211 if (!workstr) {
212 free(workstr_list);
213 return NULL;
214 }
215
216 lxc_iterate_parts(token, workstr, delimiter) {
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
235 static struct lxc_list *get_config_list(struct lxc_container *c, char *key)
236 {
237 int len = 0;
238 char *value = NULL;
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
245 value = (char *)malloc(sizeof(char) * len + 1);
246 if (!value)
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
265 static int get_config_integer(struct lxc_container *c, char *key)
266 {
267 int len = 0, ret = 0;
268 char *value = NULL;
269
270 len = c->get_config_item(c, key, NULL, 0);
271 if (len < 0)
272 return 0;
273
274 value = (char *)malloc(sizeof(char) * len + 1);
275 if (!value)
276 return 0;
277
278 if (c->get_config_item(c, key, value, len + 1) != len) {
279 free(value);
280 return 0;
281 }
282
283 if (lxc_safe_int(value, &ret) < 0)
284 printf("Could not parse config item.\n");
285
286 free(value);
287
288 return ret;
289 }
290
291 static int cmporder(const void *p1, const void *p2)
292 {
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)
300 return strncmp(c1->name, c2->name, strlen(c1->name));
301
302 return (c1_order - c2_order);
303 }
304
305 static int toss_list(struct lxc_list *c_groups_list)
306 {
307 struct lxc_list *it, *next;
308
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);
316 }
317 free(c_groups_list);
318
319 return 1;
320 }
321
322 int main(int argc, char *argv[])
323 {
324 int count = 0, failed = 0, i = 0, ret = 0;
325 struct lxc_list *cmd_group;
326 struct lxc_container **containers = NULL;
327 struct lxc_list **c_groups_lists = NULL;
328 struct lxc_log log;
329
330 if (lxc_arguments_parse(&my_args, argc, argv))
331 exit(EXIT_FAILURE);
332
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 }
345
346 count = list_defined_containers(my_args.lxcpath[0], NULL, &containers);
347
348 if (count < 0)
349 exit(EXIT_FAILURE);
350
351 /* Allocate an array for our container group lists */
352 if (!my_args.all)
353 c_groups_lists = calloc( count, sizeof( struct lxc_list * ) );
354
355 qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
356
357 if (cmd_groups_list && my_args.all)
358 ERROR("Specifying -a (all) with -g (groups) doesn't make sense. All option overrides");
359
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)
366 cmd_groups_list = accumulate_list( "" , ",", NULL );
367
368 lxc_list_for_each(cmd_group, cmd_groups_list) {
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.
374 */
375 for (i = 0; i < count; i++) {
376 struct lxc_container *c = containers[i];
377
378 if (!c)
379 /* Skip - must have been already processed */
380 continue;
381
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.
385 */
386 if (!c->may_control(c)) {
387 /* We're done with this container */
388 if (lxc_container_put(c) > 0)
389 containers[i] = NULL;
390
391 continue;
392 }
393
394 if (!my_args.ignore_auto &&
395 get_config_integer(c, "lxc.start.auto") != 1) {
396 /* We're done with this container */
397 if (lxc_container_put(c) > 0)
398 containers[i] = NULL;
399
400 continue;
401 }
402
403 if (!my_args.all) {
404 /* Filter by group */
405 if (!c_groups_lists[i]) {
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]);
411 if (ret == 0) {
412 /* Not in the target group this pass so
413 * leave in the list for subsequent
414 * passes.
415 */
416 continue;
417 }
418 }
419
420 /* We have a candidate continer to process */
421 c->want_daemonize(c, 1);
422
423 if (my_args.shutdown) {
424 /* Shutdown the container */
425 if (c->is_running(c)) {
426 if (my_args.list) {
427 printf("%s\n", c->name);
428 fflush(stdout);
429 }
430 else {
431 if (!c->shutdown(c, my_args.timeout)) {
432 if (!c->stop(c))
433 ERROR("Error shutting down container: %s", c->name);
434 }
435 }
436 }
437 } else if (my_args.hardstop) {
438 /* Kill the container */
439 if (c->is_running(c)) {
440 if (my_args.list) {
441 printf("%s\n", c->name);
442 fflush(stdout);
443 }
444 else {
445 if (!c->stop(c))
446 ERROR("Error killing container: %s", c->name);
447 }
448 }
449 } else if (my_args.reboot) {
450 /* Reboot the container */
451 if (c->is_running(c)) {
452 if (my_args.list) {
453 printf("%s %d\n", c->name,
454 get_config_integer(c, "lxc.start.delay"));
455 fflush(stdout);
456 }
457 else {
458 if (!c->reboot(c))
459 ERROR("Error rebooting container: %s", c->name);
460 else
461 sleep(get_config_integer(c, "lxc.start.delay"));
462 }
463 }
464 } else {
465 /* Start the container */
466 if (!c->is_running(c)) {
467 if (my_args.list) {
468 printf("%s %d\n", c->name,
469 get_config_integer(c, "lxc.start.delay"));
470 fflush(stdout);
471 }
472 else {
473 if (!c->start(c, 0, NULL))
474 ERROR("Error starting container: %s", c->name);
475 else
476 sleep(get_config_integer(c, "lxc.start.delay"));
477 }
478 }
479 }
480
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 */
486 if (lxc_container_put(c) > 0)
487 containers[i] = NULL;
488
489 if (c_groups_lists) {
490 toss_list(c_groups_lists[i]);
491 c_groups_lists[i] = NULL;
492 }
493 }
494
495 }
496
497 /* clean up any lingering detritus, if container exists here
498 * then it must have failed to start.
499 */
500 failed = 0;
501 for (i = 0; i < count; i++) {
502 if (containers[i]) {
503 failed++;
504 lxc_container_put(containers[i]);
505 }
506 if (c_groups_lists && c_groups_lists[i])
507 toss_list(c_groups_lists[i]);
508 }
509
510 free(c_groups_lists);
511 toss_list(cmd_groups_list);
512 free(containers);
513
514 if (failed == count)
515 exit(1); /* Total failure */
516 else if (failed > 0)
517 exit(2); /* Partial failure */
518
519 exit(EXIT_SUCCESS);
520 }