]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/tools/lxc_autostart.c
1209897ec81ba99b882c43efc1f0a2864867e20e
3 * Copyright © 2013 Stéphane Graber <stgraber@ubuntu.com>
4 * Copyright © 2013 Canonical Ltd.
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.
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.
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
29 #include <lxc/lxccontainer.h>
31 #include "arguments.h"
37 lxc_log_define(lxc_autostart
, lxc
);
39 static struct lxc_list
*accumulate_list(char *input
, char *delimiter
, struct lxc_list
*str_list
);
41 struct lxc_list
*cmd_groups_list
= NULL
;
43 static int my_parser(struct lxc_arguments
*args
, int c
, char *arg
)
62 args
->ignore_auto
= 1;
65 cmd_groups_list
= accumulate_list(arg
, ",", cmd_groups_list
);
68 if (lxc_safe_long(arg
, &args
->timeout
) < 0)
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'},
88 static struct lxc_arguments my_args
= {
89 .progname
= "lxc-autostart",
92 lxc-autostart managed auto-started containers\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\
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
,
110 static int list_contains_entry(char *str_ptr
, struct lxc_list
*p1
) {
111 struct lxc_list
*it1
;
114 * If the entry is NULL or the empty string and the list
115 * is NULL, we have a match
117 if (!p1
&& (!str_ptr
|| !*str_ptr
))
123 lxc_list_for_each(it1
, p1
) {
124 if (strncmp(it1
->elem
, str_ptr
, strlen(it1
->elem
)) == 0)
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.
135 static struct lxc_list
*accumulate_list(char *input
, char *delimiter
,
136 struct lxc_list
*str_list
)
138 char *workstr
= NULL
;
139 char *workptr
= NULL
;
140 char *next_ptr
= NULL
;
141 struct lxc_list
*worklist
;
142 struct lxc_list
*workstr_list
;
144 workstr
= strdup(input
);
148 workstr_list
= str_list
;
150 workstr_list
= malloc(sizeof(*workstr_list
));
156 lxc_list_init(workstr_list
);
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...
163 next_ptr
= strchr(workptr
, *delimiter
);
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.
172 if (list_contains_entry(workptr
, workstr_list
)) {
174 ERROR("Duplicate group \"%s\" in list - ignoring", workptr
);
176 ERROR("Duplicate NULL group in list - ignoring");
178 worklist
= malloc(sizeof(*worklist
));
182 worklist
->elem
= strdup(workptr
);
183 if (!worklist
->elem
) {
188 lxc_list_add_tail(workstr_list
, worklist
);
197 static struct lxc_list
*get_list(char *input
, char *delimiter
)
199 char *workstr
= NULL
;
201 struct lxc_list
*worklist
;
202 struct lxc_list
*workstr_list
;
204 workstr_list
= malloc(sizeof(*workstr_list
));
208 lxc_list_init(workstr_list
);
210 workstr
= strdup(input
);
216 lxc_iterate_parts(token
, workstr
, delimiter
) {
217 worklist
= malloc(sizeof(*worklist
));
221 worklist
->elem
= strdup(token
);
222 if (!worklist
->elem
) {
227 lxc_list_add_tail(workstr_list
, worklist
);
235 static struct lxc_list
*get_config_list(struct lxc_container
*c
, char *key
)
239 struct lxc_list
*config_list
= NULL
;
241 len
= c
->get_config_item(c
, key
, NULL
, 0);
245 value
= (char *)malloc(sizeof(char) * len
+ 1);
249 if (c
->get_config_item(c
, key
, value
, len
+ 1) != len
) {
254 if (strlen(value
) == 0) {
259 config_list
= get_list(value
, "\n");
265 static int get_config_integer(struct lxc_container
*c
, char *key
)
267 int len
= 0, ret
= 0;
270 len
= c
->get_config_item(c
, key
, NULL
, 0);
274 value
= (char *)malloc(sizeof(char) * len
+ 1);
278 if (c
->get_config_item(c
, key
, value
, len
+ 1) != len
) {
283 if (lxc_safe_int(value
, &ret
) < 0)
284 printf("Could not parse config item.\n");
291 static int cmporder(const void *p1
, const void *p2
)
293 struct lxc_container
*c1
= *(struct lxc_container
**)p1
;
294 struct lxc_container
*c2
= *(struct lxc_container
**)p2
;
296 int c1_order
= get_config_integer(c1
, "lxc.start.order");
297 int c2_order
= get_config_integer(c2
, "lxc.start.order");
299 if (c1_order
== c2_order
)
300 return strncmp(c1
->name
, c2
->name
, strlen(c1
->name
));
302 return (c1_order
- c2_order
);
305 static int toss_list(struct lxc_list
*c_groups_list
)
307 struct lxc_list
*it
, *next
;
312 lxc_list_for_each_safe(it
, c_groups_list
, next
) {
322 int main(int argc
, char *argv
[])
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
;
330 if (lxc_arguments_parse(&my_args
, argc
, argv
))
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];
342 if (lxc_log_init(&log
))
346 count
= list_defined_containers(my_args
.lxcpath
[0], NULL
, &containers
);
351 /* Allocate an array for our container group lists */
353 c_groups_lists
= calloc( count
, sizeof( struct lxc_list
* ) );
355 qsort(&containers
[0], count
, sizeof(struct lxc_container
*), cmporder
);
357 if (cmd_groups_list
&& my_args
.all
)
358 ERROR("Specifying -a (all) with -g (groups) doesn't make sense. All option overrides");
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...
365 if (!cmd_groups_list
)
366 cmd_groups_list
= accumulate_list( "" , ",", NULL
);
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
375 for (i
= 0; i
< count
; i
++) {
376 struct lxc_container
*c
= containers
[i
];
379 /* Skip - must have been already processed */
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.
386 if (!c
->may_control(c
)) {
387 /* We're done with this container */
388 if (lxc_container_put(c
) > 0)
389 containers
[i
] = NULL
;
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
;
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");
410 ret
= list_contains_entry(cmd_group
->elem
, c_groups_lists
[i
]);
412 /* Not in the target group this pass so
413 * leave in the list for subsequent
420 /* We have a candidate continer to process */
421 c
->want_daemonize(c
, 1);
423 if (my_args
.shutdown
) {
424 /* Shutdown the container */
425 if (c
->is_running(c
)) {
427 printf("%s\n", c
->name
);
431 if (!c
->shutdown(c
, my_args
.timeout
)) {
433 ERROR("Error shutting down container: %s", c
->name
);
437 } else if (my_args
.hardstop
) {
438 /* Kill the container */
439 if (c
->is_running(c
)) {
441 printf("%s\n", c
->name
);
446 ERROR("Error killing container: %s", c
->name
);
449 } else if (my_args
.reboot
) {
450 /* Reboot the container */
451 if (c
->is_running(c
)) {
453 printf("%s %d\n", c
->name
,
454 get_config_integer(c
, "lxc.start.delay"));
459 ERROR("Error rebooting container: %s", c
->name
);
461 sleep(get_config_integer(c
, "lxc.start.delay"));
465 /* Start the container */
466 if (!c
->is_running(c
)) {
468 printf("%s %d\n", c
->name
,
469 get_config_integer(c
, "lxc.start.delay"));
473 if (!c
->start(c
, 0, NULL
))
474 ERROR("Error starting container: %s", c
->name
);
476 sleep(get_config_integer(c
, "lxc.start.delay"));
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.
486 if (lxc_container_put(c
) > 0)
487 containers
[i
] = NULL
;
489 if (c_groups_lists
) {
490 toss_list(c_groups_lists
[i
]);
491 c_groups_lists
[i
] = NULL
;
497 /* clean up any lingering detritus, if container exists here
498 * then it must have failed to start.
501 for (i
= 0; i
< count
; i
++) {
504 lxc_container_put(containers
[i
]);
506 if (c_groups_lists
&& c_groups_lists
[i
])
507 toss_list(c_groups_lists
[i
]);
510 free(c_groups_lists
);
511 toss_list(cmd_groups_list
);
515 exit(1); /* Total failure */
517 exit(2); /* Partial failure */