diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-06-04 16:19:00 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-06-04 16:21:17 +0200 |
commit | 811ba7a0e292eda0f2f470613cc28a97bda7ee66 (patch) | |
tree | 881f55ee5157cfcf266c674bbe65ffc0930fb6dd | |
parent | e9fc29f4ecc9509ccc02eb8a014341e26c0d7831 (diff) |
socket: add new Symlinks= option for socket units
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.
-rw-r--r-- | man/systemd.socket.xml | 34 | ||||
-rw-r--r-- | src/core/dbus-socket.c | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 69 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/socket.c | 68 | ||||
-rw-r--r-- | src/core/socket.h | 2 |
7 files changed, 160 insertions, 16 deletions
diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index d2149409bc..f65704d67b 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -738,17 +738,35 @@ 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 diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 7eeccf6def..f0c4568a9b 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -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), diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 48e2aae72d..95f59f55d9 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 9df78082ae..64d4c2f639 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 279efa983c..9e4494cf98 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -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); diff --git a/src/core/socket.c b/src/core/socket.c index 624e28744f..c158aaf1de 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -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( diff --git a/src/core/socket.h b/src/core/socket.h index 42b1a1fe06..f6bc37df8d 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -125,6 +125,8 @@ struct Socket { SocketResult result; + char **symlinks; + bool accept; bool remove_on_stop; |