diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/automount.c | 120 | ||||
-rw-r--r-- | src/core/automount.h | 4 | ||||
-rw-r--r-- | src/core/busname.c | 11 | ||||
-rw-r--r-- | src/core/busname.h | 1 | ||||
-rw-r--r-- | src/core/cgroup.c | 6 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 2 | ||||
-rw-r--r-- | src/core/dbus.c | 75 | ||||
-rw-r--r-- | src/core/dbus.h | 2 | ||||
-rw-r--r-- | src/core/device.c | 2 | ||||
-rw-r--r-- | src/core/execute.c | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 29 | ||||
-rw-r--r-- | src/core/manager.c | 164 | ||||
-rw-r--r-- | src/core/manager.h | 3 | ||||
-rw-r--r-- | src/core/mount.c | 32 | ||||
-rw-r--r-- | src/core/mount.h | 1 | ||||
-rw-r--r-- | src/core/path.c | 8 | ||||
-rw-r--r-- | src/core/path.h | 1 | ||||
-rw-r--r-- | src/core/scope.c | 2 | ||||
-rw-r--r-- | src/core/service.c | 9 | ||||
-rw-r--r-- | src/core/service.h | 1 | ||||
-rw-r--r-- | src/core/slice.c | 2 | ||||
-rw-r--r-- | src/core/socket.c | 62 | ||||
-rw-r--r-- | src/core/socket.h | 1 | ||||
-rw-r--r-- | src/core/swap.c | 13 | ||||
-rw-r--r-- | src/core/swap.h | 1 | ||||
-rw-r--r-- | src/core/timer.c | 10 | ||||
-rw-r--r-- | src/core/timer.h | 1 | ||||
-rw-r--r-- | src/core/unit.c | 51 | ||||
-rw-r--r-- | src/core/unit.h | 8 |
29 files changed, 410 insertions, 214 deletions
diff --git a/src/core/automount.c b/src/core/automount.c index 7c55d7bc49..f06d837e30 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -75,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free); static int open_dev_autofs(Manager *m); static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata); +static int automount_start_expire(Automount *a); +static void automount_stop_expire(Automount *a); +static int automount_send_ready(Automount *a, Set *tokens, int status); static void automount_init(Unit *u) { Automount *a = AUTOMOUNT(u); @@ -87,8 +90,6 @@ static void automount_init(Unit *u) { UNIT(a)->ignore_on_isolate = true; } -static int automount_send_ready(Automount *a, Set *tokens, int status); - static void unmount_autofs(Automount *a) { int r; @@ -235,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) { old_state = a->state; a->state = state; + if (state != AUTOMOUNT_RUNNING) + automount_stop_expire(a); + if (state != AUTOMOUNT_WAITING && state != AUTOMOUNT_RUNNING) unmount_autofs(a); @@ -408,7 +412,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in init_autofs_dev_ioctl(¶m); param.ioctlfd = ioctl_fd; - if (status) { + if (status != 0) { param.fail.token = token; param.fail.status = status; } else @@ -435,7 +439,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { if (ioctl_fd < 0) return ioctl_fd; - if (status) + if (status != 0) log_unit_debug_errno(UNIT(a), status, "Sending failure: %m"); else log_unit_debug(UNIT(a), "Sending success."); @@ -462,59 +466,54 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { return r; } -static int automount_start_expire(Automount *a); - -int automount_update_mount(Automount *a, MountState old_state, MountState state) { +static void automount_trigger_notify(Unit *u, Unit *other) { + Automount *a = AUTOMOUNT(u); int r; assert(a); + assert(other); + + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) + return; + + /* Don't propagate state changes from the mount if we are already down */ + if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) + return; + + /* Propagate start limit hit state */ + if (other->start_limit_hit) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); + return; + } + + /* Don't propagate anything if there's still a job queued */ + if (other->job) + return; + + /* The mount is successfully established */ + if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { + (void) automount_send_ready(a, a->tokens, 0); - switch (state) { - case MOUNT_MOUNTED: - case MOUNT_REMOUNTING: - automount_send_ready(a, a->tokens, 0); r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - break; - case MOUNT_DEAD: - case MOUNT_UNMOUNTING: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->tokens, -ENODEV); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); - break; - default: - break; - } - switch (state) { - case MOUNT_DEAD: - automount_send_ready(a, a->expire_tokens, 0); - break; - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->expire_tokens, -ENODEV); - break; - default: - break; + automount_set_state(a, AUTOMOUNT_RUNNING); } - return 0; + /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ + if (IN_SET(MOUNT(other)->state, + MOUNT_DEAD, MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, + MOUNT_FAILED)) { + + (void) automount_send_ready(a, a->tokens, -ENODEV); + + automount_set_state(a, AUTOMOUNT_WAITING); + } } static void automount_enter_waiting(Automount *a) { @@ -699,6 +698,15 @@ static int automount_start_expire(Automount *a) { return 0; } +static void automount_stop_expire(Automount *a) { + assert(a); + + if (!a->expire_event_source) + return; + + (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); +} + static void automount_enter_runnning(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; @@ -751,6 +759,7 @@ fail: static int automount_start(Unit *u) { Automount *a = AUTOMOUNT(u); Unit *trigger; + int r; assert(a); assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); @@ -766,6 +775,12 @@ static int automount_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + a->result = AUTOMOUNT_SUCCESS; automount_enter_waiting(a); return 1; @@ -958,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo case autofs_ptype_expire_direct: log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); + automount_stop_expire(a); r = set_ensure_allocated(&a->expire_tokens, NULL); if (r < 0) { @@ -1037,7 +1052,9 @@ static bool automount_supported(void) { static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { [AUTOMOUNT_SUCCESS] = "success", - [AUTOMOUNT_FAILURE_RESOURCES] = "resources" + [AUTOMOUNT_FAILURE_RESOURCES] = "resources", + [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); @@ -1050,9 +1067,6 @@ const UnitVTable automount_vtable = { "Automount\0" "Install\0", - .no_alias = true, - .no_instances = true, - .init = automount_init, .load = automount_load, .done = automount_done, @@ -1072,6 +1086,8 @@ const UnitVTable automount_vtable = { .check_gc = automount_check_gc, + .trigger_notify = automount_trigger_notify, + .reset_failed = automount_reset_failed, .bus_vtable = bus_automount_vtable, diff --git a/src/core/automount.h b/src/core/automount.h index cf5b1cf994..76a201178e 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -26,6 +26,8 @@ typedef struct Automount Automount; typedef enum AutomountResult { AUTOMOUNT_SUCCESS, AUTOMOUNT_FAILURE_RESOURCES, + AUTOMOUNT_FAILURE_START_LIMIT_HIT, + AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT, _AUTOMOUNT_RESULT_MAX, _AUTOMOUNT_RESULT_INVALID = -1 } AutomountResult; @@ -53,7 +55,5 @@ struct Automount { extern const UnitVTable automount_vtable; -int automount_update_mount(Automount *a, MountState old_state, MountState state); - const char* automount_result_to_string(AutomountResult i) _const_; AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/busname.c b/src/core/busname.c index f4f433340c..f03a95c24e 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -607,6 +607,7 @@ fail: static int busname_start(Unit *u) { BusName *n = BUSNAME(u); + int r; assert(n); @@ -632,6 +633,12 @@ static int busname_start(Unit *u) { assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); + r = unit_start_limit_test(u); + if (r < 0) { + busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT); + return r; + } + n->result = BUSNAME_SUCCESS; busname_enter_making(n); @@ -1014,6 +1021,7 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", [BUSNAME_FAILURE_SIGNAL] = "signal", [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", + [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit", [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit", }; @@ -1028,9 +1036,6 @@ const UnitVTable busname_vtable = { "Install\0", .private_section = "BusName", - .no_alias = true, - .no_instances = true, - .init = busname_init, .done = busname_done, .load = busname_load, diff --git a/src/core/busname.h b/src/core/busname.h index 52c4055dbb..a8562db458 100644 --- a/src/core/busname.h +++ b/src/core/busname.h @@ -32,6 +32,7 @@ typedef enum BusNameResult { BUSNAME_FAILURE_EXIT_CODE, BUSNAME_FAILURE_SIGNAL, BUSNAME_FAILURE_CORE_DUMP, + BUSNAME_FAILURE_START_LIMIT_HIT, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT, _BUSNAME_RESULT_MAX, _BUSNAME_RESULT_INVALID = -1 diff --git a/src/core/cgroup.c b/src/core/cgroup.c index d90b73b456..996efde22b 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1315,7 +1315,9 @@ int manager_setup_cgroup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to watch control group inotify object: %m"); - r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_IDLE - 5); + /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also + * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ + r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return log_error_errno(r, "Failed to set priority of inotify event source: %m"); @@ -1461,6 +1463,8 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { assert(m); assert(cgroup); + log_debug("Got cgroup empty notification for: %s", cgroup); + u = manager_get_unit_by_cgroup(m, cgroup); if (!u) return 0; diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index bb09a515f8..961340608d 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -149,7 +149,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus.c b/src/core/dbus.c index 263955d874..3422a02d68 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -71,28 +71,42 @@ int bus_send_queued_message(Manager *m) { return 0; } +int bus_forward_agent_released(Manager *m, const char *path) { + int r; + + assert(m); + assert(path); + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (!m->system_bus) + return 0; + + /* If we are running a system instance we forward the agent message on the system bus, so that the user + * instances get notified about this, too */ + + r = sd_bus_emit_signal(m->system_bus, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + "s", path); + if (r < 0) + return log_warning_errno(r, "Failed to propagate agent release message: %m"); + + return 1; +} + static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *cgroup, *me; Manager *m = userdata; + const char *cgroup; uid_t sender_uid; - sd_bus *bus; int r; assert(message); assert(m); - /* ignore recursive events sent by us on the system/user bus */ - bus = sd_bus_message_get_bus(message); - if (!sd_bus_is_server(bus)) { - r = sd_bus_get_unique_name(bus, &me); - if (r < 0) - return r; - - if (streq_ptr(sd_bus_message_get_sender(message), me)) - return 0; - } - /* only accept org.freedesktop.systemd1.Agent from UID=0 */ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -110,16 +124,6 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus } manager_notify_cgroup_empty(m, cgroup); - - /* if running as system-instance, forward under our name */ - if (MANAGER_IS_SYSTEM(m) && m->system_bus) { - r = sd_bus_message_rewind(message, 1); - if (r >= 0) - r = sd_bus_send(m->system_bus, message, NULL); - if (r < 0) - log_warning_errno(r, "Failed to forward Released message: %m"); - } - return 0; } @@ -690,25 +694,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } - if (MANAGER_IS_SYSTEM(m)) { - /* When we run as system instance we get the Released - * signal via a direct connection */ - - r = sd_bus_add_match( - bus, - NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); - - if (r < 0) { - log_warning_errno(r, "Failed to register Released match on new connection bus: %m"); - return 0; - } - } - r = bus_setup_disconnected_match(m, bus); if (r < 0) return 0; @@ -906,8 +891,8 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { assert(m); assert(bus); - /* On kdbus or if we are a user instance we get the Released message via the system bus */ - if (MANAGER_IS_USER(m) || m->kdbus_fd >= 0) { + /* if we are a user instance we get the Released message via the system bus */ + if (MANAGER_IS_USER(m)) { r = sd_bus_add_match( bus, NULL, @@ -990,7 +975,7 @@ static int bus_init_private(Manager *m) { return 0; strcpy(sa.un.sun_path, "/run/systemd/private"); - salen = offsetof(union sockaddr_union, un.sun_path) + strlen("/run/systemd/private"); + salen = SOCKADDR_UN_LEN(sa.un); } else { size_t left = sizeof(sa.un.sun_path); char *p = sa.un.sun_path; diff --git a/src/core/dbus.h b/src/core/dbus.h index e16a84fbb8..6baaffbd75 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -40,3 +40,5 @@ int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error); + +int bus_forward_agent_released(Manager *m, const char *path); diff --git a/src/core/device.c b/src/core/device.c index d01bec53d8..16e56efcc3 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -841,8 +841,6 @@ const UnitVTable device_vtable = { "Device\0" "Install\0", - .no_instances = true, - .init = device_init, .done = device_done, .load = unit_load_fragment_and_dropin_optional, diff --git a/src/core/execute.c b/src/core/execute.c index ac2ac39892..5eb3f13695 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -271,7 +271,7 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { } } - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) r = -errno; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c4566f7709..1a8c03904c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3427,10 +3427,10 @@ int config_parse_protect_system( #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { + char *id = NULL; unsigned c = 0; int fd, r; FILE *f; - char *id = NULL; assert(filename); assert(*filename); @@ -3452,7 +3452,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { * the names of this unit, but only if it is a valid * unit name. */ name = basename(*filename); - if (unit_name_is_valid(name, UNIT_NAME_ANY)) { id = set_get(names, name); @@ -3492,6 +3491,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { *_f = f; *_final = id; + return 0; } @@ -3552,13 +3552,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { } static int load_from_path(Unit *u, const char *path) { - int r; _cleanup_set_free_free_ Set *symlink_names = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *filename = NULL; char *id = NULL; Unit *merged; struct stat st; + int r; assert(u); assert(path); @@ -3597,18 +3597,14 @@ static int load_from_path(Unit *u, const char *path) { r = -ENOENT; else r = open_follow(&filename, &f, symlink_names, &id); + if (r >= 0) + break; + filename = mfree(filename); + if (r != -ENOENT) + return r; - if (r < 0) { - filename = mfree(filename); - if (r != -ENOENT) - return r; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - continue; - } - - break; + /* Empty the symlink names for the next run */ + set_clear_free(symlink_names); } } @@ -3616,6 +3612,11 @@ static int load_from_path(Unit *u, const char *path) { /* Hmm, no suitable file found? */ return 0; + if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { + log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); + return -ELOOP; + } + merged = u; r = merge_by_names(&merged, symlink_names, id); if (r < 0) diff --git a/src/core/manager.c b/src/core/manager.c index bd00c224f4..e192cd475d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -87,6 +87,7 @@ #include "watchdog.h" #define NOTIFY_RCVBUF_SIZE (8*1024*1024) +#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024) /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) @@ -94,6 +95,7 @@ #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); @@ -484,11 +486,11 @@ static int manager_setup_signals(Manager *m) { (void) sd_event_source_set_description(m->signal_event_source, "manager-signal"); - /* Process signals a bit earlier than the rest of things, but - * later than notify_fd processing, so that the notify - * processing can still figure out to which process/service a - * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); + /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the + * notify processing can still figure out to which process/service a message belongs, before we reap the + * process. Also, process this before handling cgroup notifications, so that we always collect child exit + * status information before detecting that there's no process in a cgroup. */ + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); if (r < 0) return r; @@ -581,12 +583,12 @@ int manager_new(UnitFileScope scope, 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->cgroup_inotify_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = + m->ask_password_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; @@ -703,7 +705,7 @@ static int manager_setup_notify(Manager *m) { (void) unlink(m->notify_socket); strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); @@ -722,8 +724,8 @@ static int manager_setup_notify(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate notify event source: %m"); - /* Process signals a bit earlier than SIGCHLD, so that we can - * still identify to which service an exit message belongs */ + /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which + * service an exit message belongs. */ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -734,6 +736,79 @@ static int manager_setup_notify(Manager *m) { return 0; } +static int manager_setup_cgroups_agent(Manager *m) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + int r; + + /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering + * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and + * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on + * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number + * of D-Bus connections may be queued until the kernel will start dropping further incoming connections, + * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX + * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and + * we thus won't lose messages. + * + * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen + * to it. The system instance hence listens on this special socket, but the user instances listen on the system + * bus for these messages. */ + + if (m->test_run) + return 0; + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */ + return 0; + + if (m->cgroups_agent_fd < 0) { + _cleanup_close_ int fd = -1; + + /* First free all secondary fields */ + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m"); + + fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE); + + (void) unlink(sa.un.sun_path); + + /* Only allow root to connect to this socket */ + RUN_WITH_UMASK(0077) + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + m->cgroups_agent_fd = fd; + fd = -1; + } + + if (!m->cgroups_agent_event_source) { + r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate cgroups agent event source: %m"); + + /* Process cgroups notifications early, but after having processed service notification messages or + * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification, + * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of + * cgroup inotify for the unified cgroup stuff. */ + r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5); + if (r < 0) + return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); + + (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent"); + } + + return 0; +} + static int manager_setup_kdbus(Manager *m) { _cleanup_free_ char *p = NULL; @@ -944,12 +1019,14 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->signal_event_source); sd_event_source_unref(m->notify_event_source); + sd_event_source_unref(m->cgroups_agent_event_source); sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); safe_close(m->signal_fd); safe_close(m->notify_fd); + safe_close(m->cgroups_agent_fd); safe_close(m->time_change_fd); safe_close(m->kdbus_fd); @@ -1142,6 +1219,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (q < 0 && r == 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r == 0) + r = q; + /* We might have deserialized the kdbus control fd, but if we * didn't, then let's create the bus now. */ manager_setup_kdbus(m); @@ -1479,6 +1560,35 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + char buf[PATH_MAX+1]; + ssize_t n; + + n = recv(fd, buf, sizeof(buf), 0); + if (n < 0) + return log_error_errno(errno, "Failed to read cgroups agent message: %m"); + if (n == 0) { + log_error("Got zero-length cgroups agent message, ignoring."); + return 0; + } + if ((size_t) n >= sizeof(buf)) { + log_error("Got overly long cgroups agent message, ignoring."); + return 0; + } + + if (memchr(buf, 0, n)) { + log_error("Got cgroups agent message with embedded NUL byte, ignoring."); + return 0; + } + buf[n] = 0; + + manager_notify_cgroup_empty(m, buf); + bus_forward_agent_released(m, buf); + + return 0; +} + static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; @@ -2135,11 +2245,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { } void manager_send_unit_plymouth(Manager *m, Unit *u) { - union sockaddr_union sa = PLYMOUTH_SOCKET; - - int n = 0; + static const union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_free_ char *message = NULL; _cleanup_close_ int fd = -1; + int n = 0; /* Don't generate plymouth events if the service was already * started and we're just deserializing */ @@ -2165,7 +2274,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { return; } - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) log_error_errno(errno, "connect() failed: %m"); @@ -2265,6 +2374,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "notify-socket=%s\n", m->notify_socket); } + if (m->cgroups_agent_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->cgroups_agent_fd); + if (copy < 0) + return copy; + + fprintf(f, "cgroups-agent-fd=%i\n", copy); + } + if (m->kdbus_fd >= 0) { int copy; @@ -2432,6 +2551,17 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { free(m->notify_socket); m->notify_socket = n; + } else if (startswith(l, "cgroups-agent-fd=")) { + int fd; + + if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse cgroups agent fd: %s", l + 10); + else { + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + safe_close(m->cgroups_agent_fd); + m->cgroups_agent_fd = fdset_remove(fds, fd); + } + } else if (startswith(l, "kdbus-fd=")) { int fd; @@ -2552,6 +2682,10 @@ int manager_reload(Manager *m) { if (q < 0 && r >= 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r >= 0) + r = q; + /* Third, fire things up! */ manager_coldplug(m); diff --git a/src/core/manager.h b/src/core/manager.h index 17f84e6963..4bccca75cb 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -132,6 +132,9 @@ struct Manager { int notify_fd; sd_event_source *notify_event_source; + int cgroups_agent_fd; + sd_event_source *cgroups_agent_event_source; + int signal_fd; sd_event_source *signal_event_source; diff --git a/src/core/mount.c b/src/core/mount.c index cc07873b24..5a8c26b9e1 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -584,23 +584,6 @@ static int mount_load(Unit *u) { return mount_verify(m); } -static int mount_notify_automount(Mount *m, MountState old_state, MountState state) { - Unit *p; - int r; - Iterator i; - - assert(m); - - SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i) - if (p->type == UNIT_AUTOMOUNT) { - r = automount_update_mount(AUTOMOUNT(p), old_state, state); - if (r < 0) - return r; - } - - return 0; -} - static void mount_set_state(Mount *m, MountState state) { MountState old_state; assert(m); @@ -624,8 +607,6 @@ static void mount_set_state(Mount *m, MountState state) { m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - mount_notify_automount(m, old_state, state); - if (state != old_state) log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); @@ -984,6 +965,7 @@ fail: static int mount_start(Unit *u) { Mount *m = MOUNT(u); + int r; assert(m); @@ -1002,6 +984,12 @@ static int mount_start(Unit *u) { assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + m->result = MOUNT_SUCCESS; m->reload_result = MOUNT_SUCCESS; m->reset_cpu_usage = true; @@ -1821,7 +1809,8 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { [MOUNT_FAILURE_TIMEOUT] = "timeout", [MOUNT_FAILURE_EXIT_CODE] = "exit-code", [MOUNT_FAILURE_SIGNAL] = "signal", - [MOUNT_FAILURE_CORE_DUMP] = "core-dump" + [MOUNT_FAILURE_CORE_DUMP] = "core-dump", + [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); @@ -1839,9 +1828,6 @@ const UnitVTable mount_vtable = { "Install\0", .private_section = "Mount", - .no_alias = true, - .no_instances = true, - .init = mount_init, .load = mount_load, .done = mount_done, diff --git a/src/core/mount.h b/src/core/mount.h index 3b343c6b1f..da529c44f4 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -39,6 +39,7 @@ typedef enum MountResult { MOUNT_FAILURE_EXIT_CODE, MOUNT_FAILURE_SIGNAL, MOUNT_FAILURE_CORE_DUMP, + MOUNT_FAILURE_START_LIMIT_HIT, _MOUNT_RESULT_MAX, _MOUNT_RESULT_INVALID = -1 } MountResult; diff --git a/src/core/path.c b/src/core/path.c index 5e7b3eb234..0dd0d375d8 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -560,6 +560,7 @@ static void path_mkdir(Path *p) { static int path_start(Unit *u) { Path *p = PATH(u); Unit *trigger; + int r; assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); @@ -570,6 +571,12 @@ static int path_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); + return r; + } + path_mkdir(p); p->result = PATH_SUCCESS; @@ -739,6 +746,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); static const char* const path_result_table[_PATH_RESULT_MAX] = { [PATH_SUCCESS] = "success", [PATH_FAILURE_RESOURCES] = "resources", + [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); diff --git a/src/core/path.h b/src/core/path.h index bbbcebd78e..4230c8fb99 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -62,6 +62,7 @@ static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { typedef enum PathResult { PATH_SUCCESS, PATH_FAILURE_RESOURCES, + PATH_FAILURE_START_LIMIT_HIT, _PATH_RESULT_MAX, _PATH_RESULT_INVALID = -1 } PathResult; diff --git a/src/core/scope.c b/src/core/scope.c index 7078d1f7e9..238f63a729 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -569,8 +569,6 @@ const UnitVTable scope_vtable = { "Install\0", .private_section = "Scope", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = scope_init, diff --git a/src/core/service.c b/src/core/service.c index f7a3fcf2b9..7ebabca5d6 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1957,6 +1957,7 @@ fail: static int service_start(Unit *u) { Service *s = SERVICE(u); + int r; assert(s); @@ -1983,6 +1984,13 @@ static int service_start(Unit *u) { assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_start_limit_test(u); + if (r < 0) { + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); + return r; + } + s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; s->main_pid_known = false; @@ -3266,6 +3274,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); diff --git a/src/core/service.h b/src/core/service.h index c7f1e81bdb..4af3d40439 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -86,6 +86,7 @@ typedef enum ServiceResult { SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP, SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT_HIT, _SERVICE_RESULT_MAX, _SERVICE_RESULT_INVALID = -1 } ServiceResult; diff --git a/src/core/slice.c b/src/core/slice.c index 63a77c9bca..c7700b8857 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -309,8 +309,6 @@ const UnitVTable slice_vtable = { "Install\0", .private_section = "Slice", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = slice_init, diff --git a/src/core/socket.c b/src/core/socket.c index 7eeed068bd..d4b409ef53 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -100,7 +100,8 @@ static void socket_init(Unit *u) { s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500); + s->trigger_limit.interval = USEC_INFINITY; + s->trigger_limit.burst = (unsigned) -1; } static void socket_unwatch_control_pid(Socket *s) { @@ -328,6 +329,25 @@ static int socket_add_extras(Socket *s) { assert(s); + /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit + * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept() + * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly + * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic + * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to + * process whatever is queued in one go, and thus should normally never have to be started frequently. This is + * different for Accept=yes where each connection is processed by a new service instance, and thus frequent + * service starts are typical. */ + + if (s->trigger_limit.interval == USEC_INFINITY) + s->trigger_limit.interval = 2 * USEC_PER_SEC; + + if (s->trigger_limit.burst == (unsigned) -1) { + if (s->accept) + s->trigger_limit.burst = 200; + else + s->trigger_limit.burst = 20; + } + if (have_non_accept_socket(s)) { if (!UNIT_DEREF(s->service)) { @@ -620,8 +640,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { if (!isempty(s->user) || !isempty(s->group)) fprintf(f, - "%sOwnerUser: %s\n" - "%sOwnerGroup: %s\n", + "%sSocketUser: %s\n" + "%sSocketGroup: %s\n", prefix, strna(s->user), prefix, strna(s->group)); @@ -670,6 +690,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); } + fprintf(f, + "%sTriggerLimitIntervalSec: %s\n" + "%sTriggerLimitBurst: %u\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), + prefix, s->trigger_limit.burst); + exec_context_dump(&s->exec_context, f, prefix); kill_context_dump(&s->kill_context, f, prefix); @@ -1271,11 +1297,13 @@ static int socket_open_fds(Socket *s) { /* Apply the socket protocol */ switch(p->address.type) { + case SOCK_STREAM: case SOCK_SEQPACKET: if (p->socket->socket_protocol == IPPROTO_SCTP) p->address.protocol = p->socket->socket_protocol; break; + case SOCK_DGRAM: if (p->socket->socket_protocol == IPPROTO_UDPLITE) p->address.protocol = p->socket->socket_protocol; @@ -1339,8 +1367,7 @@ static int socket_open_fds(Socket *s) { } break; - case SOCKET_USB_FUNCTION: - { + case SOCKET_USB_FUNCTION: { _cleanup_free_ char *ep = NULL; ep = path_make_absolute("ep0", p->path); @@ -2057,6 +2084,7 @@ fail: static int socket_start(Unit *u) { Socket *s = SOCKET(u); + int r; assert(s); @@ -2101,6 +2129,12 @@ static int socket_start(Unit *u) { assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SOCKET_SUCCESS; s->reset_cpu_usage = true; @@ -2735,17 +2769,26 @@ static void socket_trigger_notify(Unit *u, Unit *other) { assert(u); assert(other); - /* Don't propagate state changes from the service if we are - already down or accepting connections */ - if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING) || s->accept) + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + return; + + /* Don't propagate state changes from the service if we are already down */ + if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING)) return; + /* We don't care for the service state if we are in Accept=yes mode */ + if (s->accept) + return; + + /* Propagate start limit hit state */ if (other->start_limit_hit) { socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT); return; } - if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + /* Don't propagate anything if there's still a job queued */ + if (other->job) return; if (IN_SET(SERVICE(other)->state, @@ -2818,6 +2861,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { [SOCKET_FAILURE_EXIT_CODE] = "exit-code", [SOCKET_FAILURE_SIGNAL] = "signal", [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit", [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" }; diff --git a/src/core/socket.h b/src/core/socket.h index 2a4b1bb674..0f1ac69c6f 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -52,6 +52,7 @@ typedef enum SocketResult { SOCKET_FAILURE_EXIT_CODE, SOCKET_FAILURE_SIGNAL, SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_START_LIMIT_HIT, SOCKET_FAILURE_TRIGGER_LIMIT_HIT, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, _SOCKET_RESULT_MAX, diff --git a/src/core/swap.c b/src/core/swap.c index d8802470d2..a532b15be8 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -814,6 +814,7 @@ fail: static int swap_start(Unit *u) { Swap *s = SWAP(u), *other; + int r; assert(s); @@ -842,6 +843,12 @@ static int swap_start(Unit *u) { if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) return -EAGAIN; + r = unit_start_limit_test(u); + if (r < 0) { + swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SWAP_SUCCESS; s->reset_cpu_usage = true; @@ -1447,7 +1454,8 @@ static const char* const swap_result_table[_SWAP_RESULT_MAX] = { [SWAP_FAILURE_TIMEOUT] = "timeout", [SWAP_FAILURE_EXIT_CODE] = "exit-code", [SWAP_FAILURE_SIGNAL] = "signal", - [SWAP_FAILURE_CORE_DUMP] = "core-dump" + [SWAP_FAILURE_CORE_DUMP] = "core-dump", + [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); @@ -1465,9 +1473,6 @@ const UnitVTable swap_vtable = { "Install\0", .private_section = "Swap", - .no_alias = true, - .no_instances = true, - .init = swap_init, .load = swap_load, .done = swap_done, diff --git a/src/core/swap.h b/src/core/swap.h index ac7a63d81b..fbf66debdc 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -38,6 +38,7 @@ typedef enum SwapResult { SWAP_FAILURE_EXIT_CODE, SWAP_FAILURE_SIGNAL, SWAP_FAILURE_CORE_DUMP, + SWAP_FAILURE_START_LIMIT_HIT, _SWAP_RESULT_MAX, _SWAP_RESULT_INVALID = -1 } SwapResult; diff --git a/src/core/timer.c b/src/core/timer.c index f8f5f4b2e4..3206296f09 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -599,6 +599,7 @@ static int timer_start(Unit *u) { Timer *t = TIMER(u); TimerValue *v; Unit *trigger; + int r; assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); @@ -609,6 +610,12 @@ static int timer_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); + return r; + } + t->last_trigger = DUAL_TIMESTAMP_NULL; /* Reenable all timers that depend on unit activation time */ @@ -808,7 +815,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); static const char* const timer_result_table[_TIMER_RESULT_MAX] = { [TIMER_SUCCESS] = "success", - [TIMER_FAILURE_RESOURCES] = "resources" + [TIMER_FAILURE_RESOURCES] = "resources", + [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); diff --git a/src/core/timer.h b/src/core/timer.h index 698e6da2f5..9c4b64f898 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -48,6 +48,7 @@ typedef struct TimerValue { typedef enum TimerResult { TIMER_SUCCESS, TIMER_FAILURE_RESOURCES, + TIMER_FAILURE_START_LIMIT_HIT, _TIMER_RESULT_MAX, _TIMER_RESULT_INVALID = -1 } TimerResult; diff --git a/src/core/unit.c b/src/core/unit.c index 81cd7ee2b8..d8ab5781b0 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -193,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) { if (r < 0) return r; - if (i && unit_vtable[t]->no_instances) + if (i && !unit_type_may_template(t)) return -EINVAL; /* Ensure that this unit is either instanced or not instanced, @@ -202,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) { if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) return -EINVAL; - if (unit_vtable[t]->no_alias && !set_isempty(u->names)) + if (!unit_type_may_alias(t) && !set_isempty(u->names)) return -EEXIST; if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) @@ -720,6 +720,9 @@ int unit_merge(Unit *u, Unit *other) { if (!u->instance != !other->instance) return -EINVAL; + if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ + return -EEXIST; + if (other->load_state != UNIT_STUB && other->load_state != UNIT_NOT_FOUND) return -EEXIST; @@ -776,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) { } int unit_merge_by_name(Unit *u, const char *name) { + _cleanup_free_ char *s = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name); @@ -1244,7 +1247,7 @@ int unit_load(Unit *u) { fclose(u->transient_file); u->transient_file = NULL; - u->dropin_mtime = now(CLOCK_REALTIME); + u->fragment_mtime = now(CLOCK_REALTIME); } if (UNIT_VTABLE(u)->load) { @@ -1459,7 +1462,7 @@ void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { unit_status_print_starting_stopping(u, t); } -static int unit_start_limit_test(Unit *u) { +int unit_start_limit_test(Unit *u) { assert(u); if (ratelimit_test(&u->start_limit)) { @@ -1485,7 +1488,6 @@ static int unit_start_limit_test(Unit *u) { int unit_start(Unit *u) { UnitActiveState state; Unit *following; - int r; assert(u); @@ -1538,11 +1540,6 @@ int unit_start(Unit *u) { if (!UNIT_VTABLE(u)->start) return -EBADR; - /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); - if (r < 0) - return r; - /* We don't suppress calls to ->start() here when we are * already starting, to allow this request to be used as a * "hurry up" call, for example when the unit is in some "auto @@ -2948,7 +2945,7 @@ int unit_coldplug(Unit *u) { return 0; } -static bool fragment_mtime_changed(const char *path, usec_t mtime) { +static bool fragment_mtime_newer(const char *path, usec_t mtime) { struct stat st; if (!path) @@ -2960,7 +2957,7 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) { if (mtime > 0) /* For non-empty files check the mtime */ - return timespec_load(&st.st_mtim) != mtime; + return timespec_load(&st.st_mtim) > mtime; else if (!null_or_empty(&st)) /* For masked files check if they are still so */ return true; @@ -2971,32 +2968,24 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) { bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; char **path; - unsigned loaded_cnt, current_cnt; assert(u); - if (fragment_mtime_changed(u->fragment_path, u->fragment_mtime) || - fragment_mtime_changed(u->source_path, u->source_mtime)) + if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime)) return true; - (void) unit_find_dropin_paths(u, &t); - loaded_cnt = strv_length(t); - current_cnt = strv_length(u->dropin_paths); - - if (loaded_cnt == current_cnt) { - if (loaded_cnt == 0) - return false; + if (fragment_mtime_newer(u->source_path, u->source_mtime)) + return true; - if (strv_overlap(u->dropin_paths, t)) { - STRV_FOREACH(path, u->dropin_paths) - if (fragment_mtime_changed(*path, u->dropin_mtime)) - return true; + (void) unit_find_dropin_paths(u, &t); + if (!strv_equal(u->dropin_paths, t)) + return true; - return false; - } - } + STRV_FOREACH(path, u->dropin_paths) + if (fragment_mtime_newer(*path, u->dropin_mtime)) + return true; - return true; + return false; } void unit_reset_failed(Unit *u) { diff --git a/src/core/unit.h b/src/core/unit.h index 78f950c051..08a927962d 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -417,12 +417,6 @@ struct UnitVTable { /* The strings to print in status messages */ UnitStatusMessageFormats status_message_formats; - /* Can units of this type have multiple names? */ - bool no_alias:1; - - /* Instances make no sense for this type */ - bool no_instances:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; @@ -618,6 +612,8 @@ static inline bool unit_supported(Unit *u) { void unit_warn_if_dir_nonempty(Unit *u, const char* where); int unit_fail_if_symlink(Unit *u, const char* where); +int unit_start_limit_test(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ |