[lxc-devel] [PATCH 4/4] Add lxc-autostart
Serge Hallyn
serge.hallyn at ubuntu.com
Thu Dec 19 17:07:55 UTC 2013
Quoting Stéphane Graber (stgraber at ubuntu.com):
> Signed-off-by: Stéphane Graber <stgraber at 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.hallyn at 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 <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,
> +};
> +
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 at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
More information about the lxc-devel
mailing list