]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/service.c
Merge pull request #1668 from ssahani/net1
[thirdparty/systemd.git] / src / core / service.c
index 248a9e8c623deb4d25961c025a8bf1dbd602622e..abcbd4954f181de77f3fe42798956ec1df0c5062 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 "fd-util.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 "service.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"
 
 static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [SERVICE_DEAD] = UNIT_INACTIVE,
@@ -108,6 +111,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);
@@ -261,6 +265,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);
 }
@@ -270,11 +275,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);
 
@@ -334,7 +343,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;
 
@@ -361,9 +370,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;
         }
@@ -376,7 +391,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);
@@ -391,7 +406,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) {
@@ -482,6 +497,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;
 }
 
@@ -878,7 +899,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) {
@@ -951,62 +971,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;
 
-                sock = SOCKET(u);
+                rfd_names = strv_new("connection", NULL);
+                if (!rfd_names)
+                        return -ENOMEM;
 
-                r = socket_collect_fds(sock, &cfds, &cn_fds);
-                if (r < 0)
-                        return r;
+                rn_fds = 1;
+        } else {
+                Iterator i;
+                Unit *u;
 
-                if (cn_fds <= 0) {
-                        free(cfds);
-                        continue;
-                }
+                /* Pass all our configured sockets for singleton services */
 
-                if (!rfds) {
-                        rfds = cfds;
-                        rn_fds = cn_fds;
-                } else {
-                        int *t;
+                SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
+                        _cleanup_free_ int *cfds = NULL;
+                        Socket *sock;
+                        int cn_fds;
 
-                        t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
-                        if (!t) {
-                                free(cfds);
-                                return -ENOMEM;
-                        }
+                        if (u->type != UNIT_SOCKET)
+                                continue;
+
+                        sock = SOCKET(u);
 
-                        memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
-                        rfds = t;
-                        rn_fds += cn_fds;
+                        cn_fds = socket_collect_fds(sock, &cfds);
+                        if (cn_fds < 0)
+                                return cn_fds;
 
-                        free(cfds);
+                        if (cn_fds <= 0)
+                                continue;
 
+                        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));
@@ -1014,15 +1051,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(
@@ -1036,23 +1090,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);
@@ -1072,16 +1128,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) {
@@ -1119,7 +1170,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);
 
@@ -1167,7 +1218,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;
 
@@ -1185,6 +1236,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;
@@ -1194,8 +1246,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,
@@ -1772,6 +1828,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) {
@@ -1995,6 +2052,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);
@@ -2013,12 +2071,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
@@ -2026,34 +2081,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) {
@@ -2070,8 +2125,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;
 }
@@ -2183,12 +2237,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)
@@ -2230,6 +2296,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);
 
@@ -2737,6 +2830,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;
@@ -2940,8 +3035,17 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds)
         if (strv_find(tags, "WATCHDOG=1"))
                 service_reset_watchdog(s);
 
-        if (strv_find(tags, "FDSTORE=1"))
-                service_add_fd_store_set(s, fds);
+        if (strv_find(tags, "FDSTORE=1")) {
+                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 */
         if (notify_dbus)
@@ -3084,27 +3188,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",