]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/service.c
service: rename service_reset_watchdog_timeout() → service_override_watchdog_timeout()
[thirdparty/systemd.git] / src / core / service.c
index 3d065bf487ce9821faf1618adf629d8323b138c0..cd33e218b64647ae669a4004a8a20e588775f1c8 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"
@@ -194,19 +195,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 +237,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 +293,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 +546,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 +561,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;
         }
 
@@ -2024,6 +2037,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;
         }
@@ -2420,13 +2439,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 +2464,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 +2483,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 +2504,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 +2562,31 @@ 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);
 
         return 0;
 }
@@ -2601,10 +2622,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 +2764,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 +2835,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 +2891,11 @@ 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 (STR_IN_SET(key, "main-command", "control-command")) {
                 r = service_deserialize_exec_command(u, key, value);
                 if (r < 0)
@@ -3648,7 +3665,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 +3720,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 +3747,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 +3774,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,