[lxc-devel] [PATCH 4/4] Add lxc-autostart
Stéphane Graber
stgraber at ubuntu.com
Thu Dec 19 15:26:43 UTC 2013
Signed-off-by: Stéphane Graber <stgraber at 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 <stgraber at 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 at 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 <stgraber at 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
More information about the lxc-devel
mailing list