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

Stéphane Graber stgraber at ubuntu.com
Mon May 19 15:22:01 UTC 2014


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!

> 
> Mike
> 
> On Thu, 2014-05-15 at 16:51 -0400, Michael H. Warfield wrote:
> > Ok...
> > 
> > Here it is.  The roll up of Dwight's earlier patch, my previous 3
> > patches plus, now, the rework to lxc-autostart to support multiple
> > invocations of the -g option, allow ordinal inclusion of the NULL group,
> > and process containers first in order of the groups specified on the
> > command line and their membership in those groups, subject to all other
> > constraints (lxc.start.auto) and orderings (lxc.start.order).
> > 
> > I have also noticed that host shutdown time can be very exhorbatently
> > long.  I added parameters to set a shutdown time of -t 5 seconds to
> > speed that process up (may still not be fast enough).  Problem here is
> > that while startup returns quickly while a container initializes and we
> > need a delay, shutdown is largely serial and not parallelized at all.
> > We may need to address that in some way moving forward.
> > 
> > Here's the grand patch:
> > 
> > -- 
> > Refactoring lxc-autostart boot process and group handling.
> > 
> > This is a rollup of 4 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 also 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.
> > 
> > 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/systemd/lxc.service.in |  17 ++
> >  config/init/sysvinit/lxc           |  66 --------
> >  config/init/sysvinit/lxc.in        |  86 ++++++++++
> >  config/init/upstart/lxc.conf       |   8 +-
> >  configure.ac                       |   2 +
> >  doc/lxc-autostart.sgml.in          |  30 ++++
> >  doc/lxc.container.conf.sgml.in     |  23 +++
> >  lxc.spec.in                        |   1 +
> >  src/lxc/lxc_autostart.c            | 331 +++++++++++++++++++++++++++----------
> >  12 files changed, 427 insertions(+), 171 deletions(-)
> >  delete mode 100644 config/init/systemd/lxc.service
> >  create mode 100644 config/init/systemd/lxc.service.in
> >  delete mode 100755 config/init/sysvinit/lxc
> >  create mode 100644 config/init/sysvinit/lxc.in
> > 
> > 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/systemd/lxc.service.in b/config/init/systemd/lxc.service.in
> > new file mode 100644
> > index 0000000..5f155b6
> > --- /dev/null
> > +++ b/config/init/systemd/lxc.service.in
> > @@ -0,0 +1,17 @@
> > +[Unit]
> > +Description=LXC Container Initialization and Autoboot Code
> > +After=syslog.target network.target
> > +
> > +[Service]
> > +Type=oneshot
> > +RemainAfterExit=yes
> > +ExecStartPre=@libexecdir@/lxc/lxc-devsetup
> > +ExecStart=@libexecdir@/lxc/lxc-autostart-helper start
> > +ExecStop=@libexecdir@/lxc/lxc-autostart-helper 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/sysvinit/lxc.in b/config/init/sysvinit/lxc.in
> > new file mode 100644
> > index 0000000..e784285
> > --- /dev/null
> > +++ b/config/init/sysvinit/lxc.in
> > @@ -0,0 +1,86 @@
> > +#!/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
> > +
> > +sysconfdir="@SYSCONFDIR@"
> > +bindir="@BINDIR@"
> > +localstatedir="@LOCALSTATEDIR@"
> > +
> > +# Source function library.
> > +test ! -r "$sysconfdir"/rc.d/init.d/functions ||
> > +        . "$sysconfdir"/rc.d/init.d/functions
> > +
> > +# Check for needed utility program
> > +[ -x "$bindir"/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 "$sysconfdir"/lxc/default.conf ] || { return 0; }
> > +
> > +    which ifconfig >/dev/null 2>&1
> > +    if [ $? = 0 ]; then
> > +        cmd="ifconfig -a"
> > +    else
> > +        which ip >/dev/null 2>&1
> > +        if [ $? = 0 ]; then
> > +            cmd="ip link list"
> > +        fi
> > +    fi
> > +    [ -n cmd ] || { return 0; }
> > +
> > +    BRNAME=`grep '^[ 	]*lxc.network.link' "$sysconfdir"/lxc/default.conf | sed 's/^.*=[ 	]*//'`
> > +    if [ -z "$BRNAME" ]; then
> > +	return 0
> > +    fi
> > +
> > +    for try in `seq 1 30`; do
> > +	eval $cmd |grep "^$BRNAME" >/dev/null 2>&1
> > +	if [ $? = 0 ]; then
> > +	    return
> > +	fi
> > +	sleep 1
> > +    done
> > +}
> > +
> > +# See how we were called.
> > +case "$1" in
> > +  start)
> > +	[ ! -f "$localstatedir"/lock/subsys/lxc ] || { exit 0; }
> > +
> > +	# Start containers
> > +	wait_for_bridge
> > +	# Start autoboot containers first then the NULL group "onboot,".
> > +	action $"Starting LXC autoboot containers: " /usr/bin/lxc-autostart -g "onboot,"
> > +	touch "$localstatedir"/lock/subsys/lxc
> > +	;;
> > +  stop)
> > +	# 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...  5 second timout may be too long.
> > +	action $"Stopping LXC containers: " "$bindir"/lxc-autostart -a -A -s -t 5
> > +	rm -f "$localstatedir"/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..b9f8174 100644
> > --- a/config/init/upstart/lxc.conf
> > +++ b/config/init/upstart/lxc.conf
> > @@ -20,12 +20,16 @@ pre-start script
> >  
> >  	[ "x$LXC_AUTO" = "xtrue" ] || exit 0
> >  
> > -	lxc-autostart -L | while read line; do
> > +	# Process the "onboot" group first then the NULL group.
> > +	lxc-autostart -L -g "onboot," | 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...  5 second timout may be too long.
> >  post-stop script
> > -	lxc-autostart -a -A -s || true
> > +	lxc-autostart -a -A -s -t 5 || 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..116d7bc 100644
> > --- a/doc/lxc-autostart.sgml.in
> > +++ b/doc/lxc-autostart.sgml.in
> > @@ -185,6 +185,36 @@ 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>
> > +
> >      &seealso;
> >  
> >      <refsect1>
> > diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
> > index 6e96889..bbae448 100644
> > --- a/doc/lxc.container.conf.sgml.in
> > +++ b/doc/lxc.container.conf.sgml.in
> > @@ -1464,6 +1464,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..34964c0 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,115 +348,166 @@ 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]);
> >  		}
> > -		free(cmd_groups_list);
> >  	}
> >  
> > +	if ( c_groups_lists )
> > +		free(c_groups_lists);
> > +
> > +	free(cmd_groups_list);
> >  	free(containers);
> >  
> >  	return 0;
> > -- 
> > 1.9.0
> > 
> > 
> 
> -- 
> 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!
> 



> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel


-- 
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/20140519/4c833163/attachment-0001.sig>


More information about the lxc-devel mailing list