socket: add new Symlinks= option for socket units
authorLennart Poettering <lennart@poettering.net>
Wed, 4 Jun 2014 14:19:00 +0000 (16:19 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 4 Jun 2014 14:21:17 +0000 (16:21 +0200)
With Symlinks= we can manage one or more symlinks to AF_UNIX or FIFO
nodes in the file system, with the same lifecycle as the socket itself.

This has two benefits: first, this allows us to remove /dev/log and
/dev/initctl from /dev, thus leaving only symlinks, device nodes and
directories in the /dev tree. More importantly however, this allows us
to move /dev/log out of /dev, while still making it accessible there, so
that PrivateDevices= can provide /dev/log too.

man/systemd.socket.xml
src/core/dbus-socket.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/socket.c
src/core/socket.h

index d214940..f65704d 100644 (file)
                                 removed when it is stopped. This
                                 applies to AF_UNIX sockets in the file
                                 system, POSIX message queues as well
-                                as FIFOs. Normally it should not be
-                                necessary to use this option, and is
-                                not recommended as services might
-                                continue to run after the socket unit
-                                has been terminated and it should
-                                still be possible to communicate with
-                                them via their file system
-                                node. Defaults to
+                                as FIFOs, as well as any symlinks to
+                                them configured with
+                                <varname>Symlinks=</varname>. Normally
+                                it should not be necessary to use this
+                                option, and is not recommended as
+                                services might continue to run after
+                                the socket unit has been terminated
+                                and it should still be possible to
+                                communicate with them via their file
+                                system node. Defaults to
                                 off.</para></listitem>
                         </varlistentry>
 
+                        <varlistentry>
+                                <term><varname>Symlinks=</varname></term>
+                                <listitem><para>Takes a list of file
+                                system paths. The specified paths will
+                                be created as symlinks to the AF_UNIX
+                                socket path or FIFO path of this
+                                socket unit.  If this setting is used
+                                only one AF_UNIX socket in the file
+                                system or one FIFO may be configured
+                                for the socket unit. Use this option
+                                to manage one or more symlinked alias
+                                names for a socket, binding their
+                                lifecycle together. Defaults to the
+                                empty list.</para></listitem>
+                        </varlistentry>
+
                 </variablelist>
 
                 <para>Check
index 7eeccf6..f0c4568 100644 (file)
@@ -108,6 +108,7 @@ const sd_bus_vtable bus_socket_vtable[] = {
         SD_BUS_PROPERTY("PassSecurity", "b", bus_property_get_bool, offsetof(Socket, pass_sec), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("RemoveOnStop", "b", bus_property_get_bool, offsetof(Socket, remove_on_stop), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Listen", "a(ss)", property_get_listen, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("Symlinks", "as", NULL, offsetof(Socket, symlinks), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Mark", "i", bus_property_get_int, offsetof(Socket, mark), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("MaxConnections", "u", bus_property_get_unsigned, offsetof(Socket, max_connections), SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST),
index 48e2aae..95f59f5 100644 (file)
@@ -243,6 +243,7 @@ Socket.ReusePort,                config_parse_bool,                  0,
 Socket.MessageQueueMaxMessages,  config_parse_long,                  0,                             offsetof(Socket, mq_maxmsg)
 Socket.MessageQueueMessageSize,  config_parse_long,                  0,                             offsetof(Socket, mq_msgsize)
 Socket.RemoveOnStop,             config_parse_bool,                  0,                             offsetof(Socket, remove_on_stop)
+Socket.Symlinks,                 config_parse_unit_path_strv_printf, 0,                             offsetof(Socket, symlinks)
 Socket.Service,                  config_parse_socket_service,        0,                             0
 m4_ifdef(`HAVE_SMACK',
 `Socket.SmackLabel,              config_parse_string,                0,                             offsetof(Socket, smack)
index 9df7808..64d4c2f 100644 (file)
@@ -197,8 +197,8 @@ int config_parse_unit_path_printf(const char *unit,
                                   void *data,
                                   void *userdata) {
 
-        Unit *u = userdata;
         _cleanup_free_ char *k = NULL;
+        Unit *u = userdata;
         int r;
 
         assert(filename);
@@ -207,12 +207,69 @@ int config_parse_unit_path_printf(const char *unit,
         assert(u);
 
         r = unit_full_printf(u, rvalue, &k);
-        if (r < 0)
-                log_syntax(unit, LOG_ERR, filename, line, -r,
-                           "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+        if (r < 0) {
+                log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", rvalue, strerror(-r));
+                return 0;
+        }
 
-        return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype,
-                                 k ? k : rvalue, data, userdata);
+        return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
+}
+
+int config_parse_unit_path_strv_printf(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char *w, *state, ***x = data;
+        Unit *u = userdata;
+        size_t l;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+        assert(u);
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                _cleanup_free_ char *k = NULL;
+                char t[l+1];
+
+                memcpy(t, w, l);
+                t[l] = 0;
+
+                r = unit_full_printf(u, t, &k);
+                if (r < 0) {
+                        log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve unit specifiers on %s, ignoring: %s", t, strerror(-r));
+                        return 0;
+                }
+
+                if (!utf8_is_valid(k)) {
+                        log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
+                        return 0;
+                }
+
+                if (!path_is_absolute(k)) {
+                        log_syntax(unit, LOG_ERR, filename, line, -r, "Symlink path %s is not absolute, ignoring: %s", k, strerror(-r));
+                        return 0;
+                }
+
+                path_kill_slashes(k);
+
+                r = strv_push(x, k);
+                if (r < 0)
+                        return log_oom();
+
+                k = NULL;
+        }
+
+        return 0;
 }
 
 int config_parse_socket_listen(const char *unit,
index 279efa9..9e4494c 100644 (file)
@@ -34,6 +34,7 @@ int config_parse_unit_deps(const char *unit, const char *filename, unsigned line
 int config_parse_unit_string_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_unit_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_unit_path_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_path_strv_printf(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_documentation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_socket_listen(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
 int config_parse_socket_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
index 624e287..c158aaf 100644 (file)
@@ -145,6 +145,8 @@ static void socket_done(Unit *u) {
         free(s->smack_ip_in);
         free(s->smack_ip_out);
 
+        strv_free(s->symlinks);
+
         s->timer_event_source = sd_event_source_unref(s->timer_event_source);
 }
 
@@ -355,6 +357,39 @@ static int socket_add_extras(Socket *s) {
         return 0;
 }
 
+static const char *socket_find_symlink_target(Socket *s) {
+        const char *found = NULL;
+        SocketPort *p;
+
+        LIST_FOREACH(port, p, s->ports) {
+                const char *f = NULL;
+
+                switch (p->type) {
+
+                case SOCKET_FIFO:
+                        f = p->path;
+                        break;
+
+                case SOCKET_SOCKET:
+                        if (p->address.sockaddr.un.sun_path[0] != 0)
+                                f = p->address.sockaddr.un.sun_path;
+                        break;
+
+                default:
+                        break;
+                }
+
+                if (f) {
+                        if (found)
+                                return NULL;
+
+                        found = f;
+                }
+        }
+
+        return found;
+}
+
 static int socket_verify(Socket *s) {
         assert(s);
 
@@ -387,6 +422,11 @@ static int socket_verify(Socket *s) {
                 return -EINVAL;
         }
 
+        if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) {
+                log_error_unit(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id);
+                return -EINVAL;
+        }
+
         return 0;
 }
 
@@ -692,6 +732,7 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) {
 
 static void socket_close_fds(Socket *s) {
         SocketPort *p;
+        char **i;
 
         assert(s);
 
@@ -732,6 +773,10 @@ static void socket_close_fds(Socket *s) {
                         }
                 }
         }
+
+        if (s->remove_on_stop)
+                STRV_FOREACH(i, s->symlinks)
+                        unlink(*i);
 }
 
 static void socket_apply_socket_options(Socket *s, int fd) {
@@ -915,7 +960,8 @@ static int special_address_create(
         assert(path);
         assert(_fd);
 
-        if ((fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW)) < 0) {
+        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW);
+        if (fd < 0) {
                 r = -errno;
                 goto fail;
         }
@@ -968,7 +1014,6 @@ static int mq_address_create(
 
         /* Include the original umask in our mask */
         umask(~mq_mode | old_mask);
-
         fd = mq_open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_CREAT, mq_mode, attr);
         umask(old_mask);
 
@@ -998,6 +1043,22 @@ fail:
         return r;
 }
 
+static int socket_symlink(Socket *s) {
+        const char *p;
+        char **i;
+
+        assert(s);
+
+        p = socket_find_symlink_target(s);
+        if (!p)
+                return 0;
+
+        STRV_FOREACH(i, s->symlinks)
+                symlink(p, *i);
+
+        return 0;
+}
+
 static int socket_open_fds(Socket *s) {
         SocketPort *p;
         int r;
@@ -1045,6 +1106,7 @@ static int socket_open_fds(Socket *s) {
 
                         p->fd = r;
                         socket_apply_socket_options(s, p->fd);
+                        socket_symlink(s);
 
                 } else  if (p->type == SOCKET_SPECIAL) {
 
@@ -1065,6 +1127,8 @@ static int socket_open_fds(Socket *s) {
                                 goto rollback;
 
                         socket_apply_fifo_options(s, p->fd);
+                        socket_symlink(s);
+
                 } else if (p->type == SOCKET_MQUEUE) {
 
                         r = mq_address_create(
index 42b1a1f..f6bc37d 100644 (file)
@@ -125,6 +125,8 @@ struct Socket {
 
         SocketResult result;
 
+        char **symlinks;
+
         bool accept;
         bool remove_on_stop;