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
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),
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)
void *data,
void *userdata) {
- Unit *u = userdata;
_cleanup_free_ char *k = NULL;
+ Unit *u = userdata;
int r;
assert(filename);
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,
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);
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);
}
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);
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;
}
static void socket_close_fds(Socket *s) {
SocketPort *p;
+ char **i;
assert(s);
}
}
}
+
+ if (s->remove_on_stop)
+ STRV_FOREACH(i, s->symlinks)
+ unlink(*i);
}
static void socket_apply_socket_options(Socket *s, int fd) {
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;
}
/* 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);
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;
p->fd = r;
socket_apply_socket_options(s, p->fd);
+ socket_symlink(s);
} else if (p->type == SOCKET_SPECIAL) {
goto rollback;
socket_apply_fifo_options(s, p->fd);
+ socket_symlink(s);
+
} else if (p->type == SOCKET_MQUEUE) {
r = mq_address_create(
SocketResult result;
+ char **symlinks;
+
bool accept;
bool remove_on_stop;