mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-06-14 22:01:43 +00:00
lxc-stop: use api, remove lxc_shutdown, extend lxc-stop functionality
implement c->reboot(c) in the api. Also if the container is not running, return -2. Currently lxc-stop will return 0, so you cannot tell the difference between successfull stopping and noop. Per stgraber's email: - Remove 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() [ NOTE: actually 60 seconds per followup thread] * 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). and update manpages. Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
parent
5cee8c5040
commit
3e625e2d2e
@ -334,7 +334,6 @@ AC_CONFIG_FILES([
|
|||||||
doc/lxc-netstat.sgml
|
doc/lxc-netstat.sgml
|
||||||
doc/lxc-ps.sgml
|
doc/lxc-ps.sgml
|
||||||
doc/lxc-restart.sgml
|
doc/lxc-restart.sgml
|
||||||
doc/lxc-shutdown.sgml
|
|
||||||
doc/lxc-start-ephemeral.sgml
|
doc/lxc-start-ephemeral.sgml
|
||||||
doc/lxc-start.sgml
|
doc/lxc-start.sgml
|
||||||
doc/lxc-stop.sgml
|
doc/lxc-stop.sgml
|
||||||
@ -383,7 +382,6 @@ AC_CONFIG_FILES([
|
|||||||
src/lxc/lxc-checkconfig
|
src/lxc/lxc-checkconfig
|
||||||
src/lxc/lxc-version
|
src/lxc/lxc-version
|
||||||
src/lxc/lxc-create
|
src/lxc/lxc-create
|
||||||
src/lxc/lxc-shutdown
|
|
||||||
src/lxc/lxc-start-ephemeral
|
src/lxc/lxc-start-ephemeral
|
||||||
src/lxc/lxc-destroy
|
src/lxc/lxc-destroy
|
||||||
src/lxc/legacy/lxc-ls
|
src/lxc/legacy/lxc-ls
|
||||||
|
@ -22,7 +22,6 @@ man_MANS = \
|
|||||||
lxc-netstat.1 \
|
lxc-netstat.1 \
|
||||||
lxc-ps.1 \
|
lxc-ps.1 \
|
||||||
lxc-restart.1 \
|
lxc-restart.1 \
|
||||||
lxc-shutdown.1 \
|
|
||||||
lxc-start.1 \
|
lxc-start.1 \
|
||||||
lxc-stop.1 \
|
lxc-stop.1 \
|
||||||
lxc-unfreeze.1 \
|
lxc-unfreeze.1 \
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
<!--
|
|
||||||
|
|
||||||
Copyright (C) 2012 Canonical, Inc
|
|
||||||
|
|
||||||
Authors: Serge Hallyn <serge.hallyn@canonical.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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-shutdown</refentrytitle>
|
|
||||||
<manvolnum>1</manvolnum>
|
|
||||||
</refmeta>
|
|
||||||
|
|
||||||
<refnamediv>
|
|
||||||
<refname>lxc-shutdown</refname>
|
|
||||||
|
|
||||||
<refpurpose>
|
|
||||||
externally shut down or reboot a container
|
|
||||||
</refpurpose>
|
|
||||||
</refnamediv>
|
|
||||||
|
|
||||||
<refsynopsisdiv>
|
|
||||||
<cmdsynopsis>
|
|
||||||
<command>lxc-shutdown</command>
|
|
||||||
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
|
||||||
<arg choice="opt">-w</arg>
|
|
||||||
<arg choice="opt">-r</arg>
|
|
||||||
</cmdsynopsis>
|
|
||||||
</refsynopsisdiv>
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Description</title>
|
|
||||||
|
|
||||||
<para>
|
|
||||||
<command>lxc-shutdown</command> sends a SIGPWR signal to the
|
|
||||||
specified container to request it to cleanly shut down. If
|
|
||||||
<optional>-w</optional> is specified, then <command>lxc-shutdown</command>
|
|
||||||
will wait until the container has shut down before exiting.
|
|
||||||
If <optional>-r</optional> is specified, the container will be
|
|
||||||
asked to reboot (using a SIGINT signal), and <optional>-w</optional>
|
|
||||||
will be ignored. If the container ignore these signals, then
|
|
||||||
nothing will happen. In that case, you can use <command>lxc-stop</command>
|
|
||||||
to force the container to stop.
|
|
||||||
</para>
|
|
||||||
|
|
||||||
</refsect1>
|
|
||||||
|
|
||||||
&commonoptions;
|
|
||||||
|
|
||||||
&seealso;
|
|
||||||
|
|
||||||
<refsect1>
|
|
||||||
<title>Author</title>
|
|
||||||
<para>Serge Hallyn <email>serge.hallyn@canonical.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:
|
|
||||||
-->
|
|
@ -50,6 +50,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>lxc-stop</command>
|
<command>lxc-stop</command>
|
||||||
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
<arg choice="req">-n <replaceable>name</replaceable></arg>
|
||||||
|
<arg choice="opt">-W</arg>
|
||||||
|
<arg choice="opt">-r</arg>
|
||||||
|
<arg choice="opt">-t <replaceable>timeout</replaceable></arg>
|
||||||
|
<arg choice="opt">-k</arg>
|
||||||
|
<arg choice="opt">-s</arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
@ -57,14 +62,90 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
<title>Description</title>
|
<title>Description</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
<command>lxc-stop</command> kills all the processes inside the
|
<command>lxc-stop</command> reboots, cleanly shuts down, or kills
|
||||||
container. This command should be used if the processes are no
|
all the processes inside the container. By default, it will
|
||||||
longer accessible and can no be exited normally.
|
request a clean shutdown of the container (by sending SIGPWR to
|
||||||
|
the container), wait 60 seconds for the container to exit, and
|
||||||
|
returns. If the container fails to cleanly exit, then after 60
|
||||||
|
seconds the container will be sent the
|
||||||
|
<command>lxc.stopsignal</command> to force it to shut down.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The <optional>-W</optional>, <optional>-r</optional>, <optional>-s</optional>
|
||||||
|
and <optional>-k</optional> options specify the action to perform.
|
||||||
|
<optional>-W</optional> indicates that after performing the specified
|
||||||
|
action, <command>lxc-stop</command> should immediately exit, while
|
||||||
|
<optional>-t TIMEOUT</optional> specifies the maximum amount of time
|
||||||
|
to wait for the container to complete the shutdown or reboot.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
&commonoptions;
|
<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. This is the legacy
|
||||||
|
<command>lxc-stop</command> behavior.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<option>-W,--nowait </option>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Simply perform the requestion action (reboot, shutdown, or hard
|
||||||
|
kill) and exit.
|
||||||
|
</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>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
<title>Diagnostic</title>
|
<title>Diagnostic</title>
|
||||||
@ -92,7 +173,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
@ -123,7 +123,6 @@ bin_SCRIPTS = \
|
|||||||
lxc-checkconfig \
|
lxc-checkconfig \
|
||||||
lxc-version \
|
lxc-version \
|
||||||
lxc-create \
|
lxc-create \
|
||||||
lxc-shutdown \
|
|
||||||
lxc-destroy
|
lxc-destroy
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -61,9 +61,13 @@ struct lxc_arguments {
|
|||||||
int ttynum;
|
int ttynum;
|
||||||
char escape;
|
char escape;
|
||||||
|
|
||||||
/* for lxc-wait */
|
/* for lxc-wait and lxc-shutdown */
|
||||||
char *states;
|
char *states;
|
||||||
long timeout;
|
long timeout;
|
||||||
|
int nowait;
|
||||||
|
int reboot;
|
||||||
|
int hardstop;
|
||||||
|
int shutdown;
|
||||||
|
|
||||||
/* close fds from parent? */
|
/* close fds from parent? */
|
||||||
int close_all_fds;
|
int close_all_fds;
|
||||||
|
@ -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
|
|
@ -28,11 +28,29 @@
|
|||||||
#include <lxc/lxc.h>
|
#include <lxc/lxc.h>
|
||||||
#include <lxc/log.h>
|
#include <lxc/log.h>
|
||||||
|
|
||||||
|
#include <lxc/lxccontainer.h>
|
||||||
#include "arguments.h"
|
#include "arguments.h"
|
||||||
#include "commands.h"
|
#include "commands.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||||
|
{
|
||||||
|
switch (c) {
|
||||||
|
case 'r': args->reboot = 1; break;
|
||||||
|
case 'W': args->nowait = 1; break;
|
||||||
|
case 't': args->timeout = atoi(arg); break;
|
||||||
|
case 'k': args->hardstop = 1; break;
|
||||||
|
case 's': args->shutdown = 1; break;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct option my_longopts[] = {
|
static const struct option my_longopts[] = {
|
||||||
|
{"reboot", no_argument, 0, 'r'},
|
||||||
|
{"nowait", no_argument, 0, 'W'},
|
||||||
|
{"timeout", required_argument, 0, 't'},
|
||||||
|
{"kill", no_argument, 0, 'k'},
|
||||||
|
{"shutdown", no_argument, 0, 's'},
|
||||||
LXC_COMMON_OPTIONS
|
LXC_COMMON_OPTIONS
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -44,14 +62,76 @@ static struct lxc_arguments my_args = {
|
|||||||
lxc-stop stops a container with the identifier NAME\n\
|
lxc-stop stops a container with the identifier NAME\n\
|
||||||
\n\
|
\n\
|
||||||
Options :\n\
|
Options :\n\
|
||||||
-n, --name=NAME NAME for name of the container\n",
|
-n, --name=NAME NAME for name of the container\n\
|
||||||
|
-r, --reboot reboot the container\n\
|
||||||
|
-W, --nowait don't wait for shutdown or reboot to complete\n\
|
||||||
|
-t, --timeout=T wait T seconds before hard-stopping\n\
|
||||||
|
-k, --kill kill container rather than request clean shutdown\n\
|
||||||
|
-s, --shutdown Only request clean shutdown, don't later force kill\n",
|
||||||
.options = my_longopts,
|
.options = my_longopts,
|
||||||
.parser = NULL,
|
.parser = my_parser,
|
||||||
.checker = NULL,
|
.checker = NULL,
|
||||||
|
.timeout = 60,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* returns -1 on failure, 0 on success */
|
||||||
|
int do_reboot_and_check(struct lxc_arguments *a, struct lxc_container *c)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
pid_t pid;
|
||||||
|
pid_t newpid;
|
||||||
|
int timeout = a->timeout;
|
||||||
|
|
||||||
|
pid = c->init_pid(c);
|
||||||
|
if (pid == -1)
|
||||||
|
return -1;
|
||||||
|
if (!c->reboot(c))
|
||||||
|
return -1;
|
||||||
|
if (a->nowait)
|
||||||
|
return 0;
|
||||||
|
if (timeout <= 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
/* can we use c-> wait for this, assuming it will
|
||||||
|
* re-enter RUNNING? For now just sleep */
|
||||||
|
int elapsed_time, curtime = 0;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
newpid = c->init_pid(c);
|
||||||
|
if (newpid != -1 && newpid != pid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = gettimeofday(&tv, NULL);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
curtime = tv.tv_sec;
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
ret = gettimeofday(&tv, NULL);
|
||||||
|
if (ret)
|
||||||
|
break;
|
||||||
|
elapsed_time = tv.tv_sec - curtime;
|
||||||
|
if (timeout - elapsed_time <= 0)
|
||||||
|
break;
|
||||||
|
timeout -= elapsed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
newpid = c->init_pid(c);
|
||||||
|
if (newpid == -1 || newpid == pid) {
|
||||||
|
printf("Reboot did not complete before timeout\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
struct lxc_container *c;
|
||||||
|
bool s;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
if (lxc_arguments_parse(&my_args, argc, argv))
|
if (lxc_arguments_parse(&my_args, argc, argv))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -59,5 +139,36 @@ int main(int argc, char *argv[])
|
|||||||
my_args.progname, my_args.quiet, my_args.lxcpath[0]))
|
my_args.progname, my_args.quiet, my_args.lxcpath[0]))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return lxc_cmd_stop(my_args.name, my_args.lxcpath[0]);
|
c = lxc_container_new(my_args.name, my_args.lxcpath[0]);
|
||||||
|
if (!c) {
|
||||||
|
fprintf(stderr, "Error opening container\n");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c->is_running(c)) {
|
||||||
|
fprintf(stderr, "%s is not running\n", c->name);
|
||||||
|
ret = -2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my_args.hardstop) {
|
||||||
|
ret = c->stop(c) ? 0 : -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (my_args.reboot) {
|
||||||
|
ret = do_reboot_and_check(&my_args, c);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = c->shutdown(c, my_args.timeout);
|
||||||
|
if (!s) {
|
||||||
|
if (!my_args.shutdown)
|
||||||
|
ret = c->wait(c, "STOPPED", -1) ? 0 : -1;
|
||||||
|
else
|
||||||
|
ret = -1; // fail
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
lxc_container_put(c);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,105 @@
|
|||||||
|
|
||||||
lxc_log_define(lxc_container, lxc);
|
lxc_log_define(lxc_container, lxc);
|
||||||
|
|
||||||
|
static bool file_exists(char *f)
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
return stat(f, &statbuf) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A few functions to help detect when a container creation failed.
|
||||||
|
* If a container creation was killed partway through, then trying
|
||||||
|
* to actually start that container could harm the host. We detect
|
||||||
|
* this by creating a 'partial' file under the container directory,
|
||||||
|
* and keeping an advisory lock. When container creation completes,
|
||||||
|
* we remove that file. When we load or try to start a container, if
|
||||||
|
* we find that file, without a flock, we remove the container.
|
||||||
|
*/
|
||||||
|
int ongoing_create(struct lxc_container *c)
|
||||||
|
{
|
||||||
|
int len = strlen(c->config_path) + strlen(c->name) + 10;
|
||||||
|
char *path = alloca(len);
|
||||||
|
int fd, ret;
|
||||||
|
ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name);
|
||||||
|
if (ret < 0 || ret >= len) {
|
||||||
|
ERROR("Error writing partial pathname");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file_exists(path))
|
||||||
|
return 0;
|
||||||
|
if (process_lock())
|
||||||
|
return -1;
|
||||||
|
if ((fd = open(path, O_RDWR)) < 0) {
|
||||||
|
// give benefit of the doubt
|
||||||
|
SYSERROR("Error opening partial file");
|
||||||
|
process_unlock();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((ret = flock(fd, LOCK_EX | LOCK_NB)) == -1 &&
|
||||||
|
errno == EWOULDBLOCK) {
|
||||||
|
// create is still ongoing
|
||||||
|
close(fd);
|
||||||
|
process_unlock();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// create completed but partial is still there.
|
||||||
|
close(fd);
|
||||||
|
process_unlock();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int create_partial(struct lxc_container *c)
|
||||||
|
{
|
||||||
|
// $lxcpath + '/' + $name + '/partial' + \0
|
||||||
|
int len = strlen(c->config_path) + strlen(c->name) + 10;
|
||||||
|
char *path = alloca(len);
|
||||||
|
int fd, ret;
|
||||||
|
ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name);
|
||||||
|
if (ret < 0 || ret >= len) {
|
||||||
|
ERROR("Error writing partial pathname");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (process_lock() < 0)
|
||||||
|
return -1;
|
||||||
|
if ((fd=open(path, O_CREAT | O_EXCL, 0755)) < 0) {
|
||||||
|
SYSERROR("Erorr creating partial file");
|
||||||
|
process_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (flock(fd, LOCK_EX) < 0) {
|
||||||
|
SYSERROR("Error locking partial file %s", path);
|
||||||
|
close(fd);
|
||||||
|
process_unlock();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
process_unlock();
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_partial(struct lxc_container *c, int fd)
|
||||||
|
{
|
||||||
|
// $lxcpath + '/' + $name + '/partial' + \0
|
||||||
|
int len = strlen(c->config_path) + strlen(c->name) + 10;
|
||||||
|
char *path = alloca(len);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
ret = snprintf(path, len, "%s/%s/partial", c->config_path, c->name);
|
||||||
|
if (ret < 0 || ret >= len) {
|
||||||
|
ERROR("Error writing partial pathname");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (process_lock())
|
||||||
|
return;
|
||||||
|
if (unlink(path) < 0)
|
||||||
|
SYSERROR("Error unlink partial file %s", path);
|
||||||
|
process_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
/* LOCKING
|
/* LOCKING
|
||||||
* 1. c->privlock protects the struct lxc_container from multiple threads.
|
* 1. c->privlock protects the struct lxc_container from multiple threads.
|
||||||
* 2. c->slock protects the on-disk container data
|
* 2. c->slock protects the on-disk container data
|
||||||
@ -163,13 +262,6 @@ int lxc_container_put(struct lxc_container *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool file_exists(char *f)
|
|
||||||
{
|
|
||||||
struct stat statbuf;
|
|
||||||
|
|
||||||
return stat(f, &statbuf) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool lxcapi_is_defined(struct lxc_container *c)
|
static bool lxcapi_is_defined(struct lxc_container *c)
|
||||||
{
|
{
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
@ -349,6 +441,19 @@ static bool lxcapi_start(struct lxc_container *c, int useinit, char * const argv
|
|||||||
if (!c->lxc_conf)
|
if (!c->lxc_conf)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if ((ret = ongoing_create(c)) < 0) {
|
||||||
|
ERROR("Error checking for incomplete creation");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ret == 2) {
|
||||||
|
ERROR("Error: %s creation was not completed", c->name);
|
||||||
|
c->destroy(c);
|
||||||
|
return false;
|
||||||
|
} else if (ret == 1) {
|
||||||
|
ERROR("Error: creation of %s is ongoing", c->name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* is this app meant to be run through lxcinit, as in lxc-execute? */
|
/* is this app meant to be run through lxcinit, as in lxc-execute? */
|
||||||
if (useinit && !argv)
|
if (useinit && !argv)
|
||||||
return false;
|
return false;
|
||||||
@ -550,7 +655,7 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar
|
|||||||
bool bret = false;
|
bool bret = false;
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
char *tpath = NULL, **newargv;
|
char *tpath = NULL, **newargv;
|
||||||
int ret, len, nargs = 0;
|
int partial_fd, ret, len, nargs = 0;
|
||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
return false;
|
return false;
|
||||||
@ -576,6 +681,10 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar
|
|||||||
if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0)
|
if (lxcapi_is_defined(c) && c->lxc_conf && c->lxc_conf->rootfs.path && access(c->lxc_conf->rootfs.path, F_OK) == 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/* Mark that this container is being created */
|
||||||
|
if ((partial_fd = create_partial(c)) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/* we're going to fork. but since we'll wait for our child, we
|
/* we're going to fork. but since we'll wait for our child, we
|
||||||
* don't need to lxc_container_get */
|
* don't need to lxc_container_get */
|
||||||
|
|
||||||
@ -659,6 +768,8 @@ static bool lxcapi_create(struct lxc_container *c, const char *t, char *const ar
|
|||||||
bret = load_config_locked(c, c->configfile);
|
bret = load_config_locked(c, c->configfile);
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
if (partial_fd >= 0)
|
||||||
|
remove_partial(c, partial_fd);
|
||||||
container_disk_unlock(c);
|
container_disk_unlock(c);
|
||||||
out:
|
out:
|
||||||
if (tpath)
|
if (tpath)
|
||||||
@ -666,6 +777,23 @@ out:
|
|||||||
return bret;
|
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)
|
static bool lxcapi_shutdown(struct lxc_container *c, int timeout)
|
||||||
{
|
{
|
||||||
bool retv;
|
bool retv;
|
||||||
@ -1699,6 +1827,12 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
|
|||||||
if (file_exists(c->configfile))
|
if (file_exists(c->configfile))
|
||||||
lxcapi_load_config(c, NULL);
|
lxcapi_load_config(c, NULL);
|
||||||
|
|
||||||
|
if (ongoing_create(c) == 2) {
|
||||||
|
ERROR("Error: %s creation was not completed", c->name);
|
||||||
|
c->destroy(c);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
// assign the member functions
|
// assign the member functions
|
||||||
c->is_defined = lxcapi_is_defined;
|
c->is_defined = lxcapi_is_defined;
|
||||||
c->state = lxcapi_state;
|
c->state = lxcapi_state;
|
||||||
@ -1720,6 +1854,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
|
|||||||
c->create = lxcapi_create;
|
c->create = lxcapi_create;
|
||||||
c->createl = lxcapi_createl;
|
c->createl = lxcapi_createl;
|
||||||
c->shutdown = lxcapi_shutdown;
|
c->shutdown = lxcapi_shutdown;
|
||||||
|
c->reboot = lxcapi_reboot;
|
||||||
c->clear_config_item = lxcapi_clear_config_item;
|
c->clear_config_item = lxcapi_clear_config_item;
|
||||||
c->get_config_item = lxcapi_get_config_item;
|
c->get_config_item = lxcapi_get_config_item;
|
||||||
c->get_cgroup_item = lxcapi_get_cgroup_item;
|
c->get_cgroup_item = lxcapi_get_cgroup_item;
|
||||||
|
@ -50,6 +50,8 @@ struct lxc_container {
|
|||||||
bool (*save_config)(struct lxc_container *c, const char *alt_file);
|
bool (*save_config)(struct lxc_container *c, const char *alt_file);
|
||||||
bool (*create)(struct lxc_container *c, const char *t, char *const argv[]);
|
bool (*create)(struct lxc_container *c, const char *t, char *const argv[]);
|
||||||
bool (*createl)(struct lxc_container *c, const char *t, ...);
|
bool (*createl)(struct lxc_container *c, const 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 */
|
/* send SIGPWR. if timeout is not 0 or -1, do a hard stop after timeout seconds */
|
||||||
bool (*shutdown)(struct lxc_container *c, int timeout);
|
bool (*shutdown)(struct lxc_container *c, int timeout);
|
||||||
/* clear all network or capability items in the in-memory configuration */
|
/* clear all network or capability items in the in-memory configuration */
|
||||||
|
Loading…
Reference in New Issue
Block a user