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