From: Stéphane Graber Date: Mon, 16 Dec 2013 20:32:47 +0000 (-0500) Subject: Add lxc-autostart X-Git-Tag: lxc-1.0.0.beta2~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a6adab20ff4b58887ff1d4314c5736f54e139386;p=thirdparty%2Flxc.git Add lxc-autostart This introduces a new lxc-autostart binary (and associated manpage) which will let you start/shutdown/kill/restart any container that's marked as lxc.start.auto=1. It respects the lxc.start.delay value, sorts by lxc.start.order and filters by lxc.group. By default it'll affect all containers that DO NOT have lxc.group set. If -g is specified, ONLY containers in those group will be affected. To have a command applied to all containers, the -a argument can be used. A -L flag is also offered for distributions wishing to start the containers themselves while still using LXC's calculated order and wait delays. Instead of performing the action, it'll print the container name and (if relevant for the action) the wait time. Signed-off-by: Stéphane Graber Acked-by: Serge E. Hallyn --- diff --git a/.gitignore b/.gitignore index a38ceb01a..1115ac6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ templates/lxc-ubuntu-cloud src/lxc/lxc-attach +src/lxc/lxc-autostart src/lxc/lxc-cgroup src/lxc/lxc-checkconfig src/lxc/lxc-checkpoint diff --git a/configure.ac b/configure.ac index 9a0b73f10..052d38cde 100644 --- a/configure.ac +++ b/configure.ac @@ -474,6 +474,7 @@ AC_CONFIG_FILES([ doc/api/Makefile doc/legacy/lxc-ls.sgml doc/lxc-attach.sgml + doc/lxc-autostart.sgml doc/lxc-cgroup.sgml doc/lxc-checkconfig.sgml doc/lxc-checkpoint.sgml diff --git a/doc/Makefile.am b/doc/Makefile.am index e327a4650..1b83f0238 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -15,6 +15,7 @@ EXTRA_DIST = \ if ENABLE_DOCBOOK man_MANS = \ lxc-attach.1 \ + lxc-autostart.1 \ lxc-cgroup.1 \ lxc-checkconfig.1 \ lxc-checkpoint.1 \ diff --git a/doc/lxc-autostart.sgml.in b/doc/lxc-autostart.sgml.in new file mode 100644 index 000000000..038abb709 --- /dev/null +++ b/doc/lxc-autostart.sgml.in @@ -0,0 +1,173 @@ + + + + +]> + + + @LXC_GENERATE_DATE@ + + lxc-autostart + 1 + + + + lxc-autostart + + + start/stop/kill auto-started containers + + + + + + lxc-autostart + -k + -L + -r + -s + -a + -g groups + -t timeout + + + + + Description + + + lxc-autostart processes containers + with lxc.start.auto set. It lets the user start, shutdown, + kill, restart containers in the right order, waiting the + right time. Supports filtering by lxc.group or just run + against all defined containers. It can also be used by + external tools in list mode where no action will be performed + and the list of affected containers (and if relevant, delays) + will be shown. + + + + The -r, -s + and -k options specify the action to perform. + If none is specified, then the containers will be started. + -a and -g are used to + specify which containers will be affected. By default only + containers without a lxc.group set will be affected. + -t TIMEOUT specifies the maximum amount + of time to wait for the container to complete the shutdown + or reboot. + + + + + Options + + + + + + + + Request a reboot of the container. + + + + + + + + + + + Only request a clean shutdown, do not kill the + container tasks if the clean shutdown fails. + + + + + + + + + + + Rather than requesting a clean shutdown of the + container, explicitly kill all tasks in the container. + + + + + + + + + + + Rather than performing the action, just print + the container name. + + + + + + + + + + + Wait TIMEOUT seconds before hard-stopping the + container of (in the reboot case) returning failure. + + + + + + + &seealso; + + + Author + Stéphane Graber stgraber@ubuntu.com + + + + diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 981e66a81..74b38e2db 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -170,6 +170,7 @@ endif bin_PROGRAMS = \ lxc-attach \ + lxc-autostart \ lxc-unshare \ lxc-stop \ lxc-start \ @@ -203,6 +204,7 @@ endif LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ @SECCOMP_LIBS@ lxc_attach_SOURCES = lxc_attach.c +lxc_autostart_SOURCES = lxc_autostart.c lxc_cgroup_SOURCES = lxc_cgroup.c lxc_checkpoint_SOURCES = lxc_checkpoint.c lxc_config_SOURCES = lxc_config.c diff --git a/src/lxc/arguments.c b/src/lxc/arguments.c index adcf8fede..bdde2f719 100644 --- a/src/lxc/arguments.c +++ b/src/lxc/arguments.c @@ -229,7 +229,7 @@ extern int lxc_arguments_parse(struct lxc_arguments *args, /* Check the command options */ - if (!args->name) { + if (!args->name && strcmp(args->progname, "lxc-autostart") != 0) { lxc_error(args, "missing container name, use --name option"); return -1; } diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index f574fc484..954ddccfc 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -89,6 +89,11 @@ struct lxc_arguments { char *lvname, *vgname, *thinpool; char *zfsroot, *lowerdir, *dir; + /* auto-start */ + int all; + int list; + char *groups; + /* remaining arguments */ char *const *argv; int argc; diff --git a/src/lxc/lxc_autostart.c b/src/lxc/lxc_autostart.c new file mode 100644 index 000000000..4e208b98d --- /dev/null +++ b/src/lxc/lxc_autostart.c @@ -0,0 +1,335 @@ +/* lxc_autostart + * + * Copyright © 2013 Stéphane Graber + * Copyright © 2013 Canonical Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include + +#include "arguments.h" +#include "list.h" +#include "log.h" + +static int my_parser(struct lxc_arguments* args, int c, char* arg) +{ + switch (c) { + case 'k': args->hardstop = 1; break; + case 'L': args->list = 1; break; + case 'r': args->reboot = 1; break; + case 's': args->shutdown = 1; break; + case 'a': args->all = 1; break; + case 'g': args->groups = arg; break; + case 't': args->timeout = atoi(arg); break; + } + return 0; +} + +static const struct option my_longopts[] = { + {"kill", no_argument, 0, 'k'}, + {"list", no_argument, 0, 'L'}, + {"reboot", no_argument, 0, 'r'}, + {"shutdown", no_argument, 0, 's'}, + {"all", no_argument, 0, 'a'}, + {"groups", required_argument, 0, 'g'}, + {"timeout", required_argument, 0, 't'}, + {"help", no_argument, 0, 'h'}, + LXC_COMMON_OPTIONS +}; + +static struct lxc_arguments my_args = { + .progname = "lxc-autostart", + .help = "\ +\n\ +lxc-autostart managed auto-started containers\n\ +\n\ +Options:\n\ + -k, --kill kill the containers instead of starting them\n\ + -L, --list list all affected containers and wait delay\n\ + -r, --reboot reboot the containers instead of starting them\n\ + -s, --shutdown shutdown the containers instead of starting them\n\ +\n\ + -a, --all list all auto-started containers (ignore groups)\n\ + -g, --groups list of groups (comma separated) to select\n\ + -t, --timeout=T wait T seconds before hard-stopping\n", + .options = my_longopts, + .parser = my_parser, + .checker = NULL, + .timeout = 30, +}; + +int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) { + struct lxc_list *it1; + struct lxc_list *it2; + + if (!p1 && !p2) + return 1; + + if (!p1) + return 0; + + if (!p2) + return 0; + + lxc_list_for_each(it1, p1) { + lxc_list_for_each(it2, p2) { + if (strcmp(it1->elem, it2->elem) == 0) + return 1; + } + } + + return 0; +} + +struct lxc_list *get_list(char *input, char *delimiter) { + char *workstr = NULL; + char *workptr = NULL; + char *sptr = NULL; + char *token = NULL; + struct lxc_list *worklist; + struct lxc_list *workstr_list; + + workstr_list = malloc(sizeof(*workstr_list)); + lxc_list_init(workstr_list); + + workstr = strdup(input); + if (!workstr) + return NULL; + + for (workptr = workstr;;workptr = NULL) { + token = strtok_r(workptr, delimiter, &sptr); + if (!token) { + break; + } + + worklist = malloc(sizeof(*worklist)); + if (!worklist) + break; + + worklist->elem = strdup(token); + if (!worklist->elem) { + free(worklist); + break; + } + + lxc_list_add_tail(workstr_list, worklist); + } + + free(workstr); + + return workstr_list; +} + +struct lxc_list *get_config_list(struct lxc_container *c, char *key) { + int len = 0; + char* value = NULL; + struct lxc_list *config_list = NULL; + + len = c->get_config_item(c, key, NULL, 0); + if (len < 0) + return NULL; + + value = (char*) malloc(sizeof(char)*len + 1); + if (value == NULL) + return NULL; + + if (c->get_config_item(c, key, value, len + 1) != len) { + free(value); + return NULL; + } + + if (strlen(value) == 0) { + free(value); + return NULL; + } + + config_list = get_list(value, "\n"); + free(value); + + return config_list; +} + +int get_config_integer(struct lxc_container *c, char *key) { + int len = 0; + int ret = 0; + char* value = NULL; + + len = c->get_config_item(c, key, NULL, 0); + if (len < 0) + return 0; + + value = (char*) malloc(sizeof(char)*len + 1); + if (value == NULL) + return 0; + + if (c->get_config_item(c, key, value, len + 1) != len) { + free(value); + return 0; + } + + ret = atoi(value); + free(value); + + return ret; +} + +static int cmporder(const void *p1, const void *p2) { + struct lxc_container *c1 = *(struct lxc_container **)p1; + struct lxc_container *c2 = *(struct lxc_container **)p2; + + int c1_order = get_config_integer(c1, "lxc.start.order"); + int c2_order = get_config_integer(c2, "lxc.start.order"); + + if (c1_order == c2_order) + return strcmp(c1->name, c2->name); + else + return (c1_order - c2_order) * -1; +} + +int main(int argc, char *argv[]) +{ + int count = 0; + int i = 0; + int ret = 0; + struct lxc_container **containers = NULL; + struct lxc_list *cmd_groups_list = NULL; + struct lxc_list *c_groups_list = NULL; + struct lxc_list *it, *next; + char *const default_start_args[] = { + "/sbin/init", + '\0', + }; + + if (lxc_arguments_parse(&my_args, argc, argv)) + return 1; + + count = list_defined_containers(NULL, NULL, &containers); + + if (count < 0) + return 1; + + qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder); + + if (my_args.groups && !my_args.all) + cmd_groups_list = get_list((char*)my_args.groups, ","); + + for (i = 0; i < count; i++) { + struct lxc_container *c = containers[i]; + + if (!c->may_control(c)) { + lxc_container_put(c); + continue; + } + + if (get_config_integer(c, "lxc.start.auto") != 1) { + lxc_container_put(c); + continue; + } + + if (!my_args.all) { + /* Filter by group */ + c_groups_list = get_config_list(c, "lxc.group"); + + ret = lists_contain_common_entry(cmd_groups_list, c_groups_list); + + if (c_groups_list) { + lxc_list_for_each_safe(it, c_groups_list, next) { + lxc_list_del(it); + free(it->elem); + free(it); + } + free(c_groups_list); + } + + if (ret == 0) { + lxc_container_put(c); + continue; + } + } + + c->want_daemonize(c, 1); + + if (my_args.shutdown) { + /* Shutdown the container */ + if (c->is_running(c)) { + if (my_args.list) + printf("%s\n", c->name); + else { + if (!c->shutdown(c, my_args.timeout)) + fprintf(stderr, "Error shutting down container: %s\n", c->name); + } + } + } + else if (my_args.hardstop) { + /* Kill the container */ + if (c->is_running(c)) { + if (my_args.list) + printf("%s\n", c->name); + else { + if (!c->stop(c)) + fprintf(stderr, "Error killing container: %s\n", c->name); + } + } + } + else if (my_args.reboot) { + /* Reboot the container */ + if (c->is_running(c)) { + if (my_args.list) + printf("%s %d\n", c->name, + get_config_integer(c, "lxc.start.delay")); + else { + if (!c->reboot(c)) + fprintf(stderr, "Error rebooting container: %s\n", c->name); + else + sleep(get_config_integer(c, "lxc.start.delay")); + } + } + } + else { + /* Start the container */ + if (!c->is_running(c)) { + if (my_args.list) + printf("%s %d\n", c->name, + get_config_integer(c, "lxc.start.delay")); + else { + if (!c->start(c, 0, default_start_args)) + fprintf(stderr, "Error starting container: %s\n", c->name); + else + sleep(get_config_integer(c, "lxc.start.delay")); + } + } + } + + + lxc_container_put(c); + } + + if (cmd_groups_list) { + lxc_list_for_each_safe(it, cmd_groups_list, next) { + lxc_list_del(it); + free(it->elem); + free(it); + } + free(cmd_groups_list); + } + + free(containers); + + return 0; +}