diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/cgroup.c | 117 | ||||
-rw-r--r-- | src/core/cgroup.h | 24 | ||||
-rw-r--r-- | src/core/dbus-manager.c | 32 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 1 | ||||
-rw-r--r-- | src/core/execute.c | 20 | ||||
-rw-r--r-- | src/core/kill.c | 5 | ||||
-rw-r--r-- | src/core/kill.h | 3 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 3 | ||||
-rw-r--r-- | src/core/load-fragment.c | 56 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/main.c | 21 | ||||
-rw-r--r-- | src/core/manager.c | 8 | ||||
-rw-r--r-- | src/core/manager.h | 14 | ||||
-rw-r--r-- | src/core/mount-setup.c | 4 | ||||
-rw-r--r-- | src/core/mount.c | 106 | ||||
-rw-r--r-- | src/core/shutdown.c | 22 | ||||
-rw-r--r-- | src/core/unit.c | 53 | ||||
-rw-r--r-- | src/core/unit.h | 5 |
18 files changed, 386 insertions, 109 deletions
diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 7d2a2ba67a..0c790c33da 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -47,6 +47,8 @@ void cgroup_context_init(CGroupContext *c) { c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; c->tasks_max = (uint64_t) -1; + + c->netclass_type = CGROUP_NETCLASS_TYPE_NONE; } void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { @@ -291,7 +293,7 @@ fail: return -errno; } -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass, ManagerState state) { bool is_root; int r; @@ -489,6 +491,17 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, "Failed to set pids.max on %s: %m", path); } + + if (mask & CGROUP_MASK_NET_CLS) { + char buf[DECIMAL_STR_MAX(uint32_t)]; + + sprintf(buf, "%" PRIu32, netclass); + + r = cg_set_attribute("net_cls", path, "net_cls.classid", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set net_cls.classid on %s: %m", path); + } } CGroupMask cgroup_context_get_mask(CGroupContext *c) { @@ -521,6 +534,9 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { c->tasks_max != (uint64_t) -1) mask |= CGROUP_MASK_PIDS; + if (c->netclass_type != CGROUP_NETCLASS_TYPE_NONE) + mask |= CGROUP_MASK_NET_CLS; + return mask; } @@ -888,6 +904,103 @@ static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { return u->cgroup_realized && u->cgroup_realized_mask == target_mask; } +static int unit_find_free_netclass_cgroup(Unit *u, uint32_t *ret) { + + uint32_t start, i; + Manager *m; + + assert(u); + + m = u->manager; + + i = start = m->cgroup_netclass_registry_last; + + do { + i++; + + if (!hashmap_get(m->cgroup_netclass_registry, UINT_TO_PTR(i))) { + m->cgroup_netclass_registry_last = i; + *ret = i; + return 0; + } + + if (i == UINT32_MAX) + i = CGROUP_NETCLASS_FIXED_MAX; + + } while (i != start); + + return -ENOBUFS; +} + +int unit_add_to_netclass_cgroup(Unit *u) { + + CGroupContext *cc; + Unit *first; + void *key; + int r; + + assert(u); + + cc = unit_get_cgroup_context(u); + if (!cc) + return 0; + + switch (cc->netclass_type) { + case CGROUP_NETCLASS_TYPE_NONE: + return 0; + + case CGROUP_NETCLASS_TYPE_FIXED: + u->cgroup_netclass_id = cc->netclass_id; + break; + + case CGROUP_NETCLASS_TYPE_AUTO: + /* Allocate a new ID in case it was requested and not done yet */ + if (u->cgroup_netclass_id == 0) { + r = unit_find_free_netclass_cgroup(u, &u->cgroup_netclass_id); + if (r < 0) + return r; + + log_debug("Dynamically assigned netclass cgroup id %" PRIu32 " to %s", u->cgroup_netclass_id, u->id); + } + + break; + } + + r = hashmap_ensure_allocated(&u->manager->cgroup_netclass_registry, &trivial_hash_ops); + if (r < 0) + return r; + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + first = hashmap_get(u->manager->cgroup_netclass_registry, key); + + if (first) { + LIST_PREPEND(cgroup_netclass, first, u); + return hashmap_replace(u->manager->cgroup_netclass_registry, key, u); + } + + return hashmap_put(u->manager->cgroup_netclass_registry, key, u); +} + +int unit_remove_from_netclass_cgroup(Unit *u) { + + Unit *head; + void *key; + + assert(u); + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + + LIST_FIND_HEAD(cgroup_netclass, u, head); + LIST_REMOVE(cgroup_netclass, head, u); + + if (head) + return hashmap_replace(u->manager->cgroup_netclass_registry, key, head); + + hashmap_remove(u->manager->cgroup_netclass_registry, key); + + return 0; +} + /* Check if necessary controllers and attributes for a unit are in place. * * If so, do nothing. @@ -923,7 +1036,7 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { return r; /* Finally, apply the necessary attributes. */ - cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state); + cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, u->cgroup_netclass_id, state); return 0; } diff --git a/src/core/cgroup.h b/src/core/cgroup.h index e897b31451..457544b49f 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -26,6 +26,11 @@ #include "list.h" #include "time-util.h" +/* Maximum value for fixed (manual) net class ID assignment, + * and also the value at which the range of automatic assignments starts + */ +#define CGROUP_NETCLASS_FIXED_MAX UINT32_C(65535) + typedef struct CGroupContext CGroupContext; typedef struct CGroupDeviceAllow CGroupDeviceAllow; typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; @@ -47,6 +52,17 @@ typedef enum CGroupDevicePolicy { _CGROUP_DEVICE_POLICY_INVALID = -1 } CGroupDevicePolicy; +typedef enum CGroupNetClassType { + /* Default - do not assign a net class */ + CGROUP_NETCLASS_TYPE_NONE, + + /* Automatically assign a net class */ + CGROUP_NETCLASS_TYPE_AUTO, + + /* Assign the net class that was provided by the user */ + CGROUP_NETCLASS_TYPE_FIXED, +} CGroupNetClassType; + struct CGroupDeviceAllow { LIST_FIELDS(CGroupDeviceAllow, device_allow); char *path; @@ -88,6 +104,9 @@ struct CGroupContext { CGroupDevicePolicy device_policy; LIST_HEAD(CGroupDeviceAllow, device_allow); + CGroupNetClassType netclass_type; + uint32_t netclass_id; + uint64_t tasks_max; bool delegate; @@ -99,7 +118,7 @@ struct CGroupContext { void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state); +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass_id, ManagerState state); CGroupMask cgroup_context_get_mask(CGroupContext *c); @@ -127,6 +146,9 @@ int unit_watch_cgroup(Unit *u); int unit_attach_pids_to_cgroup(Unit *u); +int unit_add_to_netclass_cgroup(Unit *u); +int unit_remove_from_netclass_cgroup(Unit *u); + int manager_setup_cgroup(Manager *m); void manager_shutdown_cgroup(Manager *m, bool delete); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 4e5d67fc19..561b6f8bfa 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -1201,8 +1201,10 @@ static int method_exit(sd_bus_message *message, void *userdata, sd_bus_error *er if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Exit is only supported for user service managers."); + /* Exit() (in contrast to SetExitCode()) is actually allowed even if + * we are running on the host. It will fall back on reboot() in + * systemd-shutdown if it cannot do the exit() because it isn't a + * container. */ m->exit_code = MANAGER_EXIT; @@ -1450,6 +1452,30 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd return sd_bus_reply_method_return(message, NULL); } +static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_error *error) { + uint8_t code; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = mac_selinux_access_check(message, "exit", error); + if (r < 0) + return r; + + r = sd_bus_message_read_basic(message, 'y', &code); + if (r < 0) + return r; + + if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); + + m->return_value = code; + + return sd_bus_reply_method_return(message, NULL); +} + static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; Manager *m = userdata; @@ -1933,6 +1959,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), + SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0), SD_BUS_METHOD("GetUnit", "s", "o", method_get_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitByPID", "u", "o", method_get_unit_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1986,6 +2013,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("AddDependencyUnitFiles", "asssbb", "a(sss)", method_add_dependency_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetExitCode", "y", NULL, method_set_exit_code, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("UnitNew", "so", 0), SD_BUS_SIGNAL("UnitRemoved", "so", 0), diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index f7e9795928..cd88a87340 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -679,6 +679,7 @@ 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("NetClass", "u", bus_property_get_unsigned, offsetof(Unit, cgroup_netclass_id), 0), SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/execute.c b/src/core/execute.c index 3c308e3e3e..6e14848cd4 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -629,15 +629,6 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ * we avoid NSS lookups for gid=0. */ if (context->group || username) { - - if (context->group) { - const char *g = context->group; - - r = get_group_creds(&g, &gid); - if (r < 0) - return r; - } - /* First step, initialize groups from /etc/groups */ if (username && gid != 0) { if (initgroups(username, gid) < 0) @@ -1414,6 +1405,17 @@ static int exec_child( } } + if (context->group) { + const char *g = context->group; + + r = get_group_creds(&g, &gid); + if (r < 0) { + *exit_status = EXIT_GROUP; + return r; + } + } + + /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ if (socket_fd >= 0) diff --git a/src/core/kill.c b/src/core/kill.c index 2de71c6bf9..bddfa4460f 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -60,7 +60,10 @@ DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); static const char* const kill_who_table[_KILL_WHO_MAX] = { [KILL_MAIN] = "main", [KILL_CONTROL] = "control", - [KILL_ALL] = "all" + [KILL_ALL] = "all", + [KILL_MAIN_FAIL] = "main-fail", + [KILL_CONTROL_FAIL] = "control-fail", + [KILL_ALL_FAIL] = "all-fail" }; DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho); diff --git a/src/core/kill.h b/src/core/kill.h index d5f125fa41..5d97abb104 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -50,6 +50,9 @@ typedef enum KillWho { KILL_MAIN, KILL_CONTROL, KILL_ALL, + KILL_MAIN_FAIL, + KILL_CONTROL_FAIL, + KILL_ALL_FAIL, _KILL_WHO_MAX, _KILL_WHO_INVALID = -1 } KillWho; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 69e8d7a4d4..fd293d8287 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -126,7 +126,8 @@ $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting) $1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context) -$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)' +$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) +$1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)' )m4_dnl Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b8cdde3bff..f42bee4fa9 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2964,6 +2964,48 @@ int config_parse_blockio_bandwidth( return 0; } +int config_parse_netclass( + 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) { + + CGroupContext *c = data; + unsigned v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "auto")) { + c->netclass_type = CGROUP_NETCLASS_TYPE_AUTO; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Netclass '%s' invalid. Ignoring.", rvalue); + return 0; + } + + if (v > CGROUP_NETCLASS_FIXED_MAX) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX); + + c->netclass_id = v; + c->netclass_type = CGROUP_NETCLASS_TYPE_FIXED; + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); int config_parse_job_mode_isolate( @@ -3009,6 +3051,7 @@ int config_parse_runtime_directory( void *userdata) { char***rt = data; + Unit *u = userdata; const char *word, *state; size_t l; int r; @@ -3025,12 +3068,19 @@ int config_parse_runtime_directory( } FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *n; + _cleanup_free_ char *t = NULL, *n = NULL; - n = strndup(word, l); - if (!n) + t = strndup(word, l); + if (!t) return log_oom(); + r = unit_name_printf(u, t, &n); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + continue; + } + if (!filename_is_valid(n)) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Runtime directory is not valid, ignoring assignment: %s", rvalue); diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 638b343a6e..5d0a09249f 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -90,6 +90,7 @@ int config_parse_device_allow(const char *unit, const char *filename, unsigned l int config_parse_blockio_weight(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_blockio_device_weight(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_blockio_bandwidth(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_netclass(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_job_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_job_mode_isolate(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_exec_selinux_context(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 200fe740da..9b59648279 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1254,6 +1254,7 @@ int main(int argc, char *argv[]) { char *switch_root_dir = NULL, *switch_root_init = NULL; struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0); const char *error_message = NULL; + uint8_t shutdown_exit_code = 0; #ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { @@ -1764,11 +1765,6 @@ int main(int argc, char *argv[]) { switch (m->exit_code) { - case MANAGER_EXIT: - retval = EXIT_SUCCESS; - log_debug("Exit."); - goto finish; - case MANAGER_RELOAD: log_info("Reloading."); @@ -1810,11 +1806,13 @@ int main(int argc, char *argv[]) { log_notice("Switching root."); goto finish; + case MANAGER_EXIT: case MANAGER_REBOOT: case MANAGER_POWEROFF: case MANAGER_HALT: case MANAGER_KEXEC: { static const char * const table[_MANAGER_EXIT_CODE_MAX] = { + [MANAGER_EXIT] = "exit", [MANAGER_REBOOT] = "reboot", [MANAGER_POWEROFF] = "poweroff", [MANAGER_HALT] = "halt", @@ -1836,8 +1834,10 @@ int main(int argc, char *argv[]) { finish: pager_close(); - if (m) + if (m) { arg_shutdown_watchdog = m->shutdown_watchdog; + shutdown_exit_code = m->return_value; + } m = manager_free(m); for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) @@ -1978,7 +1978,8 @@ finish: if (shutdown_verb) { char log_level[DECIMAL_STR_MAX(int) + 1]; - const char* command_line[9] = { + char exit_code[DECIMAL_STR_MAX(uint8_t) + 1]; + const char* command_line[11] = { SYSTEMD_SHUTDOWN_BINARY_PATH, shutdown_verb, "--log-level", log_level, @@ -2015,6 +2016,12 @@ finish: if (log_get_show_location()) command_line[pos++] = "--log-location"; + if (streq(shutdown_verb, "exit")) { + command_line[pos++] = "--exit-code"; + command_line[pos++] = exit_code; + xsprintf(exit_code, "%d", shutdown_exit_code); + } + assert(pos < ELEMENTSOF(command_line)); if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) { diff --git a/src/core/manager.c b/src/core/manager.c index 8e518fbaaf..98ef561aae 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -571,14 +571,16 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = - m->cgroup_inotify_fd = -1; + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1; + m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->ask_password_inotify_fd = -1; m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; + m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX; + m->test_run = test_run; /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ @@ -960,6 +962,8 @@ Manager* manager_free(Manager *m) { hashmap_free(m->cgroup_unit); set_free_free(m->unit_path_cache); + hashmap_free(m->cgroup_netclass_registry); + free(m->switch_root); free(m->switch_root_init); diff --git a/src/core/manager.h b/src/core/manager.h index 5cf0dbd508..cc0e5e3361 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -23,6 +23,7 @@ #include <stdbool.h> #include <stdio.h> +#include <libmount.h> #include "sd-bus.h" #include "sd-event.h" @@ -176,10 +177,8 @@ struct Manager { Hashmap *devices_by_sysfs; /* Data specific to the mount subsystem */ - FILE *proc_self_mountinfo; + struct libmnt_monitor *mount_monitor; sd_event_source *mount_event_source; - int utab_inotify_fd; - sd_event_source *mount_utab_event_source; /* Data specific to the swap filesystem */ FILE *proc_swaps; @@ -242,6 +241,11 @@ struct Manager { bool test_run:1; + /* If non-zero, exit with the following value when the systemd + * process terminate. Useful for containers: systemd-nspawn could get + * the return value. */ + uint8_t return_value; + ShowStatus show_status; bool confirm_spawn; bool no_console_output; @@ -303,6 +307,10 @@ struct Manager { const char *unit_log_format_string; int first_boot; + + /* Used for NetClass=auto units */ + Hashmap *cgroup_netclass_registry; + uint32_t cgroup_netclass_registry_last; }; int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 65f3d06ad0..9b16eaa0e2 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -208,7 +208,7 @@ int mount_setup_early(void) { int j; j = mount_one(mount_table + i, false); - if (r == 0) + if (j != 0 && r >= 0) r = j; } @@ -351,7 +351,7 @@ int mount_setup(bool loaded_policy) { int j; j = mount_one(mount_table + i, loaded_policy); - if (r == 0) + if (j != 0 && r >= 0) r = j; } diff --git a/src/core/mount.c b/src/core/mount.c index 1f02aa5566..e7aae6e19a 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -23,8 +23,6 @@ #include <stdio.h> #include <sys/epoll.h> #include <signal.h> -#include <libmount.h> -#include <sys/inotify.h> #include "manager.h" #include "unit.h" @@ -1535,13 +1533,13 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { } static void mount_shutdown(Manager *m) { + assert(m); m->mount_event_source = sd_event_source_unref(m->mount_event_source); - m->mount_utab_event_source = sd_event_source_unref(m->mount_utab_event_source); - m->proc_self_mountinfo = safe_fclose(m->proc_self_mountinfo); - m->utab_inotify_fd = safe_close(m->utab_inotify_fd); + mnt_unref_monitor(m->mount_monitor); + m->mount_monitor = NULL; } static int mount_get_timeout(Unit *u, uint64_t *timeout) { @@ -1560,53 +1558,41 @@ static int mount_get_timeout(Unit *u, uint64_t *timeout) { static int mount_enumerate(Manager *m) { int r; + assert(m); mnt_init_debug(0); - if (!m->proc_self_mountinfo) { - m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!m->proc_self_mountinfo) - return -errno; + if (!m->mount_monitor) { + int fd; - r = sd_event_add_io(m->event, &m->mount_event_source, fileno(m->proc_self_mountinfo), EPOLLPRI, mount_dispatch_io, m); - if (r < 0) + m->mount_monitor = mnt_new_monitor(); + if (!m->mount_monitor) { + r = -ENOMEM; goto fail; + } - /* Dispatch this before we dispatch SIGCHLD, so that - * we always get the events from /proc/self/mountinfo - * before the SIGCHLD of /usr/bin/mount. */ - r = sd_event_source_set_priority(m->mount_event_source, -10); + r = mnt_monitor_enable_kernel(m->mount_monitor, 1); if (r < 0) goto fail; - - (void) sd_event_source_set_description(m->mount_event_source, "mount-mountinfo-dispatch"); - } - - if (m->utab_inotify_fd < 0) { - m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->utab_inotify_fd < 0) { - r = -errno; + r = mnt_monitor_enable_userspace(m->mount_monitor, 1, NULL); + if (r < 0) goto fail; - } - - (void) mkdir_p_label("/run/mount", 0755); - r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO); - if (r < 0) { - r = -errno; + /* mnt_unref_monitor() will close the fd */ + fd = r = mnt_monitor_get_fd(m->mount_monitor); + if (r < 0) goto fail; - } - r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m); + r = sd_event_add_io(m->event, &m->mount_event_source, fd, EPOLLIN, mount_dispatch_io, m); if (r < 0) goto fail; - r = sd_event_source_set_priority(m->mount_utab_event_source, -10); + r = sd_event_source_set_priority(m->mount_event_source, -10); if (r < 0) goto fail; - (void) sd_event_source_set_description(m->mount_utab_event_source, "mount-utab-dispatch"); + (void) sd_event_source_set_description(m->mount_event_source, "mount-monitor-dispatch"); } r = mount_load_proc_self_mountinfo(m, false); @@ -1629,45 +1615,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, int r; assert(m); - assert(revents & (EPOLLPRI | EPOLLIN)); - - /* The manager calls this for every fd event happening on the - * /proc/self/mountinfo file, which informs us about mounting - * table changes, and for /run/mount events which we watch - * for mount options. */ + assert(revents & EPOLLIN); - if (fd == m->utab_inotify_fd) { + if (fd == mnt_monitor_get_fd(m->mount_monitor)) { bool rescan = false; - /* FIXME: We *really* need to replace this with - * libmount's own API for this, we should not hardcode - * internal behaviour of libmount here. */ - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - l = read(fd, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EAGAIN || errno == EINTR) - break; - - log_error_errno(errno, "Failed to read utab inotify: %m"); - break; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - /* Only care about changes to utab, - * but we have to monitor the - * directory to reliably get - * notifications about when utab is - * replaced using rename(2) */ - if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab")) - rescan = true; - } - } - + /* Drain all events and verify that the event is valid. + * + * Note that libmount also monitors /run/mount mkdir if the + * directory does not exist yet. The mkdir may generate event + * which is irrelevant for us. + * + * error: r < 0; valid: r == 0, false positive: rc == 1 */ + do { + r = mnt_monitor_next_change(m->mount_monitor, NULL, NULL); + if (r == 0) + rescan = true; + else if (r < 0) + return log_error_errno(r, "Failed to drain libmount events"); + } while (r == 0); + + log_debug("libmount event [rescan: %s]", yes_no(rescan)); if (!rescan) return 0; } diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 8cc6efc5b8..5296efce1d 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -48,6 +48,7 @@ #define FINALIZE_ATTEMPTS 50 static char* arg_verb; +static uint8_t arg_exit_code; static int parse_argv(int argc, char *argv[]) { enum { @@ -55,6 +56,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LOG_TARGET, ARG_LOG_COLOR, ARG_LOG_LOCATION, + ARG_EXIT_CODE, }; static const struct option options[] = { @@ -62,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) { { "log-target", required_argument, NULL, ARG_LOG_TARGET }, { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, {} }; @@ -110,6 +113,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_EXIT_CODE: + r = safe_atou8(optarg, &arg_exit_code); + if (r < 0) + log_error("Failed to parse exit code %s, ignoring", optarg); + + break; + case '\001': if (!arg_verb) arg_verb = optarg; @@ -183,6 +193,8 @@ int main(int argc, char *argv[]) { cmd = RB_HALT_SYSTEM; else if (streq(arg_verb, "kexec")) cmd = LINUX_REBOOT_CMD_KEXEC; + else if (streq(arg_verb, "exit")) + cmd = 0; /* ignored, just checking that arg_verb is valid */ else { r = -EINVAL; log_error("Unknown action '%s'.", arg_verb); @@ -339,6 +351,16 @@ int main(int argc, char *argv[]) { if (!in_container) sync(); + if (streq(arg_verb, "exit")) { + if (in_container) + exit(arg_exit_code); + else { + /* We cannot exit() on the host, fallback on another + * method. */ + cmd = RB_POWER_OFF; + } + } + switch (cmd) { case LINUX_REBOOT_CMD_KEXEC: diff --git a/src/core/unit.c b/src/core/unit.c index 3bfc2460bc..3a6313e4a2 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -452,6 +452,7 @@ static void unit_free_requires_mounts_for(Unit *u) { static void unit_done(Unit *u) { ExecContext *ec; CGroupContext *cc; + int r; assert(u); @@ -468,6 +469,10 @@ static void unit_done(Unit *u) { cc = unit_get_cgroup_context(u); if (cc) cgroup_context_done(cc); + + r = unit_remove_from_netclass_cgroup(u); + if (r < 0) + log_warning_errno(r, "Unable to remove unit from netclass group: %m"); } void unit_free(Unit *u) { @@ -1241,6 +1246,14 @@ int unit_load(Unit *u) { } unit_update_cgroup_members_masks(u); + + /* If we are reloading, we need to wait for the deserializer + * to restore the net_cls ids that have been set previously */ + if (u->manager->n_reloading <= 0) { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + return r; + } } assert((u->load_state != UNIT_MERGED) == !u->merged_into); @@ -2591,6 +2604,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "cgroup", u->cgroup_path); unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + if (u->cgroup_netclass_id) + unit_serialize_item_format(u, f, "netclass-id", "%" PRIu32, u->cgroup_netclass_id); + if (serialize_jobs) { if (u->job) { fprintf(f, "job\n"); @@ -2778,6 +2794,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->cgroup_realized = b; continue; + } else if (streq(l, "netclass-id")) { + r = safe_atou32(v, &u->cgroup_netclass_id); + if (r < 0) + log_unit_debug(u, "Failed to parse netclass ID %s, ignoring.", v); + else { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to add unit to netclass cgroup, ignoring: %m"); + } + + continue; } if (unit_can_serialize(u)) { @@ -3037,32 +3064,39 @@ int unit_kill_common( sd_bus_error *error) { int r = 0; + bool killed = false; - if (who == KILL_MAIN) { + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) { if (main_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); else if (main_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); } - if (who == KILL_CONTROL) { + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL)) { if (control_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); else if (control_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); } - if (who == KILL_CONTROL || who == KILL_ALL) - if (control_pid > 0) + if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (control_pid > 0) { if (kill(control_pid, signo) < 0) r = -errno; + else + killed = true; + } - if (who == KILL_MAIN || who == KILL_ALL) - if (main_pid > 0) + if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) + if (main_pid > 0) { if (kill(main_pid, signo) < 0) r = -errno; + else + killed = true; + } - if (who == KILL_ALL && u->cgroup_path) { + if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) { _cleanup_set_free_ Set *pid_set = NULL; int q; @@ -3074,8 +3108,13 @@ int unit_kill_common( q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set); if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; + else + killed = true; } + if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_ALL_FAIL)) + return -ESRCH; + return r; } diff --git a/src/core/unit.h b/src/core/unit.h index 3c7684411b..c868d75c79 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -161,6 +161,9 @@ struct Unit { /* CGroup realize members queue */ LIST_FIELDS(Unit, cgroup_queue); + /* Units with the same CGroup netclass */ + LIST_FIELDS(Unit, cgroup_netclass); + /* PIDs we keep an eye on. Note that a unit might have many * more, but these are the ones we care enough about to * process SIGCHLD for */ @@ -189,6 +192,8 @@ struct Unit { CGroupMask cgroup_members_mask; int cgroup_inotify_wd; + uint32_t cgroup_netclass_id; + /* How to start OnFailure units */ JobMode on_failure_job_mode; |