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