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