[lxc-devel] [PATCH] v2 Refactoring lxc-autostart boot process and group handling.

Stéphane Graber stgraber at ubuntu.com
Tue May 20 09:56:37 UTC 2014


On Mon, May 19, 2014 at 03:57:26PM -0400, Michael H. Warfield wrote:
> On Mon, 2014-05-19 at 17:22 +0200, Stéphane Graber wrote:
> > On Fri, May 16, 2014 at 02:07:31PM -0400, Michael H. Warfield wrote:
> > > Before anyone else spots it...  I did miss one spot where I failed to
> > > toss a list (cmd_group_lists) on exit.  So, some memory checkers will
> > > complain about orphaned memory or leaks (even though it's on exit).
> > > I'll fix that and add some doco once this has been reviewed further.
> 
> > Hi,
> 
> > I took a quick look at the proposed patch and don't have any issue with
> > it, so please resend with those updates done and I'll do some proper
> > testing and apply it.
> 
> > Thanks!
> 
> Ok...  Ask and yea shall receive.  Version 2 of the refactoring
> autostart patch with Dwight's patch and my other patches adding now the
> fix for the minor cleanup gotcha I spotted plus I enhanced the
> documentation in lxc-autostart.sgml.in for group handling.
> 
> While this was going on, I also pinged Dwight about parameterizing the
> bootup groups and other options in the startup scripts.  Consequently,
> with his concurrence, I've added some boot time configuration options to
> the sysvinit/systemd init script and the upstart configuration file for
> BOOTGROUPS, SHUTDOWNDELAY, OPTIONS, and STOPOPTS.  For the former
> (Oracle, RHEL Fedora, CentOS, et al), it's in /etc/sysconfig/lxc and the
> later (Ubuntu, Debian, etc) in /etc/default/lxc.  I've tested the
> sysvinit/systemd init script.  Someone needs to verify the upstart
> changes.
> 
> Attached below the jump.
> 
> Thanks!
> 
> Regards,
> Mike

 ==> Executing: "./autogen.sh" in /build/git/
+ test -d autom4te.cache
+ aclocal -I config
+ autoheader
+ autoconf
+ automake --add-missing --copy
configure.ac:31: installing 'config/compile'
configure.ac:30: installing 'config/config.guess'
configure.ac:30: installing 'config/config.sub'
configure.ac:29: installing 'config/install-sh'
configure.ac:29: installing 'config/missing'
configure.ac:565: error: required file 'config/init/systemd/lxc.service.in' not found
configure.ac:565: error: required file 'config/init/sysvinit/lxc.in' not found
src/lua-lxc/Makefile.am: installing 'config/depcomp'
+ exit 1
 ==> Cleaning up the environment
 ==> Exitting with status FAIL

Seems like "make dist" is missing a bunch of files...


> -- 
> Michael H. Warfield (AI4NB) | (770) 978-7061 |  mhw at WittsEnd.com
>    /\/\|=mhw=|\/\/          | (678) 463-0932 |  http://www.wittsend.com/mhw/
>    NIC whois: MHW9          | An optimist believes we live in the best of all
>  PGP Key: 0x674627FF        | possible worlds.  A pessimist is sure of it!
> 
> 
> -- 
> v2 - Refactoring lxc-autostart boot process and group handling.
> 
> This is a rollup of 5 earlier patches patching the systemd
> init to use the sysvinit script, adding an onboot group to the
> boot set, updating upstart to include the onboot group, and adding
> documentation for the special boot groups.
> 
> This adds new functionality to lxc-autostart.
> 
> *) The -g / --groups option is multiple cummulative entry.
> 	This may be mixed freely with the previous comma separated
> 	group list convention.  Groups are processed in the
> 	order they first appear in the aggregated group list.
> 
> *) The NULL group may be specified in the group list using either a
> 	leading comma, a trailing comma, or an embedded comma.
> 
> *) Booting proceeds in order of the groups specified on the command line
> 	then ordered by lxc.start.org and name collalating sequence.
> 
> *) Default host bootup is now specified as "-g onboot," meaning that first
> 	the "onboot" group is booted and then any remaining enabled
> 	containers in the NULL group are booted.
> 
> *) Adds documentation to lxc-autostart for -g processing order and combinations.
> 
> *) Parameterizes bootgroups, options, and shutdown delay in init scripts
> 	and services.
> 
> From the previous 4 individual patches:
> 
> Reported-by: CDR <venefax at gmail.com>
> Signed-off-by: Dwight Engen <dwight.engen at oracle.com>
> 
> - reuse the sysvinit script to ensure that if the lxc is configured to use
>   a bridge setup by libvirt, the bridge will be available before starting
>   the container
> 
> - made the sysvinit script check for the existance of ifconfig, and fall
>   back to ip link list if available
> 
> - made the lxc service also dependant on the network.target
> 
> - autoconfized the paths in the service file and sysvinit script
> 
> - v2: rename script lxc-autostart to lxc-autostart-helper to avoid confusion
> 
> From: Michael H. Warfield <mhw at WittsEnd.com>
> 
> - This adds a non-null group (onboot) to the sysvinit startup script
> for autobooting containers.  This allows for containers which are
> in other groups to be included in the autoboot process.
> 
> This script is used by both the sysvinit systems and the systemd
> systems.
> 
> From: Michael H. Warfield <mhw at WittsEnd.com>
> 
> - Add the feature to the Upstart init script to boot the "onboot"
> group dependent on the start.auto = 1 flag.  This brings the
> the Upstart behavior into congruence with the sysvinit script
> for SysV Init and Systemd.
> 
> From: Michael H. Warfield <mhw at WittsEnd.com>
> 
> Added sections to lxc-autostart and lxc.container.config to document
> the behavior of the LXC service at host system boot time with regards
> to the lxc.group and lxc.start.auto parameters.
> 
> Signed-off-by: Michael H. Warfield <mhw at WittsEnd.com>
> ---
>  .gitignore                      |   3 +
>  config/init/systemd/Makefile.am |  14 +-
>  config/init/systemd/lxc.service |  17 --
>  config/init/sysvinit/lxc        |  66 --------
>  config/init/upstart/lxc.conf    |  44 +++++-
>  configure.ac                    |   2 +
>  doc/lxc-autostart.sgml.in       |  76 ++++++++-
>  doc/lxc.container.conf.sgml.in  |  23 +++
>  lxc.spec.in                     |   1 +
>  src/lxc/lxc_autostart.c         | 333 ++++++++++++++++++++++++++++++----------
>  10 files changed, 407 insertions(+), 172 deletions(-)
>  delete mode 100644 config/init/systemd/lxc.service
>  delete mode 100755 config/init/sysvinit/lxc
> 
> diff --git a/.gitignore b/.gitignore
> index 8145f81..2b478cd 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -111,6 +111,9 @@ config/missing
>  config/libtool.m4
>  config/lt*.m4
>  config/bash/lxc
> +config/init/systemd/lxc-autostart-helper
> +config/init/systemd/lxc.service
> +config/init/sysvinit/lxc
>  
>  doc/*.1
>  doc/*.5
> diff --git a/config/init/systemd/Makefile.am b/config/init/systemd/Makefile.am
> index de5ee50..fc374c5 100644
> --- a/config/init/systemd/Makefile.am
> +++ b/config/init/systemd/Makefile.am
> @@ -5,7 +5,17 @@ EXTRA_DIST = \
>  if INIT_SCRIPT_SYSTEMD
>  SYSTEMD_UNIT_DIR = $(prefix)/lib/systemd/system
>  
> -install-systemd: lxc.service lxc-devsetup
> +lxc-autostart-helper: ../sysvinit/lxc.in $(top_builddir)/config.status
> +	$(AM_V_GEN)sed                                          \
> +	    -e 's|[@]SYSCONFDIR[@]|$(sysconfdir)|g'             \
> +	    -e 's|[@]LOCALSTATEDIR[@]|$(localstatedir)|g'       \
> +	    -e 's|[@]BINDIR[@]|$(bindir)|g'                     \
> +	    < $< > $@-t &&                                      \
> +	    chmod a+x $@-t &&                                   \
> +	    mv $@-t $@
> +BUILT_SOURCES = lxc-autostart-helper
> +
> +install-systemd: lxc.service lxc-devsetup lxc-autostart-helper
>  	$(MKDIR_P) $(DESTDIR)$(SYSTEMD_UNIT_DIR)
>  	$(INSTALL_DATA) lxc.service $(DESTDIR)$(SYSTEMD_UNIT_DIR)/
>  
> @@ -13,7 +23,7 @@ uninstall-systemd:
>  	rm -f $(DESTDIR)$(SYSTEMD_UNIT_DIR)/lxc.service
>  	rmdir $(DESTDIR)$(SYSTEMD_UNIT_DIR) || :
>  
> -pkglibexec_SCRIPTS = lxc-devsetup
> +pkglibexec_SCRIPTS = lxc-devsetup lxc-autostart-helper
>  
>  install-data-local: install-systemd
>  uninstall-local: uninstall-systemd
> diff --git a/config/init/systemd/lxc.service b/config/init/systemd/lxc.service
> deleted file mode 100644
> index aa20b91..0000000
> --- a/config/init/systemd/lxc.service
> +++ /dev/null
> @@ -1,17 +0,0 @@
> -[Unit]
> -Description=LXC Container Initialization and Autoboot Code
> -After=syslog.target
> -
> -[Service]
> -Type=oneshot
> -RemainAfterExit=yes
> -ExecStartPre=/usr/libexec/lxc/lxc-devsetup
> -ExecStart=/usr/libexec/lxc/lxc-startup start
> -ExecStop=/usr/libexec/lxc/lxc-startup stop
> -# Environment=BOOTUP=serial
> -# Environment=CONSOLETYPE=serial
> -StandardOutput=syslog
> -StandardError=syslog
> -
> -[Install]
> -WantedBy=multi-user.target
> diff --git a/config/init/sysvinit/lxc b/config/init/sysvinit/lxc
> deleted file mode 100755
> index 5ab3c46..0000000
> --- a/config/init/sysvinit/lxc
> +++ /dev/null
> @@ -1,66 +0,0 @@
> -#!/bin/sh
> -#
> -# lxc Start/Stop LXC autoboot containers
> -#
> -# chkconfig: 345 99 01
> -# description: Starts/Stops all LXC containers configured for autostart.
> -#
> -### BEGIN INIT INFO
> -# Provides: lxc
> -# Default-Start: 3 4 5
> -# Default-Stop: 0 1 6
> -# Short-Description: Bring up/down LXC autostart containers
> -# Description: Bring up/down LXC autostart containers
> -### END INIT INFO
> -
> -# Source function library.
> -. /etc/init.d/functions
> -
> -# Check for needed utility program
> -[ -x /usr/bin/lxc-autostart ] || exit 1
> -
> -# If libvirtd is providing the bridge, it might not be
> -# immediately available, so wait a bit for it before starting
> -# up the containers or else any that use the bridge will fail
> -# to start
> -wait_for_bridge()
> -{
> -    [ -f /etc/lxc/default.conf ] || { return 0; }
> -
> -    BRNAME=`grep '^[ 	]*lxc.network.link' /etc/lxc/default.conf | sed 's/^.*=[ 	]*//'`
> -    if [ -z "$BRNAME" ]; then
> -	return 0
> -    fi
> -
> -    for try in `seq 1 30`; do
> -	ifconfig -a |grep "^$BRNAME" >/dev/null 2>&1
> -	if [ $? = 0 ]; then
> -	    return
> -	fi
> -	sleep 1
> -    done
> -}
> -
> -# See how we were called.
> -case "$1" in
> -  start)
> -	[ ! -f /var/lock/subsys/lxc ] || { exit 0; }
> -
> -	# Start containers
> -	wait_for_bridge
> -	action $"Starting LXC containers: " /usr/bin/lxc-autostart
> -	touch /var/lock/subsys/lxc
> -	;;
> -  stop)
> -	action $"Stopping LXC containers: " /usr/bin/lxc-autostart -a -A -s
> -	rm -f /var/lock/subsys/lxc
> -	;;
> -  restart|reload|force-reload)
> -	$0 stop
> -	$0 start
> -	;;
> -  *)
> -	echo $"Usage: $0 {start|stop|restart|reload|force-reload}"
> -	exit 2
> -esac
> -exit $?
> diff --git a/config/init/upstart/lxc.conf b/config/init/upstart/lxc.conf
> index d5131ad..ab79aab 100644
> --- a/config/init/upstart/lxc.conf
> +++ b/config/init/upstart/lxc.conf
> @@ -6,6 +6,30 @@ stop on starting rc RUNLEVEL=[016]
>  
>  env LXC_AUTO="false"
>  
> +# These can be overridden in /etc/default/lxc
> +
> +# BOOTGROUPS - What groups should start on bootup?
> +#	Comma separated list of groups.
> +#	Leading comma, trailing comma or embedded double
> +#	comma indicates when the NULL group should be run.
> +# Example (default): boot the onboot group first then the NULL group
> +env BOOTGROUPS="onboot,"
> +
> +# SHUTDOWNDELAY - Wait time for a container to shut down.
> +#	Container shutdown can result in lengthy system
> +#	shutdown times.  Even 5 seconds per container can be
> +#	too long.
> +env SHUTDOWNDELAY=5
> +
> +# OPTIONS can be used for anything else.
> +#	If you want to boot everything then
> +#	options can be "-a" or "-a -A".
> +env OPTIONS=
> +
> +# STOPOPTS are stop options.  The can be used for anything else to stop.
> +#	If you want to kill containers fast, use -k
> +env STOPOPTS="-a -A -s"
> +
>  pre-start script
>  	[ -f /etc/default/lxc ] && . /etc/default/lxc
>  
> @@ -20,12 +44,28 @@ pre-start script
>  
>  	[ "x$LXC_AUTO" = "xtrue" ] || exit 0
>  
> -	lxc-autostart -L | while read line; do
> +	if [ -n "$BOOTGROUPS" ]
> +	then
> +		BOOTGROUPS="-g $BOOTGROUPS"
> +	fi
> +
> +	# Process the "onboot" group first then the NULL group.
> +	lxc-autostart -L $OPTIONS $BOOTGROUPS | while read line; do
>  		set -- $line
>  		(start lxc-instance NAME=$1 && sleep $2) || true
>  	done
>  end script
>  
> +# The stop is serialized and can take excessive time.  We need to avoid
> +# delaying the system shutdown / reboot as much as we can since it's not
> +# parallelized...  Even 5 second timout may be too long.
>  post-stop script
> -	lxc-autostart -a -A -s || true
> +	[ -f /etc/default/lxc ] && . /etc/default/lxc
> +
> +	if [ -n "$SHUTDOWNDELAY" ]
> +	then
> +		SHUTDOWNDELAY="-t $SHUTDOWNDELAY"
> +	fi
> +
> +	lxc-autostart $STOPOPTS $SHUTDOWNDELAY || true
>  end script
> diff --git a/configure.ac b/configure.ac
> index 8865bc8..a7fce11 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -573,7 +573,9 @@ AC_CONFIG_FILES([
>  	config/bash/lxc
>  	config/init/Makefile
>  	config/init/sysvinit/Makefile
> +	config/init/sysvinit/lxc
>  	config/init/systemd/Makefile
> +	config/init/systemd/lxc.service
>  	config/init/upstart/Makefile
>  	config/etc/Makefile
>  	config/templates/Makefile
> diff --git a/doc/lxc-autostart.sgml.in b/doc/lxc-autostart.sgml.in
> index 3d423dd..3e12d5c 100644
> --- a/doc/lxc-autostart.sgml.in
> +++ b/doc/lxc-autostart.sgml.in
> @@ -154,8 +154,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>                  </term>
>                  <listitem>
>                      <para>
> -                        Comma separate list of groups to select
> -                        (defaults to those without a lxc.group).
> +                        Comma separated list of groups to select
> +                        (defaults to those without a lxc.group - the NULL group).
> +                        This option may be specified multiple times
> +                        and the arguments concatentated.  The NULL or
> +                        empty group may be specified as a leading comma,
> +                        trailing comma, embedded double comma, or empty
> +                        argument where the NULL group should be processed.
> +                        Groups are processed in the order specified on the
> +                        command line.  Multiple invocations of the -g option
> +                        may be freely intermixed with the comma separated
> +                        lists and will be combined in specified order.
>                      </para>
>                  </listitem>
>              </varlistentry>
> @@ -185,6 +194,69 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>          </variablelist>
>      </refsect1>
>  
> +    <refsect1>
> +        <title>Autostart and System Boot</title>
> +
> +        <para>
> +            The <command>lxc-autostart</command> command is used as part of the
> +            LXC system service, when enabled to run on host system at bootup and at
> +            shutdown.  It's used to select which containers to start in what order
> +            and how much to delay between each startup when the host system boots.
> +        </para>
> +
> +        <para>
> +            Each container can be part of any number of groups or no group at all.
> +            Two groups are special. One is the NULL group, i.e. the container does
> +            not belong to any group. The other group is the "onboot" group.
> +        </para>
> +
> +        <para>
> +            When the system boots with the LXC service enabled, it will first
> +            attempt to boot any containers with lxc.start.auto == 1 that is a member
> +            of the "onboot" group. The startup will be in order of lxc.start.order.
> +            If an lxc.start.delay has been specified, that delay will be honored
> +            before attempting to start the next container to give the current
> +            container time to begin initialization and reduce overloading the host
> +            system. After starting the members of the "onboot" group, the LXC system
> +            will proceed to boot containers with lxc.start.auto == 1 which are not
> +            members of any group (the NULL group) and proceed as with the onboot
> +            group.
> +        </para>
> +    </refsect1>
> +
> +    <refsect1>
> +        <title>Startup Group Examples</title>
> +        <variablelist>
> +            <varlistentry>
> +                <term>
> +                    <option>-g "onboot,"</option>
> +                </term>
> +                <listitem>
> +                    <para>
> +                        Start the "onboot" group first then the NULL group.
> +                    </para>
> +                    <para>
> +                        This is the equivalent of: <option>-g onboot -g ""</option>.
> +                    </para>
> +                </listitem>
> +            </varlistentry>
> +            <varlistentry>
> +                <term>
> +                    <option>-g "dns,web,,onboot"</option>
> +                </term>
> +                <listitem>
> +                    <para>
> +                        Starts the "dns" group first, the "web" group second, then
> +                        the NULL group followed by the "onboot" group.
> +                    </para>
> +                    <para>
> +                        This is the equivalent of: <option>-g dns,web -g ,onboot</option> or <option>-g dns -g web -g "" -g onboot</option>.
> +                    </para>
> +                </listitem>
> +            </varlistentry>
> +        </variablelist>
> +    </refsect1>
> +
>      &seealso;
>  
>      <refsect1>
> diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
> index c7b36a4..30fe4a8 100644
> --- a/doc/lxc.container.conf.sgml.in
> +++ b/doc/lxc.container.conf.sgml.in
> @@ -1486,6 +1486,29 @@ mknod errno 0
>          </varlistentry>
>        </variablelist>
>      </refsect2>
> +
> +    <refsect2>
> +    <title>Autostart and System Boot</title>
> +    <para>
> +          Each container can be part of any number of groups or no group at all.
> +          Two groups are special. One is the NULL group, i.e. the container does
> +          not belong to any group. The other group is the "onboot" group.
> +    </para>
> +
> +    <para>
> +          When the system boots with the LXC service enabled, it will first
> +          attempt to boot any containers with lxc.start.auto == 1 that is a member
> +          of the "onboot" group. The startup will be in order of lxc.start.order.
> +          If an lxc.start.delay has been specified, that delay will be honored
> +          before attempting to start the next container to give the current
> +          container time to begin initialization and reduce overloading the host
> +          system. After starting the members of the "onboot" group, the LXC system
> +          will proceed to boot containers with lxc.start.auto == 1 which are not
> +          members of any group (the NULL group) and proceed as with the onboot
> +          group.
> +    </para>
> +
> +    </refsect2>
>    </refsect1>
>  
>    <refsect1>
> diff --git a/lxc.spec.in b/lxc.spec.in
> index 2717c83..57912a1 100644
> --- a/lxc.spec.in
> +++ b/lxc.spec.in
> @@ -154,6 +154,7 @@ rm -rf %{buildroot}
>  %attr(4111,root,root) %{_libexecdir}/%{name}/lxc-user-nic
>  %if %{with_systemd}
>  %attr(555,root,root) %{_libexecdir}/%{name}/lxc-devsetup
> +%attr(555,root,root) %{_libexecdir}/%{name}/lxc-autostart-helper
>  %endif
>  
>  %if %{with_python}
> diff --git a/src/lxc/lxc_autostart.c b/src/lxc/lxc_autostart.c
> index 1e0c608..0cab734 100644
> --- a/src/lxc/lxc_autostart.c
> +++ b/src/lxc/lxc_autostart.c
> @@ -28,6 +28,9 @@
>  #include "log.h"
>  
>  lxc_log_define(lxc_autostart_ui, lxc);
> +static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list);
> +
> +struct lxc_list *cmd_groups_list = NULL;
>  
>  static int my_parser(struct lxc_arguments* args, int c, char* arg)
>  {
> @@ -38,7 +41,7 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
>  	case 's': args->shutdown = 1; break;
>  	case 'a': args->all = 1; break;
>  	case 'A': args->ignore_auto = 1; break;
> -	case 'g': args->groups = arg; break;
> +	case 'g': cmd_groups_list = accumulate_list( arg, ",", cmd_groups_list); break;
>  	case 't': args->timeout = atoi(arg); break;
>  	}
>  	return 0;
> @@ -79,6 +82,29 @@ Options:\n\
>  	.timeout = 60,
>  };
>  
> +int list_contains_entry( char *str_ptr, struct lxc_list *p1 ) {
> +	struct lxc_list *it1;
> +
> +	/*
> +	 * If the entry is NULL or the empty string and the list
> +	 * is NULL, we have a match
> +	 */
> +	if (! p1 && ! str_ptr)
> +		return 1;
> +	if (! p1 && ! *str_ptr)
> +		return 1;
> +
> +	if (!p1)
> +		return 0;
> +
> +	lxc_list_for_each(it1, p1) {
> +		if (strcmp(it1->elem, str_ptr) == 0)
> +			return 1;
> +	}
> +
> +	return 0;
> +}
> +
>  int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) {
>  	struct lxc_list *it1;
>  	struct lxc_list *it2;
> @@ -102,6 +128,78 @@ int lists_contain_common_entry(struct lxc_list *p1, struct lxc_list *p2) {
>  	return 0;
>  }
>  
> +/*
> + * This is a variation of get_list below it.
> + * This version allows two additional features.
> + * If a list is passed to it, it adds to it.
> + * It allows for empty entries (i.e. "group1,,group2") generating
> + * 	and empty list entry.
> + */
> +static struct lxc_list *accumulate_list(char *input, char *delimiter, struct lxc_list *str_list) {
> +	char *workstr = NULL;
> +	char *workptr = NULL;
> +	char *next_ptr = NULL;
> +	struct lxc_list *worklist;
> +	struct lxc_list *workstr_list;
> +
> +	workstr = strdup(input);
> +	if (!workstr) {
> +		return NULL;
> +	}
> +
> +	workstr_list = str_list;
> +	if ( ! workstr_list ) {
> +		workstr_list = malloc(sizeof(*workstr_list));
> +		lxc_list_init(workstr_list);
> +	}
> +
> +	for (workptr = workstr; workptr; workptr = next_ptr) {
> +		/*
> +		 * We can't use strtok_r here because it collapses
> +		 * multiple delimiters into 1 making empty fields
> +		 * impossible...
> +		 */
> +		/* token = strtok_r(workptr, delimiter, &sptr); */
> +		next_ptr = strchr( workptr, *delimiter );
> +
> +		if( next_ptr ) {
> +			*next_ptr++ = '\0';
> +		}
> +
> +		/*
> +		 * At this point, we'd like to check to see if this
> +		 * group is already contained in the list and ignore
> +		 * it if it is...  This also helps us with any
> +		 * corner cases where a string begins or ends with a
> +		 * delimiter.
> +		 */
> +
> +		if ( list_contains_entry( workptr, workstr_list ) ) {
> +			if ( *workptr ) {
> +				fprintf(stderr, "Duplicate group \"%s\" in list - ignoring\n", workptr );
> +			} else {
> +				fprintf(stderr, "Duilicate NULL group in list - ignoring\n" );
> +			}
> +		} else {
> +			worklist = malloc(sizeof(*worklist));
> +			if (!worklist)
> +				break;
> +
> +			worklist->elem = strdup(workptr);
> +			if (!worklist->elem) {
> +				free(worklist);
> +				break;
> +			}
> +
> +			lxc_list_add_tail(workstr_list, worklist);
> +		}
> +	}
> +
> +	free(workstr);
> +
> +	return workstr_list;
> +}
> +
>  static struct lxc_list *get_list(char *input, char *delimiter) {
>  	char *workstr = NULL;
>  	char *workptr = NULL;
> @@ -209,15 +307,29 @@ static int cmporder(const void *p1, const void *p2) {
>  		return (c1_order - c2_order) * -1;
>  }
>  
> +static int toss_list( struct lxc_list *c_groups_list ) {
> +	struct lxc_list *it, *next;
> +
> +	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);
> +	}
> +
> +	return 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;
> +	struct lxc_list **c_groups_lists = NULL;
> +	struct lxc_list *cmd_group;
>  	char *const default_start_args[] = {
>  		"/sbin/init",
>  		NULL,
> @@ -236,112 +348,167 @@ int main(int argc, char *argv[])
>  	if (count < 0)
>  		return 1;
>  
> -	qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
> +	if (!my_args.all) {
> +		/* Allocate an array for our container group lists */
> +		c_groups_lists = calloc( count, sizeof( struct lxc_list * ) );
> +	}
>  
> -	if (my_args.groups && !my_args.all)
> -		cmd_groups_list = get_list((char*)my_args.groups, ",");
> +	qsort(&containers[0], count, sizeof(struct lxc_container *), cmporder);
>  
> -	for (i = 0; i < count; i++) {
> -		struct lxc_container *c = containers[i];
> +	if (cmd_groups_list && my_args.all) {
> +		fprintf(stderr, "Specifying -a (all) with -g (groups) doesn't make sense. All option overrides.");
> +	}
>  
> -		if (!c->may_control(c)) {
> -			lxc_container_put(c);
> -			continue;
> -		}
> +	if (!cmd_groups_list) {
> +		/*
> +		 * We need a default cmd_groups_list even for the -a
> +		 * case in order to force a pass through the loop for
> +		 * the NULL group.  This, someday, could be taken from
> +		 * a config file somewhere...
> +		 */
> +		cmd_groups_list = accumulate_list( "" , ",", NULL );
> +	}
>  
> -		if (!my_args.ignore_auto &&
> -		    get_config_integer(c, "lxc.start.auto") != 1) {
> -			lxc_container_put(c);
> -			continue;
> -		}
> +	lxc_list_for_each(cmd_group, cmd_groups_list) {
>  
> -		if (!my_args.all) {
> -			/* Filter by group */
> -			c_groups_list = get_config_list(c, "lxc.group");
> +		/*
> +		 * Prograpmmers Note:
> +		 * Because we may take several passes through the container list
> +		 * We'll switch on if the container pointer is NULL and if we process a
> +		 * container (run it or decide to ignore it) and call lxc_container_put
> +		 * then we'll NULL it out and not check it again.
> +		 */
> +		for (i = 0; i < count; i++) {
> +			struct lxc_container *c = containers[i];
>  
> -			ret = lists_contain_common_entry(cmd_groups_list, c_groups_list);
> +			if (!c)
> +				/* Skip - must have been already processed */
> +				continue;
>  
> -			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);
> +			/*
> +			 * We haven't loaded the container groups yet so
> +			 * these next two checks don't need to free them
> +			 * if they fail.  They'll fail on the first pass.
> +			 */
> +			if (!c->may_control(c)) {
> +				/* We're done with this container */
> +				if ( lxc_container_put(c) > 0 )
> +					containers[i] = NULL;
> +				continue;
>  			}
>  
> -			if (ret == 0) {
> -				lxc_container_put(c);
> +			if (!my_args.ignore_auto &&
> +			    get_config_integer(c, "lxc.start.auto") != 1) {
> +				/* We're done with this container */
> +				if ( lxc_container_put(c) > 0 )
> +					containers[i] = NULL;
>  				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)) {
> -						if (!c->stop(c)) {
> -							fprintf(stderr, "Error shutting down container: %s\n", c->name);
> +			if (!my_args.all) {
> +				/* Filter by group */
> +				if( ! c_groups_lists[i] ) {
> +					/* Now we're loading up a container's groups */
> +					c_groups_lists[i] = get_config_list(c, "lxc.group");
> +				}
> +
> +				ret = list_contains_entry(cmd_group->elem, c_groups_lists[i]);
> +
> +				if ( ret == 0 ) {
> +					/* Not in the target group this pass */
> +					/* Leave in the list for subsequent passes */
> +					continue;
> +				}
> +			}
> +
> +			/* We have a candidate continer to process */
> +			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)) {
> +							if (!c->stop(c)) {
> +								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.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 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"));
> +			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"));
> +					}
>  				}
>  			}
> -		}
>  
> +			/*
> +			 * If we get this far and we haven't hit any skip "continue"
> +			 * then we're done with this container...  We can dump any
> +			 * c_groups_list and the container itself.
> +			 */
> +			if ( lxc_container_put(c) > 0 ) {
> +				containers[i] = NULL;
> +			}
> +			if ( c_groups_lists && c_groups_lists[i] ) {
> +				toss_list(c_groups_lists[i]);
> +				c_groups_lists[i] = NULL;
> +			}
> +		}
>  
> -		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);
> +	/* clean up any lingering detritus */
> +	for (i = 0; i < count; i++) {
> +		if ( containers[i] ) {
> +			lxc_container_put(containers[i]);
> +		}
> +		if ( c_groups_lists && c_groups_lists[i] ) {
> +			toss_list(c_groups_lists[i]);
>  		}
> +	}
> +
> +	if ( c_groups_lists )
> +		free(c_groups_lists);
> +
> +	if ( cmd_groups_list ) {
> +		toss_list( cmd_groups_list );
>  		free(cmd_groups_list);
>  	}
>  
> -- 
> 1.9.0
> 
> 



-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20140520/e25e0250/attachment-0001.sig>


More information about the lxc-devel mailing list