]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/service.c
core: introduce a helper function to wrap unit_log_{success,failure}
[thirdparty/systemd.git] / src / core / service.c
index fd7982b501c9b66dab1872c90082df1bfc4b13a6..7675663f8b679cc4d1bb66ce149012e3ae1cba1b 100644 (file)
@@ -27,6 +27,7 @@
 #include "parse-util.h"
 #include "path-util.h"
 #include "process-util.h"
+#include "serialize.h"
 #include "service.h"
 #include "signal-util.h"
 #include "special.h"
@@ -48,7 +49,7 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_EXITED] = UNIT_ACTIVE,
         [SERVICE_RELOAD] = UNIT_RELOADING,
         [SERVICE_STOP] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING,
+        [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
@@ -69,7 +70,7 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
         [SERVICE_EXITED] = UNIT_ACTIVE,
         [SERVICE_RELOAD] = UNIT_RELOADING,
         [SERVICE_STOP] = UNIT_DEACTIVATING,
-        [SERVICE_STOP_SIGABRT] = UNIT_DEACTIVATING,
+        [SERVICE_STOP_WATCHDOG] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
         [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
         [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
@@ -106,6 +107,8 @@ static void service_init(Unit *u) {
 
         s->exec_context.keyring_mode = MANAGER_IS_SYSTEM(u->manager) ?
                 EXEC_KEYRING_PRIVATE : EXEC_KEYRING_INHERIT;
+
+        s->watchdog_original_usec = USEC_INFINITY;
 }
 
 static void service_unwatch_control_pid(Service *s) {
@@ -194,19 +197,21 @@ static usec_t service_get_watchdog_usec(Service *s) {
 
         if (s->watchdog_override_enable)
                 return s->watchdog_override_usec;
-        else
-                return s->watchdog_usec;
+
+        return s->watchdog_original_usec;
 }
 
 static void service_start_watchdog(Service *s) {
-        int r;
         usec_t watchdog_usec;
+        int r;
 
         assert(s);
 
         watchdog_usec = service_get_watchdog_usec(s);
-        if (IN_SET(watchdog_usec, 0, USEC_INFINITY))
+        if (IN_SET(watchdog_usec, 0, USEC_INFINITY)) {
+                service_stop_watchdog(s);
                 return;
+        }
 
         if (s->watchdog_event_source) {
                 r = sd_event_source_set_time(s->watchdog_event_source, usec_add(s->watchdog_timestamp.monotonic, watchdog_usec));
@@ -234,50 +239,55 @@ static void service_start_watchdog(Service *s) {
                  * of living before we consider a service died. */
                 r = sd_event_source_set_priority(s->watchdog_event_source, SD_EVENT_PRIORITY_IDLE);
         }
-
         if (r < 0)
                 log_unit_warning_errno(UNIT(s), r, "Failed to install watchdog timer: %m");
 }
 
-static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) {
-        assert(s);
+static void service_extend_event_source_timeout(Service *s, sd_event_source *source, usec_t extended) {
+        usec_t current;
+        int r;
 
-        if (s->timer_event_source) {
-                uint64_t current = 0, extended = 0;
-                int r;
+        assert(s);
 
-                if (IN_SET(extend_timeout_usec, 0, USEC_INFINITY))
-                        return;
+        /* Extends the specified event source timer to at least the specified time, unless it is already later
+         * anyway. */
 
-                extended = usec_add(now(CLOCK_MONOTONIC), extend_timeout_usec);
+        if (!source)
+                return;
 
-                r = sd_event_source_get_time(s->timer_event_source, &current);
-                if (r < 0)
-                        log_unit_error_errno(UNIT(s), r, "Failed to retrieve timeout timer: %m");
-                else if (extended > current) {
-                        r = sd_event_source_set_time(s->timer_event_source, extended);
-                        if (r < 0)
-                                log_unit_warning_errno(UNIT(s), r, "Failed to set timeout timer: %m");
-                }
+        r = sd_event_source_get_time(source, &current);
+        if (r < 0) {
+                const char *desc;
+                (void) sd_event_source_get_description(s->timer_event_source, &desc);
+                log_unit_warning_errno(UNIT(s), r, "Failed to retrieve timeout time for event source '%s', ignoring: %m", strna(desc));
+                return;
+        }
 
-                if (s->watchdog_event_source) {
-                        /* extend watchdog if necessary. We've asked for an extended timeout so we
-                         * shouldn't expect a watchdog timeout in the interval in between */
-                        r = sd_event_source_get_time(s->watchdog_event_source, &current);
-                        if (r < 0) {
-                                log_unit_error_errno(UNIT(s), r, "Failed to retrieve watchdog timer: %m");
-                                return;
-                        }
+        if (current >= extended) /* Current timeout is already longer, ignore this. */
+                return;
 
-                        if (extended > current) {
-                                r = sd_event_source_set_time(s->watchdog_event_source, extended);
-                                if (r < 0)
-                                        log_unit_warning_errno(UNIT(s), r, "Failed to set watchdog timer: %m");
-                        }
-                }
+        r = sd_event_source_set_time(source, extended);
+        if (r < 0) {
+                const char *desc;
+                (void) sd_event_source_get_description(s->timer_event_source, &desc);
+                log_unit_warning_errno(UNIT(s), r, "Failed to set timeout time for even source '%s', ignoring %m", strna(desc));
         }
 }
 
+static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) {
+        usec_t extended;
+
+        assert(s);
+
+        if (IN_SET(extend_timeout_usec, 0, USEC_INFINITY))
+                return;
+
+        extended = usec_add(now(CLOCK_MONOTONIC), extend_timeout_usec);
+
+        service_extend_event_source_timeout(s, s->timer_event_source, extended);
+        service_extend_event_source_timeout(s, s->watchdog_event_source, extended);
+}
+
 static void service_reset_watchdog(Service *s) {
         assert(s);
 
@@ -285,7 +295,7 @@ static void service_reset_watchdog(Service *s) {
         service_start_watchdog(s);
 }
 
-static void service_reset_watchdog_timeout(Service *s, usec_t watchdog_override_usec) {
+static void service_override_watchdog_timeout(Service *s, usec_t watchdog_override_usec) {
         assert(s);
 
         s->watchdog_override_enable = true;
@@ -538,8 +548,13 @@ static int service_verify(Service *s) {
         if (UNIT(s)->load_state != UNIT_LOADED)
                 return 0;
 
-        if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) {
-                log_unit_error(UNIT(s), "Service lacks both ExecStart= and ExecStop= setting. Refusing.");
+        if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]
+            && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+                /* FailureAction= only makes sense if one of the start or stop commands is specified.
+                 * SuccessAction= will be executed unconditionally if no commands are specified. Hence,
+                 * either a command or SuccessAction= are required. */
+
+                log_unit_error(UNIT(s), "Service has no ExecStart=, ExecStop=, or SuccessAction=. Refusing.");
                 return -ENOEXEC;
         }
 
@@ -548,8 +563,8 @@ static int service_verify(Service *s) {
                 return -ENOEXEC;
         }
 
-        if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) {
-                log_unit_error(UNIT(s), "Service has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.");
+        if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START] && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) {
+                log_unit_error(UNIT(s), "Service has no ExecStart= and no SuccessAction= settings and does not have RemainAfterExit=yes set. Refusing.");
                 return -ENOEXEC;
         }
 
@@ -609,7 +624,7 @@ static int service_add_default_dependencies(Service *s) {
                  * require it, so that we fail if we can't acquire
                  * it. */
 
-                r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+                r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
                 if (r < 0)
                         return r;
         } else {
@@ -630,7 +645,7 @@ static int service_add_default_dependencies(Service *s) {
                 return r;
 
         /* Third, add us in for normal shutdown. */
-        return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true, UNIT_DEPENDENCY_DEFAULT);
+        return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
 }
 
 static void service_fix_output(Service *s) {
@@ -1031,7 +1046,7 @@ static void service_set_state(Service *s, ServiceState state) {
                     SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING,
                     SERVICE_RELOAD,
-                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
                     SERVICE_AUTO_RESTART))
                 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@@ -1039,7 +1054,7 @@ static void service_set_state(Service *s, ServiceState state) {
         if (!IN_SET(state,
                     SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
-                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
                 service_unwatch_main_pid(s);
                 s->main_command = NULL;
@@ -1048,7 +1063,7 @@ static void service_set_state(Service *s, ServiceState state) {
         if (!IN_SET(state,
                     SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RELOAD,
-                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
                 service_unwatch_control_pid(s);
                 s->control_command = NULL;
@@ -1063,7 +1078,7 @@ static void service_set_state(Service *s, ServiceState state) {
         if (!IN_SET(state,
                     SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
-                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
             !(state == SERVICE_DEAD && UNIT(s)->job))
                 service_close_socket_fd(s);
@@ -1102,7 +1117,7 @@ static usec_t service_coldplug_timeout(Service *s) {
                 return usec_add(UNIT(s)->active_enter_timestamp.monotonic, s->runtime_max_usec);
 
         case SERVICE_STOP:
-        case SERVICE_STOP_SIGABRT:
+        case SERVICE_STOP_WATCHDOG:
         case SERVICE_STOP_SIGTERM:
         case SERVICE_STOP_SIGKILL:
         case SERVICE_STOP_POST:
@@ -1137,7 +1152,7 @@ static int service_coldplug(Unit *u) {
             (IN_SET(s->deserialized_state,
                     SERVICE_START, SERVICE_START_POST,
                     SERVICE_RUNNING, SERVICE_RELOAD,
-                    SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                    SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                     SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))) {
                 r = unit_watch_pid(UNIT(s), s->main_pid);
                 if (r < 0)
@@ -1149,7 +1164,7 @@ static int service_coldplug(Unit *u) {
             IN_SET(s->deserialized_state,
                    SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
                    SERVICE_RELOAD,
-                   SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                   SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
                 r = unit_watch_pid(UNIT(s), s->control_pid);
                 if (r < 0)
@@ -1210,7 +1225,7 @@ static int service_collect_fds(
                         return -ENOMEM;
                 rfds[0] = s->socket_fd;
 
-                rfd_names = strv_new("connection", NULL);
+                rfd_names = strv_new("connection");
                 if (!rfd_names)
                         return -ENOMEM;
 
@@ -1384,7 +1399,7 @@ static int service_spawn(
                 ExecFlags flags,
                 pid_t *_pid) {
 
-        ExecParameters exec_params = {
+        _cleanup_(exec_params_clear) ExecParameters exec_params = {
                 .flags      = flags,
                 .stdin_fd   = -1,
                 .stdout_fd  = -1,
@@ -1508,7 +1523,9 @@ static int service_spawn(
                 }
         }
 
-        unit_set_exec_params(UNIT(s), &exec_params);
+        r = unit_set_exec_params(UNIT(s), &exec_params);
+        if (r < 0)
+                return r;
 
         final_env = strv_env_merge(2, exec_params.environment, our_env, NULL);
         if (!final_env)
@@ -1521,12 +1538,12 @@ static int service_spawn(
         SET_FLAG(exec_params.flags, EXEC_NSS_BYPASS_BUS,
                  MANAGER_IS_SYSTEM(UNIT(s)->manager) && unit_has_name(UNIT(s), SPECIAL_DBUS_SERVICE));
 
-        exec_params.environment = final_env;
+        strv_free_and_replace(exec_params.environment, final_env);
         exec_params.fds = fds;
         exec_params.fd_names = fd_names;
         exec_params.n_socket_fds = n_socket_fds;
         exec_params.n_storage_fds = n_storage_fds;
-        exec_params.watchdog_usec = s->watchdog_usec;
+        exec_params.watchdog_usec = service_get_watchdog_usec(s);
         exec_params.selinux_context_net = s->socket_fd_selinux_context_net;
         if (s->type == SERVICE_IDLE)
                 exec_params.idle_pipe = UNIT(s)->manager->idle_pipe;
@@ -1682,8 +1699,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
         if (s->result == SERVICE_SUCCESS)
                 s->result = f;
 
-        if (s->result != SERVICE_SUCCESS)
-                log_unit_warning(UNIT(s), "Failed with result '%s'.", service_result_to_string(s->result));
+        unit_log_result(UNIT(s), s->result == SERVICE_SUCCESS, service_result_to_string(s->result));
 
         if (allow_restart && service_shall_restart(s))
                 s->will_auto_restart = true;
@@ -1780,8 +1796,8 @@ fail:
 static int state_to_kill_operation(ServiceState state) {
         switch (state) {
 
-        case SERVICE_STOP_SIGABRT:
-                return KILL_ABORT;
+        case SERVICE_STOP_WATCHDOG:
+                return KILL_WATCHDOG;
 
         case SERVICE_STOP_SIGTERM:
         case SERVICE_FINAL_SIGTERM:
@@ -1827,9 +1843,9 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
                         goto fail;
 
                 service_set_state(s, state);
-        } else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
+        } else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM) && s->kill_context.send_sigkill)
                 service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_SUCCESS);
-        else if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
+        else if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
                 service_enter_stop_post(s, SERVICE_SUCCESS);
         else if (state == SERVICE_FINAL_SIGTERM && s->kill_context.send_sigkill)
                 service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
@@ -1841,7 +1857,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
 fail:
         log_unit_warning_errno(UNIT(s), r, "Failed to kill processes: %m");
 
-        if (IN_SET(state, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
+        if (IN_SET(state, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL))
                 service_enter_stop_post(s, SERVICE_FAILURE_RESOURCES);
         else
                 service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true);
@@ -2024,6 +2040,12 @@ static void service_enter_start(Service *s) {
                         goto fail;
                 }
 
+                /* We force a fake state transition here. Otherwise, the unit would go directly from
+                 * SERVICE_DEAD to SERVICE_DEAD without SERVICE_ACTIVATING or SERVICE_ACTIVE
+                 * inbetween. This way we can later trigger actions that depend on the state
+                 * transition, including SuccessAction=. */
+                service_set_state(s, SERVICE_START);
+
                 service_enter_start_post(s);
                 return;
         }
@@ -2291,7 +2313,7 @@ static int service_start(Unit *u) {
         /* We cannot fulfill this request right now, try again later
          * please! */
         if (IN_SET(s->state,
-                   SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                   SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
                 return -EAGAIN;
 
@@ -2333,8 +2355,9 @@ static int service_start(Unit *u) {
 
         s->notify_state = NOTIFY_UNKNOWN;
 
+        s->watchdog_original_usec = s->watchdog_usec;
         s->watchdog_override_enable = false;
-        s->watchdog_override_usec = 0;
+        s->watchdog_override_usec = USEC_INFINITY;
 
         exec_command_reset_status_list_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
         exec_status_reset(&s->main_exec_status);
@@ -2361,7 +2384,7 @@ static int service_stop(Unit *u) {
 
         /* Already on it */
         if (IN_SET(s->state,
-                   SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
+                   SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
                    SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
                 return 0;
 
@@ -2420,13 +2443,13 @@ static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecC
 }
 
 static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) {
+        _cleanup_free_ char *args = NULL, *p = NULL;
+        size_t allocated = 0, length = 0;
         Service *s = SERVICE(u);
+        const char *type, *key;
         ServiceExecCommand id;
         unsigned idx;
-        const char *type;
         char **arg;
-        _cleanup_free_ char *args = NULL, *p = NULL;
-        size_t allocated = 0, length = 0;
 
         assert(s);
         assert(f);
@@ -2445,16 +2468,16 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
         idx = service_exec_command_index(u, id, command);
 
         STRV_FOREACH(arg, command->argv) {
-                size_t n;
                 _cleanup_free_ char *e = NULL;
+                size_t n;
 
-                e = xescape(*arg, WHITESPACE);
+                e = cescape(*arg);
                 if (!e)
-                        return -ENOMEM;
+                        return log_oom();
 
                 n = strlen(e);
                 if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
-                        return -ENOMEM;
+                        return log_oom();
 
                 if (length > 0)
                         args[length++] = ' ';
@@ -2464,16 +2487,16 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
         }
 
         if (!GREEDY_REALLOC(args, allocated, length + 1))
-                return -ENOMEM;
+                return log_oom();
+
         args[length++] = 0;
 
-        p = xescape(command->path, WHITESPACE);
+        p = cescape(command->path);
         if (!p)
                 return -ENOMEM;
 
-        fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args);
-
-        return 0;
+        key = strjoina(type, "-command");
+        return serialize_item_format(f, key, "%s %u %s %s", service_exec_command_to_string(id), idx, p, args);
 }
 
 static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
@@ -2485,54 +2508,55 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         assert(f);
         assert(fds);
 
-        unit_serialize_item(u, f, "state", service_state_to_string(s->state));
-        unit_serialize_item(u, f, "result", service_result_to_string(s->result));
-        unit_serialize_item(u, f, "reload-result", service_result_to_string(s->reload_result));
+        (void) serialize_item(f, "state", service_state_to_string(s->state));
+        (void) serialize_item(f, "result", service_result_to_string(s->result));
+        (void) serialize_item(f, "reload-result", service_result_to_string(s->reload_result));
 
         if (s->control_pid > 0)
-                unit_serialize_item_format(u, f, "control-pid", PID_FMT, s->control_pid);
+                (void) serialize_item_format(f, "control-pid", PID_FMT, s->control_pid);
 
         if (s->main_pid_known && s->main_pid > 0)
-                unit_serialize_item_format(u, f, "main-pid", PID_FMT, s->main_pid);
+                (void) serialize_item_format(f, "main-pid", PID_FMT, s->main_pid);
 
-        unit_serialize_item(u, f, "main-pid-known", yes_no(s->main_pid_known));
-        unit_serialize_item(u, f, "bus-name-good", yes_no(s->bus_name_good));
-        unit_serialize_item(u, f, "bus-name-owner", s->bus_name_owner);
+        (void) serialize_bool(f, "main-pid-known", s->main_pid_known);
+        (void) serialize_bool(f, "bus-name-good", s->bus_name_good);
+        (void) serialize_bool(f, "bus-name-owner", s->bus_name_owner);
 
-        unit_serialize_item_format(u, f, "n-restarts", "%u", s->n_restarts);
-        unit_serialize_item(u, f, "flush-n-restarts", yes_no(s->flush_n_restarts));
+        (void) serialize_item_format(f, "n-restarts", "%u", s->n_restarts);
+        (void) serialize_bool(f, "flush-n-restarts", s->flush_n_restarts);
 
-        r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
+        r = serialize_item_escaped(f, "status-text", s->status_text);
         if (r < 0)
                 return r;
 
         service_serialize_exec_command(u, f, s->control_command);
         service_serialize_exec_command(u, f, s->main_command);
 
-        r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd);
+        r = serialize_fd(f, fds, "stdin-fd", s->stdin_fd);
         if (r < 0)
                 return r;
-        r = unit_serialize_item_fd(u, f, fds, "stdout-fd", s->stdout_fd);
+        r = serialize_fd(f, fds, "stdout-fd", s->stdout_fd);
         if (r < 0)
                 return r;
-        r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd);
+        r = serialize_fd(f, fds, "stderr-fd", s->stderr_fd);
         if (r < 0)
                 return r;
 
         if (s->exec_fd_event_source) {
-                r = unit_serialize_item_fd(u, f, fds, "exec-fd", sd_event_source_get_io_fd(s->exec_fd_event_source));
+                r = serialize_fd(f, fds, "exec-fd", sd_event_source_get_io_fd(s->exec_fd_event_source));
                 if (r < 0)
                         return r;
-                unit_serialize_item(u, f, "exec-fd-hot", yes_no(s->exec_fd_hot));
+
+                (void) serialize_bool(f, "exec-fd-hot", s->exec_fd_hot);
         }
 
         if (UNIT_ISSET(s->accept_socket)) {
-                r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
+                r = serialize_item(f, "accept-socket", UNIT_DEREF(s->accept_socket)->id);
                 if (r < 0)
                         return r;
         }
 
-        r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
+        r = serialize_fd(f, fds, "socket-fd", s->socket_fd);
         if (r < 0)
                 return r;
 
@@ -2542,30 +2566,34 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
 
                 copy = fdset_put_dup(fds, fs->fd);
                 if (copy < 0)
-                        return copy;
+                        return log_error_errno(copy, "Failed to copy file descriptor for serialization: %m");
 
                 c = cescape(fs->fdname);
+                if (!c)
+                        return log_oom();
 
-                unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
+                (void) serialize_item_format(f, "fd-store-fd", "%i %s", copy, c);
         }
 
         if (s->main_exec_status.pid > 0) {
-                unit_serialize_item_format(u, f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
-                dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
-                dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
+                (void) serialize_item_format(f, "main-exec-status-pid", PID_FMT, s->main_exec_status.pid);
+                (void) serialize_dual_timestamp(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
+                (void) serialize_dual_timestamp(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
 
                 if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
-                        unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
-                        unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
+                        (void) serialize_item_format(f, "main-exec-status-code", "%i", s->main_exec_status.code);
+                        (void) serialize_item_format(f, "main-exec-status-status", "%i", s->main_exec_status.status);
                 }
         }
 
-        dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
-
-        unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+        (void) serialize_dual_timestamp(f, "watchdog-timestamp", &s->watchdog_timestamp);
+        (void) serialize_bool(f, "forbid-restart", s->forbid_restart);
 
         if (s->watchdog_override_enable)
-               unit_serialize_item_format(u, f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec);
+                (void) serialize_item_format(f, "watchdog-override-usec", USEC_FMT, s->watchdog_override_usec);
+
+        if (s->watchdog_original_usec != USEC_INFINITY)
+                (void) serialize_item_format(f, "watchdog-original-usec", USEC_FMT, s->watchdog_original_usec);
 
         return 0;
 }
@@ -2601,10 +2629,10 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char
                 _cleanup_free_ char *arg = NULL;
 
                 r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
+                if (r < 0)
+                        return r;
                 if (r == 0)
                         break;
-                else if (r < 0)
-                        return r;
 
                 switch (state) {
                 case STATE_EXEC_COMMAND_TYPE:
@@ -2743,18 +2771,16 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
 
                 r = cunescape(value, 0, &t);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to unescape status text: %s", value);
-                else {
-                        free(s->status_text);
-                        s->status_text = t;
-                }
+                        log_unit_debug_errno(u, r, "Failed to unescape status text '%s': %m", value);
+                else
+                        free_and_replace(s->status_text, t);
 
         } else if (streq(key, "accept-socket")) {
                 Unit *socket;
 
                 r = manager_load_unit(u->manager, value, NULL, NULL, &socket);
                 if (r < 0)
-                        log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value);
+                        log_unit_debug_errno(u, r, "Failed to load accept-socket unit '%s': %m", value);
                 else {
                         unit_ref_set(&s->accept_socket, u, socket);
                         SOCKET(socket)->n_connections++;
@@ -2816,11 +2842,11 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 else
                         s->main_exec_status.status = i;
         } else if (streq(key, "main-exec-status-start"))
-                dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
+                deserialize_dual_timestamp(value, &s->main_exec_status.start_timestamp);
         else if (streq(key, "main-exec-status-exit"))
-                dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
+                deserialize_dual_timestamp(value, &s->main_exec_status.exit_timestamp);
         else if (streq(key, "watchdog-timestamp"))
-                dual_timestamp_deserialize(value, &s->watchdog_timestamp);
+                deserialize_dual_timestamp(value, &s->watchdog_timestamp);
         else if (streq(key, "forbid-restart")) {
                 int b;
 
@@ -2872,13 +2898,15 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                                 safe_close(fd);
                 }
         } else if (streq(key, "watchdog-override-usec")) {
-                usec_t watchdog_override_usec;
-                if (timestamp_deserialize(value, &watchdog_override_usec) < 0)
+                if (deserialize_usec(value, &s->watchdog_override_usec) < 0)
                         log_unit_debug(u, "Failed to parse watchdog_override_usec value: %s", value);
-                else {
+                else
                         s->watchdog_override_enable = true;
-                        s->watchdog_override_usec = watchdog_override_usec;
-                }
+
+        } else if (streq(key, "watchdog-original-usec")) {
+                if (deserialize_usec(value, &s->watchdog_original_usec) < 0)
+                        log_unit_debug(u, "Failed to parse watchdog_original_usec value: %s", value);
+
         } else if (STR_IN_SET(key, "main-command", "control-command")) {
                 r = service_deserialize_exec_command(u, key, value);
                 if (r < 0)
@@ -3130,7 +3158,7 @@ static void service_notify_cgroup_empty_event(Unit *u) {
                 service_enter_running(s, SERVICE_SUCCESS);
                 break;
 
-        case SERVICE_STOP_SIGABRT:
+        case SERVICE_STOP_WATCHDOG:
         case SERVICE_STOP_SIGTERM:
         case SERVICE_STOP_SIGKILL:
 
@@ -3204,21 +3232,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                 /* When this is a successful exit, let's log about the exit code on DEBUG level. If this is a failure
                  * and the process exited on its own via exit(), then let's make this a NOTICE, under the assumption
-                 * that the service already logged the reason at a higher log level on its own. However, if the service
-                 * died due to a signal, then it most likely didn't say anything about any reason, hence let's raise
-                 * our log level to WARNING then. */
-
-                log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG :
-                           (code == CLD_EXITED ? LOG_NOTICE : LOG_WARNING),
-                           LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s",
-                                            sigchld_code_to_string(code), status,
-                                            strna(code == CLD_EXITED
-                                                  ? exit_status_to_string(status, EXIT_STATUS_FULL)
-                                                  : signal_to_string(status))),
-                           "EXIT_CODE=%s", sigchld_code_to_string(code),
-                           "EXIT_STATUS=%i", status,
-                           LOG_UNIT_ID(u),
-                           LOG_UNIT_INVOCATION_ID(u));
+                 * that the service already logged the reason at a higher log level on its own. (Internally,
+                 * unit_log_process_exit() will possibly bump this to WARNING if the service died due to a signal.) */
+                unit_log_process_exit(
+                                u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+                                "Main process",
+                                service_exec_command_to_string(SERVICE_EXEC_START),
+                                code, status);
 
                 if (s->result == SERVICE_SUCCESS)
                         s->result = f;
@@ -3274,7 +3294,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 service_enter_running(s, f);
                                 break;
 
-                        case SERVICE_STOP_SIGABRT:
+                        case SERVICE_STOP_WATCHDOG:
                         case SERVICE_STOP_SIGTERM:
                         case SERVICE_STOP_SIGKILL:
 
@@ -3307,9 +3327,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 f = SERVICE_SUCCESS;
                 }
 
-                log_unit_full(u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
-                              "Control process exited, code=%s status=%i",
-                              sigchld_code_to_string(code), status);
+                unit_log_process_exit(
+                                u, f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE,
+                                "Control process",
+                                service_exec_command_to_string(s->control_command_id),
+                                code, status);
 
                 if (s->result == SERVICE_SUCCESS)
                         s->result = f;
@@ -3409,7 +3431,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                 service_enter_signal(s, SERVICE_STOP_SIGTERM, f);
                                 break;
 
-                        case SERVICE_STOP_SIGABRT:
+                        case SERVICE_STOP_WATCHDOG:
                         case SERVICE_STOP_SIGTERM:
                         case SERVICE_STOP_SIGKILL:
                                 if (main_pid_good(s) <= 0)
@@ -3480,8 +3502,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
                 break;
 
-        case SERVICE_STOP_SIGABRT:
-                log_unit_warning(UNIT(s), "State 'stop-sigabrt' timed out. Terminating.");
+        case SERVICE_STOP_WATCHDOG:
+                log_unit_warning(UNIT(s), "State 'stop-watchdog' timed out. Terminating.");
                 service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT);
                 break;
 
@@ -3560,7 +3582,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void
                 log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!",
                                format_timespan(t, sizeof(t), watchdog_usec, 1));
 
-                service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG);
+                service_enter_signal(s, SERVICE_STOP_WATCHDOG, SERVICE_FAILURE_WATCHDOG);
         } else
                 log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!",
                                  format_timespan(t, sizeof(t), watchdog_usec, 1));
@@ -3648,7 +3670,11 @@ static void service_notify_message(
                         }
                         if (r > 0) {
                                 service_set_main_pid(s, new_main_pid);
-                                unit_watch_pid(UNIT(s), new_main_pid);
+
+                                r = unit_watch_pid(UNIT(s), new_main_pid);
+                                if (r < 0)
+                                        log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid);
+
                                 notify_dbus = true;
                         }
                 }
@@ -3699,8 +3725,12 @@ static void service_notify_message(
                 _cleanup_free_ char *t = NULL;
 
                 if (!isempty(e)) {
-                        if (!utf8_is_valid(e))
-                                log_unit_warning(u, "Status message in notification message is not UTF-8 clean.");
+                        /* Note that this size limit check is mostly paranoia: since the datagram size we are willing
+                         * to process is already limited to NOTIFY_BUFFER_MAX, this limit here should never be hit. */
+                        if (strlen(e) > STATUS_TEXT_MAX)
+                                log_unit_warning(u, "Status message overly long (%zu > %u), ignoring.", strlen(e), STATUS_TEXT_MAX);
+                        else if (!utf8_is_valid(e))
+                                log_unit_warning(u, "Status message in notification message is not UTF-8 clean, ignoring.");
                         else {
                                 t = strdup(e);
                                 if (!t)
@@ -3722,7 +3752,7 @@ static void service_notify_message(
                 status_errno = parse_errno(e);
                 if (status_errno < 0)
                         log_unit_warning_errno(u, status_errno,
-                                               "Failed to parse ERRNO= field in notification message: %s", e);
+                                               "Failed to parse ERRNO= field value '%s' in notification message: %m", e);
                 else if (s->status_errno != status_errno) {
                         s->status_errno = status_errno;
                         notify_dbus = true;
@@ -3749,7 +3779,7 @@ static void service_notify_message(
                 if (safe_atou64(e, &watchdog_override_usec) < 0)
                         log_unit_warning(u, "Failed to parse WATCHDOG_USEC=%s", e);
                 else
-                        service_reset_watchdog_timeout(s, watchdog_override_usec);
+                        service_override_watchdog_timeout(s, watchdog_override_usec);
         }
 
         /* Process FD store messages. Either FDSTOREREMOVE=1 for removal, or FDSTORE=1 for addition. In both cases,
@@ -3967,7 +3997,7 @@ static bool service_needs_console(Unit *u) {
                       SERVICE_RUNNING,
                       SERVICE_RELOAD,
                       SERVICE_STOP,
-                      SERVICE_STOP_SIGABRT,
+                      SERVICE_STOP_WATCHDOG,
                       SERVICE_STOP_SIGTERM,
                       SERVICE_STOP_SIGKILL,
                       SERVICE_STOP_POST,