mirror of
https://github.com/morgan9e/systemd
synced 2026-04-14 00:14:32 +09:00
Add ExtraFileDescriptor property to StartTransientUnit dbus API
This adds the ExtraFileDescriptor property to StartTransient dbus API with format "a(hs)" - array of (file descriptor, name) pairs. The FD will be passed to the unit via sd_notify like Socket and OpenFile. systemctl show also shows ExtraFileDescriptorName for these transient units. We only show the name passed to dbus as the FD numbers will change once passed over the unix socket and are duplicated, so its confusing to display the numbers. We do not add this functionality for systemd-run or general systemd service units as it is not useful for general systemd services. Arguably, it could be useful for systemd-run in bash scripts but we prefer to be cautious and not expose the API yet. Fixes: #34396
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user