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