[systemd-devel] [PATCH 1/3] core: Add LISTEN_NAMES environment variable

Krzysztof Opasiak k.opasiak at samsung.com
Fri May 15 08:09:10 PDT 2015


When passing file descriptors to service systemd
pass also two environment variable:
- LISTEN_PID - PID of service
- LISTEN_FDS - Number of file descriptors passed to service

Passed fds may have different types: socket, fifo etc.
To distinguish them sd-daemon library provides a set of
sd_is_*() functions which does stat on given fd and path
and check if this fd is relaten with this path.

This commit adds third environment variable:
- LISTEN_NAMES - paths/addresses of passed fds

this variable consist of fds names separated by :.
Each fd name consist of two parts:
fd_type=fd_address

where:
fd_type is a type of fd (socket, mq, fifo etc)
fd_address is a suitable address or path.

Systemd may store fds passed by services so it may
be not feasible to determine what is their type.
Due to this we allow to pass empty field for some file
descriptor. This will mark fds as unknown and sd-library
will still do 2 stats to determine fd type.

Another use case for this convention is starting service
in chroot or namespace. In this case we cannot pass
any fs related path as we don't know what is inside
a container. We simply leave empty all fs-dependent fields.
---
 src/core/execute.c |   69 ++++++++++++++++++++++++++++++--
 src/core/execute.h |    4 +-
 src/core/service.c |  112 ++++++++++++++++++++++++++++++++++++++++++++--------
 src/core/socket.c  |  106 +++++++++++++++++++++++++++++++++++++++++++------
 src/core/socket.h  |    3 +-
 5 files changed, 260 insertions(+), 34 deletions(-)

diff --git a/src/core/execute.c b/src/core/execute.c
index 0cca481..39bfdc9 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1171,9 +1171,55 @@ static void do_idle_pipe_dance(int idle_pipe[4]) {
         safe_close(idle_pipe[3]);
 }
 
+static int build_listen_names(const char **fds_names, unsigned n_fds, char **env)
+{
+        unsigned i, j;
+        unsigned pos;
+        int size;
+        char *names = NULL;
+        static const char separator = ':';
+        static const char escape = '\\';
+        static const char *prefix = "LISTEN_NAMES=";
+
+        assert(fds_names);
+        assert(env);
+
+        size = strlen(prefix);
+        for (i = 0; i < n_fds; ++i) {
+                size += 1; /* for separator */
+                if (!fds_names[i])
+                        continue;
+
+                for (j = 0; fds_names[i][j]; ++j)
+                        if (fds_names[i][j] == separator)
+                                size += 2;
+                        else
+                                size += 1;
+        }
+
+        names = malloc(size);
+        if (!names)
+                return -ENOMEM;
+
+        strcpy(names, prefix);
+        pos = strlen(prefix);
+        for (i = 0; i < n_fds; ++i) {
+                for (j = 0; fds_names[i] && fds_names[i][j]; ++j) {
+                        if (fds_names[i][j] == separator)
+                                names[pos++] = escape;
+                        names[pos++] = fds_names[i][j];
+                }
+                names[pos++] = separator;
+        }
+        names[pos - 1] = '\0';
+        *env = names;
+        return 0;
+}
+
 static int build_environment(
                 const ExecContext *c,
                 unsigned n_fds,
+                const char **fds_names,
                 usec_t watchdog_usec,
                 const char *home,
                 const char *username,
@@ -1183,6 +1229,7 @@ static int build_environment(
         _cleanup_strv_free_ char **our_env = NULL;
         unsigned n_env = 0;
         char *x;
+        int r;
 
         assert(c);
         assert(ret);
@@ -1199,6 +1246,13 @@ static int build_environment(
                 if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
                         return -ENOMEM;
                 our_env[n_env++] = x;
+
+                if (fds_names) {
+                        r = build_listen_names(fds_names, n_fds, &x);
+                        if (r)
+                                return r;
+                        our_env[n_env++] = x;
+                }
         }
 
         if (watchdog_usec > 0) {
@@ -1295,7 +1349,9 @@ static int exec_child(
                 ExecRuntime *runtime,
                 char **argv,
                 int socket_fd,
-                int *fds, unsigned n_fds,
+                int *fds,
+                const char **fds_names,
+                unsigned n_fds,
                 char **files_env,
                 int *exit_status) {
 
@@ -1787,7 +1843,7 @@ static int exec_child(
 #endif
         }
 
-        r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
+        r = build_environment(context, n_fds, fds_names, params->watchdog_usec, home, username, shell, &our_env);
         if (r < 0) {
                 *exit_status = EXIT_MEMORY;
                 return r;
@@ -1841,7 +1897,9 @@ int exec_spawn(Unit *unit,
                pid_t *ret) {
 
         _cleanup_strv_free_ char **files_env = NULL;
-        int *fds = NULL; unsigned n_fds = 0;
+        int *fds = NULL;
+        const char **fds_names = NULL;
+        unsigned n_fds = 0;
         _cleanup_free_ char *line = NULL;
         int socket_fd, r;
         char **argv;
@@ -1867,6 +1925,7 @@ int exec_spawn(Unit *unit,
         } else {
                 socket_fd = -1;
                 fds = params->fds;
+                fds_names = (const char **) params->fds_names;
                 n_fds = params->n_fds;
         }
 
@@ -1898,7 +1957,9 @@ int exec_spawn(Unit *unit,
                                runtime,
                                argv,
                                socket_fd,
-                               fds, n_fds,
+                               fds,
+                               fds_names,
+                               n_fds,
                                files_env,
                                &exit_status);
                 if (r < 0) {
diff --git a/src/core/execute.h b/src/core/execute.h
index f5d5c1d..8239142 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -198,7 +198,9 @@ struct ExecContext {
 
 struct ExecParameters {
         char **argv;
-        int *fds; unsigned n_fds;
+        int *fds;
+        char **fds_names;
+        unsigned n_fds;
         char **environment;
         bool apply_permissions;
         bool apply_chroot;
diff --git a/src/core/service.c b/src/core/service.c
index 07347b9..584b883 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -959,15 +959,19 @@ 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, bool skip_fs_paths,
+                               int **fds, char ***fds_names, unsigned *n_fds) {
         _cleanup_free_ int *rfds = NULL;
+        _cleanup_free_ char **rfds_names = NULL;
         unsigned rn_fds = 0;
         Iterator i;
+        unsigned j;
         int r;
         Unit *u;
 
         assert(s);
         assert(fds);
+        assert(fds_names);
         assert(n_fds);
 
         if (s->socket_fd >= 0)
@@ -975,6 +979,7 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
 
         SET_FOREACH(u, UNIT(s)->dependencies[UNIT_TRIGGERED_BY], i) {
                 int *cfds;
+                char **cfds_names;
                 unsigned cn_fds;
                 Socket *sock;
 
@@ -983,54 +988,98 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) {
 
                 sock = SOCKET(u);
 
-                r = socket_collect_fds(sock, &cfds, &cn_fds);
+                r = socket_collect_fds(sock, skip_fs_paths,
+                                       &cfds, &cfds_names, &cn_fds);
                 if (r < 0)
                         return r;
 
                 if (cn_fds <= 0) {
                         free(cfds);
+                        free(cfds_names);
                         continue;
                 }
 
                 if (!rfds) {
                         rfds = cfds;
+                        rfds_names = cfds_names;
                         rn_fds = cn_fds;
                 } else {
-                        int *t;
+                        int *tfds;
+                        char **tnames;
 
-                        t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int));
-                        if (!t) {
-                                free(cfds);
-                                return -ENOMEM;
+                        tfds = realloc(rfds, (rn_fds + cn_fds) * sizeof(*tfds));
+                        if (!tfds) {
+                                r = -ENOMEM;
+                                goto free_cvars;
+                        }
+
+                        memcpy(tfds + rn_fds, cfds, cn_fds * sizeof(*tfds));
+                        rfds = tfds;
+
+                        tnames = realloc(rfds_names,
+                                         (rn_fds + cn_fds) * sizeof(*tnames));
+                        if (!tnames) {
+                                r = -ENOMEM;
+                                goto free_cvars;
                         }
 
-                        memcpy(t + rn_fds, cfds, cn_fds * sizeof(int));
-                        rfds = t;
+                        memcpy(tnames + rn_fds, cfds_names, cn_fds * sizeof(*tnames));
+                        rfds_names = tnames;
                         rn_fds += cn_fds;
+                        r = 0;
 
+                free_cvars:
                         free(cfds);
 
+                        if (r)
+                                for (j = 0; j < cn_fds; ++j)
+                                        free(cfds_names[j]);
+                        free(cfds_names);
+
+                        if (r)
+                                goto cleanup_rvars;
                 }
         }
 
         if (s->n_fd_store > 0) {
                 ServiceFDStore *fs;
                 int *t;
+                char **tnames;
 
-                t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int));
-                if (!t)
-                        return -ENOMEM;
-
+                t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(*rfds));
+                if (!t) {
+                        r = -ENOMEM;
+                        goto cleanup_rvars;
+                }
                 rfds = t;
-                LIST_FOREACH(fd_store, fs, s->fd_store)
-                        rfds[rn_fds++] = fs->fd;
+
+                tnames = realloc(rfds_names,
+                            (rn_fds + s->n_fd_store) * sizeof(*rfds_names));
+                if (!tnames) {
+                        r = -ENOMEM;
+                        goto cleanup_rvars;
+                }
+                rfds_names = tnames;
+
+                LIST_FOREACH(fd_store, fs, s->fd_store) {
+                        rfds[rn_fds] = fs->fd;
+                        rfds_names[rn_fds] = NULL;
+                        ++rn_fds;
+                }
         }
 
         *fds = rfds;
+        *fds_names = rfds_names;
         *n_fds = rn_fds;
 
         rfds = NULL;
+        rfds_names = NULL;
         return 0;
+
+cleanup_rvars:
+        for (j = 0; j < rn_fds; ++j)
+                free(rfds_names[j]);
+        return r;
 }
 
 static int service_spawn(
@@ -1046,7 +1095,9 @@ static int service_spawn(
 
         pid_t pid;
         int r;
+        unsigned i;
         int *fds = NULL;
+        char **fds_names = NULL;
         _cleanup_free_ int *fdsbuf = NULL;
         unsigned n_fds = 0, n_env = 0;
         _cleanup_free_ char *bus_endpoint_path = NULL;
@@ -1083,8 +1134,31 @@ static int service_spawn(
                 if (s->socket_fd >= 0) {
                         fds = &s->socket_fd;
                         n_fds = 1;
+                        fds_names = new(char *, 1);
+                        memset(fds_names, 0, sizeof(char *));
                 } else {
-                        r = service_collect_fds(s, &fdsbuf, &n_fds);
+                        /* If we should setup namespace or chroot, we
+                         * should not add any fs paths to LISTEN_NAMES
+                         * as those paths may point to other files in ns.
+                         * Instead of this we simply store NULL in that case
+                         * and stat() in child to determine if path and fd
+                         * points to the same file */
+
+                        bool skip_fs_paths = !strv_isempty(s->exec_context.read_write_dirs) ||
+                                !strv_isempty(s->exec_context.read_only_dirs) ||
+                                !strv_isempty(s->exec_context.inaccessible_dirs) ||
+                                s->exec_context.mount_flags != 0 ||
+                                (s->exec_context.private_tmp &&
+                                 s->exec_runtime &&
+                                 (s->exec_runtime->tmp_dir || s->exec_runtime->var_tmp_dir)) ||
+                                s->exec_context.bus_endpoint ||
+                                s->exec_context.private_devices ||
+                                s->exec_context.protect_system != PROTECT_SYSTEM_NO ||
+                                s->exec_context.protect_home != PROTECT_HOME_NO ||
+                                (exec_params.apply_chroot && s->exec_context.root_directory);
+
+                        r = service_collect_fds(s, skip_fs_paths,
+                                                &fdsbuf, &fds_names, &n_fds);
                         if (r < 0)
                                 goto fail;
 
@@ -1195,6 +1269,7 @@ static int service_spawn(
 
         exec_params.argv = argv;
         exec_params.fds = fds;
+        exec_params.fds_names = fds_names;
         exec_params.n_fds = n_fds;
         exec_params.environment = final_env;
         exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
@@ -1213,6 +1288,11 @@ static int service_spawn(
                        &exec_params,
                        s->exec_runtime,
                        &pid);
+
+        for (i = 0; i < n_fds; ++i)
+                free(fds_names[i]);
+        free(fds_names);
+
         if (r < 0)
                 goto fail;
 
diff --git a/src/core/socket.c b/src/core/socket.c
index 17b8a50..30c9974 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -2498,9 +2498,67 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use
         return 0;
 }
 
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
+static int socket_export_path(SocketPort *p, bool skip_fs_paths, char **path)
+{
+        int ret = 0;
+        bool should_free = false;
+        bool has_fs_path = true;
+        const char *type_str = NULL;
+        char *address = NULL;
+
+        switch (p->type) {
+        case SOCKET_SOCKET:
+                type_str = socket_address_family(&p->address) == AF_NETLINK ?
+                        "netlink" : "socket";
+                has_fs_path = socket_address_family(&p->address) == AF_UNIX;
+                ret = socket_address_print(&p->address, &address);
+                if (ret < 0)
+                        return ret;
+                should_free = true;
+                break;
+
+        case SOCKET_SPECIAL:
+                type_str = "special";
+                address = p->path;
+                break;
+
+        case SOCKET_MQUEUE:
+                type_str = "mqueue";
+                address = p->path;
+                break;
+
+        case SOCKET_FIFO:
+                type_str = "fifo";
+                address = p->path;
+                break;
+
+        default:
+                assert_not_reached("Unreachable socket type");
+        }
+
+        if (!has_fs_path || !skip_fs_paths) {
+                ret = asprintf(path, "%s=%s", type_str, address);
+                if (ret < 0)
+                        ret = -ENOMEM;
+                else
+                        ret = 0;
+        } else {
+                *path = NULL;
+        }
+
+        if (should_free)
+                free(address);
+
+        return ret;
+}
+
+int socket_collect_fds(Socket *s, bool skip_fs_paths,
+                       int **fds, char ***fds_names, unsigned *n_fds) {
         int *rfds;
-        unsigned rn_fds, k;
+        char **rfds_names;
+        unsigned rn_fds;
+        int k = 0;
+        int ret = 0;
         SocketPort *p;
 
         assert(s);
@@ -2514,27 +2572,51 @@ int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds) {
                 if (p->fd >= 0)
                         rn_fds++;
 
-        if (rn_fds <= 0) {
-                *fds = NULL;
-                *n_fds = 0;
-                return 0;
-        }
+        if (rn_fds <= 0)
+                goto out;
 
+        ret = -ENOMEM;
         rfds = new(int, rn_fds);
         if (!rfds)
-                return -ENOMEM;
+                goto out;
+
+        rfds_names = new(char *, rn_fds);
+        if (!rfds_names)
+                goto err_free_rfds;
 
         k = 0;
-        LIST_FOREACH(port, p, s->ports)
-                if (p->fd >= 0)
-                        rfds[k++] = p->fd;
+        LIST_FOREACH(port, p, s->ports) {
+                if (p->fd < 0)
+                        continue;
+
+                rfds[k] = p->fd;
+                ret = socket_export_path(p, skip_fs_paths, rfds_names + k);
+                if (ret)
+                        goto err_free_rfds_names;
+                log_info("socket_port_path: %s\n", rfds_names[k]);
+                ++k;
+        }
 
-        assert(k == rn_fds);
+        assert((unsigned)k == rn_fds);
 
         *fds = rfds;
+        *fds_names = rfds_names;
         *n_fds = rn_fds;
 
         return 0;
+
+err_free_rfds_names:
+        while (--k >= 0)
+                free(rfds_names[k]);
+        free(rfds_names);
+err_free_rfds:
+        free(rfds);
+out:
+        *fds = NULL;
+        *fds_names = NULL;
+        *n_fds = 0;
+
+        return ret;
 }
 
 static void socket_reset_failed(Unit *u) {
diff --git a/src/core/socket.h b/src/core/socket.h
index fa3ebda..6936e5a 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -171,7 +171,8 @@ struct Socket {
 };
 
 /* Called from the service code when collecting fds */
-int socket_collect_fds(Socket *s, int **fds, unsigned *n_fds);
+int socket_collect_fds(Socket *s, bool skip_fs_paths,
+                       int **fds, char ***fds_names, unsigned *n_fds);
 
 /* Called from the service code when a per-connection service ended */
 void socket_connection_unref(Socket *s);
-- 
1.7.9.5



More information about the systemd-devel mailing list