[lxc-devel] [PATCH 1/3] lxc-shutdown: switch from script to program using api.

Stéphane Graber stgraber at ubuntu.com
Fri May 17 19:28:53 UTC 2013


On 05/17/2013 03:15 PM, Serge Hallyn wrote:
> implement c->reboot(c) in the api.  Remove lxc-shutdown.in, the
> bash script.  Replace it with lxc_shutdown.c which uses the
> API.


Thanks for those changes. As we briefly discussed on IRC, I think we
should rework a bit the set of binaries we have to stop containers, move
to a single one that tries to "do the right thing" by default.

So my suggestion is basically to:
 - Kill lxc-shutdown
 - Change lxc-stop so that:
   * Default behaviour is to call shutdown(), wait 15s for STOPPED, if
not STOPPED, print a message to the user and call stop()
   * We have a -r option to reboot the container (with proper check that
the container indeed rebooted within the next 15s)
   * We have a -s option to shutdown the container without the automatic
fallback to stop()
   * Add a -k option allowing a user to just kill a container
(equivalent to old lxc-stop, no shutdown() call and no delay).

We'd therefore end up with a single binary which does shutdown, stop and
reboot, properly checks that the actions are carried out and supports
timing out and fallback to kill.

The 15s timeout would be over-ridable through -t, 15s is a guess as to
how long users would be ready to wait for a container to die assuming
some complex processes (database and similar) need enough time to sync
their data and exit.

Does that sound reasonable to everyone?


> Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
> ---
>  configure.ac            |   1 -
>  src/lxc/Makefile.am     |   3 +-
>  src/lxc/arguments.h     |   4 +-
>  src/lxc/lxc-shutdown.in | 165 ------------------------------------------------
>  src/lxc/lxc_shutdown.c  | 117 ++++++++++++++++++++++++++++++++++
>  src/lxc/lxccontainer.c  |  18 ++++++
>  src/lxc/lxccontainer.h  |   2 +
>  7 files changed, 142 insertions(+), 168 deletions(-)
>  delete mode 100644 src/lxc/lxc-shutdown.in
>  create mode 100644 src/lxc/lxc_shutdown.c
> 
> diff --git a/configure.ac b/configure.ac
> index 414d71b..83d997b 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -383,7 +383,6 @@ AC_CONFIG_FILES([
>  	src/lxc/lxc-checkconfig
>  	src/lxc/lxc-version
>  	src/lxc/lxc-create
> -	src/lxc/lxc-shutdown
>  	src/lxc/lxc-start-ephemeral
>  	src/lxc/lxc-destroy
>  	src/lxc/legacy/lxc-ls
> diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
> index 4a19061..ce79904 100644
> --- a/src/lxc/Makefile.am
> +++ b/src/lxc/Makefile.am
> @@ -124,7 +124,6 @@ bin_SCRIPTS = \
>  	lxc-checkconfig \
>  	lxc-version \
>  	lxc-create \
> -	lxc-shutdown \
>  	lxc-destroy
>  
>  EXTRA_DIST = \
> @@ -147,6 +146,7 @@ endif
>  bin_PROGRAMS = \
>  	lxc-attach \
>  	lxc-unshare \
> +	lxc-shutdown \
>  	lxc-stop \
>  	lxc-start \
>  	lxc-clone \
> @@ -191,6 +191,7 @@ lxc_restart_SOURCES = lxc_restart.c
>  lxc_clone_SOURCES = lxc_clone.c
>  lxc_start_SOURCES = lxc_start.c
>  lxc_stop_SOURCES = lxc_stop.c
> +lxc_shutdown_SOURCES = lxc_shutdown.c
>  lxc_unfreeze_SOURCES = lxc_unfreeze.c
>  lxc_unshare_SOURCES = lxc_unshare.c
>  lxc_wait_SOURCES = lxc_wait.c
> diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h
> index 002a919..a0f0fc9 100644
> --- a/src/lxc/arguments.h
> +++ b/src/lxc/arguments.h
> @@ -61,9 +61,11 @@ struct lxc_arguments {
>  	int ttynum;
>  	char escape;
>  
> -	/* for lxc-wait */
> +	/* for lxc-wait and lxc-shutdown */
>  	char *states;
>  	long timeout;
> +	int wait;
> +	int reboot;
>  
>  	/* close fds from parent? */
>  	int close_all_fds;
> diff --git a/src/lxc/lxc-shutdown.in b/src/lxc/lxc-shutdown.in
> deleted file mode 100644
> index c81e736..0000000
> --- a/src/lxc/lxc-shutdown.in
> +++ /dev/null
> @@ -1,165 +0,0 @@
> -#!/bin/sh
> -
> -# (C) Copyright Canonical 2011,2012
> -
> -# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> -
> -set -e
> -
> -. @DATADIR@/lxc/lxc.functions
> -
> -usage() {
> -    echo "usage: lxc-shutdown -n name [-w] [-r] [-P lxcpath]"
> -    echo "  Cleanly shut down a container."
> -    echo "  -w: wait for shutdown to complete."
> -    echo "  -r: reboot (ignore -w)."
> -    echo "  -t timeout: wait at most timeout seconds (implies -w), then kill"
> -    echo "              the container."
> -    echo "  -P lxcpath: path to the lxc container directories."
> -}
> -
> -alarm() {
> -    trap 'exit 0' TERM
> -    pid=$1
> -    timeout=$2
> -    sleep $timeout
> -    kill $pid
> -}
> -
> -dolxcstop()
> -{
> -    echo "Calling lxc-stop on $lxc_name"
> -    lxc-stop -n $lxc_name -P "$lxc_path"
> -    exit 0
> -}
> -
> -usage_err() {
> -    [ -n "$1" ] && echo "$1" >&2
> -    usage
> -    exit 1
> -}
> -
> -optarg_check() {
> -    [ -n "$2" ] || usage_err "option '$1' requires an argument"
> -}
> -
> -timeout="-1"
> -
> -reboot=0
> -dowait=0
> -
> -while [ $# -gt 0 ]; do
> -    opt="$1"
> -    shift
> -    case "$opt" in
> -    -h|--help)
> -        usage
> -        exit 0
> -        ;;
> -    -n|--name)
> -        optarg_check $opt "$1"
> -        lxc_name=$1
> -        shift
> -        ;;
> -    -w|--wait)
> -        dowait=1
> -        ;;
> -    -r|--reboot)
> -        reboot=1
> -        ;;
> -    -t|--timeout)
> -        optarg_check $opt "$1"
> -        timeout=$1
> -        dowait=1
> -        shift
> -        ;;
> -    -P|--lxcpath)
> -        optarg_check $opt "$1"
> -        lxc_path=$1
> -        dowait=1
> -        shift
> -        ;;
> -    --)
> -        break;;
> -    -?)
> -        usage_err "unknown option '$opt'"
> -        ;;
> -    -*)
> -        # split opts -abc into -a -b -c
> -        set -- $(echo "${opt#-}" | sed 's/\(.\)/ -\1/g') "$@"
> -        ;;
> -    *)
> -        usage_err "unknown option '$opt'"
> -        exit 1
> -        ;;
> -    esac
> -done
> -
> -if [ -z "$lxc_name" ]; then
> -    echo "no container name specified"
> -    usage
> -    exit 1
> -fi
> -
> -if [ ! -d "$lxc_path" ]; then
> -    echo "$lxc_path: no such directory"
> -    exit 1
> -fi
> -
> -if [ "$(id -u)" != "0" ]; then
> -   echo "This command has to be run as root"
> -   exit 1
> -fi
> -
> -which lxc-info > /dev/null 2>&1 || { echo "lxc-info not found."; exit 1; }
> -which lxc-wait > /dev/null 2>&1 || { echo "lxc-wait not found."; exit 1; }
> -
> -pid=`lxc-info -n $lxc_name -P "$lxc_path" -p 2>/dev/null | awk '{ print $2 }'`
> -if [ "$pid" = "-1" ]; then
> -    echo "$lxc_name is not running"
> -    exit 1
> -fi
> -
> -if [ $reboot -eq 1 ]; then
> -    kill -s INT $pid
> -    exit 0
> -else
> -    kill -s PWR $pid
> -fi
> -
> -if [ $dowait -eq 0 ]; then
> -    exit 0
> -fi
> -
> -if [ $timeout != "-1" ]; then
> -    trap dolxcstop EXIT
> -    alarm $$ $timeout 2>/dev/null &
> -    alarmpid=$!
> -fi
> -
> -while ! lxc-info -n $lxc_name -P "$lxc_path" --state-is STOPPED; do
> -    sleep 1
> -done
> -
> -if [ $timeout != "-1" ]; then
> -    trap - EXIT
> -    # include subprocesses; otherwise, we may have to wait until sleep completes
> -    # if called from a non-interactive context
> -    kill $alarmpid $(ps --no-headers --ppid $alarmpid -o pid) 2>/dev/null || :
> -fi
> -
> -echo "Container $lxc_name has shut down"
> -
> -exit 0
> diff --git a/src/lxc/lxc_shutdown.c b/src/lxc/lxc_shutdown.c
> new file mode 100644
> index 0000000..c9b5083
> --- /dev/null
> +++ b/src/lxc/lxc_shutdown.c
> @@ -0,0 +1,117 @@
> +/*
> + *
> + * Copyright © 2013 Serge Hallyn <serge.hallyn at ubuntu.com>.
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program 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 General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program; if not, write to the Free Software Foundation, Inc.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
> + */
> +
> +#include "../lxc/lxccontainer.h"
> +
> +/*
> + * usage: 
> + lxc-shutdown -h
> + usage: lxc-shutdown -n name [-w] [-r] [-P lxcpath]
> +	Cleanly shut down a container.
> +	-w: wait for shutdown to complete.
> +	-r: reboot (ignore -w).
> +	-t timeout: wait at most timeout seconds (implies -w), then kill
> +	   the container.
> +	-P lxcpath: path to the lxc container directories.
> +*/
> +
> +#include <stdio.h>
> +#include <libgen.h>
> +#include <unistd.h>
> +#include <sys/types.h>
> +
> +#include <lxc/lxc.h>
> +#include <lxc/log.h>
> +
> +#include "arguments.h"
> +#include "utils.h"
> +
> +lxc_log_define(lxc_shutdown, lxc);
> +
> +static int my_parser(struct lxc_arguments* args, int c, char* arg)
> +{
> +	switch (c) {
> +	case 'r': args->reboot = 1; break;
> +	case 'w': args->wait = 1; break;
> +	case 't': args->timeout = atoi(arg); break;
> +	}
> +	return 0;
> +}
> +
> +static const struct option my_longopts[] = {
> +	{"reboot", no_argument, 0, 'r'},
> +	{"wait", no_argument, 0, 'w'},
> +	{"timeout", required_argument, 0, 't'},
> +	LXC_COMMON_OPTIONS
> +};
> +
> +static struct lxc_arguments my_args = {
> +	.progname = "lxc-shutdown",
> +	.help     = "\
> +--name=NAME [-w] [-r] [-t timeout] [-P lxcpath]\n\
> +\n\
> +lxc-stop stops a container with the identifier NAME\n\
> +\n\
> +Options :\n\
> +  -n, --name=NAME   NAME for name of the container\n\
> +  -t, --timeout=T   wait T seconds before hardkilling\n\
> +  -w, --wait        wait for the container to shut down\n\
> +  -r, --reboot      reboot the container (ignore wait)\n",
> +	.options  = my_longopts,
> +	.parser   = my_parser,
> +	.checker  = NULL,
> +};
> +
> +int main(int argc, char *argv[])
> +{
> +	struct lxc_container *c;
> +
> +	/* this is a short term test.  We'll probably want to check for
> +	 * write access to lxcpath instead */
> +	if (geteuid()) {
> +		fprintf(stderr, "%s must be run as root\n", argv[0]);
> +		exit(1);
> +	}
> +
> +	if (lxc_arguments_parse(&my_args, argc, argv))
> +		exit(1);
> +
> +	if (lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
> +			 my_args.progname, my_args.quiet, my_args.lxcpath[0]))
> +		exit(1);
> +
> +	c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
> +	if (!c) {
> +		fprintf(stderr, "System error loading container\n");
> +		exit(1);
> +	}
> +
> +	if (!c->is_running(c)) {
> +		fprintf(stderr, "%s is not running\n", my_args.name);
> +		exit(1);
> +	}
> +
> +	if (my_args.reboot)
> +		exit(c->reboot(c) ? 0 : 1);
> +
> +	if (!c->shutdown(c, my_args.timeout))
> +		exit(c->wait(c, "STOPPED", -1) ? 0 : 1);
> +
> +	exit(0);
> +}
> diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c
> index 23db6f2..2126c89 100644
> --- a/src/lxc/lxccontainer.c
> +++ b/src/lxc/lxccontainer.c
> @@ -689,6 +689,23 @@ out:
>  	return bret;
>  }
>  
> +static bool lxcapi_reboot(struct lxc_container *c)
> +{
> +	pid_t pid;
> +
> +	if (!c)
> +		return false;
> +	if (!c->is_running(c))
> +		return false;
> +	pid = c->init_pid(c);
> +	if (pid <= 0)
> +		return false;
> +	if (kill(pid, SIGINT) < 0)
> +		return false;
> +	return true;
> +
> +}
> +
>  static bool lxcapi_shutdown(struct lxc_container *c, int timeout)
>  {
>  	bool retv;
> @@ -1635,6 +1652,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
>  	c->create = lxcapi_create;
>  	c->createl = lxcapi_createl;
>  	c->shutdown = lxcapi_shutdown;
> +	c->reboot = lxcapi_reboot;
>  	c->clear_config_item = lxcapi_clear_config_item;
>  	c->get_config_item = lxcapi_get_config_item;
>  	c->get_cgroup_item = lxcapi_get_cgroup_item;
> diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h
> index 67fbed4..c718d14 100644
> --- a/src/lxc/lxccontainer.h
> +++ b/src/lxc/lxccontainer.h
> @@ -50,6 +50,8 @@ struct lxc_container {
>  	bool (*save_config)(struct lxc_container *c, const char *alt_file);
>  	bool (*create)(struct lxc_container *c, char *t, char *const argv[]);
>  	bool (*createl)(struct lxc_container *c, char *t, ...);
> +	/* send SIGINT to ask container to reboot */
> +	bool (*reboot)(struct lxc_container *c);
>  	/* send SIGPWR.  if timeout is not 0 or -1, do a hard stop after timeout seconds */
>  	bool (*shutdown)(struct lxc_container *c, int timeout);
>  	/* clear all network or capability items in the in-memory configuration */
> 


-- 
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: 901 bytes
Desc: OpenPGP digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20130517/35bd3923/attachment.pgp>


More information about the lxc-devel mailing list