Skip to content

Commit

Permalink
systemd: return a non-zero exit code when requested
Browse files Browse the repository at this point in the history
  • Loading branch information
alban committed Sep 18, 2015
1 parent 7ee7b22 commit 0295672
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 7 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,7 @@ dist_systemunit_DATA = \
units/getty.target \
units/halt.target \
units/kexec.target \
units/exit.target \
units/local-fs.target \
units/local-fs-pre.target \
units/initrd.target \
Expand Down Expand Up @@ -545,6 +546,7 @@ nodist_systemunit_DATA = \
units/systemd-poweroff.service \
units/systemd-reboot.service \
units/systemd-kexec.service \
units/systemd-exit.service \
units/systemd-fsck@.service \
units/systemd-fsck-root.service \
units/systemd-machine-id-commit.service \
Expand Down
10 changes: 7 additions & 3 deletions man/systemctl.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1612,13 +1612,17 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
</varlistentry>

<varlistentry>
<term><command>exit</command></term>
<term><command>exit <optional><replaceable>EXIT_CODE</replaceable></optional></command></term>

<listitem>
<para>Ask the systemd manager to quit. This is only
supported for user service managers (i.e. in conjunction
with the <option>--user</option> option) and will fail
otherwise.</para>
with the <option>--user</option> option) or in containers
and will fail otherwise.</para>

<para>The systemd manager can exit with a non-zero exit
code if the optional argument
<replaceable>EXIT_CODE</replaceable> is given.</para>
</listitem>
</varlistentry>

Expand Down
27 changes: 27 additions & 0 deletions src/core/dbus-manager.c
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,31 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
return sd_bus_reply_method_return(message, NULL);
}

static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) {
unsigned code;
Manager *m = userdata;
int r;

assert(message);
assert(m);

/* FIXME: is is correct to use "halt"? */
r = mac_selinux_access_check(message, "halt", error);
if (r < 0)
return r;

r = sd_bus_message_read_basic(message, 'u', &code);
if (r < 0)
return r;

if (detect_container(NULL) <= 0)
return sd_bus_error_setf(error, BUS_ERROR_NOT_ALLOWED, "ExitCode can only be set in containers or user service manager.");

m->return_value = code;

return sd_bus_reply_method_return(message, NULL);
}

static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
Manager *m = userdata;
Expand Down Expand Up @@ -1954,6 +1979,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0),
SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0),
SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0),
SD_BUS_PROPERTY("ExitCode", "u", bus_property_get_unsigned, offsetof(Manager, return_value), 0),

SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
Expand Down Expand Up @@ -2007,6 +2033,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetExitCode", "u", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED),

SD_BUS_SIGNAL("UnitNew", "so", 0),
SD_BUS_SIGNAL("UnitRemoved", "so", 0),
Expand Down
10 changes: 8 additions & 2 deletions src/core/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,7 @@ int main(int argc, char *argv[]) {
char *switch_root_dir = NULL, *switch_root_init = NULL;
struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0);
const char *error_message = NULL;
unsigned return_value = 0;

#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
Expand Down Expand Up @@ -1835,8 +1836,10 @@ int main(int argc, char *argv[]) {
finish:
pager_close();

if (m)
if (m) {
arg_shutdown_watchdog = m->shutdown_watchdog;
return_value = m->return_value;
}
m = manager_free(m);

for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) {
Expand Down Expand Up @@ -1988,7 +1991,7 @@ int main(int argc, char *argv[]) {

if (shutdown_verb) {
char log_level[DECIMAL_STR_MAX(int) + 1];
const char* command_line[9] = {
const char* command_line[10] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
"--log-level", log_level,
Expand Down Expand Up @@ -2025,6 +2028,9 @@ int main(int argc, char *argv[]) {
if (log_get_show_location())
command_line[pos++] = "--log-location";

if (return_value > 0)
command_line[pos++] = "--exit_code=1";

assert(pos < ELEMENTSOF(command_line));

if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
Expand Down
5 changes: 5 additions & 0 deletions src/core/manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ struct Manager {

const char *unit_log_field;
const char *unit_log_format_string;

/* If non-zero, exit with the following value when the systemd
* process terminate. Useful for containers: systemd-nspawn could get
* the return value. */
unsigned return_value;
};

int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m);
Expand Down
15 changes: 15 additions & 0 deletions src/core/shutdown.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,23 @@
#define FINALIZE_ATTEMPTS 50

static char* arg_verb;
unsigned exit_code;

static int parse_argv(int argc, char *argv[]) {
enum {
ARG_LOG_LEVEL = 0x100,
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
ARG_EXIT_CODE,
};

static const struct option options[] = {
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
{ "exit-code", optional_argument, NULL, ARG_EXIT_CODE },
{}
};

Expand Down Expand Up @@ -110,6 +113,15 @@ static int parse_argv(int argc, char *argv[]) {

break;

case ARG_EXIT_CODE:
if (optarg) {
r = safe_atou(optarg, &exit_code);
if (r < 0)
log_error("Failed to parse exit code %s, ignoring", optarg);
}

break;

case '\001':
if (!arg_verb)
arg_verb = optarg;
Expand Down Expand Up @@ -395,6 +407,9 @@ int main(int argc, char *argv[]) {
assert_not_reached("Unknown magic");
}

if (exit_code > 0) {
exit(exit_code);
}
reboot(cmd);
if (errno == EPERM && in_container) {
/* If we are in a container, and we lacked
Expand Down
1 change: 1 addition & 0 deletions src/libsystemd/sd-bus/bus-common-errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning"
#define BUS_ERROR_NOT_ALLOWED "org.freedesktop.systemd1.NotAllowed"

#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
Expand Down
29 changes: 27 additions & 2 deletions src/systemctl/systemctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -4838,6 +4838,31 @@ static int daemon_reload(sd_bus *bus, char **args) {
/* "daemon-reload" */ "Reload";
}

if (streq(args[0], "exit")) {
unsigned code = 0;
if (strv_length(args) > 1) {
r = safe_atou(args[1], &code);
if (r < 0) {
log_error("Invalid exit code.");
return -EINVAL;
}
}

r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"SetExitCode",
&error,
NULL,
"u", 1);
if (r < 0) {
log_error("Failed to execute operation: %s", bus_error_message(&error, r));
return r;
}
}

r = sd_bus_call_method(
bus,
"org.freedesktop.systemd1",
Expand Down Expand Up @@ -6155,7 +6180,7 @@ static void systemctl_help(void) {
" poweroff Shut down and power-off the system\n"
" reboot [ARG] Shut down and reboot the system\n"
" kexec Shut down and reboot the system with kexec\n"
" exit Request user instance exit\n"
" exit [EXIT_CODE] Request user instance or container exit\n"
" switch-root ROOT [INIT] Change to a different root file system\n"
" suspend Suspend the system\n"
" hibernate Hibernate the system\n"
Expand Down Expand Up @@ -7132,7 +7157,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
{ "default", EQUAL, 1, start_special },
{ "rescue", EQUAL, 1, start_special },
{ "emergency", EQUAL, 1, start_special },
{ "exit", EQUAL, 1, start_special },
{ "exit", LESS, 2, start_special },
{ "reset-failed", MORE, 1, reset_failed },
{ "enable", MORE, 2, enable_unit, NOBUS },
{ "disable", MORE, 2, enable_unit, NOBUS },
Expand Down
18 changes: 18 additions & 0 deletions units/exit.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This file is part of systemd.
#
# systemd 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.

[Unit]
Description=Exit the container
Documentation=man:systemd.special(7)
ConditionVirtualization=container
DefaultDependencies=no
Requires=systemd-exit.service
After=systemd-exit.service
AllowIsolate=yes

[Install]
Alias=ctrl-alt-del.target
17 changes: 17 additions & 0 deletions units/systemd-exit.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This file is part of systemd.
#
# systemd 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.

[Unit]
Description=Exit the Session
Documentation=man:systemd.special(7)
DefaultDependencies=no
Requires=shutdown.target
After=shutdown.target

[Service]
Type=oneshot
ExecStart=/usr/bin/systemctl exit 1

0 comments on commit 0295672

Please sign in to comment.