summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2014-06-04 16:19:00 +0200
committerLennart Poettering <lennart@poettering.net>2014-06-04 16:21:17 +0200
commit811ba7a0e292eda0f2f470613cc28a97bda7ee66 (patch)
tree881f55ee5157cfcf266c674bbe65ffc0930fb6dd
parente9fc29f4ecc9509ccc02eb8a014341e26c0d7831 (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.xml34
-rw-r--r--src/core/dbus-socket.c1
-rw-r--r--src/core/load-fragment-gperf.gperf.m41
-rw-r--r--src/core/load-fragment.c69
-rw-r--r--src/core/load-fragment.h1
-rw-r--r--src/core/socket.c68
-rw-r--r--src/core/socket.h2
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;