mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
Merge pull request #34556 from ryantimwilson/extra-fds
Add ExtraFileDescriptor property to StartTransientUnit dbus API
This commit is contained in:
commit
ac42b151f8
@ -2790,6 +2790,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly a(sst) OpenFile = [...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as ExtraFileDescriptorNames = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly i ReloadSignal = ...;
|
||||
readonly t ExecMainStartTimestamp = ...;
|
||||
readonly t ExecMainStartTimestampMonotonic = ...;
|
||||
@ -4098,6 +4100,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="OpenFile"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ExtraFileDescriptorNames"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ReloadSignal"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
|
||||
@ -4843,6 +4847,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
<varname>StateDirectory</varname>, <varname>CacheDirectory</varname> and <varname>LogsDirectory</varname>,
|
||||
which will create a symlink of the given name to the respective directory. The messages take an unused
|
||||
<varname>flags</varname> parameter, reserved for future backward-compatible changes.</para>
|
||||
|
||||
<para><varname>ExtraFileDescriptorNames</varname> contains file descriptor names passed to the service via
|
||||
the <varname>ExtraFileDescriptors</varname> property in the <function>StartTransientUnit()</function>
|
||||
method. See <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
for more details on how to retrieve these file descriptors. Unlike the <varname>ExtraFileDescriptors</varname>
|
||||
input property, <varname>ExtraFileDescriptorNames</varname> only contains names and not the file descriptors.</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@ -12209,6 +12219,7 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>LiveMountResult</varname>,
|
||||
<varname>PrivateTmpEx</varname>,
|
||||
<varname>ImportCredentialEx</varname>,
|
||||
<varname>ExtraFileDescriptorNames</varname>,
|
||||
<varname>BindLogSockets</varname>, and
|
||||
<varname>PrivateUsersEx</varname> were added in version 257.</para>
|
||||
</refsect2>
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "capsule-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fdset.h"
|
||||
#include "fileio.h"
|
||||
#include "format-table.h"
|
||||
#include "glyph-util.h"
|
||||
@ -1477,12 +1478,13 @@ static int status(int argc, char **argv, void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) {
|
||||
static int message_append_cmdline(sd_bus_message *m, const char *signature, FDSet **passed_fdset, char ***x) {
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(signature);
|
||||
assert(passed_fdset);
|
||||
assert(x);
|
||||
|
||||
p = *x;
|
||||
@ -1631,7 +1633,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
|
||||
return bus_log_create_error(r);
|
||||
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
r = message_append_cmdline(m, s, &p);
|
||||
r = message_append_cmdline(m, s, passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1648,7 +1650,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = message_append_cmdline(m, v, &p);
|
||||
r = message_append_cmdline(m, v, passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1680,7 +1682,7 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = message_append_cmdline(m, s, &p);
|
||||
r = message_append_cmdline(m, s, passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1691,9 +1693,25 @@ static int message_append_cmdline(sd_bus_message *m, const char *signature, char
|
||||
break;
|
||||
}
|
||||
|
||||
case SD_BUS_TYPE_UNIX_FD:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"UNIX file descriptor not supported as type.");
|
||||
case SD_BUS_TYPE_UNIX_FD: {
|
||||
int fd;
|
||||
|
||||
fd = parse_fd(v);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to parse '%s' as a file descriptor: %m", v);
|
||||
|
||||
if (!*passed_fdset) {
|
||||
r = fdset_new_fill(/* filter_cloexec= */ 0, passed_fdset);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create fd set: %m");
|
||||
}
|
||||
|
||||
if (!fdset_contains(*passed_fdset, fd))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EBADF), "Failed to find file descriptor '%s' among passed file descriptors.", v);
|
||||
|
||||
r = sd_bus_message_append_basic(m, t, &fd);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
@ -2049,6 +2067,7 @@ static int call(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
||||
_cleanup_fdset_free_ FDSet *passed_fdset = NULL;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(false, &bus);
|
||||
@ -2085,7 +2104,7 @@ static int call(int argc, char **argv, void *userdata) {
|
||||
|
||||
p = argv+6;
|
||||
|
||||
r = message_append_cmdline(m, argv[5], &p);
|
||||
r = message_append_cmdline(m, argv[5], &passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2151,6 +2170,7 @@ static int call(int argc, char **argv, void *userdata) {
|
||||
static int emit_signal(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
_cleanup_fdset_free_ FDSet *passed_fdset = NULL;
|
||||
int r;
|
||||
|
||||
r = acquire_bus(false, &bus);
|
||||
@ -2176,7 +2196,7 @@ static int emit_signal(int argc, char **argv, void *userdata) {
|
||||
|
||||
p = argv+5;
|
||||
|
||||
r = message_append_cmdline(m, argv[4], &p);
|
||||
r = message_append_cmdline(m, argv[4], &passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2265,6 +2285,7 @@ static int set_property(int argc, char **argv, void *userdata) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
_cleanup_fdset_free_ FDSet *passed_fdset = NULL;
|
||||
char **p;
|
||||
int r;
|
||||
|
||||
@ -2286,7 +2307,7 @@ static int set_property(int argc, char **argv, void *userdata) {
|
||||
return bus_log_create_error(r);
|
||||
|
||||
p = argv + 6;
|
||||
r = message_append_cmdline(m, argv[5], &p);
|
||||
r = message_append_cmdline(m, argv[5], &passed_fdset, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -69,6 +69,34 @@ static int property_get_open_files(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_extra_file_descriptors(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *error) {
|
||||
|
||||
ServiceExtraFD **extra_fds = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = sd_bus_message_open_container(reply, 'a', "s");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
LIST_FOREACH(extra_fd, efd, *extra_fds) {
|
||||
r = sd_bus_message_append_basic(reply, 's', efd->fdname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_exit_status_set(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -339,6 +367,7 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("OOMPolicy", "s", bus_property_get_oom_policy, offsetof(Service, oom_policy), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("OpenFile", "a(sst)", property_get_open_files, offsetof(Service, open_files), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ExtraFileDescriptorNames", "as", property_get_extra_file_descriptors, offsetof(Service, extra_fds), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadSignal", "i", bus_property_get_int, offsetof(Service, reload_signal), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
@ -718,6 +747,58 @@ static int bus_service_set_transient_property(
|
||||
if (streq(name, "ReloadSignal"))
|
||||
return bus_set_transient_reload_signal(u, name, &s->reload_signal, message, flags, error);
|
||||
|
||||
if (streq(name, "ExtraFileDescriptors")) {
|
||||
int fd;
|
||||
const char *fdname;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(hs)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (;;) {
|
||||
_cleanup_(service_extra_fd_freep) ServiceExtraFD *efd = NULL;
|
||||
|
||||
r = sd_bus_message_read(message, "(hs)", &fd, &fdname);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
/* Disallow empty string for ExtraFileDescriptors.
|
||||
* Unlike OpenFile, StandardInput and friends, there isn't a good sane
|
||||
* default for an arbitrary FD. */
|
||||
if (fd < 0 || isempty(fdname) || !fdname_is_valid(fdname))
|
||||
return -EINVAL;
|
||||
|
||||
if (UNIT_WRITE_FLAGS_NOOP(flags))
|
||||
continue;
|
||||
|
||||
efd = new(ServiceExtraFD, 1);
|
||||
if (!efd)
|
||||
return -ENOMEM;
|
||||
|
||||
*efd = (ServiceExtraFD) {
|
||||
.fd = -EBADF,
|
||||
.fdname = strdup(fdname),
|
||||
};
|
||||
|
||||
if (!efd->fdname)
|
||||
return -ENOMEM;
|
||||
|
||||
efd->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||
if (efd->fd < 0)
|
||||
return -errno;
|
||||
|
||||
LIST_APPEND(extra_fd, s->extra_fds, TAKE_PTR(efd));
|
||||
}
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3912,7 +3912,7 @@ static int exec_context_named_iofds(
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
stdio_fdname[i] = exec_context_fdname(c, i);
|
||||
|
||||
n_fds = p->n_storage_fds + p->n_socket_fds;
|
||||
n_fds = p->n_storage_fds + p->n_socket_fds + p->n_extra_fds;
|
||||
|
||||
for (size_t i = 0; i < n_fds && targets > 0; i++)
|
||||
if (named_iofds[STDIN_FILENO] < 0 &&
|
||||
@ -4096,7 +4096,7 @@ int exec_invoke(
|
||||
int ngids_after_pam = 0;
|
||||
|
||||
int socket_fd = -EBADF, named_iofds[3] = EBADF_TRIPLET;
|
||||
size_t n_storage_fds, n_socket_fds;
|
||||
size_t n_storage_fds, n_socket_fds, n_extra_fds;
|
||||
|
||||
assert(command);
|
||||
assert(context);
|
||||
@ -4133,12 +4133,13 @@ int exec_invoke(
|
||||
return log_exec_error_errno(context, params, SYNTHETIC_ERRNO(EINVAL), "Got no socket.");
|
||||
|
||||
socket_fd = params->fds[0];
|
||||
n_storage_fds = n_socket_fds = 0;
|
||||
n_storage_fds = n_socket_fds = n_extra_fds = 0;
|
||||
} else {
|
||||
n_socket_fds = params->n_socket_fds;
|
||||
n_storage_fds = params->n_storage_fds;
|
||||
n_extra_fds = params->n_extra_fds;
|
||||
}
|
||||
n_fds = n_socket_fds + n_storage_fds;
|
||||
n_fds = n_socket_fds + n_storage_fds + n_extra_fds;
|
||||
|
||||
r = exec_context_named_iofds(context, params, named_iofds);
|
||||
if (r < 0)
|
||||
|
@ -1282,7 +1282,13 @@ static int exec_parameters_serialize(const ExecParameters *p, const ExecContext
|
||||
return r;
|
||||
}
|
||||
|
||||
r = serialize_fd_many(f, fds, "exec-parameters-fds", p->fds, p->n_socket_fds + p->n_storage_fds);
|
||||
if (p->n_extra_fds > 0) {
|
||||
r = serialize_item_format(f, "exec-parameters-n-extra-fds", "%zu", p->n_extra_fds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = serialize_fd_many(f, fds, "exec-parameters-fds", p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1478,27 +1484,37 @@ static int exec_parameters_deserialize(ExecParameters *p, FILE *f, FDSet *fds) {
|
||||
|
||||
if (p->n_storage_fds > (size_t) nr_open)
|
||||
return -EINVAL; /* too many, someone is playing games with us */
|
||||
} else if ((val = startswith(l, "exec-parameters-n-extra-fds="))) {
|
||||
if (p->fds)
|
||||
return -EINVAL; /* Already received */
|
||||
|
||||
r = safe_atozu(val, &p->n_extra_fds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (p->n_extra_fds > (size_t) nr_open)
|
||||
return -EINVAL; /* too many, someone is playing games with us */
|
||||
} else if ((val = startswith(l, "exec-parameters-fds="))) {
|
||||
if (p->n_socket_fds + p->n_storage_fds == 0)
|
||||
if (p->n_socket_fds + p->n_storage_fds + p->n_extra_fds == 0)
|
||||
return log_warning_errno(
|
||||
SYNTHETIC_ERRNO(EINVAL),
|
||||
"Got exec-parameters-fds= without "
|
||||
"prior exec-parameters-n-socket-fds= or exec-parameters-n-storage-fds=");
|
||||
if (p->n_socket_fds + p->n_storage_fds > (size_t) nr_open)
|
||||
"prior exec-parameters-n-socket-fds= or exec-parameters-n-storage-fds= or exec-parameters-n-extra-fds=");
|
||||
if (p->n_socket_fds + p->n_storage_fds + p->n_extra_fds > (size_t) nr_open)
|
||||
return -EINVAL; /* too many, someone is playing games with us */
|
||||
|
||||
if (p->fds)
|
||||
return -EINVAL; /* duplicated */
|
||||
|
||||
p->fds = new(int, p->n_socket_fds + p->n_storage_fds);
|
||||
p->fds = new(int, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
|
||||
if (!p->fds)
|
||||
return log_oom_debug();
|
||||
|
||||
/* Ensure we don't leave any FD uninitialized on error, it makes the fuzzer sad */
|
||||
FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_storage_fds)
|
||||
FOREACH_ARRAY(i, p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds)
|
||||
*i = -EBADF;
|
||||
|
||||
r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_storage_fds, p->fds);
|
||||
r = deserialize_fd_many(fds, val, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds, p->fds);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
|
@ -391,7 +391,7 @@ int exec_spawn(
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(!params->fds || FLAGS_SET(params->flags, EXEC_PASS_FDS));
|
||||
assert(params->fds || (params->n_socket_fds + params->n_storage_fds == 0));
|
||||
assert(params->fds || (params->n_socket_fds + params->n_storage_fds + params->n_extra_fds == 0));
|
||||
assert(!params->files_env); /* We fill this field, ensure it comes NULL-initialized to us */
|
||||
assert(ret);
|
||||
|
||||
@ -2632,7 +2632,7 @@ void exec_params_deep_clear(ExecParameters *p) {
|
||||
* to be fully cleaned up to make sanitizers and analyzers happy, as opposed as the shallow clean
|
||||
* function above. */
|
||||
|
||||
close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds);
|
||||
close_many_unset(p->fds, p->n_socket_fds + p->n_storage_fds + p->n_extra_fds);
|
||||
|
||||
p->cgroup_path = mfree(p->cgroup_path);
|
||||
|
||||
|
@ -421,6 +421,7 @@ struct ExecParameters {
|
||||
char **fd_names;
|
||||
size_t n_socket_fds;
|
||||
size_t n_storage_fds;
|
||||
size_t n_extra_fds;
|
||||
|
||||
ExecFlags flags;
|
||||
bool selinux_context_net:1;
|
||||
|
@ -58,8 +58,8 @@ static void exec_fuzz_one(FILE *f, FDSet *fdset) {
|
||||
params.user_lookup_fd = -EBADF;
|
||||
params.bpf_restrict_fs_map_fd = -EBADF;
|
||||
if (!params.fds)
|
||||
params.n_socket_fds = params.n_storage_fds = 0;
|
||||
for (size_t i = 0; params.fds && i < params.n_socket_fds + params.n_storage_fds; i++)
|
||||
params.n_socket_fds = params.n_storage_fds = params.n_extra_fds = 0;
|
||||
for (size_t i = 0; params.fds && i < params.n_socket_fds + params.n_storage_fds + params.n_extra_fds; i++)
|
||||
params.fds[i] = -EBADF;
|
||||
|
||||
exec_command_done_array(&command, /* n= */ 1);
|
||||
|
@ -454,6 +454,21 @@ static void service_release_fd_store(Service *s) {
|
||||
assert(s->n_fd_store == 0);
|
||||
}
|
||||
|
||||
ServiceExtraFD* service_extra_fd_free(ServiceExtraFD *efd) {
|
||||
if (!efd)
|
||||
return NULL;
|
||||
|
||||
efd->fd = asynchronous_close(efd->fd);
|
||||
free(efd->fdname);
|
||||
return mfree(efd);
|
||||
}
|
||||
|
||||
static void service_release_extra_fds(Service *s) {
|
||||
assert(s);
|
||||
|
||||
LIST_CLEAR(extra_fd, s->extra_fds, service_extra_fd_free);
|
||||
}
|
||||
|
||||
static void service_release_stdio_fd(Service *s) {
|
||||
assert(s);
|
||||
|
||||
@ -510,6 +525,7 @@ static void service_done(Unit *u) {
|
||||
service_release_socket_fd(s);
|
||||
service_release_stdio_fd(s);
|
||||
service_release_fd_store(s);
|
||||
service_release_extra_fds(s);
|
||||
|
||||
s->mount_request = sd_bus_message_unref(s->mount_request);
|
||||
}
|
||||
@ -903,40 +919,59 @@ static int service_load(Unit *u) {
|
||||
return service_verify(s);
|
||||
}
|
||||
|
||||
static int service_dump_fd(int fd, const char *fdname, const char *header, FILE *f, const char *prefix) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
struct stat st;
|
||||
int flags;
|
||||
|
||||
if (fstat(fd, &st) < 0)
|
||||
return log_debug_errno(errno, "Failed to stat fdstore entry: %m");
|
||||
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0)
|
||||
return log_debug_errno(errno, "Failed to get fdstore entry flags: %m");
|
||||
|
||||
(void) fd_get_path(fd, &path);
|
||||
|
||||
fprintf(f,
|
||||
"%s%s '%s' (type=%s; dev=" DEVNUM_FORMAT_STR "; inode=%" PRIu64 "; rdev=" DEVNUM_FORMAT_STR "; path=%s; access=%s)\n",
|
||||
prefix,
|
||||
header,
|
||||
fdname,
|
||||
strna(inode_type_to_string(st.st_mode)),
|
||||
DEVNUM_FORMAT_VAL(st.st_dev),
|
||||
(uint64_t) st.st_ino,
|
||||
DEVNUM_FORMAT_VAL(st.st_rdev),
|
||||
strna(path),
|
||||
strna(accmode_to_string(flags)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void service_dump_fdstore(Service *s, FILE *f, const char *prefix) {
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(prefix);
|
||||
|
||||
LIST_FOREACH(fd_store, i, s->fd_store) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
struct stat st;
|
||||
int flags;
|
||||
LIST_FOREACH(fd_store, i, s->fd_store)
|
||||
(void) service_dump_fd(i->fd,
|
||||
i->fdname,
|
||||
i == s->fd_store ? "File Descriptor Store Entry:" : " ",
|
||||
f,
|
||||
prefix);
|
||||
}
|
||||
|
||||
if (fstat(i->fd, &st) < 0) {
|
||||
log_debug_errno(errno, "Failed to stat fdstore entry: %m");
|
||||
continue;
|
||||
}
|
||||
static void service_dump_extra_fds(Service *s, FILE *f, const char *prefix) {
|
||||
assert(s);
|
||||
assert(f);
|
||||
assert(prefix);
|
||||
|
||||
flags = fcntl(i->fd, F_GETFL);
|
||||
if (flags < 0) {
|
||||
log_debug_errno(errno, "Failed to get fdstore entry flags: %m");
|
||||
continue;
|
||||
}
|
||||
|
||||
(void) fd_get_path(i->fd, &path);
|
||||
|
||||
fprintf(f,
|
||||
"%s%s '%s' (type=%s; dev=" DEVNUM_FORMAT_STR "; inode=%" PRIu64 "; rdev=" DEVNUM_FORMAT_STR "; path=%s; access=%s)\n",
|
||||
prefix, i == s->fd_store ? "File Descriptor Store Entry:" : " ",
|
||||
i->fdname,
|
||||
strna(inode_type_to_string(st.st_mode)),
|
||||
DEVNUM_FORMAT_VAL(st.st_dev),
|
||||
(uint64_t) st.st_ino,
|
||||
DEVNUM_FORMAT_VAL(st.st_rdev),
|
||||
strna(path),
|
||||
strna(accmode_to_string(flags)));
|
||||
}
|
||||
LIST_FOREACH(extra_fd, i, s->extra_fds)
|
||||
(void) service_dump_fd(i->fd,
|
||||
i->fdname,
|
||||
i == s->extra_fds ? "Extra File Descriptor Entry:" : " ",
|
||||
f,
|
||||
prefix);
|
||||
}
|
||||
|
||||
static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
@ -1093,6 +1128,8 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||
fprintf(f, "%sOpen File: %s\n", prefix, ofs);
|
||||
}
|
||||
|
||||
service_dump_extra_fds(s, f, prefix);
|
||||
|
||||
cgroup_context_dump(UNIT(s), f, prefix);
|
||||
}
|
||||
|
||||
@ -1423,11 +1460,12 @@ static int service_collect_fds(
|
||||
int **fds,
|
||||
char ***fd_names,
|
||||
size_t *n_socket_fds,
|
||||
size_t *n_storage_fds) {
|
||||
size_t *n_storage_fds,
|
||||
size_t *n_extra_fds) {
|
||||
|
||||
_cleanup_strv_free_ char **rfd_names = NULL;
|
||||
_cleanup_free_ int *rfds = NULL;
|
||||
size_t rn_socket_fds = 0, rn_storage_fds = 0;
|
||||
size_t rn_socket_fds = 0, rn_storage_fds = 0, rn_extra_fds = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
@ -1435,6 +1473,7 @@ static int service_collect_fds(
|
||||
assert(fd_names);
|
||||
assert(n_socket_fds);
|
||||
assert(n_storage_fds);
|
||||
assert(n_extra_fds);
|
||||
|
||||
if (s->socket_fd >= 0) {
|
||||
Socket *sock = ASSERT_PTR(SOCKET(UNIT_DEREF(s->accept_socket)));
|
||||
@ -1512,10 +1551,44 @@ static int service_collect_fds(
|
||||
rfd_names[n_fds] = NULL;
|
||||
}
|
||||
|
||||
LIST_FOREACH(extra_fd, extra_fd, s->extra_fds)
|
||||
rn_extra_fds++;
|
||||
|
||||
if (rn_extra_fds > 0) {
|
||||
size_t n_fds;
|
||||
char **nl;
|
||||
int *t;
|
||||
|
||||
t = reallocarray(rfds, rn_socket_fds + rn_storage_fds + rn_extra_fds, sizeof(int));
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
rfds = t;
|
||||
|
||||
nl = reallocarray(rfd_names, rn_socket_fds + rn_storage_fds + rn_extra_fds + 1, sizeof(char *));
|
||||
if (!nl)
|
||||
return -ENOMEM;
|
||||
|
||||
rfd_names = nl;
|
||||
n_fds = rn_socket_fds + rn_storage_fds;
|
||||
|
||||
LIST_FOREACH(extra_fd, extra_fd, s->extra_fds) {
|
||||
rfds[n_fds] = extra_fd->fd;
|
||||
rfd_names[n_fds] = strdup(extra_fd->fdname);
|
||||
if (!rfd_names[n_fds])
|
||||
return -ENOMEM;
|
||||
|
||||
n_fds++;
|
||||
}
|
||||
|
||||
rfd_names[n_fds] = NULL;
|
||||
}
|
||||
|
||||
*fds = TAKE_PTR(rfds);
|
||||
*fd_names = TAKE_PTR(rfd_names);
|
||||
*n_socket_fds = rn_socket_fds;
|
||||
*n_storage_fds = rn_storage_fds;
|
||||
*n_extra_fds = rn_extra_fds;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1714,7 +1787,8 @@ static int service_spawn_internal(
|
||||
&exec_params.fds,
|
||||
&exec_params.fd_names,
|
||||
&exec_params.n_socket_fds,
|
||||
&exec_params.n_storage_fds);
|
||||
&exec_params.n_storage_fds,
|
||||
&exec_params.n_extra_fds);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1722,7 +1796,7 @@ static int service_spawn_internal(
|
||||
|
||||
exec_params.flags |= EXEC_PASS_FDS;
|
||||
|
||||
log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds);
|
||||
log_unit_debug(UNIT(s), "Passing %zu fds to service", exec_params.n_socket_fds + exec_params.n_storage_fds + exec_params.n_extra_fds);
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(exec_params.flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
|
||||
@ -5288,6 +5362,7 @@ static void service_release_resources(Unit *u) {
|
||||
|
||||
service_release_socket_fd(s);
|
||||
service_release_stdio_fd(s);
|
||||
service_release_extra_fds(s);
|
||||
|
||||
if (s->fd_store_preserve_mode != EXEC_PRESERVE_YES)
|
||||
service_release_fd_store(s);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
typedef struct Service Service;
|
||||
typedef struct ServiceFDStore ServiceFDStore;
|
||||
typedef struct ServiceExtraFD ServiceExtraFD;
|
||||
|
||||
#include "exit-status.h"
|
||||
#include "kill.h"
|
||||
@ -111,6 +112,13 @@ struct ServiceFDStore {
|
||||
LIST_FIELDS(ServiceFDStore, fd_store);
|
||||
};
|
||||
|
||||
struct ServiceExtraFD {
|
||||
int fd;
|
||||
char *fdname;
|
||||
|
||||
LIST_FIELDS(ServiceExtraFD, extra_fd);
|
||||
};
|
||||
|
||||
struct Service {
|
||||
Unit meta;
|
||||
|
||||
@ -231,6 +239,9 @@ struct Service {
|
||||
|
||||
LIST_HEAD(OpenFile, open_files);
|
||||
|
||||
/* If service spawned from transient unit, extra file descriptors can be passed via dbus API */
|
||||
LIST_HEAD(ServiceExtraFD, extra_fds);
|
||||
|
||||
int reload_signal;
|
||||
usec_t reload_begin_usec;
|
||||
|
||||
@ -295,3 +306,6 @@ DEFINE_CAST(SERVICE, Service);
|
||||
|
||||
/* Only exported for unit tests */
|
||||
int service_deserialize_exec_command(Unit *u, const char *key, const char *value);
|
||||
|
||||
ServiceExtraFD* service_extra_fd_free(ServiceExtraFD *efd);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ServiceExtraFD*, service_extra_fd_free);
|
||||
|
@ -2017,6 +2017,21 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
return 1;
|
||||
} else if (streq(name, "ExtraFileDescriptorNames")) {
|
||||
_cleanup_strv_free_ char **extra_fd_names = NULL;
|
||||
_cleanup_free_ char *joined = NULL;
|
||||
|
||||
r = sd_bus_message_read_strv(m, &extra_fd_names);
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
joined = strv_join(extra_fd_names, " ");
|
||||
if (!joined)
|
||||
return log_oom();
|
||||
|
||||
bus_print_property_value(name, expected_value, flags, joined);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
56
test/units/TEST-23-UNIT-FILE.ExtraFileDescriptors.sh
Executable file
56
test/units/TEST-23-UNIT-FILE.ExtraFileDescriptors.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
# shellcheck source=test/units/util.sh
|
||||
. "$(dirname "$0")"/util.sh
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
|
||||
rm -rf /tmp/test-extra-fd/
|
||||
}
|
||||
|
||||
trap at_exit EXIT
|
||||
|
||||
mkdir /tmp/test-extra-fd
|
||||
echo "Hello" > /tmp/test-extra-fd/1.txt
|
||||
echo "Extra" > /tmp/test-extra-fd/2.txt
|
||||
|
||||
systemd-analyze log-level debug
|
||||
|
||||
# Open files and assign FD to variables
|
||||
exec {TEST_FD1}</tmp/test-extra-fd/1.txt
|
||||
exec {TEST_FD2}</tmp/test-extra-fd/2.txt
|
||||
|
||||
TEST_UNIT="test-23-extra-fd.service"
|
||||
|
||||
busctl call \
|
||||
org.freedesktop.systemd1 /org/freedesktop/systemd1 \
|
||||
org.freedesktop.systemd1.Manager StartTransientUnit \
|
||||
"ssa(sv)a(sa(sv))" "$TEST_UNIT" replace 4 \
|
||||
ExecStart "a(sasb)" 1 \
|
||||
/usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh \
|
||||
5 /usr/lib/systemd/tests/testdata/units/TEST-23-UNIT-FILE-openfile-child.sh 2 "test:other" "Hello" "Extra" \
|
||||
true \
|
||||
RemainAfterExit "b" true \
|
||||
Type "s" oneshot \
|
||||
ExtraFileDescriptors "a(hs)" 2 \
|
||||
"$TEST_FD1" test \
|
||||
"$TEST_FD2" other \
|
||||
0
|
||||
|
||||
cmp -b <(systemctl show -p ExtraFileDescriptorNames "$TEST_UNIT") <<EOF
|
||||
ExtraFileDescriptorNames=test other
|
||||
EOF
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
timeout 10s bash -xec 'while [[ "$(systemctl show -P SubState test-23-extra-fd.service)" != "exited" ]]; do sleep .5; done'
|
||||
|
||||
assert_eq "$(systemctl show -P Result "$TEST_UNIT")" "success"
|
||||
assert_eq "$(systemctl show -P ExecMainStatus "$TEST_UNIT")" "0"
|
||||
|
||||
systemctl stop "$TEST_UNIT"
|
||||
|
||||
systemctl log-level info
|
Loading…
Reference in New Issue
Block a user