]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/service.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / core / service.c
index 3c4232417da597fecf7923e6448aed3fb0f9855a..042ce5d54dfd98b72fe0fd51035fd1a79730d101 100644 (file)
 #include <unistd.h>
 
 #include "async.h"
-#include "manager.h"
-#include "unit.h"
-#include "service.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
-#include "log.h"
-#include "strv.h"
-#include "unit-name.h"
-#include "unit-printf.h"
+#include "bus-error.h"
+#include "bus-kernel.h"
+#include "bus-util.h"
 #include "dbus-service.h"
-#include "special.h"
-#include "exit-status.h"
 #include "def.h"
-#include "path-util.h"
-#include "util.h"
-#include "utf8.h"
 #include "env-util.h"
+#include "escape.h"
+#include "exit-status.h"
 #include "fileio.h"
-#include "bus-error.h"
-#include "bus-util.h"
-#include "bus-kernel.h"
 #include "formats-util.h"
+#include "load-dropin.h"
+#include "load-fragment.h"
+#include "log.h"
+#include "manager.h"
+#include "path-util.h"
 #include "process-util.h"
 #include "signal-util.h"
+#include "special.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "unit-printf.h"
+#include "unit.h"
+#include "utf8.h"
+#include "util.h"
+#include "service.h"
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_DEAD] = UNIT_INACTIVE,
@@ -108,6 +110,7 @@ static void service_init(Unit *u) {
         s->type = _SERVICE_TYPE_INVALID;
         s->socket_fd = -1;
         s->bus_endpoint_fd = -1;
+        s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
         s->guess_main_pid = true;
 
         RATELIMIT_INIT(s->start_limit, u->manager->default_start_limit_interval, u->manager->default_start_limit_burst);
@@ -142,8 +145,7 @@ static void service_unwatch_pid_file(Service *s) {
         log_unit_debug(UNIT(s), "Stopping watch for PID file %s", s->pid_file_pathspec->path);
         path_spec_unwatch(s->pid_file_pathspec);
         path_spec_done(s->pid_file_pathspec);
-        free(s->pid_file_pathspec);
-        s->pid_file_pathspec = NULL;
+        s->pid_file_pathspec = mfree(s->pid_file_pathspec);
 }
 
 static int service_set_main_pid(Service *s, pid_t pid) {
@@ -262,6 +264,7 @@ static void service_fd_store_unlink(ServiceFDStore *fs) {
                 sd_event_source_unref(fs->event_source);
         }
 
+        free(fs->fdname);
         safe_close(fs->fd);
         free(fs);
 }
@@ -271,11 +274,15 @@ static void service_release_resources(Unit *u) {
 
         assert(s);
 
-        if (!s->fd_store)
+        if (!s->fd_store && s->stdin_fd < 0 && s->stdout_fd < 0 && s->stderr_fd < 0)
                 return;
 
         log_unit_debug(u, "Releasing all resources.");
 
+        s->stdin_fd = safe_close(s->stdin_fd);
+        s->stdout_fd = safe_close(s->stdout_fd);
+        s->stderr_fd = safe_close(s->stderr_fd);
+
         while (s->fd_store)
                 service_fd_store_unlink(s->fd_store);
 
@@ -287,14 +294,9 @@ static void service_done(Unit *u) {
 
         assert(s);
 
-        free(s->pid_file);
-        s->pid_file = NULL;
-
-        free(s->status_text);
-        s->status_text = NULL;
-
-        free(s->reboot_arg);
-        s->reboot_arg = NULL;
+        s->pid_file = mfree(s->pid_file);
+        s->status_text = mfree(s->status_text);
+        s->reboot_arg = mfree(s->reboot_arg);
 
         s->exec_runtime = exec_runtime_unref(s->exec_runtime);
         exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
@@ -313,8 +315,7 @@ static void service_done(Unit *u) {
 
         if (s->bus_name)  {
                 unit_unwatch_bus_name(u, s->bus_name);
-                free(s->bus_name);
-                s->bus_name = NULL;
+                s->bus_name = mfree(s->bus_name);
         }
 
         s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd);
@@ -341,7 +342,7 @@ static int on_fd_store_io(sd_event_source *e, int fd, uint32_t revents, void *us
         return 0;
 }
 
-static int service_add_fd_store(Service *s, int fd) {
+static int service_add_fd_store(Service *s, int fd, const char *name) {
         ServiceFDStore *fs;
         int r;
 
@@ -368,9 +369,15 @@ static int service_add_fd_store(Service *s, int fd) {
 
         fs->fd = fd;
         fs->service = s;
+        fs->fdname = strdup(name ?: "stored");
+        if (!fs->fdname) {
+                free(fs);
+                return -ENOMEM;
+        }
 
         r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs);
         if (r < 0) {
+                free(fs->fdname);
                 free(fs);
                 return r;
         }
@@ -383,7 +390,7 @@ static int service_add_fd_store(Service *s, int fd) {
         return 1;
 }
 
-static int service_add_fd_store_set(Service *s, FDSet *fds) {
+static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) {
         int r;
 
         assert(s);
@@ -398,7 +405,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds) {
                 if (fd < 0)
                         break;
 
-                r = service_add_fd_store(s, fd);
+                r = service_add_fd_store(s, fd, name);
                 if (r < 0)
                         return log_unit_error_errno(UNIT(s), r, "Couldn't add fd to fd store: %m");
                 if (r > 0) {
@@ -489,6 +496,12 @@ static int service_verify(Service *s) {
                 return -EINVAL;
         }
 
+        if (s->usb_function_descriptors && !s->usb_function_strings)
+                log_unit_warning(UNIT(s), "Service has USBFunctionDescriptors= setting, but no USBFunctionStrings=. Ignoring.");
+
+        if (!s->usb_function_descriptors && s->usb_function_strings)
+                log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring.");
+
         return 0;
 }
 
@@ -702,13 +715,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 fprintf(f, "%sStatus Text: %s\n",
                         prefix, s->status_text);
 
-        if (s->n_fd_store_max > 0) {
+        if (s->n_fd_store_max > 0)
                 fprintf(f,
                         "%sFile Descriptor Store Max: %u\n"
                         "%sFile Descriptor Store Current: %u\n",
                         prefix, s->n_fd_store_max,
                         prefix, s->n_fd_store);
-        }
 }
 
 static int service_load_pid_file(Service *s, bool may_warn) {
@@ -767,7 +779,7 @@ static int service_load_pid_file(Service *s, bool may_warn) {
 }
 
 static int service_search_main_pid(Service *s) {
-        pid_t pid;
+        pid_t pid = 0;
         int r;
 
         assert(s);
@@ -782,9 +794,9 @@ static int service_search_main_pid(Service *s) {
 
         assert(s->main_pid <= 0);
 
-        pid = unit_search_main_pid(UNIT(s));
-        if (pid <= 0)
-                return -ENOENT;
+        r = unit_search_main_pid(UNIT(s), &pid);
+        if (r < 0)
+                return r;
 
         log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid);
         r = service_set_main_pid(s, pid);
@@ -860,7 +872,7 @@ static void service_set_state(Service *s, ServiceState state) {
         /* For the inactive states unit_notify() will trim the cgroup,
          * but for exit we have to do that ourselves... */
         if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0)
-                unit_destroy_cgroup_if_empty(UNIT(s));
+                unit_prune_cgroup(UNIT(s));
 
         /* For remain_after_exit services, let's see if we can "release" the
          * hold on the console, since unit_notify() only does that in case of
@@ -886,7 +898,6 @@ static void service_set_state(Service *s, ServiceState state) {
                 log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state));
 
         unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
-        s->reload_result = SERVICE_SUCCESS;
 }
 
 static int service_coldplug(Unit *u) {
@@ -959,62 +970,79 @@ static int service_coldplug(Unit *u) {
         return 0;
 }
 
-static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
+static int service_collect_fds(Service *s, int **fds, char ***fd_names) {
+        _cleanup_strv_free_ char **rfd_names = NULL;
         _cleanup_free_ int *rfds = NULL;
-        unsigned rn_fds = 0;
-        Iterator i;
-        int r;
-        Unit *u;
+        int rn_fds = 0, r;
 
         assert(s);
         assert(fds);
-        assert(n_fds);
+        assert(fd_names);
 
-        if (s->socket_fd >= 0)
-                return 0;
+        if (s->socket_fd >= 0) {
 
-        SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
-                int *cfds;
-                unsigned cn_fds;
-                Socket *sock;
+                /* Pass the per-connection socket */
 
-                if (u->type != UNIT_SOCKET)
-                        continue;
+                rfds = new(int, 1);
+                if (!rfds)
+                        return -ENOMEM;
+                rfds[0] = s->socket_fd;
+
+                rfd_names = strv_new("connection", NULL);
+                if (!rfd_names)
+                        return -ENOMEM;
 
-                sock = SOCKET(u);
+                rn_fds = 1;
+        } else {
+                Iterator i;
+                Unit *u;
 
-                r = socket_collect_fds(sock, &cfds, &cn_fds);
-                if (r < 0)
-                        return r;
+                /* Pass all our configured sockets for singleton services */
 
-                if (cn_fds <= 0) {
-                        free(cfds);
-                        continue;
-                }
+                SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
+                        _cleanup_free_ int *cfds = NULL;
+                        Socket *sock;
+                        int cn_fds;
 
-                if (!rfds) {
-                        rfds = cfds;
-                        rn_fds = cn_fds;
-                } else {
-                        int *t;
+                        if (u->type != UNIT_SOCKET)
+                                continue;
 
-                        t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
-                        if (!t) {
-                                free(cfds);
-                                return -ENOMEM;
-                        }
+                        sock = SOCKET(u);
+
+                        cn_fds = socket_collect_fds(sock, &cfds);
+                        if (cn_fds < 0)
+                                return cn_fds;
 
-                        memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
-                        rfds = t;
-                        rn_fds += cn_fds;
+                        if (cn_fds <= 0)
+                                continue;
 
-                        free(cfds);
+                        if (!rfds) {
+                                rfds = cfds;
+                                rn_fds = cn_fds;
 
+                                cfds = NULL;
+                        } else {
+                                int *t;
+
+                                t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
+                                if (!t)
+                                        return -ENOMEM;
+
+                                memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
+
+                                rfds = t;
+                                rn_fds += cn_fds;
+                        }
+
+                        r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds);
+                        if (r < 0)
+                                return r;
                 }
         }
 
         if (s->n_fd_store > 0) {
                 ServiceFDStore *fs;
+                char **nl;
                 int *t;
 
                 t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int));
@@ -1022,15 +1050,32 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
                         return -ENOMEM;
 
                 rfds = t;
-                LIST_FOREACH(fd_store, fs, s->fd_store)
-                        rfds[rn_fds++] = fs->fd;
+
+                nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*));
+                if (!nl)
+                        return -ENOMEM;
+
+                rfd_names = nl;
+
+                LIST_FOREACH(fd_store, fs, s->fd_store) {
+                        rfds[rn_fds] = fs->fd;
+                        rfd_names[rn_fds] = strdup(strempty(fs->fdname));
+                        if (!rfd_names[rn_fds])
+                                return -ENOMEM;
+
+                        rn_fds++;
+                }
+
+                rfd_names[rn_fds] = NULL;
         }
 
         *fds = rfds;
-        *n_fds = rn_fds;
+        *fd_names = rfd_names;
 
         rfds = NULL;
-        return 0;
+        rfd_names = NULL;
+
+        return rn_fds;
 }
 
 static int service_spawn(
@@ -1044,23 +1089,25 @@ static int service_spawn(
                 bool is_control,
                 pid_t *_pid) {
 
-        pid_t pid;
-        int r;
-        int *fds = NULL;
-        _cleanup_free_ int *fdsbuf = NULL;
-        unsigned n_fds = 0, n_env = 0;
+        _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
         _cleanup_free_ char *bus_endpoint_path = NULL;
-        _cleanup_strv_free_ char
-                **argv = NULL, **final_env = NULL, **our_env = NULL;
+        _cleanup_free_ int *fds = NULL;
+        unsigned n_fds = 0, n_env = 0;
         const char *path;
+        pid_t pid;
+
         ExecParameters exec_params = {
-                .apply_permissions   = apply_permissions,
-                .apply_chroot        = apply_chroot,
-                .apply_tty_stdin     = apply_tty_stdin,
-                .bus_endpoint_fd     = -1,
-                .selinux_context_net = s->socket_fd_selinux_context_net
+                .apply_permissions = apply_permissions,
+                .apply_chroot      = apply_chroot,
+                .apply_tty_stdin   = apply_tty_stdin,
+                .bus_endpoint_fd   = -1,
+                .stdin_fd          = -1,
+                .stdout_fd         = -1,
+                .stderr_fd         = -1,
         };
 
+        int r;
+
         assert(s);
         assert(c);
         assert(_pid);
@@ -1080,16 +1127,11 @@ static int service_spawn(
             s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
             s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
 
-                if (s->socket_fd >= 0) {
-                        fds = &s->socket_fd;
-                        n_fds = 1;
-                } else {
-                        r = service_collect_fds(s, &fdsbuf, &n_fds);
-                        if (r < 0)
-                                goto fail;
+                r = service_collect_fds(s, &fds, &fd_names);
+                if (r < 0)
+                        goto fail;
 
-                        fds = fdsbuf;
-                }
+                n_fds = r;
         }
 
         if (timeout > 0) {
@@ -1127,7 +1169,7 @@ static int service_spawn(
                         goto fail;
                 }
 
-        if (UNIT_DEREF(s->accept_socket)) {
+        if (s->socket_fd >= 0) {
                 union sockaddr_union sa;
                 socklen_t salen = sizeof(sa);
 
@@ -1175,7 +1217,7 @@ static int service_spawn(
 
         if (is_control && UNIT(s)->cgroup_path) {
                 path = strjoina(UNIT(s)->cgroup_path, "/control");
-                cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
+                (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
         } else
                 path = UNIT(s)->cgroup_path;
 
@@ -1193,6 +1235,7 @@ static int service_spawn(
 
         exec_params.argv = argv;
         exec_params.fds = fds;
+        exec_params.fd_names = fd_names;
         exec_params.n_fds = n_fds;
         exec_params.environment = final_env;
         exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
@@ -1202,8 +1245,12 @@ static int service_spawn(
         exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager);
         exec_params.watchdog_usec = s->watchdog_usec;
         exec_params.bus_endpoint_path = bus_endpoint_path;
+        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;
+        exec_params.stdin_fd = s->stdin_fd;
+        exec_params.stdout_fd = s->stdout_fd;
+        exec_params.stderr_fd = s->stderr_fd;
 
         r = exec_spawn(UNIT(s),
                        c,
@@ -1269,7 +1316,7 @@ static int cgroup_good(Service *s) {
         if (!UNIT(s)->cgroup_path)
                 return 0;
 
-        r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true);
+        r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
         if (r < 0)
                 return r;
 
@@ -1520,18 +1567,33 @@ fail:
         service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES);
 }
 
+static bool service_good(Service *s) {
+        int main_pid_ok;
+        assert(s);
+
+        if (s->type == SERVICE_DBUS && !s->bus_name_good)
+                return false;
+
+        main_pid_ok = main_pid_good(s);
+        if (main_pid_ok > 0) /* It's alive */
+                return true;
+        if (main_pid_ok == 0) /* It's dead */
+                return false;
+
+        /* OK, we don't know anything about the main PID, maybe
+         * because there is none. Let's check the control group
+         * instead. */
+
+        return cgroup_good(s) != 0;
+}
+
 static void service_enter_running(Service *s, ServiceResult f) {
-        int main_pid_ok, cgroup_ok;
         assert(s);
 
         if (f != SERVICE_SUCCESS)
                 s->result = f;
 
-        main_pid_ok = main_pid_good(s);
-        cgroup_ok = cgroup_good(s);
-
-        if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) &&
-            (s->bus_name_good || s->type != SERVICE_DBUS)) {
+        if (service_good(s)) {
 
                 /* If there are any queued up sd_notify()
                  * notifications, process them now */
@@ -1765,6 +1827,7 @@ static void service_enter_reload(Service *s) {
         assert(s);
 
         service_unwatch_control_pid(s);
+        s->reload_result = SERVICE_SUCCESS;
 
         s->control_command = s->exec_command[SERVICE_EXEC_RELOAD];
         if (s->control_command) {
@@ -1924,8 +1987,7 @@ static int service_start(Unit *u) {
         s->forbid_restart = false;
         s->reset_cpu_usage = true;
 
-        free(s->status_text);
-        s->status_text = NULL;
+        s->status_text = mfree(s->status_text);
         s->status_errno = 0;
 
         s->notify_state = NOTIFY_UNKNOWN;
@@ -1989,6 +2051,7 @@ _pure_ static bool service_can_reload(Unit *u) {
 static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         Service *s = SERVICE(u);
         ServiceFDStore *fs;
+        int r;
 
         assert(u);
         assert(f);
@@ -2007,12 +2070,9 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         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));
 
-        if (s->status_text) {
-                _cleanup_free_ char *c = NULL;
-
-                c = cescape(s->status_text);
-                unit_serialize_item(u, f, "status-text", strempty(c));
-        }
+        r = unit_serialize_item_escaped(u, f, "status-text", s->status_text);
+        if (r < 0)
+                return r;
 
         /* FIXME: There's a minor uncleanliness here: if there are
          * multiple commands attached here, we will start from the
@@ -2020,34 +2080,34 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         if (s->control_command_id >= 0)
                 unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id));
 
-        if (s->socket_fd >= 0) {
-                int copy;
-
-                copy = fdset_put_dup(fds, s->socket_fd);
-                if (copy < 0)
-                        return copy;
-
-                unit_serialize_item_format(u, f, "socket-fd", "%i", copy);
-        }
-
-        if (s->bus_endpoint_fd >= 0) {
-                int copy;
-
-                copy = fdset_put_dup(fds, s->bus_endpoint_fd);
-                if (copy < 0)
-                        return copy;
+        r = unit_serialize_item_fd(u, 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);
+        if (r < 0)
+                return r;
+        r = unit_serialize_item_fd(u, f, fds, "stderr-fd", s->stderr_fd);
+        if (r < 0)
+                return r;
 
-                unit_serialize_item_format(u, f, "endpoint-fd", "%i", copy);
-        }
+        r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd);
+        if (r < 0)
+                return r;
+        r = unit_serialize_item_fd(u, f, fds, "endpoint-fd", s->bus_endpoint_fd);
+        if (r < 0)
+                return r;
 
         LIST_FOREACH(fd_store, fs, s->fd_store) {
+                _cleanup_free_ char *c = NULL;
                 int copy;
 
                 copy = fdset_put_dup(fds, fs->fd);
                 if (copy < 0)
                         return copy;
 
-                unit_serialize_item_format(u, f, "fd-store-fd", "%i", copy);
+                c = cescape(fs->fdname);
+
+                unit_serialize_item_format(u, f, "fd-store-fd", "%i %s", copy, strempty(c));
         }
 
         if (s->main_exec_status.pid > 0) {
@@ -2064,8 +2124,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
         if (dual_timestamp_is_set(&s->watchdog_timestamp))
                 dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp);
 
-        if (s->forbid_restart)
-                unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
+        unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
 
         return 0;
 }
@@ -2177,12 +2236,24 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         s->bus_endpoint_fd = fdset_remove(fds, fd);
                 }
         } else if (streq(key, "fd-store-fd")) {
+                const char *fdv;
+                size_t pf;
                 int fd;
 
-                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                pf = strcspn(value, WHITESPACE);
+                fdv = strndupa(value, pf);
+
+                if (safe_atoi(fdv, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
                         log_unit_debug(u, "Failed to parse fd-store-fd value: %s", value);
                 else {
-                        r = service_add_fd_store(s, fd);
+                        _cleanup_free_ char *t = NULL;
+                        const char *fdn;
+
+                        fdn = value + pf;
+                        fdn += strspn(fdn, WHITESPACE);
+                        (void) cunescape(fdn, 0, &t);
+
+                        r = service_add_fd_store(s, fd, t);
                         if (r < 0)
                                 log_unit_error_errno(u, r, "Failed to add fd to store: %m");
                         else if (r > 0)
@@ -2224,6 +2295,33 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                         log_unit_debug(u, "Failed to parse forbid-restart value: %s", value);
                 else
                         s->forbid_restart = b;
+        } else if (streq(key, "stdin-fd")) {
+                int fd;
+
+                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                        log_unit_debug(u, "Failed to parse stdin-fd value: %s", value);
+                else {
+                        asynchronous_close(s->stdin_fd);
+                        s->stdin_fd = fdset_remove(fds, fd);
+                }
+        } else if (streq(key, "stdout-fd")) {
+                int fd;
+
+                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                        log_unit_debug(u, "Failed to parse stdout-fd value: %s", value);
+                else {
+                        asynchronous_close(s->stdout_fd);
+                        s->stdout_fd = fdset_remove(fds, fd);
+                }
+        } else if (streq(key, "stderr-fd")) {
+                int fd;
+
+                if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
+                        log_unit_debug(u, "Failed to parse stderr-fd value: %s", value);
+                else {
+                        asynchronous_close(s->stderr_fd);
+                        s->stderr_fd = fdset_remove(fds, fd);
+                }
         } else
                 log_unit_debug(u, "Unknown serialization key: %s", key);
 
@@ -2629,7 +2727,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                                 break;
                                         }
                                 } else
-                                        service_search_main_pid(s);
+                                        (void) service_search_main_pid(s);
 
                                 service_enter_start_post(s);
                                 break;
@@ -2651,7 +2749,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                                 break;
                                         }
                                 } else
-                                        service_search_main_pid(s);
+                                        (void) service_search_main_pid(s);
 
                                 service_enter_running(s, SERVICE_SUCCESS);
                                 break;
@@ -2659,7 +2757,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                         case SERVICE_RELOAD:
                                 if (f == SERVICE_SUCCESS) {
                                         service_load_pid_file(s, true);
-                                        service_search_main_pid(s);
+                                        (void) service_search_main_pid(s);
                                 }
 
                                 s->reload_result = f;
@@ -2731,6 +2829,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
 
         case SERVICE_RELOAD:
                 log_unit_warning(UNIT(s), "Reload operation timed out. Stopping.");
+                service_unwatch_control_pid(s);
+                service_kill_control_processes(s);
                 s->reload_result = SERVICE_FAILURE_TIMEOUT;
                 service_enter_running(s, SERVICE_SUCCESS);
                 break;
@@ -2931,13 +3031,19 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
         }
 
         /* Interpret WATCHDOG= */
-        if (strv_find(tags, "WATCHDOG=1")) {
+        if (strv_find(tags, "WATCHDOG=1"))
                 service_reset_watchdog(s);
-        }
 
-        /* Add the passed fds to the fd store */
         if (strv_find(tags, "FDSTORE=1")) {
-                service_add_fd_store_set(s, fds);
+                const char *name;
+
+                name = strv_find_startswith(tags, "FDNAME=");
+                if (name && !fdname_is_valid(name)) {
+                        log_unit_warning(u, "Passed FDNAME= name is invalid, ignoring.");
+                        name = NULL;
+                }
+
+                service_add_fd_store_set(s, fds, name);
         }
 
         /* Notify clients about changed status or main pid */
@@ -3081,27 +3187,6 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
         return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
 }
 
-static const char* const service_state_table[_SERVICE_STATE_MAX] = {
-        [SERVICE_DEAD] = "dead",
-        [SERVICE_START_PRE] = "start-pre",
-        [SERVICE_START] = "start",
-        [SERVICE_START_POST] = "start-post",
-        [SERVICE_RUNNING] = "running",
-        [SERVICE_EXITED] = "exited",
-        [SERVICE_RELOAD] = "reload",
-        [SERVICE_STOP] = "stop",
-        [SERVICE_STOP_SIGABRT] = "stop-sigabrt",
-        [SERVICE_STOP_SIGTERM] = "stop-sigterm",
-        [SERVICE_STOP_SIGKILL] = "stop-sigkill",
-        [SERVICE_STOP_POST] = "stop-post",
-        [SERVICE_FINAL_SIGTERM] = "final-sigterm",
-        [SERVICE_FINAL_SIGKILL] = "final-sigkill",
-        [SERVICE_FAILED] = "failed",
-        [SERVICE_AUTO_RESTART] = "auto-restart",
-};
-
-DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
-
 static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
         [SERVICE_RESTART_NO] = "no",
         [SERVICE_RESTART_ON_SUCCESS] = "on-success",