[Lxc-users] container shutdown

Serge Hallyn serge.hallyn at canonical.com
Sun Mar 18 23:00:23 UTC 2012


Hi,

Thanks to Jäkel's and Fajar's great ideas, we can now cleanly shut down
a container by sending it SIGPWR.  I'm attaching two ways to do that.
In-line is a patch which modifies lxc-stop to take optional -s and -t
args - -s for shutdown (meaning send SIGPWR), and -t for a timeout,
after sending SIGPWR, to hard-kill the container.

Attached is a lxc-shutdown script (as an alternative) so lxc-stop can
continue to work as it has.

Both are in the bzr tree at
lp:~serge-hallyn/ubuntu/precise/lxc/lxc-shutdown, which builds and gives
you both.

What do we prefer?

thanks,
-serge

Signed-off-by: Serge Hallyn <serge.hallyn at canonical.com>
---
 doc/lxc-stop.sgml.in |   10 ++++++++++
 src/lxc/arguments.h  |    4 ++++
 src/lxc/commands.c   |    8 ++++----
 src/lxc/lxc.h        |    5 ++++-
 src/lxc/lxc_stop.c   |   22 +++++++++++++++++++---
 src/lxc/stop.c       |   27 +++++++++++++++++++++++++--
 6 files changed, 66 insertions(+), 10 deletions(-)

Index: lxc/doc/lxc-stop.sgml.in
===================================================================
--- lxc.orig/doc/lxc-stop.sgml.in	2012-03-18 16:33:06.254906000 -0500
+++ lxc/doc/lxc-stop.sgml.in	2012-03-18 16:34:11.970538920 -0500
@@ -49,6 +49,7 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>lxc-stop <replaceable>-n name</replaceable>
+      <optional>-s</optional> <optional>-t timeout</optional>
       </command>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -62,6 +63,15 @@
       longer accessible and can no be exited normally.
     </para>
 
+    <para>
+      If <optional>-s</optional> (<optional>--shutdown</optional>) is
+      specified, then ask the container to shut down cleanly by sending
+      a <emphasis>SIGPWR</emphasis> signal.  If <optional>-t timeout</optional>
+      is also given, then <emphasis>timeout</emphasis> seconds after sending
+      SIGPWR, if the container is still up, proceed to kill the container.
+      Note that <optional>-t timeout</optional> implies <optional>-s</optional>.
+    </para>
+
   </refsect1>
 
   &commonoptions;
Index: lxc/src/lxc/arguments.h
===================================================================
--- lxc.orig/src/lxc/arguments.h	2012-03-18 16:33:06.254906000 -0500
+++ lxc/src/lxc/arguments.h	2012-03-18 16:34:19.442575978 -0500
@@ -46,6 +46,10 @@
 	const char *rcfile;
 	const char *console;
 
+	/* for lxc-stop */
+	int timeout;
+	int shutdown;
+
 	/* for lxc-checkpoint/restart */
 	const char *statefile;
 	int statefd;
Index: lxc/src/lxc/commands.c
===================================================================
--- lxc.orig/src/lxc/commands.c	2012-03-18 16:33:06.254906000 -0500
+++ lxc/src/lxc/commands.c	2012-03-18 16:34:26.862612782 -0500
@@ -162,10 +162,10 @@
 	typedef int (*callback)(int, struct lxc_request *, struct lxc_handler *);
 
 	callback cb[LXC_COMMAND_MAX] = {
-		[LXC_COMMAND_TTY]   = lxc_console_callback,
-		[LXC_COMMAND_STOP]  = lxc_stop_callback,
-		[LXC_COMMAND_STATE] = lxc_state_callback,
-		[LXC_COMMAND_PID]   = lxc_pid_callback,
+		[LXC_COMMAND_TTY]       = lxc_console_callback,
+		[LXC_COMMAND_STOP]      = lxc_stop_callback,
+		[LXC_COMMAND_STATE]     = lxc_state_callback,
+		[LXC_COMMAND_PID]       = lxc_pid_callback,
 	};
 
 	if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
Index: lxc/src/lxc/lxc_stop.c
===================================================================
--- lxc.orig/src/lxc/lxc_stop.c	2012-03-18 16:33:06.254906000 -0500
+++ lxc/src/lxc/lxc_stop.c	2012-03-18 17:24:47.137589512 -0500
@@ -30,7 +30,18 @@
 
 #include "arguments.h"
 
+static int my_parser(struct lxc_arguments* args, int c, char* arg)
+{
+	switch (c) {
+	case 's': args->shutdown = 1; break;
+	case 't': args->timeout = arg; args->shutdown = 1; break;
+	}
+	return 0;
+}
+
 static const struct option my_longopts[] = {
+	{"shutdown", no_argument, 0, 's'},
+	{"timeout", required_argument, 0, 't'},
 	LXC_COMMON_OPTIONS
 };
 
@@ -42,10 +53,15 @@
 lxc-stop stops a container with the identifier NAME\n\
 \n\
 Options :\n\
-  -n, --name=NAME   NAME for name of the container\n",
+  -n, --name=NAME   NAME for name of the container\n\
+  -s, --shutdown    Ask container to shut down cleanly\n\
+  -t, --timeout=t   Imply -s and hard-kill container after t seconds\n\
+                    (default is -1, no timeout)\n",
 	.options  = my_longopts,
-	.parser   = NULL,
+	.parser   = my_parser,
 	.checker  = NULL,
+	.timeout  = -1,
+	.shutdown = 0,
 };
 
 int main(int argc, char *argv[])
@@ -57,5 +73,5 @@
 			 my_args.progname, my_args.quiet))
 		return -1;
 
-	return lxc_stop(my_args.name);
+	return lxc_stop(my_args.name, my_args.shutdown, my_args.timeout);
 }
Index: lxc/src/lxc/stop.c
===================================================================
--- lxc.orig/src/lxc/stop.c	2012-03-18 16:33:06.254906000 -0500
+++ lxc/src/lxc/stop.c	2012-03-18 17:53:49.638230102 -0500
@@ -40,7 +40,7 @@
 
 lxc_log_define(lxc_stop, lxc);
 
-int lxc_stop(const char *name)
+int lxc_stop(const char *name, int shutdown, int timeout)
 {
 	struct lxc_command command = {
 		.request = { .type = LXC_COMMAND_STOP },
@@ -48,9 +48,30 @@
 
 	int ret, stopped = 0;
 
+	if (shutdown) {
+		int pid = get_init_pid(name);
+		if (pid == -1) {
+			INFO("'%s' is already stopped", name);
+			return 0;
+		}
+		ret = kill(pid, SIGPWR);
+		if (ret < 0) {
+			ERROR("failed to send SIGPWR");
+			return -1;
+		}
+		if (timeout < 0)
+			goto out;
+		while (timeout-- > 0) {
+			lxc_state_t state = lxc_getstate(name);
+			if (state == STOPPED)
+				goto out;
+			sleep(1);
+		}
+	}
 	ret = lxc_command(name, &command,&stopped);
 	if (ret < 0 && stopped) {
-		INFO("'%s' is already stopped", name);
+		if (!shutdown)
+			INFO("'%s' is already stopped", name);
 		return 0;
 	}
 
@@ -68,6 +89,8 @@
 		return -1;
 	}
 
+out:
+
 	INFO("'%s' has stopped", name);
 
 	return 0;
Index: lxc/src/lxc/lxc.h
===================================================================
--- lxc.orig/src/lxc/lxc.h	2012-03-16 21:39:54.517075000 -0500
+++ lxc/src/lxc/lxc.h	2012-03-18 16:42:23.336975492 -0500
@@ -52,9 +52,12 @@
  * Stop the container previously started with lxc_start, all
  * the processes running inside this container will be killed.
  * @name : the name of the container
+ * @shutdown : 1 if SIGPWR should be sent, 0 if SIGKILL
+ * @timeout : if not -1, wait this many seconds after sending
+ * SIGPWR before sending SIGKILL.
  * Returns 0 on success, < 0 otherwise
  */
-extern int lxc_stop(const char *name);
+extern int lxc_stop(const char *name, int shutdown, int timeout);
 
 /*
  * Open the monitoring mechanism for a specific container
-------------- next part --------------
#!/bin/bash

# (C) Copyright Canonical 2011,2012

set -e

usage() {
    echo "usage: lxc-shutdown -n name [-t timeout]"
    echo "  Cleanly shut down a container."
    echo "  If timeout is not specified, exit after sending SIGPWR"
    echo "  Otherwise, wait timeout seconds before calling lxc-stop"
    echo "  to hard-kill the container"
}

alarm() {
    pid=$1
    timeout=$2
    sleep $timeout
    kill $pid
}

shortoptions='hn:t:'
longoptions='help,name:,timeout:'

getopt=$(getopt -o $shortoptions --longoptions  $longoptions -- "$@")
if [ $? != 0 ]; then
    usage
    exit 1;
fi

eval set -- "$getopt"

timeout="-1"

while true; do
    case "$1" in
    -h|--help)
        help
        exit 1
        ;;
    -n|--name)
        shift
        lxc_name=$1
        shift
        ;;
    -t|--timeout)
        shift
        timeout=$1
        shift
        ;;
    --)
        shift
        break;;
    *)
        echo $1
        usage
        exit 1
        ;;
    esac
done

if [ -z "$lxc_name" ]; then
    echo "no container name specified"
    usage
    exit 1
fi

if [ "$(id -u)" != "0" ]; then
   echo "This command has to be run as root"
   exit 1
fi

type lxc-info > /dev/null || { echo "lxc-info not found."; exit 1; }

pid=`lxc-info -n $lxc_name -p 2>/dev/null | awk '{ print $2 }'`
if [ "$pid" = "-1" ]; then
	echo "$lxc_name is not running"
	exit 1
fi

kill -PWR $pid

if [ "$timeout" = "-1" ]; then
    exit 0
fi

echo "Waiting $timeout seconds for container to finish shutting down..."

dolxcstop()
{
    echo "Calling lxc-stop on $lxc_name"
    lxc-stop -n $lxc_name
    exit 0
}

trap dolxcstop EXIT
alarm $$ $timeout &
alarmpid=$!
lxc-wait -n $lxc_name -s STOPPED
echo "container has shut down"
kill $alarmpid

exit 0


More information about the lxc-users mailing list