core: define "exit" and "exit-force" actions for user units and only accept that

We would accept e.g. FailureAction=reboot-force in user units and then do an
exit in the user manager. Let's be stricter, and define "exit"/"exit-force" as
the only supported actions in user units.

v2:
- rename 'exit' to 'exit-force' and add new 'exit'
- add test for the parsing function
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-10-16 13:28:39 +02:00
parent 454dd6ce7a
commit 54fcb6192c
8 changed files with 195 additions and 22 deletions

4
TODO
View File

@ -2,6 +2,10 @@ Bugfixes:
* copy.c: set the right chattrs before copying files and others after * copy.c: set the right chattrs before copying files and others after
* Many manager configuration settings that are only applicable to user
manager or system manager can be always set. It would be better to reject
them when parsing config.
External: External:
* Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros. * Fedora: add an rpmlint check that verifies that all unit files in the RPM are listed in %systemd_post macros.

View File

@ -868,18 +868,24 @@
<term><varname>FailureAction=</varname></term> <term><varname>FailureAction=</varname></term>
<term><varname>SuccessAction=</varname></term> <term><varname>SuccessAction=</varname></term>
<listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive <listitem><para>Configure the action to take when the unit stops and enters a failed state or inactive state.
state. Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>, Takes one of <option>none</option>, <option>reboot</option>, <option>reboot-force</option>,
<option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option> or <option>reboot-immediate</option>, <option>poweroff</option>, <option>poweroff-force</option>,
<option>poweroff-immediate</option>. If <option>none</option> is set, no action will be triggered. <option>poweroff-immediate</option>, <option>exit</option>, and <option>exit-force</option>. In system mode,
<option>reboot</option> causes a reboot following the normal shutdown procedure (i.e. equivalent to all options except <option>exit</option> and <option>exit-force</option> are allowed. In user mode, only
<command>systemctl reboot</command>). <option>reboot-force</option> causes a forced reboot which will <option>none</option>, <option>exit</option>, and <option>exit-force</option> are allowed. Both options default
terminate all processes forcibly but should cause no dirty file systems on reboot (i.e. equivalent to to <option>none</option>.</para>
<command>systemctl reboot -f</command>) and <option>reboot-immediate</option> causes immediate execution of the
<para>If <option>none</option> is set, no action will be triggered. <option>reboot</option> causes a reboot
following the normal shutdown procedure (i.e. equivalent to <command>systemctl reboot</command>).
<option>reboot-force</option> causes a forced reboot which will terminate all processes forcibly but should
cause no dirty file systems on reboot (i.e. equivalent to <command>systemctl reboot -f</command>) and
<option>reboot-immediate</option> causes immediate execution of the
<citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which <citerefentry><refentrytitle>reboot</refentrytitle><manvolnum>2</manvolnum></citerefentry> system call, which
might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>, might result in data loss. Similarly, <option>poweroff</option>, <option>poweroff-force</option>,
<option>poweroff-immediate</option> have the effect of powering down the system with similar semantics. Both <option>poweroff-immediate</option> have the effect of powering down the system with similar
options default to <option>none</option>.</para></listitem> semantics. <option>exit</option> causes the user manager to exit following the normal shutdown procedure, and
<option>exit-force</option> causes it terminate without shutting down services.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -1299,8 +1299,43 @@ static int bus_unit_set_live_property(
return 0; return 0;
} }
static int bus_set_transient_emergency_action(
Unit *u,
const char *name,
EmergencyAction *p,
sd_bus_message *message,
UnitWriteFlags flags,
sd_bus_error *error) {
const char *s;
EmergencyAction v;
int r;
bool system;
assert(p);
r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;
system = MANAGER_IS_SYSTEM(u->manager);
r = parse_emergency_action(s, system, &v);
if (v < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
v == -EOPNOTSUPP ? "EmergencyAction setting invalid for manager type: %s"
: "Invalid %s setting: %s",
name, s);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
*p = v;
unit_write_settingf(u, flags, name,
"%s=%s", name, s);
}
return 1;
}
static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string);
static int bus_set_transient_conditions( static int bus_set_transient_conditions(

View File

@ -36,15 +36,6 @@ int emergency_action(
return -ECANCELED; return -ECANCELED;
} }
if (!MANAGER_IS_SYSTEM(m)) {
/* Downgrade all options to simply exiting if we run
* in user mode */
log_warning("Exiting: %s", reason);
m->objective = MANAGER_EXIT;
return -ECANCELED;
}
switch (action) { switch (action) {
case EMERGENCY_ACTION_REBOOT: case EMERGENCY_ACTION_REBOOT:
@ -78,11 +69,26 @@ int emergency_action(
(void) reboot(RB_AUTOBOOT); (void) reboot(RB_AUTOBOOT);
break; break;
case EMERGENCY_ACTION_EXIT:
assert(MANAGER_IS_USER(m));
log_and_status(m, "Exiting", reason);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break;
case EMERGENCY_ACTION_POWEROFF: case EMERGENCY_ACTION_POWEROFF:
log_and_status(m, "Powering off", reason); log_and_status(m, "Powering off", reason);
(void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL);
break; break;
case EMERGENCY_ACTION_EXIT_FORCE:
assert(MANAGER_IS_USER(m));
log_and_status(m, "Exiting immediately", reason);
m->objective = MANAGER_EXIT;
break;
case EMERGENCY_ACTION_POWEROFF_FORCE: case EMERGENCY_ACTION_POWEROFF_FORCE:
log_and_status(m, "Forcibly powering off", reason); log_and_status(m, "Forcibly powering off", reason);
m->objective = MANAGER_POWEROFF; m->objective = MANAGER_POWEROFF;
@ -111,6 +117,27 @@ static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = {
[EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate",
[EMERGENCY_ACTION_POWEROFF] = "poweroff", [EMERGENCY_ACTION_POWEROFF] = "poweroff",
[EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force", [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force",
[EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate" [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate",
[EMERGENCY_ACTION_EXIT] = "exit",
[EMERGENCY_ACTION_EXIT_FORCE] = "exit-force",
}; };
DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction); DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction);
int parse_emergency_action(
const char *value,
bool system,
EmergencyAction *ret) {
EmergencyAction x;
x = emergency_action_from_string(value);
if (x < 0)
return -EINVAL;
if ((system && x >= _EMERGENCY_ACTION_FIRST_USER_ACTION) ||
(!system && x != EMERGENCY_ACTION_NONE && x < _EMERGENCY_ACTION_FIRST_USER_ACTION))
return -EOPNOTSUPP;
*ret = x;
return 0;
}

View File

@ -9,6 +9,9 @@ typedef enum EmergencyAction {
EMERGENCY_ACTION_POWEROFF, EMERGENCY_ACTION_POWEROFF,
EMERGENCY_ACTION_POWEROFF_FORCE, EMERGENCY_ACTION_POWEROFF_FORCE,
EMERGENCY_ACTION_POWEROFF_IMMEDIATE, EMERGENCY_ACTION_POWEROFF_IMMEDIATE,
EMERGENCY_ACTION_EXIT,
_EMERGENCY_ACTION_FIRST_USER_ACTION = EMERGENCY_ACTION_EXIT,
EMERGENCY_ACTION_EXIT_FORCE,
_EMERGENCY_ACTION_MAX, _EMERGENCY_ACTION_MAX,
_EMERGENCY_ACTION_INVALID = -1 _EMERGENCY_ACTION_INVALID = -1
} EmergencyAction; } EmergencyAction;
@ -20,3 +23,5 @@ int emergency_action(Manager *m, EmergencyAction action, const char *reboot_arg,
const char* emergency_action_to_string(EmergencyAction i) _const_; const char* emergency_action_to_string(EmergencyAction i) _const_;
EmergencyAction emergency_action_from_string(const char *s) _pure_; EmergencyAction emergency_action_from_string(const char *s) _pure_;
int parse_emergency_action(const char *value, bool system, EmergencyAction *ret);

View File

@ -76,7 +76,6 @@ DEFINE_CONFIG_PARSE(config_parse_socket_protocol, supported_socket_protocol_from
DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits"); DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy"); DEFINE_CONFIG_PARSE_ENUM(config_parse_device_policy, cgroup_device_policy, CGroupDevicePolicy, "Failed to parse device policy");
DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_keyring_mode, exec_keyring_mode, ExecKeyringMode, "Failed to parse keyring mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode");
DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode");
@ -4185,6 +4184,47 @@ int config_parse_job_running_timeout_sec(
return 0; return 0;
} }
int config_parse_emergency_action(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
Manager *m = NULL;
EmergencyAction *x = data;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
if (unit)
m = ((Unit*) userdata)->manager;
else
m = data;
r = parse_emergency_action(rvalue, MANAGER_IS_SYSTEM(m), x);
if (r < 0) {
if (r == -EOPNOTSUPP)
log_syntax(unit, LOG_ERR, filename, line, r,
"%s= specified as %s mode action, ignoring: %s",
lvalue, MANAGER_IS_SYSTEM(m) ? "user" : "system", rvalue);
else
log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to parse %s=, ignoring: %s", lvalue, rvalue);
return 0;
}
return 0;
}
#define FOLLOW_MAX 8 #define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {

View File

@ -63,6 +63,11 @@ tests += [
libmount, libmount,
libblkid]], libblkid]],
[['src/test/test-emergency-action.c'],
[libcore,
libshared],
[]],
[['src/test/test-job-type.c'], [['src/test/test-job-type.c'],
[libcore, [libcore,
libshared], libshared],

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: LGPL-2.1+ */
#include "emergency-action.h"
#include "tests.h"
static void test_parse_emergency_action(void) {
EmergencyAction x;
log_info("/* %s */", __func__);
assert_se(parse_emergency_action("none", false, &x) == 0);
assert_se(x == EMERGENCY_ACTION_NONE);
assert_se(parse_emergency_action("reboot", false, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("reboot-force", false, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("reboot-immediate", false, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("poweroff", false, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("poweroff-force", false, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("poweroff-immediate", false, &x) == -EOPNOTSUPP);
assert_se(x == EMERGENCY_ACTION_NONE);
assert_se(parse_emergency_action("exit", false, &x) == 0);
assert_se(x == EMERGENCY_ACTION_EXIT);
assert_se(parse_emergency_action("exit-force", false, &x) == 0);
assert_se(x == EMERGENCY_ACTION_EXIT_FORCE);
assert_se(parse_emergency_action("exit-forcee", false, &x) == -EINVAL);
assert_se(parse_emergency_action("none", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_NONE);
assert_se(parse_emergency_action("reboot", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_REBOOT);
assert_se(parse_emergency_action("reboot-force", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_REBOOT_FORCE);
assert_se(parse_emergency_action("reboot-immediate", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_REBOOT_IMMEDIATE);
assert_se(parse_emergency_action("poweroff", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_POWEROFF);
assert_se(parse_emergency_action("poweroff-force", true, &x) == 0);
assert_se(x == EMERGENCY_ACTION_POWEROFF_FORCE);
assert_se(parse_emergency_action("poweroff-immediate", true, &x) == 0);
assert_se(parse_emergency_action("exit", true, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("exit-force", true, &x) == -EOPNOTSUPP);
assert_se(parse_emergency_action("exit-forcee", true, &x) == -EINVAL);
assert_se(x == EMERGENCY_ACTION_POWEROFF_IMMEDIATE);
}
int main(int argc, char **argv) {
test_setup_logging(LOG_INFO);
test_parse_emergency_action();
return EXIT_SUCCESS;
}