mirror of
https://github.com/systemd/systemd.git
synced 2025-01-11 09:18:07 +03:00
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.
This commit is contained in:
parent
e9fc29f4ec
commit
811ba7a0e2
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -125,6 +125,8 @@ struct Socket {
|
||||
|
||||
SocketResult result;
|
||||
|
||||
char **symlinks;
|
||||
|
||||
bool accept;
|
||||
bool remove_on_stop;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user