Quoting Stéphane Graber (stgra...@ubuntu.com): > Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
You're trying to shame me aren't you, with your .gitignore entry and your manpage and your idunno... Acked-by: Serge E. Hallyn <serge.hal...@ubuntu.com> One trivial comment below. > --- > .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, > +}; > + compare_lists needs a comment explaining what it actually does, because it doesnt' work the way you'd normally expect a cmp function to. Just "Returns true if both lists are empty, or if any item in @p1 is present in @p2." Or rename it to contains_list_entry_in() or something. > +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 _______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel