diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/dbus-manager.c | 81 | ||||
-rw-r--r-- | src/core/dbus-service.c | 6 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 12 | ||||
-rw-r--r-- | src/core/device.c | 6 | ||||
-rw-r--r-- | src/core/emergency-action.c (renamed from src/core/failure-action.c) | 65 | ||||
-rw-r--r-- | src/core/emergency-action.h (renamed from src/core/failure-action.h) | 28 | ||||
-rw-r--r-- | src/core/execute.c | 651 | ||||
-rw-r--r-- | src/core/job.c | 21 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 8 | ||||
-rw-r--r-- | src/core/load-fragment.c | 65 | ||||
-rw-r--r-- | src/core/load-fragment.h | 2 | ||||
-rw-r--r-- | src/core/main.c | 9 | ||||
-rw-r--r-- | src/core/manager.c | 33 | ||||
-rw-r--r-- | src/core/manager.h | 13 | ||||
-rw-r--r-- | src/core/mount.c | 82 | ||||
-rw-r--r-- | src/core/org.freedesktop.systemd1.conf | 4 | ||||
-rw-r--r-- | src/core/scope.c | 53 | ||||
-rw-r--r-- | src/core/service.c | 63 | ||||
-rw-r--r-- | src/core/service.h | 2 | ||||
-rw-r--r-- | src/core/slice.c | 47 | ||||
-rw-r--r-- | src/core/swap.c | 6 | ||||
-rw-r--r-- | src/core/unit.c | 59 | ||||
-rw-r--r-- | src/core/unit.h | 15 |
23 files changed, 736 insertions, 595 deletions
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 12eb55cb7f..d7d3d3c8ce 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -47,6 +47,11 @@ #include "virt.h" #include "watchdog.h" +static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) { + return (runtime ? UNIT_FILE_RUNTIME : 0) | + (force ? UNIT_FILE_FORCE : 0); +} + static int property_get_version( sd_bus *bus, const char *path, @@ -1948,13 +1953,14 @@ static int install_error( static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes), + int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), bool carries_install_info, sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; + UnitFileFlags flags; int runtime, force, r; assert(message); @@ -1968,13 +1974,15 @@ static int method_enable_unit_files_generic( if (r < 0) return r; + flags = unit_file_bools_to_flags(runtime, force); + r = bus_verify_manage_unit_files_async(m, message, error); if (r < 0) return r; if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); + r = call(m->unit_file_scope, flags, NULL, l, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -1993,8 +2001,8 @@ static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bu return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); } -static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) { - return unit_file_preset(scope, runtime, root_dir, files, UNIT_FILE_PRESET_FULL, force, changes, n_changes); +static int unit_file_preset_without_mode(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes) { + return unit_file_preset(scope, flags, root_dir, files, UNIT_FILE_PRESET_FULL, changes, n_changes); } static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2013,6 +2021,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use Manager *m = userdata; UnitFilePresetMode mm; int runtime, force, r; + UnitFileFlags flags; const char *mode; assert(message); @@ -2026,6 +2035,8 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use if (r < 0) return r; + flags = unit_file_bools_to_flags(runtime, force); + if (isempty(mode)) mm = UNIT_FILE_PRESET_FULL; else { @@ -2040,7 +2051,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); + r = unit_file_preset(m->unit_file_scope, flags, NULL, l, mm, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2050,7 +2061,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use static int method_disable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), + int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; @@ -2075,7 +2086,7 @@ static int method_disable_unit_files_generic( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); + r = call(m->unit_file_scope, runtime ? UNIT_FILE_RUNTIME : 0, NULL, l, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2141,7 +2152,7 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); + r = unit_file_set_default(m->unit_file_scope, force ? UNIT_FILE_FORCE : 0, NULL, name, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2154,6 +2165,7 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, Manager *m = userdata; UnitFilePresetMode mm; const char *mode; + UnitFileFlags flags; int force, runtime, r; assert(message); @@ -2167,6 +2179,8 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, if (r < 0) return r; + flags = unit_file_bools_to_flags(runtime, force); + if (isempty(mode)) mm = UNIT_FILE_PRESET_FULL; else { @@ -2181,7 +2195,7 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); + r = unit_file_preset_all(m->unit_file_scope, flags, NULL, mm, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); @@ -2196,6 +2210,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd int runtime, force, r; char *target, *type; UnitDependency dep; + UnitFileFlags flags; assert(message); assert(m); @@ -2214,17 +2229,62 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd if (r < 0) return r; + flags = unit_file_bools_to_flags(runtime, force); + dep = unit_dependency_from_string(type); if (dep < 0) return -EINVAL; - r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); + r = unit_file_add_dependency(m->unit_file_scope, flags, NULL, l, target, dep, &changes, &n_changes); if (r < 0) return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } +static int method_get_unit_file_links(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0, i; + UnitFileFlags flags; + const char *name; + char **p; + int runtime, r; + + r = sd_bus_message_read(message, "sb", &name, &runtime); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, SD_BUS_TYPE_ARRAY, "s"); + if (r < 0) + return r; + + p = STRV_MAKE(name); + flags = UNIT_FILE_DRY_RUN | + (runtime ? UNIT_FILE_RUNTIME : 0); + + r = unit_file_disable(UNIT_FILE_SYSTEM, flags, NULL, p, &changes, &n_changes); + if (r < 0) + return log_error_errno(r, "Failed to get file links for %s: %m", name); + + for (i = 0; i < n_changes; i++) + if (changes[i].type == UNIT_FILE_UNLINK) { + r = sd_bus_message_append(reply, "s", changes[i].path); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_VTABLE_START(0), @@ -2370,6 +2430,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("GetUnitFileLinks", "sb", "as", method_get_unit_file_links, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("LookupDynamicUserByName", "s", "u", method_lookup_dynamic_user_by_name, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("LookupDynamicUserByUID", "u", "s", method_lookup_dynamic_user_by_uid, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 3c55e0f7fe..61b83d2d62 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -36,7 +36,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, service_result, ServiceResult); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, ServiceRestart); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_START(0), @@ -50,7 +50,7 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST), BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0), - SD_BUS_PROPERTY("FailureAction", "s", property_get_failure_action, offsetof(Service, failure_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Service, emergency_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PermissionsStartOnly", "b", bus_property_get_bool, offsetof(Service, permissions_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST), @@ -79,7 +79,7 @@ const sd_bus_vtable bus_service_vtable[] = { /* The following four are obsolete, and thus marked hidden here. They moved into the Unit interface */ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 6948daf682..b6cb6e1350 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -37,7 +37,7 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode); -static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_failure_action, failure_action, FailureAction); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); static int property_get_names( sd_bus *bus, @@ -263,10 +263,7 @@ static int property_get_can_stop( assert(reply); assert(u); - /* On the lower levels we assume that every unit we can start - * we can also stop */ - - return sd_bus_message_append(reply, "b", unit_can_start(u) && !u->refuse_manual_stop); + return sd_bus_message_append(reply, "b", unit_can_stop(u) && !u->refuse_manual_stop); } static int property_get_can_reload( @@ -750,7 +747,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -760,9 +757,10 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitAction", "s", property_get_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("InvocationID", "ay", bus_property_get_id128, offsetof(Unit, invocation_id), 0), diff --git a/src/core/device.c b/src/core/device.c index bb10148223..498351af11 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -331,11 +331,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa if (!u) { delete = true; - u = unit_new(m, sizeof(Device)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Device), e, &u); if (r < 0) goto fail; diff --git a/src/core/failure-action.c b/src/core/emergency-action.c index ddae46190f..90232bc57a 100644 --- a/src/core/failure-action.c +++ b/src/core/emergency-action.c @@ -23,59 +23,60 @@ #include "bus-error.h" #include "bus-util.h" -#include "failure-action.h" +#include "emergency-action.h" #include "special.h" #include "string-table.h" #include "terminal-util.h" -static void log_and_status(Manager *m, const char *message) { - log_warning("%s", message); +static void log_and_status(Manager *m, const char *message, const char *reason) { + log_warning("%s: %s", message, reason); manager_status_printf(m, STATUS_TYPE_EMERGENCY, ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, - "%s", message); + "%s: %s", message, reason); } -int failure_action( +int emergency_action( Manager *m, - FailureAction action, - const char *reboot_arg) { + EmergencyAction action, + const char *reboot_arg, + const char *reason) { assert(m); assert(action >= 0); - assert(action < _FAILURE_ACTION_MAX); + assert(action < _EMERGENCY_ACTION_MAX); - if (action == FAILURE_ACTION_NONE) + if (action == EMERGENCY_ACTION_NONE) return -ECANCELED; if (!MANAGER_IS_SYSTEM(m)) { /* Downgrade all options to simply exiting if we run * in user mode */ - log_warning("Exiting as result of failure."); + log_warning("Exiting: %s", reason); m->exit_code = MANAGER_EXIT; return -ECANCELED; } switch (action) { - case FAILURE_ACTION_REBOOT: - log_and_status(m, "Rebooting as result of failure."); + case EMERGENCY_ACTION_REBOOT: + log_and_status(m, "Rebooting", reason); (void) update_reboot_parameter_and_warn(reboot_arg); (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); break; - case FAILURE_ACTION_REBOOT_FORCE: - log_and_status(m, "Forcibly rebooting as result of failure."); + case EMERGENCY_ACTION_REBOOT_FORCE: + log_and_status(m, "Forcibly rebooting", reason); (void) update_reboot_parameter_and_warn(reboot_arg); m->exit_code = MANAGER_REBOOT; break; - case FAILURE_ACTION_REBOOT_IMMEDIATE: - log_and_status(m, "Rebooting immediately as result of failure."); + case EMERGENCY_ACTION_REBOOT_IMMEDIATE: + log_and_status(m, "Rebooting immediately", reason); sync(); @@ -89,18 +90,18 @@ int failure_action( reboot(RB_AUTOBOOT); break; - case FAILURE_ACTION_POWEROFF: - log_and_status(m, "Powering off as result of failure."); + case EMERGENCY_ACTION_POWEROFF: + 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); break; - case FAILURE_ACTION_POWEROFF_FORCE: - log_and_status(m, "Forcibly powering off as result of failure."); + case EMERGENCY_ACTION_POWEROFF_FORCE: + log_and_status(m, "Forcibly powering off", reason); m->exit_code = MANAGER_POWEROFF; break; - case FAILURE_ACTION_POWEROFF_IMMEDIATE: - log_and_status(m, "Powering off immediately as result of failure."); + case EMERGENCY_ACTION_POWEROFF_IMMEDIATE: + log_and_status(m, "Powering off immediately", reason); sync(); @@ -109,19 +110,19 @@ int failure_action( break; default: - assert_not_reached("Unknown failure action"); + assert_not_reached("Unknown emergency action"); } return -ECANCELED; } -static const char* const failure_action_table[_FAILURE_ACTION_MAX] = { - [FAILURE_ACTION_NONE] = "none", - [FAILURE_ACTION_REBOOT] = "reboot", - [FAILURE_ACTION_REBOOT_FORCE] = "reboot-force", - [FAILURE_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", - [FAILURE_ACTION_POWEROFF] = "poweroff", - [FAILURE_ACTION_POWEROFF_FORCE] = "poweroff-force", - [FAILURE_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate" +static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = { + [EMERGENCY_ACTION_NONE] = "none", + [EMERGENCY_ACTION_REBOOT] = "reboot", + [EMERGENCY_ACTION_REBOOT_FORCE] = "reboot-force", + [EMERGENCY_ACTION_REBOOT_IMMEDIATE] = "reboot-immediate", + [EMERGENCY_ACTION_POWEROFF] = "poweroff", + [EMERGENCY_ACTION_POWEROFF_FORCE] = "poweroff-force", + [EMERGENCY_ACTION_POWEROFF_IMMEDIATE] = "poweroff-immediate" }; -DEFINE_STRING_TABLE_LOOKUP(failure_action, FailureAction); +DEFINE_STRING_TABLE_LOOKUP(emergency_action, EmergencyAction); diff --git a/src/core/failure-action.h b/src/core/emergency-action.h index 1adac4ad5c..8804b59752 100644 --- a/src/core/failure-action.h +++ b/src/core/emergency-action.h @@ -20,22 +20,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef enum FailureAction { - FAILURE_ACTION_NONE, - FAILURE_ACTION_REBOOT, - FAILURE_ACTION_REBOOT_FORCE, - FAILURE_ACTION_REBOOT_IMMEDIATE, - FAILURE_ACTION_POWEROFF, - FAILURE_ACTION_POWEROFF_FORCE, - FAILURE_ACTION_POWEROFF_IMMEDIATE, - _FAILURE_ACTION_MAX, - _FAILURE_ACTION_INVALID = -1 -} FailureAction; +typedef enum EmergencyAction { + EMERGENCY_ACTION_NONE, + EMERGENCY_ACTION_REBOOT, + EMERGENCY_ACTION_REBOOT_FORCE, + EMERGENCY_ACTION_REBOOT_IMMEDIATE, + EMERGENCY_ACTION_POWEROFF, + EMERGENCY_ACTION_POWEROFF_FORCE, + EMERGENCY_ACTION_POWEROFF_IMMEDIATE, + _EMERGENCY_ACTION_MAX, + _EMERGENCY_ACTION_INVALID = -1 +} EmergencyAction; #include "macro.h" #include "manager.h" -int failure_action(Manager *m, FailureAction action, const char *reboot_arg); +int emergency_action(Manager *m, EmergencyAction action, const char *reboot_arg, const char *reason); -const char* failure_action_to_string(FailureAction i) _const_; -FailureAction failure_action_from_string(const char *s) _pure_; +const char* emergency_action_to_string(EmergencyAction i) _const_; +EmergencyAction emergency_action_from_string(const char *s) _pure_; diff --git a/src/core/execute.c b/src/core/execute.c index 23a702e93e..5bb23e2e4a 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -29,8 +29,10 @@ #include <sys/mman.h> #include <sys/personality.h> #include <sys/prctl.h> +#include <sys/shm.h> #include <sys/socket.h> #include <sys/stat.h> +#include <sys/types.h> #include <sys/un.h> #include <unistd.h> #include <utmpx.h> @@ -730,74 +732,157 @@ static int ask_for_confirmation(char *response, char **argv) { return r; } -static int enforce_groups(const ExecContext *context, const char *username, gid_t gid) { - bool keep_groups = false; +static int get_fixed_user(const ExecContext *c, const char **user, + uid_t *uid, gid_t *gid, + const char **home, const char **shell) { int r; + const char *name; - assert(context); + assert(c); + + if (!c->user) + return 0; + + /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway + * (i.e. are "/" or "/bin/nologin"). */ + + name = c->user; + r = get_user_creds_clean(&name, uid, gid, home, shell); + if (r < 0) + return r; + + *user = name; + return 0; +} + +static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid) { + int r; + const char *name; + + assert(c); + + if (!c->group) + return 0; + + name = c->group; + r = get_group_creds(&name, gid); + if (r < 0) + return r; + + *group = name; + return 0; +} - /* Lookup and set GID and supplementary group list. Here too - * we avoid NSS lookups for gid=0. */ +static int get_supplementary_groups(const ExecContext *c, const char *user, + const char *group, gid_t gid, + gid_t **supplementary_gids, int *ngids) { + char **i; + int r, k = 0; + int ngroups_max; + bool keep_groups = false; + gid_t *groups = NULL; + _cleanup_free_ gid_t *l_gids = NULL; - if (context->group || username) { + assert(c); + + /* + * If user is given, then lookup GID and supplementary groups list. + * We avoid NSS lookups for gid=0. Also we have to initialize groups + * here and as early as possible so we keep the list of supplementary + * groups of the caller. + */ + if (user && gid_is_valid(gid) && gid != 0) { /* First step, initialize groups from /etc/groups */ - if (username && gid != 0) { - if (initgroups(username, gid) < 0) - return -errno; + if (initgroups(user, gid) < 0) + return -errno; - keep_groups = true; - } + keep_groups = true; + } - /* Second step, set our gids */ - if (setresgid(gid, gid, gid) < 0) + if (!c->supplementary_groups) + return 0; + + /* + * If SupplementaryGroups= was passed then NGROUPS_MAX has to + * be positive, otherwise fail. + */ + errno = 0; + ngroups_max = (int) sysconf(_SC_NGROUPS_MAX); + if (ngroups_max <= 0) { + if (errno > 0) return -errno; + else + return -EOPNOTSUPP; /* For all other values */ } - if (context->supplementary_groups) { - int ngroups_max, k; - gid_t *gids; - char **i; + l_gids = new(gid_t, ngroups_max); + if (!l_gids) + return -ENOMEM; - /* Final step, initialize any manually set supplementary groups */ - assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0); + if (keep_groups) { + /* + * Lookup the list of groups that the user belongs to, we + * avoid NSS lookups here too for gid=0. + */ + k = ngroups_max; + if (getgrouplist(user, gid, l_gids, &k) < 0) + return -EINVAL; + } else + k = 0; - if (!(gids = new(gid_t, ngroups_max))) - return -ENOMEM; + STRV_FOREACH(i, c->supplementary_groups) { + const char *g; - if (keep_groups) { - k = getgroups(ngroups_max, gids); - if (k < 0) { - free(gids); - return -errno; - } - } else - k = 0; + if (k >= ngroups_max) + return -E2BIG; - STRV_FOREACH(i, context->supplementary_groups) { - const char *g; + g = *i; + r = get_group_creds(&g, l_gids+k); + if (r < 0) + return r; - if (k >= ngroups_max) { - free(gids); - return -E2BIG; - } + k++; + } - g = *i; - r = get_group_creds(&g, gids+k); - if (r < 0) { - free(gids); - return r; - } + /* + * Sets ngids to zero to drop all supplementary groups, happens + * when we are under root and SupplementaryGroups= is empty. + */ + if (k == 0) { + *ngids = 0; + return 0; + } - k++; - } + /* Otherwise get the final list of supplementary groups */ + groups = memdup(l_gids, sizeof(gid_t) * k); + if (!groups) + return -ENOMEM; - r = maybe_setgroups(k, gids); - if (r < 0) { - free(gids); + *supplementary_gids = groups; + *ngids = k; + + groups = NULL; + + return 0; +} + +static int enforce_groups(const ExecContext *context, gid_t gid, + gid_t *supplementary_gids, int ngids) { + int r; + + assert(context); + + /* Handle SupplementaryGroups= even if it is empty */ + if (context->supplementary_groups) { + r = maybe_setgroups(ngids, supplementary_gids); + if (r < 0) return r; - } + } - free(gids); + if (gid_is_valid(gid)) { + /* Then set our gids */ + if (setresgid(gid, gid, gid) < 0) + return -errno; } return 0; @@ -806,6 +891,9 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ static int enforce_user(const ExecContext *context, uid_t uid) { assert(context); + if (!uid_is_valid(uid)) + return 0; + /* Sets (but doesn't look up) the uid and make sure we keep the * capabilities while doing so. */ @@ -1099,18 +1187,19 @@ static void rename_process_from_path(const char *path) { #ifdef HAVE_SECCOMP static bool skip_seccomp_unavailable(const Unit* u, const char* msg) { - if (!is_seccomp_available()) { - log_open(); - log_unit_debug(u, "SECCOMP features not detected in the kernel, skipping %s", msg); - log_close(); - return true; - } - return false; + + if (is_seccomp_available()) + return false; + + log_open(); + log_unit_debug(u, "SECCOMP features not detected in the kernel, skipping %s", msg); + log_close(); + return true; } static int apply_seccomp(const Unit* u, const ExecContext *c) { uint32_t negative_action, action; - scmp_filter_ctx *seccomp; + scmp_filter_ctx seccomp; Iterator i; void *id; int r; @@ -1161,7 +1250,7 @@ finish: } static int apply_address_families(const Unit* u, const ExecContext *c) { - scmp_filter_ctx *seccomp; + scmp_filter_ctx seccomp; Iterator i; int r; @@ -1170,13 +1259,9 @@ static int apply_address_families(const Unit* u, const ExecContext *c) { if (skip_seccomp_unavailable(u, "RestrictAddressFamilies=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); + r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); if (r < 0) - goto finish; + return r; if (c->address_families_whitelist) { int af, first = 0, last = 0; @@ -1273,10 +1358,6 @@ static int apply_address_families(const Unit* u, const ExecContext *c) { } } - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - r = seccomp_load(seccomp); finish: @@ -1285,7 +1366,7 @@ finish: } static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) { - scmp_filter_ctx *seccomp; + scmp_filter_ctx seccomp; int r; assert(c); @@ -1293,13 +1374,9 @@ static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) if (skip_seccomp_unavailable(u, "MemoryDenyWriteExecute=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); + r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); if (r < 0) - goto finish; + return r; r = seccomp_rule_add( seccomp, @@ -1319,7 +1396,12 @@ static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) if (r < 0) goto finish; - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); + r = seccomp_rule_add( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(shmat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); if (r < 0) goto finish; @@ -1337,7 +1419,7 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { SCHED_IDLE, }; - scmp_filter_ctx *seccomp; + scmp_filter_ctx seccomp; unsigned i; int r, p, max_policy = 0; @@ -1346,13 +1428,9 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { if (skip_seccomp_unavailable(u, "RestrictRealtime=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); + r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); if (r < 0) - goto finish; + return r; /* Determine the highest policy constant we want to allow */ for (i = 0; i < ELEMENTSOF(permitted_policies); i++) @@ -1396,10 +1474,6 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { if (r < 0) goto finish; - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - r = seccomp_load(seccomp); finish: @@ -1407,8 +1481,8 @@ finish: return r; } -static int apply_protect_sysctl(Unit *u, const ExecContext *c) { - scmp_filter_ctx *seccomp; +static int apply_protect_sysctl(const Unit *u, const ExecContext *c) { + scmp_filter_ctx seccomp; int r; assert(c); @@ -1419,13 +1493,9 @@ static int apply_protect_sysctl(Unit *u, const ExecContext *c) { if (skip_seccomp_unavailable(u, "ProtectKernelTunables=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); + r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); if (r < 0) - goto finish; + return r; r = seccomp_rule_add( seccomp, @@ -1435,10 +1505,6 @@ static int apply_protect_sysctl(Unit *u, const ExecContext *c) { if (r < 0) goto finish; - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - r = seccomp_load(seccomp); finish: @@ -1446,57 +1512,18 @@ finish: return r; } -static int apply_protect_kernel_modules(Unit *u, const ExecContext *c) { - static const int module_syscalls[] = { - SCMP_SYS(delete_module), - SCMP_SYS(finit_module), - SCMP_SYS(init_module), - }; - - scmp_filter_ctx *seccomp; - unsigned i; - int r; - +static int apply_protect_kernel_modules(const Unit *u, const ExecContext *c) { assert(c); - /* Turn of module syscalls on ProtectKernelModules=yes */ + /* Turn off module syscalls on ProtectKernelModules=yes */ if (skip_seccomp_unavailable(u, "ProtectKernelModules=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); - if (r < 0) - goto finish; - - for (i = 0; i < ELEMENTSOF(module_syscalls); i++) { - r = seccomp_rule_add(seccomp, SCMP_ACT_ERRNO(EPERM), - module_syscalls[i], 0); - if (r < 0) - goto finish; - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; + return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM)); } -static int apply_private_devices(Unit *u, const ExecContext *c) { - const SystemCallFilterSet *set; - scmp_filter_ctx *seccomp; - const char *sys; - bool syscalls_found = false; - int r; - +static int apply_private_devices(const Unit *u, const ExecContext *c) { assert(c); /* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */ @@ -1504,61 +1531,7 @@ static int apply_private_devices(Unit *u, const ExecContext *c) { if (skip_seccomp_unavailable(u, "PrivateDevices=")) return 0; - seccomp = seccomp_init(SCMP_ACT_ALLOW); - if (!seccomp) - return -ENOMEM; - - r = seccomp_add_secondary_archs(seccomp); - if (r < 0) - goto finish; - - for (set = syscall_filter_sets; set->set_name; set++) - if (streq(set->set_name, "@raw-io")) { - syscalls_found = true; - break; - } - - /* We should never fail here */ - if (!syscalls_found) { - r = -EOPNOTSUPP; - goto finish; - } - - NULSTR_FOREACH(sys, set->value) { - int id; - bool add = true; - -#ifndef __NR_s390_pci_mmio_read - if (streq(sys, "s390_pci_mmio_read")) - add = false; -#endif -#ifndef __NR_s390_pci_mmio_write - if (streq(sys, "s390_pci_mmio_write")) - add = false; -#endif - - if (!add) - continue; - - id = seccomp_syscall_resolve_name(sys); - - r = seccomp_rule_add( - seccomp, - SCMP_ACT_ERRNO(EPERM), - id, 0); - if (r < 0) - goto finish; - } - - r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); - if (r < 0) - goto finish; - - r = seccomp_load(seccomp); - -finish: - seccomp_release(seccomp); - return r; + return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM)); } #endif @@ -1804,9 +1777,9 @@ static int setup_private_users(uid_t uid, gid_t gid) { asprintf(&uid_map, "0 0 1\n" /* Map root → root */ UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */ - uid, uid); /* The case where the above is the same */ + uid, uid); else - uid_map = strdup("0 0 1\n"); + uid_map = strdup("0 0 1\n"); /* The case where the above is the same */ if (!uid_map) return -ENOMEM; @@ -2041,6 +2014,92 @@ static int compile_read_write_paths( return 0; } +static int apply_mount_namespace(Unit *u, const ExecContext *context, + const ExecParameters *params, + ExecRuntime *runtime) { + int r; + _cleanup_free_ char **rw = NULL; + char *tmp = NULL, *var = NULL; + const char *root_dir = NULL; + NameSpaceInfo ns_info = { + .private_dev = context->private_devices, + .protect_control_groups = context->protect_control_groups, + .protect_kernel_tunables = context->protect_kernel_tunables, + .protect_kernel_modules = context->protect_kernel_modules, + }; + + assert(context); + + /* The runtime struct only contains the parent of the private /tmp, + * which is non-accessible to world users. Inside of it there's a /tmp + * that is sticky, and that's the one we want to use here. */ + + if (context->private_tmp && runtime) { + if (runtime->tmp_dir) + tmp = strjoina(runtime->tmp_dir, "/tmp"); + if (runtime->var_tmp_dir) + var = strjoina(runtime->var_tmp_dir, "/tmp"); + } + + r = compile_read_write_paths(context, params, &rw); + if (r < 0) + return r; + + if (params->flags & EXEC_APPLY_CHROOT) + root_dir = context->root_directory; + + r = setup_namespace(root_dir, &ns_info, rw, + context->read_only_paths, + context->inaccessible_paths, + tmp, + var, + context->protect_home, + context->protect_system, + context->mount_flags); + + /* If we couldn't set up the namespace this is probably due to a + * missing capability. In this case, silently proceeed. */ + if (IN_SET(r, -EPERM, -EACCES)) { + log_open(); + log_unit_debug_errno(u, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m"); + log_close(); + r = 0; + } + + return r; +} + +static int apply_working_directory(const ExecContext *context, + const ExecParameters *params, + const char *home, + const bool needs_mount_ns) { + const char *d; + const char *wd; + + assert(context); + + if (context->working_directory_home) + wd = home; + else if (context->working_directory) + wd = context->working_directory; + else + wd = "/"; + + if (params->flags & EXEC_APPLY_CHROOT) { + if (!needs_mount_ns && context->root_directory) + if (chroot(context->root_directory) < 0) + return -errno; + + d = wd; + } else + d = strjoina(strempty(context->root_directory), "/", strempty(wd)); + + if (chdir(d) < 0 && !context->working_directory_missing_ok) + return -errno; + + return 0; +} + static void append_socket_pair(int *array, unsigned *n, int pair[2]) { assert(array); assert(n); @@ -2175,13 +2234,15 @@ static int exec_child( _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; _cleanup_free_ char *mac_selinux_context_net = NULL; - const char *username = NULL, *home = NULL, *shell = NULL, *wd; + _cleanup_free_ gid_t *supplementary_gids = NULL; + const char *username = NULL, *groupname = NULL; + const char *home = NULL, *shell = NULL; dev_t journal_stream_dev = 0; ino_t journal_stream_ino = 0; bool needs_mount_namespace; uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; - int i, r; + int i, r, ngids = 0; assert(unit); assert(command); @@ -2273,29 +2334,27 @@ static int exec_child( username = dcreds->user->name; } else { - if (context->user) { - username = context->user; - r = get_user_creds_clean(&username, &uid, &gid, &home, &shell); - if (r < 0) { - *exit_status = EXIT_USER; - return r; - } - - /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway - * (i.e. are "/" or "/bin/nologin"). */ + r = get_fixed_user(context, &username, &uid, &gid, &home, &shell); + if (r < 0) { + *exit_status = EXIT_USER; + return r; } - if (context->group) { - const char *g = context->group; - - r = get_group_creds(&g, &gid); - if (r < 0) { - *exit_status = EXIT_GROUP; - return r; - } + r = get_fixed_group(context, &groupname, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; } } + /* Initialize user supplementary groups and get SupplementaryGroups= ones */ + r = get_supplementary_groups(context, username, groupname, gid, + &supplementary_gids, &ngids); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + r = send_user_lookup(unit, user_lookup_fd, uid, gid); if (r < 0) { *exit_status = EXIT_USER; @@ -2480,12 +2539,6 @@ static int exec_child( (void) umask(context->umask); if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { - r = setup_smack(context, command); - if (r < 0) { - *exit_status = EXIT_SMACK_PROCESS_LABEL; - return r; - } - if (context->pam_name && username) { r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds); if (r < 0) { @@ -2505,97 +2558,29 @@ static int exec_child( needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); if (needs_mount_namespace) { - _cleanup_free_ char **rw = NULL; - char *tmp = NULL, *var = NULL; - NameSpaceInfo ns_info = { - .private_dev = context->private_devices, - .protect_control_groups = context->protect_control_groups, - .protect_kernel_tunables = context->protect_kernel_tunables, - .protect_kernel_modules = context->protect_kernel_modules, - }; - - /* The runtime struct only contains the parent - * of the private /tmp, which is - * non-accessible to world users. Inside of it - * there's a /tmp that is sticky, and that's - * the one we want to use here. */ - - if (context->private_tmp && runtime) { - if (runtime->tmp_dir) - tmp = strjoina(runtime->tmp_dir, "/tmp"); - if (runtime->var_tmp_dir) - var = strjoina(runtime->var_tmp_dir, "/tmp"); - } - - r = compile_read_write_paths(context, params, &rw); + r = apply_mount_namespace(unit, context, params, runtime); if (r < 0) { *exit_status = EXIT_NAMESPACE; return r; } + } - r = setup_namespace( - (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL, - &ns_info, - rw, - context->read_only_paths, - context->inaccessible_paths, - tmp, - var, - context->protect_home, - context->protect_system, - context->mount_flags); - - /* If we couldn't set up the namespace this is - * probably due to a missing capability. In this case, - * silently proceeed. */ - if (r == -EPERM || r == -EACCES) { - log_open(); - log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m"); - log_close(); - } else if (r < 0) { - *exit_status = EXIT_NAMESPACE; - return r; - } + /* Apply just after mount namespace setup */ + r = apply_working_directory(context, params, home, needs_mount_namespace); + if (r < 0) { + *exit_status = EXIT_CHROOT; + return r; } + /* Drop groups as early as possbile */ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { - r = enforce_groups(context, username, gid); + r = enforce_groups(context, gid, supplementary_gids, ngids); if (r < 0) { *exit_status = EXIT_GROUP; return r; } } - if (context->working_directory_home) - wd = home; - else if (context->working_directory) - wd = context->working_directory; - else - wd = "/"; - - if (params->flags & EXEC_APPLY_CHROOT) { - if (!needs_mount_namespace && context->root_directory) - if (chroot(context->root_directory) < 0) { - *exit_status = EXIT_CHROOT; - return -errno; - } - - if (chdir(wd) < 0 && - !context->working_directory_missing_ok) { - *exit_status = EXIT_CHDIR; - return -errno; - } - } else { - const char *d; - - d = strjoina(strempty(context->root_directory), "/", strempty(wd)); - if (chdir(d) < 0 && - !context->working_directory_missing_ok) { - *exit_status = EXIT_CHDIR; - return -errno; - } - } - #ifdef HAVE_SELINUX if ((params->flags & EXEC_APPLY_PERMISSIONS) && mac_selinux_use() && @@ -2703,6 +2688,41 @@ static int exec_child( } } + /* Apply the MAC contexts late, but before seccomp syscall filtering, as those should really be last to + * influence our own codepaths as little as possible. Moreover, applying MAC contexts usually requires + * syscalls that are subject to seccomp filtering, hence should probably be applied before the syscalls + * are restricted. */ + +#ifdef HAVE_SELINUX + if (mac_selinux_use()) { + char *exec_context = mac_selinux_context_net ?: context->selinux_context; + + if (exec_context) { + r = setexeccon(exec_context); + if (r < 0) { + *exit_status = EXIT_SELINUX_CONTEXT; + return r; + } + } + } +#endif + + r = setup_smack(context, command); + if (r < 0) { + *exit_status = EXIT_SMACK_PROCESS_LABEL; + return r; + } + +#ifdef HAVE_APPARMOR + if (context->apparmor_profile && mac_apparmor_use()) { + r = aa_change_onexec(context->apparmor_profile); + if (r < 0 && !context->apparmor_profile_ignore) { + *exit_status = EXIT_APPARMOR_PROFILE; + return -errno; + } + } +#endif + /* PR_GET_SECUREBITS is not privileged, while * PR_SET_SECUREBITS is. So to suppress * potential EPERMs we'll try not to call @@ -2768,6 +2788,8 @@ static int exec_child( } } + /* This really should remain the last step before the execve(), to make sure our own code is unaffected + * by the filter as little as possible. */ if (context_has_syscall_filters(context)) { r = apply_seccomp(unit, context); if (r < 0) { @@ -2776,30 +2798,6 @@ static int exec_child( } } #endif - -#ifdef HAVE_SELINUX - if (mac_selinux_use()) { - char *exec_context = mac_selinux_context_net ?: context->selinux_context; - - if (exec_context) { - r = setexeccon(exec_context); - if (r < 0) { - *exit_status = EXIT_SELINUX_CONTEXT; - return r; - } - } - } -#endif - -#ifdef HAVE_APPARMOR - if (context->apparmor_profile && mac_apparmor_use()) { - r = aa_change_onexec(context->apparmor_profile); - if (r < 0 && !context->apparmor_profile_ignore) { - *exit_status = EXIT_APPARMOR_PROFILE; - return -errno; - } - } -#endif } final_argv = replace_env_argv(argv, accum_env); @@ -3621,7 +3619,8 @@ char *exec_command_line(char **argv) { STRV_FOREACH(a, argv) k += strlen(*a)+3; - if (!(n = new(char, k))) + n = new(char, k); + if (!n) return NULL; p = n; diff --git a/src/core/job.c b/src/core/job.c index 7faf2ef686..ac6910a906 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -690,16 +690,16 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR } static void job_print_status_message(Unit *u, JobType t, JobResult result) { - static struct { + static const struct { const char *color, *word; } const statuses[_JOB_RESULT_MAX] = { - [JOB_DONE] = {ANSI_GREEN, " OK "}, - [JOB_TIMEOUT] = {ANSI_HIGHLIGHT_RED, " TIME "}, - [JOB_FAILED] = {ANSI_HIGHLIGHT_RED, "FAILED"}, - [JOB_DEPENDENCY] = {ANSI_HIGHLIGHT_YELLOW, "DEPEND"}, - [JOB_SKIPPED] = {ANSI_HIGHLIGHT, " INFO "}, - [JOB_ASSERT] = {ANSI_HIGHLIGHT_YELLOW, "ASSERT"}, - [JOB_UNSUPPORTED] = {ANSI_HIGHLIGHT_YELLOW, "UNSUPP"}, + [JOB_DONE] = { ANSI_GREEN, " OK " }, + [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " }, + [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" }, + [JOB_DEPENDENCY] = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" }, + [JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " }, + [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" }, + [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" }, }; const char *format; @@ -767,8 +767,9 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { if (!format) return; + /* The description might be longer than the buffer, but that's OK, we'll just truncate it here */ DISABLE_WARNING_FORMAT_NONLITERAL; - xsprintf(buf, format, unit_description(u)); + snprintf(buf, sizeof(buf), format, unit_description(u)); REENABLE_WARNING; switch (t) { @@ -927,7 +928,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user u = j->unit; job_finish_and_invalidate(j, JOB_TIMEOUT, true, false); - failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); + emergency_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg, "job timed out"); return 0; } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 08c88b6b53..af2f9d960b 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -188,13 +188,13 @@ Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0 Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout) -Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) +Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) m4_dnl The following is a legacy alias name for compatibility Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) -Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) +Unit.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action) Unit.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions) Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions) @@ -251,9 +251,9 @@ Service.WatchdogSec, config_parse_sec, 0, m4_dnl The following three only exist for compatibility, they moved into Unit, see above Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) -Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) +Service.StartLimitAction, config_parse_emergency_action, 0, offsetof(Unit, start_limit_action) Service.RebootArgument, config_parse_string, 0, offsetof(Unit, reboot_arg) -Service.FailureAction, config_parse_failure_action, 0, offsetof(Service, failure_action) +Service.FailureAction, config_parse_emergency_action, 0, offsetof(Service, emergency_action) Service.Type, config_parse_service_type, 0, offsetof(Service, type) Service.Restart, config_parse_service_restart, 0, offsetof(Service, restart) Service.PermissionsStartOnly, config_parse_bool, 0, offsetof(Service, permissions_start_only) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a69f60097d..cbc826809e 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2523,7 +2523,7 @@ int config_parse_unit_condition_null( } DEFINE_CONFIG_PARSE_ENUM(config_parse_notify_access, notify_access, NotifyAccess, "Failed to parse notify access specifier"); -DEFINE_CONFIG_PARSE_ENUM(config_parse_failure_action, failure_action, FailureAction, "Failed to parse failure action specifier"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_emergency_action, emergency_action, EmergencyAction, "Failed to parse failure action specifier"); int config_parse_unit_requires_mounts_for( const char *unit, @@ -2618,6 +2618,7 @@ int config_parse_documentation(const char *unit, } #ifdef HAVE_SECCOMP + static int syscall_filter_parse_one( const char *unit, const char *filename, @@ -2628,27 +2629,29 @@ static int syscall_filter_parse_one( bool warn) { int r; - if (*t == '@') { - const SystemCallFilterSet *set; + if (t[0] == '@') { + const SyscallFilterSet *set; + const char *i; - for (set = syscall_filter_sets; set->set_name; set++) - if (streq(set->set_name, t)) { - const char *sys; + set = syscall_filter_set_find(t); + if (!set) { + if (warn) + log_syntax(unit, LOG_WARNING, filename, line, 0, "Don't know system call group, ignoring: %s", t); + return 0; + } - NULSTR_FOREACH(sys, set->value) { - r = syscall_filter_parse_one(unit, filename, line, c, invert, sys, false); - if (r < 0) - return r; - } - break; - } + NULSTR_FOREACH(i, set->value) { + r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false); + if (r < 0) + return r; + } } else { int id; id = seccomp_syscall_resolve_name(t); if (id == __NR_SCMP_ERROR) { if (warn) - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse system call, ignoring: %s", t); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t); return 0; } @@ -2662,8 +2665,9 @@ static int syscall_filter_parse_one( if (r < 0) return log_oom(); } else - set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); + (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); } + return 0; } @@ -2682,8 +2686,7 @@ int config_parse_syscall_filter( ExecContext *c = data; Unit *u = userdata; bool invert = false; - const char *word, *state; - size_t l; + const char *p; int r; assert(filename); @@ -2722,24 +2725,24 @@ int config_parse_syscall_filter( } } - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; + p = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL; - t = strndup(word, l); - if (!t) + r = extract_first_word(&p, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + break; + } - r = syscall_filter_parse_one(unit, filename, line, c, invert, t, true); + r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true); if (r < 0) return r; } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); - - /* Turn on NNP, but only if it wasn't configured explicitly - * before, and only if we are in user mode. */ - if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager)) - c->no_new_privileges = true; return 0; } @@ -3829,7 +3832,7 @@ int config_parse_no_new_privileges( return 0; } - c->no_new_privileges = !!k; + c->no_new_privileges = k; c->no_new_privileges_set = true; return 0; @@ -4315,7 +4318,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_unit_slice, "SLICE" }, { config_parse_documentation, "URL" }, { config_parse_service_timeout, "SECONDS" }, - { config_parse_failure_action, "ACTION" }, + { config_parse_emergency_action, "ACTION" }, { config_parse_set_status, "STATUS" }, { config_parse_service_sockets, "SOCKETS" }, { config_parse_environ, "ENVIRON" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 6d1fe55bcd..c05f205c37 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -75,7 +75,7 @@ int config_parse_unit_condition_string(const char *unit, const char *filename, u int config_parse_unit_condition_null(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); int config_parse_kill_mode(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); int config_parse_notify_access(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); -int config_parse_failure_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); +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); int config_parse_unit_requires_mounts_for(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); int config_parse_syscall_filter(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); int config_parse_syscall_archs(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); diff --git a/src/core/main.c b/src/core/main.c index ffc7725f16..94602611a7 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -89,6 +89,7 @@ #include "user-util.h" #include "virt.h" #include "watchdog.h" +#include "emergency-action.h" static enum { ACTION_RUN, @@ -131,7 +132,7 @@ static bool arg_default_memory_accounting = false; static bool arg_default_tasks_accounting = true; static uint64_t arg_default_tasks_max = UINT64_MAX; static sd_id128_t arg_machine_id = {}; -static CADBurstAction arg_cad_burst_action = CAD_BURST_ACTION_REBOOT; +static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; noreturn static void freeze_or_reboot(void) { @@ -649,8 +650,6 @@ static int config_parse_join_controllers(const char *unit, return 0; } -static DEFINE_CONFIG_PARSE_ENUM(config_parse_cad_burst_action, cad_burst_action, CADBurstAction, "Failed to parse service restart specifier"); - static int parse_config_file(void) { const ConfigTableItem items[] = { @@ -705,7 +704,7 @@ static int parse_config_file(void) { { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, - { "Manager", "CtrlAltDelBurstAction", config_parse_cad_burst_action, 0, &arg_cad_burst_action}, + { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action }, {} }; @@ -1778,7 +1777,7 @@ int main(int argc, char *argv[]) { (void) bump_rlimit_nofile(&saved_rlimit_nofile); if (empty_etc) { - r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, false, NULL, 0); + r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); if (r < 0) log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, "Failed to populate /etc with preset unit settings, ignoring: %m"); else diff --git a/src/core/manager.c b/src/core/manager.c index fa02389167..52174eac07 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1911,28 +1911,11 @@ static void manager_handle_ctrl_alt_del(Manager *m) { * 7 times within 2s, we reboot/shutdown immediately, * unless it was disabled in system.conf */ - if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == CAD_BURST_ACTION_IGNORE) + if (ratelimit_test(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE) manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY); - else { - switch (m->cad_burst_action) { - - case CAD_BURST_ACTION_REBOOT: - m->exit_code = MANAGER_REBOOT; - break; - - case CAD_BURST_ACTION_POWEROFF: - m->exit_code = MANAGER_POWEROFF; - break; - - default: - assert_not_reached("Unknown action."); - } - - log_notice("Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.", - cad_burst_action_to_string(m->cad_burst_action)); - status_printf(NULL, true, false, "Ctrl-Alt-Del was pressed more than 7 times within 2s, performing immediate %s.", - cad_burst_action_to_string(m->cad_burst_action)); - } + else + emergency_action(m, m->cad_burst_action, NULL, + "Ctrl-Alt-Del was pressed more than 7 times within 2s"); } static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { @@ -3591,11 +3574,3 @@ static const char *const manager_state_table[_MANAGER_STATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState); - -static const char *const cad_burst_action_table[_CAD_BURST_ACTION_MAX] = { - [CAD_BURST_ACTION_IGNORE] = "ignore", - [CAD_BURST_ACTION_REBOOT] = "reboot-force", - [CAD_BURST_ACTION_POWEROFF] = "poweroff-force", -}; - -DEFINE_STRING_TABLE_LOOKUP(cad_burst_action, CADBurstAction); diff --git a/src/core/manager.h b/src/core/manager.h index 29fe14e10b..35172fdba9 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -62,14 +62,6 @@ typedef enum ManagerExitCode { _MANAGER_EXIT_CODE_INVALID = -1 } ManagerExitCode; -typedef enum CADBurstAction { - CAD_BURST_ACTION_IGNORE, - CAD_BURST_ACTION_REBOOT, - CAD_BURST_ACTION_POWEROFF, - _CAD_BURST_ACTION_MAX, - _CAD_BURST_ACTION_INVALID = -1 -} CADBurstAction; - typedef enum StatusType { STATUS_TYPE_EPHEMERAL, STATUS_TYPE_NORMAL, @@ -315,7 +307,7 @@ struct Manager { /* When the user hits C-A-D more than 7 times per 2s, do something immediately... */ RateLimit ctrl_alt_del_ratelimit; - CADBurstAction cad_burst_action; + EmergencyAction cad_burst_action; const char *unit_log_field; const char *unit_log_format_string; @@ -411,6 +403,3 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value); const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; - -const char *cad_burst_action_to_string(CADBurstAction a) _const_; -CADBurstAction cad_burst_action_from_string(const char *s) _pure_; diff --git a/src/core/mount.c b/src/core/mount.c index da480001e1..d749e49df5 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -159,17 +159,6 @@ static void mount_init(Unit *u) { m->timeout_usec = u->manager->default_timeout_start_usec; m->directory_mode = 0755; - if (unit_has_name(u, "-.mount")) { - /* Don't allow start/stop for root directory */ - u->refuse_manual_start = true; - u->refuse_manual_stop = true; - } else { - /* The stdio/kmsg bridge socket is on /, in order to avoid a - * dep loop, don't use kmsg logging for -.mount */ - m->exec_context.std_output = u->manager->default_std_output; - m->exec_context.std_error = u->manager->default_std_error; - } - /* We need to make sure that /usr/bin/mount is always called * in the same process group as us, so that the autofs kernel * side doesn't send us another mount request while we are @@ -577,6 +566,25 @@ static int mount_add_extras(Mount *m) { return 0; } +static int mount_load_root_mount(Unit *u) { + assert(u); + + if (!unit_has_name(u, SPECIAL_ROOT_MOUNT)) + return 0; + + u->perpetual = true; + u->default_dependencies = false; + + /* The stdio/kmsg bridge socket is on /, in order to avoid a dep loop, don't use kmsg logging for -.mount */ + MOUNT(u)->exec_context.std_output = EXEC_OUTPUT_NULL; + MOUNT(u)->exec_context.std_input = EXEC_INPUT_NULL; + + if (!u->description) + u->description = strdup("Root Mount"); + + return 1; +} + static int mount_load(Unit *u) { Mount *m = MOUNT(u); int r; @@ -584,11 +592,14 @@ static int mount_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - if (m->from_proc_self_mountinfo) + r = mount_load_root_mount(u); + if (r < 0) + return r; + + if (m->from_proc_self_mountinfo || u->perpetual) r = unit_load_fragment_and_dropin_optional(u); else r = unit_load_fragment_and_dropin(u); - if (r < 0) return r; @@ -1393,11 +1404,7 @@ static int mount_setup_unit( if (!u) { delete = true; - u = unit_new(m, sizeof(Mount)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Mount), e, &u); if (r < 0) goto fail; @@ -1592,11 +1599,46 @@ static int mount_get_timeout(Unit *u, usec_t *timeout) { return 1; } +static int synthesize_root_mount(Manager *m) { + Unit *u; + int r; + + assert(m); + + /* Whatever happens, we know for sure that the root directory is around, and cannot go away. Let's + * unconditionally synthesize it here and mark it as perpetual. */ + + u = manager_get_unit(m, SPECIAL_ROOT_MOUNT); + if (!u) { + r = unit_new_for_name(m, sizeof(Mount), SPECIAL_ROOT_MOUNT, &u); + if (r < 0) + return log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_MOUNT " unit: %m"); + } + + u->perpetual = true; + MOUNT(u)->deserialized_state = MOUNT_MOUNTED; + + unit_add_to_load_queue(u); + unit_add_to_dbus_queue(u); + + return 0; +} + +static bool mount_is_mounted(Mount *m) { + assert(m); + + return UNIT(m)->perpetual || m->is_mounted; +} + static void mount_enumerate(Manager *m) { int r; assert(m); + r = synthesize_root_mount(m); + if (r < 0) + goto fail; + mnt_init_debug(0); if (!m->mount_monitor) { @@ -1703,7 +1745,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { Mount *mount = MOUNT(u); - if (!mount->is_mounted) { + if (!mount_is_mounted(mount)) { /* A mount point is not around right now. It * might be gone, or might never have @@ -1764,7 +1806,7 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, } } - if (mount->is_mounted && + if (mount_is_mounted(mount) && mount->from_proc_self_mountinfo && mount->parameters_proc_self_mountinfo.what) { diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6caa15b0b8..a61677e645 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -94,6 +94,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="GetUnitFileLinks"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListJobs"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/scope.c b/src/core/scope.c index e7583f6d89..d6e1f8e392 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -147,6 +147,32 @@ static int scope_verify(Scope *s) { return 0; } +static int scope_load_init_scope(Unit *u) { + assert(u); + + if (!unit_has_name(u, SPECIAL_INIT_SCOPE)) + return 0; + + u->transient = true; + u->perpetual = true; + + /* init.scope is a bit special, as it has to stick around forever. Because of its special semantics we + * synthesize it here, instead of relying on the unit file on disk. */ + + u->default_dependencies = false; + u->ignore_on_isolate = true; + + SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; + + /* Prettify things, if we can. */ + if (!u->description) + u->description = strdup("System and Service Manager"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd(1)"); + + return 1; +} + static int scope_load(Unit *u) { Scope *s = SCOPE(u); int r; @@ -158,6 +184,9 @@ static int scope_load(Unit *u) { /* Refuse to load non-transient scope units, but allow them while reloading. */ return -ENOENT; + r = scope_load_init_scope(u); + if (r < 0) + return r; r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; @@ -534,34 +563,16 @@ static void scope_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_INIT_SCOPE); if (!u) { - u = unit_new(m, sizeof(Scope)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_INIT_SCOPE); + r = unit_new_for_name(m, sizeof(Scope), SPECIAL_INIT_SCOPE, &u); if (r < 0) { - unit_free(u); - log_error_errno(r, "Failed to add init.scope name"); + log_error_errno(r, "Failed to allocate the special " SPECIAL_INIT_SCOPE " unit: %m"); return; } } u->transient = true; - u->default_dependencies = false; - u->no_gc = true; - u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; + u->perpetual = true; SCOPE(u)->deserialized_state = SCOPE_RUNNING; - SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; - - /* Prettify things, if we can. */ - if (!u->description) - u->description = strdup("System and Service Manager"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd(1)"); unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); diff --git a/src/core/service.c b/src/core/service.c index 5a9b01dadb..f6acc2f129 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -289,7 +289,17 @@ static void service_fd_store_unlink(ServiceFDStore *fs) { free(fs); } -static void service_release_resources(Unit *u) { +static void service_release_fd_store(Service *s) { + assert(s); + + log_unit_debug(UNIT(s), "Releasing all stored fds"); + while (s->fd_store) + service_fd_store_unlink(s->fd_store); + + assert(s->n_fd_store == 0); +} + +static void service_release_resources(Unit *u, bool inactive) { Service *s = SERVICE(u); assert(s); @@ -297,16 +307,14 @@ static void service_release_resources(Unit *u) { if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0) return; - log_unit_debug(u, "Releasing all resources."); + log_unit_debug(u, "Releasing resources."); s->stdin_fd = safe_close(s->stdin_fd); s->stdout_fd = safe_close(s->stdout_fd); s->stderr_fd = safe_close(s->stderr_fd); - while (s->fd_store) - service_fd_store_unlink(s->fd_store); - - assert(s->n_fd_store == 0); + if (inactive) + service_release_fd_store(s); } static void service_done(Unit *u) { @@ -350,7 +358,7 @@ static void service_done(Unit *u) { s->timer_event_source = sd_event_source_unref(s->timer_event_source); - service_release_resources(u); + service_release_resources(u, true); } static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *userdata) { @@ -360,6 +368,10 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us assert(fs); /* If we get either EPOLLHUP or EPOLLERR, it's time to remove this entry from the fd store */ + log_unit_debug(UNIT(fs->service), + "Received %s on stored fd %d (%s), closing.", + revents & EPOLLERR ? "EPOLLERR" : "EPOLLHUP", + fs->fd, strna(fs->fdname)); service_fd_store_unlink(fs); return 0; } @@ -368,20 +380,23 @@ static int service_add_fd_store(Service *s, int fd, const char *name) { ServiceFDStore *fs; int r; + /* fd is always consumed if we return >= 0 */ + assert(s); assert(fd >= 0); if (s->n_fd_store >= s->n_fd_store_max) - return 0; + return -EXFULL; /* Our store is full. + * Use this errno rather than E[NM]FILE to distinguish from + * the case where systemd itself hits the file limit. */ LIST_FOREACH(fd_store, fs, s->fd_store) { r = same_fd(fs->fd, fd); if (r < 0) return r; if (r > 0) { - /* Already included */ safe_close(fd); - return 1; + return 0; /* fd already included */ } } @@ -409,7 +424,7 @@ static int service_add_fd_store(Service *s, int fd, const char *name) { LIST_PREPEND(fd_store, s->fd_store, fs); s->n_fd_store++; - return 1; + return 1; /* fd newly stored */ } static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { @@ -417,10 +432,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { assert(s); - if (fdset_size(fds) <= 0) - return 0; - - while (s->n_fd_store < s->n_fd_store_max) { + while (fdset_size(fds) > 0) { _cleanup_close_ int fd = -1; fd = fdset_steal_first(fds); @@ -428,17 +440,17 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { break; r = service_add_fd_store(s, fd, name); + if (r == -EXFULL) + return log_unit_warning_errno(UNIT(s), r, + "Cannot store more fds than FileDescriptorStoreMax=%u, closing remaining.", + s->n_fd_store_max); if (r < 0) - return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m"); - if (r > 0) { - log_unit_debug(UNIT(s), "Added fd to fd store."); - fd = -1; - } + return log_unit_error_errno(UNIT(s), r, "Failed to add fd to store: %m"); + if (r > 0) + log_unit_debug(UNIT(s), "Added fd %u (%s) to fd store.", fd, strna(name)); + fd = -1; } - if (fdset_size(fds) > 0) - log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max); - return 0; } @@ -1225,6 +1237,7 @@ static int service_spawn( return r; n_fds = r; + log_unit_debug(UNIT(s), "Passing %i fds to service", n_fds); } r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); @@ -1455,7 +1468,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (s->result != SERVICE_SUCCESS) { log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result)); - failure_action(UNIT(s)->manager, s->failure_action, UNIT(s)->reboot_arg); + emergency_action(UNIT(s)->manager, s->emergency_action, UNIT(s)->reboot_arg, "service failed"); } if (allow_restart && service_shall_restart(s)) { @@ -2336,7 +2349,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, r = service_add_fd_store(s, fd, t); if (r < 0) log_unit_error_errno(u, r, "Failed to add fd to store: %m"); - else if (r > 0) + else fdset_remove(fds, fd); } diff --git a/src/core/service.h b/src/core/service.h index 888007cc0b..2869144fcb 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -178,7 +178,7 @@ struct Service { char *status_text; int status_errno; - FailureAction failure_action; + EmergencyAction emergency_action; UnitRef accept_socket; diff --git a/src/core/slice.c b/src/core/slice.c index 03fe797f27..ed5d3fd701 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -130,6 +130,28 @@ static int slice_verify(Slice *s) { return 0; } +static int slice_load_root_slice(Unit *u) { + assert(u); + + if (!unit_has_name(u, SPECIAL_ROOT_SLICE)) + return 0; + + u->perpetual = true; + + /* The root slice is a bit special. For example it is always running and cannot be terminated. Because of its + * special semantics we synthesize it here, instead of relying on the unit file on disk. */ + + u->default_dependencies = false; + u->ignore_on_isolate = true; + + if (!u->description) + u->description = strdup("Root Slice"); + if (!u->documentation) + u->documentation = strv_new("man:systemd.special(7)", NULL); + + return 1; +} + static int slice_load(Unit *u) { Slice *s = SLICE(u); int r; @@ -137,6 +159,9 @@ static int slice_load(Unit *u) { assert(s); assert(u->load_state == UNIT_STUB); + r = slice_load_root_slice(u); + if (r < 0) + return r; r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; @@ -274,32 +299,16 @@ static void slice_enumerate(Manager *m) { u = manager_get_unit(m, SPECIAL_ROOT_SLICE); if (!u) { - u = unit_new(m, sizeof(Slice)); - if (!u) { - log_oom(); - return; - } - - r = unit_add_name(u, SPECIAL_ROOT_SLICE); + r = unit_new_for_name(m, sizeof(Slice), SPECIAL_ROOT_SLICE, &u); if (r < 0) { - unit_free(u); - log_error_errno(r, "Failed to add -.slice name"); + log_error_errno(r, "Failed to allocate the special " SPECIAL_ROOT_SLICE " unit: %m"); return; } } - u->default_dependencies = false; - u->no_gc = true; - u->ignore_on_isolate = true; - u->refuse_manual_start = true; - u->refuse_manual_stop = true; + u->perpetual = true; SLICE(u)->deserialized_state = SLICE_ACTIVE; - if (!u->description) - u->description = strdup("Root Slice"); - if (!u->documentation) - (void) strv_extend(&u->documentation, "man:systemd.special(7)"); - unit_add_to_load_queue(u); unit_add_to_dbus_queue(u); } diff --git a/src/core/swap.c b/src/core/swap.c index b592abb9fb..2228a254bb 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -381,11 +381,7 @@ static int swap_setup_unit( if (!u) { delete = true; - u = unit_new(m, sizeof(Swap)); - if (!u) - return log_oom(); - - r = unit_add_name(u, e); + r = unit_new_for_name(m, sizeof(Swap), e, &u); if (r < 0) goto fail; diff --git a/src/core/unit.c b/src/core/unit.c index 14e1d7d20d..fa1f3d9d4b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -109,6 +109,24 @@ Unit *unit_new(Manager *m, size_t size) { return u; } +int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret) { + Unit *u; + int r; + + u = unit_new(m, size); + if (!u) + return -ENOMEM; + + r = unit_add_name(u, name); + if (r < 0) { + unit_free(u); + return r; + } + + *ret = u; + return r; +} + bool unit_has_name(Unit *u, const char *name) { assert(u); assert(name); @@ -302,6 +320,7 @@ int unit_set_description(Unit *u, const char *description) { bool unit_check_gc(Unit *u) { UnitActiveState state; + bool inactive; assert(u); if (u->job) @@ -311,19 +330,20 @@ bool unit_check_gc(Unit *u) { return true; state = unit_active_state(u); + inactive = state == UNIT_INACTIVE; /* If the unit is inactive and failed and no job is queued for * it, then release its runtime resources */ if (UNIT_IS_INACTIVE_OR_FAILED(state) && UNIT_VTABLE(u)->release_resources) - UNIT_VTABLE(u)->release_resources(u); + UNIT_VTABLE(u)->release_resources(u, inactive); /* But we keep the unit object around for longer when it is * referenced or configured to not be gc'ed */ - if (state != UNIT_INACTIVE) + if (!inactive) return true; - if (u->no_gc) + if (u->perpetual) return true; if (u->refs) @@ -924,6 +944,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tGC Check Good: %s\n" "%s\tNeed Daemon Reload: %s\n" "%s\tTransient: %s\n" + "%s\tPerpetual: %s\n" "%s\tSlice: %s\n" "%s\tCGroup: %s\n" "%s\tCGroup realized: %s\n" @@ -942,6 +963,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(unit_check_gc(u)), prefix, yes_no(unit_need_daemon_reload(u)), prefix, yes_no(u->transient), + prefix, yes_no(u->perpetual), prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), prefix, yes_no(u->cgroup_realized), @@ -982,8 +1004,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->job_timeout != USEC_INFINITY) fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0)); - if (u->job_timeout_action != FAILURE_ACTION_NONE) - fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, failure_action_to_string(u->job_timeout_action)); + if (u->job_timeout_action != EMERGENCY_ACTION_NONE) + fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, emergency_action_to_string(u->job_timeout_action)); if (u->job_timeout_reboot_arg) fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg); @@ -1450,7 +1472,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { format = unit_get_status_message_format(u, t); DISABLE_WARNING_FORMAT_NONLITERAL; - xsprintf(buf, format, unit_description(u)); + snprintf(buf, sizeof buf, format, unit_description(u)); REENABLE_WARNING; mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : @@ -1490,7 +1512,7 @@ int unit_start_limit_test(Unit *u) { log_unit_warning(u, "Start request repeated too quickly."); u->start_limit_hit = true; - return failure_action(u->manager, u->start_limit_action, u->reboot_arg); + return emergency_action(u->manager, u->start_limit_action, u->reboot_arg, "unit failed"); } /* Errors: @@ -1616,6 +1638,18 @@ int unit_stop(Unit *u) { return UNIT_VTABLE(u)->stop(u); } +bool unit_can_stop(Unit *u) { + assert(u); + + if (!unit_supported(u)) + return false; + + if (u->perpetual) + return false; + + return !!UNIT_VTABLE(u)->stop; +} + /* Errors: * -EBADR: This unit type does not support reloading. * -ENOEXEC: Unit is not started. @@ -2150,13 +2184,20 @@ bool unit_job_is_applicable(Unit *u, JobType j) { case JOB_VERIFY_ACTIVE: case JOB_START: - case JOB_STOP: case JOB_NOP: + /* Note that we don't check unit_can_start() here. That's because .device units and suchlike are not + * startable by us but may appear due to external events, and it thus makes sense to permit enqueing + * jobs for it. */ return true; + case JOB_STOP: + /* Similar as above. However, perpetual units can never be stopped (neither explicitly nor due to + * external events), hence it makes no sense to permit enqueing such a request either. */ + return !u->perpetual; + case JOB_RESTART: case JOB_TRY_RESTART: - return unit_can_start(u); + return unit_can_stop(u) && unit_can_start(u); case JOB_RELOAD: case JOB_TRY_RELOAD: diff --git a/src/core/unit.h b/src/core/unit.h index a8dd3e602c..991543664b 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -29,7 +29,7 @@ typedef struct UnitRef UnitRef; typedef struct UnitStatusMessageFormats UnitStatusMessageFormats; #include "condition.h" -#include "failure-action.h" +#include "emergency-action.h" #include "install.h" #include "list.h" #include "unit-name.h" @@ -114,7 +114,7 @@ struct Unit { /* Job timeout and action to take */ usec_t job_timeout; - FailureAction job_timeout_action; + EmergencyAction job_timeout_action; char *job_timeout_reboot_arg; /* References to this */ @@ -178,7 +178,7 @@ struct Unit { /* Put a ratelimit on unit starting */ RateLimit start_limit; - FailureAction start_limit_action; + EmergencyAction start_limit_action; char *reboot_arg; /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */ @@ -236,6 +236,9 @@ struct Unit { /* Is this a transient unit? */ bool transient; + /* Is this a unit that is always running and cannot be stopped? */ + bool perpetual; + bool in_load_queue:1; bool in_dbus_queue:1; bool in_cleanup_queue:1; @@ -244,8 +247,6 @@ struct Unit { bool sent_dbus_new_signal:1; - bool no_gc:1; - bool in_audit:1; bool cgroup_realized:1; @@ -372,7 +373,7 @@ struct UnitVTable { /* When the unit is not running and no job for it queued we * shall release its runtime resources */ - void (*release_resources)(Unit *u); + void (*release_resources)(Unit *u, bool inactive); /* Invoked on every child that died */ void (*sigchld_event)(Unit *u, pid_t pid, int code, int status); @@ -480,6 +481,7 @@ DEFINE_CAST(SCOPE, Scope); Unit *unit_new(Manager *m, size_t size); void unit_free(Unit *u); +int unit_new_for_name(Manager *m, size_t size, const char *name, Unit **ret); int unit_add_name(Unit *u, const char *name); int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_reference); @@ -524,6 +526,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix); bool unit_can_reload(Unit *u) _pure_; bool unit_can_start(Unit *u) _pure_; +bool unit_can_stop(Unit *u) _pure_; bool unit_can_isolate(Unit *u) _pure_; int unit_start(Unit *u); |