]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
Add lxc-autostart
authorStéphane Graber <stgraber@ubuntu.com>
Mon, 16 Dec 2013 20:32:47 +0000 (15:32 -0500)
committerStéphane Graber <stgraber@ubuntu.com>
Thu, 19 Dec 2013 20:56:25 +0000 (21:56 +0100)
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 <stgraber@ubuntu.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
.gitignore
configure.ac
doc/Makefile.am
doc/lxc-autostart.sgml.in [new file with mode: 0644]
src/lxc/Makefile.am
src/lxc/arguments.c
src/lxc/arguments.h
src/lxc/lxc_autostart.c [new file with mode: 0644]

index a38ceb01a80d42dfd113c7ae34584804c92fe622..1115ac6aaa80f475da0aabeeb332c90042eb0989 100644 (file)
@@ -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
index 9a0b73f106bc277b3fd52265e3864f90069aa4ec..052d38cde341b3c3cc28455600fac9137a142094 100644 (file)
@@ -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
index e327a46506a7d624e9deebed6585299aee629347..1b83f023833c670aa14afceb941eec4ae16bfcd2 100644 (file)
@@ -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 (file)
index 0000000..038abb7
--- /dev/null
@@ -0,0 +1,173 @@
+<!--
+
+lxc-autostart
+
+(C) Copyright 2013 Canonical Ltd.
+
+Authors:
+Stéphane Graber <stgraber@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>stgraber@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:
+-->
index 981e66a817ab13f2ebee91acb65d44ffb584e983..74b38e2dbbfa2ee285d3f0c5611591ccdc9465b0 100644 (file)
@@ -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
index adcf8fede75f7f6444b3d4499299e014b97f964f..bdde2f719b505b51da32fd0369a3e8aaeb0d3c3f 100644 (file)
@@ -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;
        }
index f574fc484a5416d5948abfdbb4bd57069ef7d24d..954ddccfcb125cf2128d3c858055f42be603779a 100644 (file)
@@ -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 (file)
index 0000000..4e208b9
--- /dev/null
@@ -0,0 +1,335 @@
+/* lxc_autostart
+ *
+ * Copyright © 2013 Stéphane Graber <stgraber@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 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;
+}