Signed-off-by: Stéphane Graber <stgra...@ubuntu.com> --- .gitignore | 1 + configure.ac | 1 + doc/Makefile.am | 1 + doc/lxc-autostart.sgml.in | 173 ++++++++++++++++++++++++ src/lxc/Makefile.am | 2 + src/lxc/arguments.c | 2 +- src/lxc/arguments.h | 5 + src/lxc/lxc_autostart.c | 335 ++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 519 insertions(+), 1 deletion(-) create mode 100644 doc/lxc-autostart.sgml.in create mode 100644 src/lxc/lxc_autostart.c
diff --git a/.gitignore b/.gitignore index a38ceb0..1115ac6 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 9a0b73f..052d38c 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 e327a46..1b83f02 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 0000000..038abb7 --- /dev/null +++ b/doc/lxc-autostart.sgml.in @@ -0,0 +1,173 @@ +<!-- + +lxc-autostart + +(C) Copyright 2013 Canonical Ltd. + +Authors: +Stéphane Graber <stgra...@ubuntu.com> + +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 + +--> + +<!DOCTYPE refentry PUBLIC @docdtd@ [ + <!ENTITY commonoptions SYSTEM "@builddir@/common_options.sgml"> + <!ENTITY seealso SYSTEM "@builddir@/see_also.sgml"> +]> + +<refentry> + <docinfo><date>@LXC_GENERATE_DATE@</date></docinfo> + <refmeta> + <refentrytitle>lxc-autostart</refentrytitle> + <manvolnum>1</manvolnum> + </refmeta> + + <refnamediv> + <refname>lxc-autostart</refname> + + <refpurpose> + start/stop/kill auto-started containers + </refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>lxc-autostart</command> + <arg choice="opt">-k</arg> + <arg choice="opt">-L</arg> + <arg choice="opt">-r</arg> + <arg choice="opt">-s</arg> + <arg choice="opt">-a</arg> + <arg choice="opt">-g <replaceable>groups</replaceable></arg> + <arg choice="opt">-t <replaceable>timeout</replaceable></arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>lxc-autostart</command> 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. + </para> + + <para> + The <optional>-r</optional>, <optional>-s</optional> + and <optional>-k</optional> options specify the action to perform. + If none is specified, then the containers will be started. + <optional>-a</optional> and <optional>-g</optional> are used to + specify which containers will be affected. By default only + containers without a lxc.group set will be affected. + <optional>-t TIMEOUT</optional> specifies the maximum amount + of time to wait for the container to complete the shutdown + or reboot. + </para> + </refsect1> + + <refsect1> + <title>Options</title> + <variablelist> + <varlistentry> + <term> + <option>-r,--reboot </option> + </term> + <listitem> + <para> + Request a reboot of the container. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>-s,--shutdown </option> + </term> + <listitem> + <para> + Only request a clean shutdown, do not kill the + container tasks if the clean shutdown fails. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>-k,--kill </option> + </term> + <listitem> + <para> + Rather than requesting a clean shutdown of the + container, explicitly kill all tasks in the container. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>-L,--list </option> + </term> + <listitem> + <para> + Rather than performing the action, just print + the container name. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term> + <option>-t,--timeout <replaceable>TIMEOUT</replaceable></option> + </term> + <listitem> + <para> + Wait TIMEOUT seconds before hard-stopping the + container of (in the reboot case) returning failure. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + &seealso; + + <refsect1> + <title>Author</title> + <para>Stéphane Graber <email>stgra...@ubuntu.com</email></para> + </refsect1> +</refentry> + +<!-- Keep this comment at the end of the file +Local variables: +mode: sgml +sgml-omittag:t +sgml-shorttag:t +sgml-minimize-attributes:nil +sgml-always-quote-attributes:t +sgml-indent-step:2 +sgml-indent-data:t +sgml-parent-document:nil +sgml-default-dtd-file:nil +sgml-exposed-tags:nil +sgml-local-catalogs:nil +sgml-local-ecat-files:nil +End: +--> diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 981e66a..74b38e2 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 adcf8fe..bdde2f7 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 f574fc4..954ddcc 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 0000000..48554a1 --- /dev/null +++ b/src/lxc/lxc_autostart.c @@ -0,0 +1,335 @@ +/* lxc_autostart + * + * Copyright © 2013 Stéphane Graber <stgra...@ubuntu.com> + * 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 <string.h> +#include <unistd.h> + +#include <lxc/lxccontainer.h> + +#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 compare_lists(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 = compare_lists(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; +} -- 1.8.5.2 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel