mirror of
https://github.com/systemd/systemd.git
synced 2024-12-04 15:53:41 +08:00
core: add OpenFile setting
This commit is contained in:
parent
81315baa68
commit
cd48e23f6a
@ -2576,6 +2576,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
readonly u NRestarts = ...;
|
readonly u NRestarts = ...;
|
||||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
readonly s OOMPolicy = '...';
|
readonly s OOMPolicy = '...';
|
||||||
|
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||||
|
readonly a(sst) OpenFile = [...];
|
||||||
readonly t ExecMainStartTimestamp = ...;
|
readonly t ExecMainStartTimestamp = ...;
|
||||||
readonly t ExecMainStartTimestampMonotonic = ...;
|
readonly t ExecMainStartTimestampMonotonic = ...;
|
||||||
readonly t ExecMainExitTimestamp = ...;
|
readonly t ExecMainExitTimestamp = ...;
|
||||||
@ -3173,6 +3175,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
|
|
||||||
<!--property OOMPolicy is not documented!-->
|
<!--property OOMPolicy is not documented!-->
|
||||||
|
|
||||||
|
<!--property OpenFile is not documented!-->
|
||||||
|
|
||||||
<!--property ExecCondition is not documented!-->
|
<!--property ExecCondition is not documented!-->
|
||||||
|
|
||||||
<!--property ExecConditionEx is not documented!-->
|
<!--property ExecConditionEx is not documented!-->
|
||||||
@ -3729,6 +3733,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
|||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="OOMPolicy"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="OOMPolicy"/>
|
||||||
|
|
||||||
|
<variablelist class="dbus-property" generated="True" extra-ref="OpenFile"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
|
||||||
|
|
||||||
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestampMonotonic"/>
|
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestampMonotonic"/>
|
||||||
|
@ -1156,6 +1156,37 @@
|
|||||||
kills, this setting determines the state of the unit after <command>systemd-oomd</command> kills a
|
kills, this setting determines the state of the unit after <command>systemd-oomd</command> kills a
|
||||||
cgroup associated with it.</para></listitem>
|
cgroup associated with it.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>OpenFile=</varname></term>
|
||||||
|
<listitem><para>Takes an argument of the form <literal>path<optional><replaceable>:fd-name:options</replaceable></optional></literal>,
|
||||||
|
where:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem><simpara><literal>path</literal> is a path to a file or an <constant>AF_UNIX</constant> socket in the file system;</simpara></listitem>
|
||||||
|
<listitem><simpara><literal>fd-name</literal> is a name that will be associated with the file descriptor;
|
||||||
|
the name may contain any ASCII character, but must exclude control characters and ":", and must be at most 255 characters in length;
|
||||||
|
it is optional and, if not provided, defaults to the file name;</simpara></listitem>
|
||||||
|
<listitem><simpara><literal>options</literal> is a comma-separated list of access options;
|
||||||
|
possible values are
|
||||||
|
<literal>read-only</literal>,
|
||||||
|
<literal>append</literal>,
|
||||||
|
<literal>truncate</literal>,
|
||||||
|
<literal>graceful</literal>;
|
||||||
|
if not specified, files will be opened in <constant>rw</constant> mode;
|
||||||
|
if <literal>graceful</literal> is specified, errors during file/socket opening are ignored.
|
||||||
|
Specifying the same option several times is treated as an error.</simpara></listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
The file or socket is opened by the service manager and the file descriptor is passed to the service.
|
||||||
|
If the path is a socket, we call <function>connect()</function> on it.
|
||||||
|
See <citerefentry><refentrytitle>sd_listen_fds</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||||
|
for more details on how to retrieve these file descriptors.</para>
|
||||||
|
|
||||||
|
<para>This setting is useful to allow services to access files/sockets that they can't access themselves
|
||||||
|
(due to running in a separate mount namespace, not having privileges, ...).</para>
|
||||||
|
|
||||||
|
<para>This setting can be specified multiple times, in which case all the specified paths are opened and the file descriptors passed to the service.
|
||||||
|
If the empty string is assigned, the entire list of open files defined prior to this is reset.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para id='shared-unit-options'>Check
|
<para id='shared-unit-options'>Check
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "locale-util.h"
|
#include "locale-util.h"
|
||||||
#include "mount-util.h"
|
#include "mount-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "selinux-access.h"
|
#include "selinux-access.h"
|
||||||
@ -36,6 +37,34 @@ static BUS_DEFINE_PROPERTY_GET(property_get_timeout_abort_usec, "t", Service, se
|
|||||||
static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
|
static BUS_DEFINE_PROPERTY_GET(property_get_watchdog_usec, "t", Service, service_get_watchdog_usec);
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode);
|
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_timeout_failure_mode, service_timeout_failure_mode, ServiceTimeoutFailureMode);
|
||||||
|
|
||||||
|
static int property_get_open_files(
|
||||||
|
sd_bus *bus,
|
||||||
|
const char *path,
|
||||||
|
const char *interface,
|
||||||
|
const char *property,
|
||||||
|
sd_bus_message *reply,
|
||||||
|
void *userdata,
|
||||||
|
sd_bus_error *error) {
|
||||||
|
|
||||||
|
OpenFile **open_files = ASSERT_PTR(userdata);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
assert(reply);
|
||||||
|
|
||||||
|
r = sd_bus_message_open_container(reply, 'a', "(sst)");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
LIST_FOREACH(open_files, of, *open_files) {
|
||||||
|
r = sd_bus_message_append(reply, "(sst)", of->path, of->fdname, of->flags);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sd_bus_message_close_container(reply);
|
||||||
|
}
|
||||||
|
|
||||||
static int property_get_exit_status_set(
|
static int property_get_exit_status_set(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
@ -228,6 +257,7 @@ const sd_bus_vtable bus_service_vtable[] = {
|
|||||||
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_PROPERTY("GID", "u", bus_property_get_gid, offsetof(Unit, ref_gid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_PROPERTY("NRestarts", "u", bus_property_get_unsigned, offsetof(Service, n_restarts), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
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("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),
|
||||||
|
|
||||||
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||||
@ -532,6 +562,56 @@ static int bus_service_set_transient_property(
|
|||||||
if (streq(name, "StandardErrorFileDescriptor"))
|
if (streq(name, "StandardErrorFileDescriptor"))
|
||||||
return bus_set_transient_std_fd(u, name, &s->stderr_fd, &s->exec_context.stdio_as_fds, message, flags, error);
|
return bus_set_transient_std_fd(u, name, &s->stderr_fd, &s->exec_context.stdio_as_fds, message, flags, error);
|
||||||
|
|
||||||
|
if (streq(name, "OpenFile")) {
|
||||||
|
const char *path, *fdname;
|
||||||
|
uint64_t offlags;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(message, 'a', "(sst)");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
while ((r = sd_bus_message_read(message, "(sst)", &path, &fdname, &offlags)) > 0) {
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
_cleanup_free_ char *ofs = NULL;
|
||||||
|
|
||||||
|
of = new(OpenFile, 1);
|
||||||
|
if (!of)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*of = (OpenFile) {
|
||||||
|
.path = strdup(path),
|
||||||
|
.fdname = strdup(fdname),
|
||||||
|
.flags = offlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!of->path || !of->fdname)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = open_file_validate(of);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (UNIT_WRITE_FLAGS_NOOP(flags))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &ofs);
|
||||||
|
if (r < 0)
|
||||||
|
return sd_bus_error_set_errnof(
|
||||||
|
error, r, "Failed to convert OpenFile= value to string: %m");
|
||||||
|
|
||||||
|
LIST_APPEND(open_files, s->open_files, TAKE_PTR(of));
|
||||||
|
unit_write_settingf(u, flags | UNIT_ESCAPE_SPECIFIERS, name, "OpenFile=%s", ofs);
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(message);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,11 +151,14 @@ static int shift_fds(int fds[], size_t n_fds) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int flags_fds(const int fds[], size_t n_socket_fds, size_t n_storage_fds, bool nonblock) {
|
static int flags_fds(
|
||||||
size_t n_fds;
|
const int fds[],
|
||||||
|
size_t n_socket_fds,
|
||||||
|
size_t n_fds,
|
||||||
|
bool nonblock) {
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
n_fds = n_socket_fds + n_storage_fds;
|
|
||||||
if (n_fds <= 0)
|
if (n_fds <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -1805,6 +1808,7 @@ static int build_environment(
|
|||||||
const ExecContext *c,
|
const ExecContext *c,
|
||||||
const ExecParameters *p,
|
const ExecParameters *p,
|
||||||
size_t n_fds,
|
size_t n_fds,
|
||||||
|
char **fdnames,
|
||||||
const char *home,
|
const char *home,
|
||||||
const char *username,
|
const char *username,
|
||||||
const char *shell,
|
const char *shell,
|
||||||
@ -1837,7 +1841,7 @@ static int build_environment(
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
our_env[n_env++] = x;
|
our_env[n_env++] = x;
|
||||||
|
|
||||||
joined = strv_join(p->fd_names, ":");
|
joined = strv_join(fdnames, ":");
|
||||||
if (!joined)
|
if (!joined)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
@ -4105,6 +4109,123 @@ static int add_shifted_fd(int *fds, size_t fds_size, size_t *n_fds, int fd, int
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int connect_unix_harder(Unit *u, const OpenFile *of, int ofd) {
|
||||||
|
union sockaddr_union addr = {
|
||||||
|
.un.sun_family = AF_UNIX,
|
||||||
|
};
|
||||||
|
socklen_t sa_len;
|
||||||
|
static const int socket_types[] = { SOCK_DGRAM, SOCK_STREAM, SOCK_SEQPACKET };
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(of);
|
||||||
|
assert(ofd >= 0);
|
||||||
|
|
||||||
|
r = sockaddr_un_set_path(&addr.un, FORMAT_PROC_FD_PATH(ofd));
|
||||||
|
if (r < 0)
|
||||||
|
return log_unit_error_errno(u, r, "Failed to set sockaddr for %s: %m", of->path);
|
||||||
|
|
||||||
|
sa_len = r;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(socket_types); i++) {
|
||||||
|
_cleanup_close_ int fd = -EBADF;
|
||||||
|
|
||||||
|
fd = socket(AF_UNIX, socket_types[i] | SOCK_CLOEXEC, 0);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_unit_error_errno(u, errno, "Failed to create socket for %s: %m", of->path);
|
||||||
|
|
||||||
|
r = RET_NERRNO(connect(fd, &addr.sa, sa_len));
|
||||||
|
if (r == -EPROTOTYPE)
|
||||||
|
continue;
|
||||||
|
if (r < 0)
|
||||||
|
return log_unit_error_errno(u, r, "Failed to connect socket for %s: %m", of->path);
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EPROTOTYPE), "Failed to connect socket for \"%s\".", of->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_open_file_fd(Unit *u, const OpenFile *of) {
|
||||||
|
struct stat st;
|
||||||
|
_cleanup_close_ int fd = -EBADF, ofd = -EBADF;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(of);
|
||||||
|
|
||||||
|
ofd = open(of->path, O_PATH | O_CLOEXEC);
|
||||||
|
if (ofd < 0)
|
||||||
|
return log_error_errno(errno, "Could not open \"%s\": %m", of->path);
|
||||||
|
if (fstat(ofd, &st) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to stat %s: %m", of->path);
|
||||||
|
|
||||||
|
if (S_ISSOCK(st.st_mode)) {
|
||||||
|
fd = connect_unix_harder(u, of, ofd);
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
if (FLAGS_SET(of->flags, OPENFILE_READ_ONLY) && shutdown(fd, SHUT_WR) < 0)
|
||||||
|
return log_error_errno(errno, "Failed to shutdown send for socket %s: %m", of->path);
|
||||||
|
|
||||||
|
log_unit_debug(u, "socket %s opened (fd=%d)", of->path, fd);
|
||||||
|
} else {
|
||||||
|
int flags = FLAGS_SET(of->flags, OPENFILE_READ_ONLY) ? O_RDONLY : O_RDWR;
|
||||||
|
if (FLAGS_SET(of->flags, OPENFILE_APPEND))
|
||||||
|
flags |= O_APPEND;
|
||||||
|
else if (FLAGS_SET(of->flags, OPENFILE_TRUNCATE))
|
||||||
|
flags |= O_TRUNC;
|
||||||
|
|
||||||
|
fd = fd_reopen(ofd, flags | O_CLOEXEC);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_unit_error_errno(u, fd, "Failed to open file %s: %m", of->path);
|
||||||
|
|
||||||
|
log_unit_debug(u, "file %s opened (fd=%d)", of->path, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TAKE_FD(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_open_file_fds(
|
||||||
|
Unit *u,
|
||||||
|
OpenFile* open_files,
|
||||||
|
int **fds,
|
||||||
|
char ***fdnames,
|
||||||
|
size_t *n_fds) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(fds);
|
||||||
|
assert(fdnames);
|
||||||
|
assert(n_fds);
|
||||||
|
|
||||||
|
LIST_FOREACH(open_files, of, open_files) {
|
||||||
|
_cleanup_close_ int fd = -EBADF;
|
||||||
|
|
||||||
|
fd = get_open_file_fd(u, of);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (FLAGS_SET(of->flags, OPENFILE_GRACEFUL)) {
|
||||||
|
log_unit_debug_errno(u, fd, "Failed to get OpenFile= file descriptor for %s, ignoring: %m", of->path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GREEDY_REALLOC(*fds, *n_fds + 1))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = strv_extend(fdnames, of->fdname);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
(*fds)[*n_fds] = TAKE_FD(fd);
|
||||||
|
|
||||||
|
(*n_fds)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int exec_child(
|
static int exec_child(
|
||||||
Unit *unit,
|
Unit *unit,
|
||||||
const ExecCommand *command,
|
const ExecCommand *command,
|
||||||
@ -4114,7 +4235,7 @@ static int exec_child(
|
|||||||
DynamicCreds *dcreds,
|
DynamicCreds *dcreds,
|
||||||
int socket_fd,
|
int socket_fd,
|
||||||
const int named_iofds[static 3],
|
const int named_iofds[static 3],
|
||||||
int *fds,
|
int *params_fds,
|
||||||
size_t n_socket_fds,
|
size_t n_socket_fds,
|
||||||
size_t n_storage_fds,
|
size_t n_storage_fds,
|
||||||
char **files_env,
|
char **files_env,
|
||||||
@ -4154,6 +4275,8 @@ static int exec_child(
|
|||||||
int secure_bits;
|
int secure_bits;
|
||||||
_cleanup_free_ gid_t *gids_after_pam = NULL;
|
_cleanup_free_ gid_t *gids_after_pam = NULL;
|
||||||
int ngids_after_pam = 0;
|
int ngids_after_pam = 0;
|
||||||
|
_cleanup_free_ int *fds = NULL;
|
||||||
|
_cleanup_strv_free_ char **fdnames = NULL;
|
||||||
|
|
||||||
assert(unit);
|
assert(unit);
|
||||||
assert(command);
|
assert(command);
|
||||||
@ -4196,6 +4319,24 @@ static int exec_child(
|
|||||||
/* In case anything used libc syslog(), close this here, too */
|
/* In case anything used libc syslog(), close this here, too */
|
||||||
closelog();
|
closelog();
|
||||||
|
|
||||||
|
fds = newdup(int, params_fds, n_fds);
|
||||||
|
if (!fds) {
|
||||||
|
*exit_status = EXIT_MEMORY;
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
fdnames = strv_copy((char**) params->fd_names);
|
||||||
|
if (!fdnames) {
|
||||||
|
*exit_status = EXIT_MEMORY;
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = collect_open_file_fds(unit, params->open_files, &fds, &fdnames, &n_fds);
|
||||||
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_FDS;
|
||||||
|
return log_unit_error_errno(unit, r, "Failed to get OpenFile= file descriptors: %m");
|
||||||
|
}
|
||||||
|
|
||||||
int keep_fds[n_fds + 3];
|
int keep_fds[n_fds + 3];
|
||||||
memcpy_safe(keep_fds, fds, n_fds * sizeof(int));
|
memcpy_safe(keep_fds, fds, n_fds * sizeof(int));
|
||||||
n_keep_fds = n_fds;
|
n_keep_fds = n_fds;
|
||||||
@ -4551,6 +4692,7 @@ static int exec_child(
|
|||||||
context,
|
context,
|
||||||
params,
|
params,
|
||||||
n_fds,
|
n_fds,
|
||||||
|
fdnames,
|
||||||
home,
|
home,
|
||||||
username,
|
username,
|
||||||
shell,
|
shell,
|
||||||
@ -4843,7 +4985,7 @@ static int exec_child(
|
|||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
r = shift_fds(fds, n_fds);
|
r = shift_fds(fds, n_fds);
|
||||||
if (r >= 0)
|
if (r >= 0)
|
||||||
r = flags_fds(fds, n_socket_fds, n_storage_fds, context->non_blocking);
|
r = flags_fds(fds, n_socket_fds, n_fds, context->non_blocking);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_FDS;
|
*exit_status = EXIT_FDS;
|
||||||
return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m");
|
return log_unit_error_errno(unit, r, "Failed to adjust passed file descriptors: %m");
|
||||||
|
@ -23,6 +23,7 @@ typedef struct Manager Manager;
|
|||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
#include "nsflags.h"
|
#include "nsflags.h"
|
||||||
#include "numa-util.h"
|
#include "numa-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "set.h"
|
#include "set.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
@ -427,6 +428,8 @@ struct ExecParameters {
|
|||||||
int exec_fd;
|
int exec_fd;
|
||||||
|
|
||||||
const char *notify_socket;
|
const char *notify_socket;
|
||||||
|
|
||||||
|
LIST_HEAD(OpenFile, open_files);
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "unit.h"
|
#include "unit.h"
|
||||||
|
@ -426,6 +426,7 @@ Service.BusPolicy, config_parse_warn_compat,
|
|||||||
Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
|
Service.USBFunctionDescriptors, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_descriptors)
|
||||||
Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
|
Service.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
|
||||||
Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
|
Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
|
||||||
|
Service.OpenFile, config_parse_open_file, 0, offsetof(Service, open_files)
|
||||||
{{ EXEC_CONTEXT_CONFIG_ITEMS('Service') }}
|
{{ EXEC_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||||
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
|
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||||
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
|
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "missing_ioprio.h"
|
#include "missing_ioprio.h"
|
||||||
#include "mountpoint-util.h"
|
#include "mountpoint-util.h"
|
||||||
#include "nulstr-util.h"
|
#include "nulstr-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "parse-helpers.h"
|
#include "parse-helpers.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
@ -6532,3 +6533,39 @@ int config_parse_log_filter_patterns(
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int config_parse_open_file(
|
||||||
|
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) {
|
||||||
|
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
OpenFile **head = ASSERT_PTR(data);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(filename);
|
||||||
|
assert(lvalue);
|
||||||
|
assert(rvalue);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
open_file_free_many(head);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = open_file_parse(rvalue, &of);
|
||||||
|
if (r < 0) {
|
||||||
|
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse OpenFile= setting, ignoring: %s", rvalue);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_APPEND(open_files, *head, TAKE_PTR(of));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -151,6 +151,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
|
|||||||
CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
|
CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
|
CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
|
||||||
CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
|
CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
|
||||||
|
CONFIG_PARSER_PROTOTYPE(config_parse_open_file);
|
||||||
|
|
||||||
/* gperf prototypes */
|
/* gperf prototypes */
|
||||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "load-fragment.h"
|
#include "load-fragment.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
@ -360,6 +361,8 @@ static void service_done(Unit *u) {
|
|||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
|
|
||||||
|
open_file_free_many(&s->open_files);
|
||||||
|
|
||||||
s->pid_file = mfree(s->pid_file);
|
s->pid_file = mfree(s->pid_file);
|
||||||
s->status_text = mfree(s->status_text);
|
s->status_text = mfree(s->status_text);
|
||||||
|
|
||||||
@ -925,6 +928,21 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
|||||||
prefix, s->n_fd_store_max,
|
prefix, s->n_fd_store_max,
|
||||||
prefix, s->n_fd_store);
|
prefix, s->n_fd_store);
|
||||||
|
|
||||||
|
if (s->open_files)
|
||||||
|
LIST_FOREACH(open_files, of, s->open_files) {
|
||||||
|
_cleanup_free_ char *ofs = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &ofs);
|
||||||
|
if (r < 0) {
|
||||||
|
log_debug_errno(r,
|
||||||
|
"Failed to convert OpenFile= setting to string, ignoring: %m");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "%sOpen File: %s\n", prefix, ofs);
|
||||||
|
}
|
||||||
|
|
||||||
cgroup_context_dump(UNIT(s), f, prefix);
|
cgroup_context_dump(UNIT(s), f, prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1528,6 +1546,8 @@ static int service_spawn_internal(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
exec_params.open_files = s->open_files;
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ typedef struct ServiceFDStore ServiceFDStore;
|
|||||||
|
|
||||||
#include "exit-status.h"
|
#include "exit-status.h"
|
||||||
#include "kill.h"
|
#include "kill.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
#include "ratelimit.h"
|
#include "ratelimit.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
@ -215,6 +216,8 @@ struct Service {
|
|||||||
bool flush_n_restarts;
|
bool flush_n_restarts;
|
||||||
|
|
||||||
OOMPolicy oom_policy;
|
OOMPolicy oom_policy;
|
||||||
|
|
||||||
|
LIST_HEAD(OpenFile, open_files);
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline usec_t service_timeout_abort_usec(Service *s) {
|
static inline usec_t service_timeout_abort_usec(Service *s) {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "mountpoint-util.h"
|
#include "mountpoint-util.h"
|
||||||
#include "nsflags.h"
|
#include "nsflags.h"
|
||||||
#include "numa-util.h"
|
#include "numa-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "parse-helpers.h"
|
#include "parse-helpers.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
@ -410,6 +411,23 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bus_append_open_file(sd_bus_message *m, const char *field, const char *eq) {
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = open_file_parse(eq, &of);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse OpenFile= setting: %m");
|
||||||
|
|
||||||
|
r = sd_bus_message_append(m, "(sv)", field, "a(sst)", (size_t) 1, of->path, of->fdname, of->flags);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
|
static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -2300,6 +2318,9 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (streq(field, "OpenFile"))
|
||||||
|
return bus_append_open_file(m, field, eq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,6 +243,8 @@ shared_sources = files(
|
|||||||
'nsflags.h',
|
'nsflags.h',
|
||||||
'numa-util.c',
|
'numa-util.c',
|
||||||
'numa-util.h',
|
'numa-util.h',
|
||||||
|
'open-file.c',
|
||||||
|
'open-file.h',
|
||||||
'openssl-util.c',
|
'openssl-util.c',
|
||||||
'openssl-util.h',
|
'openssl-util.h',
|
||||||
'output-mode.c',
|
'output-mode.c',
|
||||||
|
150
src/shared/open-file.c
Normal file
150
src/shared/open-file.c
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "escape.h"
|
||||||
|
#include "extract-word.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
|
#include "path-util.h"
|
||||||
|
#include "string-table.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
|
||||||
|
int open_file_parse(const char *v, OpenFile **ret) {
|
||||||
|
_cleanup_free_ char *options = NULL;
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(v);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
of = new0(OpenFile, 1);
|
||||||
|
if (!of)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = extract_many_words(&v, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_CUNESCAPE, &of->path, &of->fdname, &options, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Enforce that at most 3 colon-separated words are present */
|
||||||
|
if (!isempty(v))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
for (const char *p = options;;) {
|
||||||
|
OpenFileFlag flag;
|
||||||
|
_cleanup_free_ char *word = NULL;
|
||||||
|
|
||||||
|
r = extract_first_word(&p, &word, ",", 0);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
flag = open_file_flags_from_string(word);
|
||||||
|
if (flag < 0)
|
||||||
|
return flag;
|
||||||
|
|
||||||
|
if ((flag & of->flags) != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
of->flags |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isempty(of->fdname)) {
|
||||||
|
free(of->fdname);
|
||||||
|
r = path_extract_filename(of->path, &of->fdname);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = open_file_validate(of);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(of);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_file_validate(const OpenFile *of) {
|
||||||
|
assert(of);
|
||||||
|
|
||||||
|
if (!path_is_valid(of->path) || !path_is_absolute(of->path))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!fdname_is_valid(of->fdname))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((FLAGS_SET(of->flags, OPENFILE_READ_ONLY) + FLAGS_SET(of->flags, OPENFILE_APPEND) +
|
||||||
|
FLAGS_SET(of->flags, OPENFILE_TRUNCATE)) > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((of->flags & ~_OPENFILE_MASK_PUBLIC) != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int open_file_to_string(const OpenFile *of, char **ret) {
|
||||||
|
_cleanup_free_ char *options = NULL, *fname = NULL, *s = NULL;
|
||||||
|
bool has_fdname = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(of);
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
|
s = shell_escape(of->path, ":");
|
||||||
|
if (!s)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = path_extract_filename(of->path, &fname);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
has_fdname = !streq(fname, of->fdname);
|
||||||
|
if (has_fdname)
|
||||||
|
if (!strextend(&s, ":", of->fdname))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (OpenFileFlag flag = OPENFILE_READ_ONLY; flag < _OPENFILE_MAX; flag <<= 1)
|
||||||
|
if (FLAGS_SET(of->flags, flag) && !strextend_with_separator(&options, ",", open_file_flags_to_string(flag)))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (options)
|
||||||
|
if (!(has_fdname ? strextend(&s, ":", options) : strextend(&s, "::", options)))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(s);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenFile *open_file_free(OpenFile *of) {
|
||||||
|
if (!of)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
free(of->path);
|
||||||
|
free(of->fdname);
|
||||||
|
return mfree(of);
|
||||||
|
}
|
||||||
|
|
||||||
|
void open_file_free_many(OpenFile **head) {
|
||||||
|
OpenFile *of;
|
||||||
|
|
||||||
|
while ((of = *head)) {
|
||||||
|
LIST_REMOVE(open_files, *head, of);
|
||||||
|
of = open_file_free(of);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char * const open_file_flags_table[_OPENFILE_MAX] = {
|
||||||
|
[OPENFILE_READ_ONLY] = "read-only",
|
||||||
|
[OPENFILE_APPEND] = "append",
|
||||||
|
[OPENFILE_TRUNCATE] = "truncate",
|
||||||
|
[OPENFILE_GRACEFUL] = "graceful",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP(open_file_flags, OpenFileFlag);
|
36
src/shared/open-file.h
Normal file
36
src/shared/open-file.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
typedef enum OpenFileFlag {
|
||||||
|
OPENFILE_READ_ONLY = 1 << 0,
|
||||||
|
OPENFILE_APPEND = 1 << 1,
|
||||||
|
OPENFILE_TRUNCATE = 1 << 2,
|
||||||
|
OPENFILE_GRACEFUL = 1 << 3,
|
||||||
|
_OPENFILE_MAX,
|
||||||
|
_OPENFILE_INVALID = -EINVAL,
|
||||||
|
_OPENFILE_MASK_PUBLIC = OPENFILE_READ_ONLY | OPENFILE_APPEND | OPENFILE_TRUNCATE | OPENFILE_GRACEFUL,
|
||||||
|
} OpenFileFlag;
|
||||||
|
|
||||||
|
typedef struct OpenFile {
|
||||||
|
char *path;
|
||||||
|
char *fdname;
|
||||||
|
OpenFileFlag flags;
|
||||||
|
LIST_FIELDS(struct OpenFile, open_files);
|
||||||
|
} OpenFile;
|
||||||
|
|
||||||
|
int open_file_parse(const char *v, OpenFile **ret);
|
||||||
|
|
||||||
|
int open_file_validate(const OpenFile *of);
|
||||||
|
|
||||||
|
int open_file_to_string(const OpenFile *of, char **ret);
|
||||||
|
|
||||||
|
OpenFile *open_file_free(OpenFile *of);
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(OpenFile*, open_file_free);
|
||||||
|
|
||||||
|
void open_file_free_many(OpenFile **head);
|
||||||
|
|
||||||
|
const char *open_file_flags_to_string(OpenFileFlag t) _const_;
|
||||||
|
OpenFileFlag open_file_flags_from_string(const char *t) _pure_;
|
@ -23,6 +23,7 @@
|
|||||||
#include "locale-util.h"
|
#include "locale-util.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
#include "numa-util.h"
|
#include "numa-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
@ -1857,6 +1858,38 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_parse_error(r);
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
} else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "OpenFile")) {
|
||||||
|
char *path, *fdname;
|
||||||
|
uint64_t offlags;
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sst)");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
while ((r = sd_bus_message_read(m, "(sst)", &path, &fdname, &offlags)) > 0) {
|
||||||
|
_cleanup_free_ char *ofs = NULL;
|
||||||
|
|
||||||
|
r = open_file_to_string(
|
||||||
|
&(OpenFile){
|
||||||
|
.path = path,
|
||||||
|
.fdname = fdname,
|
||||||
|
.flags = offlags,
|
||||||
|
},
|
||||||
|
&ofs);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(
|
||||||
|
r, "Failed to convert OpenFile= value to string: %m");
|
||||||
|
|
||||||
|
bus_print_property_value(name, expected_value, flags, ofs);
|
||||||
|
}
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(m);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -695,6 +695,8 @@ tests += [
|
|||||||
[files('test-hmac.c')],
|
[files('test-hmac.c')],
|
||||||
|
|
||||||
[files('test-sha256.c')],
|
[files('test-sha256.c')],
|
||||||
|
|
||||||
|
[files('test-open-file.c')],
|
||||||
]
|
]
|
||||||
|
|
||||||
############################################################
|
############################################################
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "load-fragment.h"
|
#include "load-fragment.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
|
#include "open-file.h"
|
||||||
#include "pcre2-util.h"
|
#include "pcre2-util.h"
|
||||||
#include "rm-rf.h"
|
#include "rm-rf.h"
|
||||||
#include "specifier.h"
|
#include "specifier.h"
|
||||||
@ -1048,6 +1049,50 @@ TEST(config_parse_log_filter_patterns) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(config_parse_open_file) {
|
||||||
|
_cleanup_(manager_freep) Manager *m = NULL;
|
||||||
|
_cleanup_(unit_freep) Unit *u = NULL;
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
|
||||||
|
if (manager_errno_skip_test(r)) {
|
||||||
|
log_notice_errno(r, "Skipping test: manager_new: %m");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(manager_startup(m, NULL, NULL, NULL) >= 0);
|
||||||
|
|
||||||
|
assert_se(u = unit_new(m, sizeof(Service)));
|
||||||
|
assert_se(unit_add_name(u, "foobar.service") == 0);
|
||||||
|
|
||||||
|
r = config_parse_open_file(NULL, "fake", 1, "section", 1,
|
||||||
|
"OpenFile", 0, "/proc/1/ns/mnt:host-mount-namespace:read-only",
|
||||||
|
&of, u);
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(of);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == OPENFILE_READ_ONLY);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = config_parse_open_file(NULL, "fake", 1, "section", 1,
|
||||||
|
"OpenFile", 0, "/proc/1/ns/mnt::read-only",
|
||||||
|
&of, u);
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(of);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "mnt"));
|
||||||
|
assert_se(of->flags == OPENFILE_READ_ONLY);
|
||||||
|
|
||||||
|
r = config_parse_open_file(NULL, "fake", 1, "section", 1,
|
||||||
|
"OpenFile", 0, "",
|
||||||
|
&of, u);
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(!of);
|
||||||
|
}
|
||||||
|
|
||||||
static int intro(void) {
|
static int intro(void) {
|
||||||
if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
|
if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
|
||||||
return log_tests_skipped("cgroupfs not available");
|
return log_tests_skipped("cgroupfs not available");
|
||||||
|
185
src/test/test-open-file.c
Normal file
185
src/test/test-open-file.c
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "open-file.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
TEST(open_file_parse) {
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == OPENFILE_READ_ONLY);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "mnt"));
|
||||||
|
assert_se(of->flags == 0);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == 0);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt::read-only", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "mnt"));
|
||||||
|
assert_se(of->flags == OPENFILE_READ_ONLY);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("../file.dat:file:read-only", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:rw", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:append", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == OPENFILE_APPEND);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:truncate", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == OPENFILE_TRUNCATE);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,append", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,truncate", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:append,truncate", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,read-only", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:graceful", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == OPENFILE_GRACEFUL);
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only,graceful", &of);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(of->path, "/proc/1/ns/mnt"));
|
||||||
|
assert_se(streq(of->fdname, "host-mount-namespace"));
|
||||||
|
assert_se(of->flags == (OPENFILE_READ_ONLY | OPENFILE_GRACEFUL));
|
||||||
|
|
||||||
|
of = open_file_free(of);
|
||||||
|
r = open_file_parse("/proc/1/ns/mnt:host-mount-namespace:read-only:other", &of);
|
||||||
|
|
||||||
|
assert_se(r == -EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(open_file_to_string) {
|
||||||
|
_cleanup_free_ char *s = NULL;
|
||||||
|
_cleanup_(open_file_freep) OpenFile *of = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_se(of = new (OpenFile, 1));
|
||||||
|
*of = (OpenFile){ .path = strdup("/proc/1/ns/mnt"),
|
||||||
|
.fdname = strdup("host-mount-namespace"),
|
||||||
|
.flags = OPENFILE_READ_ONLY };
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
of->flags = OPENFILE_APPEND;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:append"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
of->flags = OPENFILE_TRUNCATE;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:truncate"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
of->flags = OPENFILE_GRACEFUL;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:graceful"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
of->flags = OPENFILE_READ_ONLY | OPENFILE_GRACEFUL;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace:read-only,graceful"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
of->flags = 0;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt:host-mount-namespace"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
assert_se(free_and_strdup(&of->fdname, "mnt"));
|
||||||
|
of->flags = OPENFILE_READ_ONLY;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/proc/1/ns/mnt::read-only"));
|
||||||
|
|
||||||
|
s = mfree(s);
|
||||||
|
assert_se(free_and_strdup(&of->path, "/path:with:colon"));
|
||||||
|
assert_se(free_and_strdup(&of->fdname, "path:with:colon"));
|
||||||
|
of->flags = 0;
|
||||||
|
|
||||||
|
r = open_file_to_string(of, &s);
|
||||||
|
|
||||||
|
assert_se(r >= 0);
|
||||||
|
assert_se(streq(s, "/path\\:with\\:colon"));
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_TEST_MAIN(LOG_INFO);
|
1
test/TEST-77-OPENFILE/Makefile
Symbolic link
1
test/TEST-77-OPENFILE/Makefile
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../TEST-01-BASIC/Makefile
|
16
test/TEST-77-OPENFILE/test.sh
Executable file
16
test/TEST-77-OPENFILE/test.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -e
|
||||||
|
|
||||||
|
TEST_DESCRIPTION="Openfile tests"
|
||||||
|
|
||||||
|
# shellcheck source=test/test-functions
|
||||||
|
. "${TEST_BASE_DIR:?}/test-functions"
|
||||||
|
|
||||||
|
test_append_files() {
|
||||||
|
local workspace="${1:?}"
|
||||||
|
echo "Open" > "$workspace/test-77-open.dat"
|
||||||
|
echo "File" > "$workspace/test-77-file.dat"
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test "$@"
|
7
test/units/testsuite-77-netcat.service
Normal file
7
test/units/testsuite-77-netcat.service
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
[Unit]
|
||||||
|
Description=TEST-77-OPENFILE
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||||
|
Type=simple
|
4
test/units/testsuite-77-netcat.sh
Executable file
4
test/units/testsuite-77-netcat.sh
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
echo "Socket" | nc -lkU /tmp/test.sock
|
14
test/units/testsuite-77-run.sh
Executable file
14
test/units/testsuite-77-run.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# shellcheck source=test/units/assert.sh
|
||||||
|
. "$(dirname "$0")"/assert.sh
|
||||||
|
|
||||||
|
export SYSTEMD_LOG_LEVEL=debug
|
||||||
|
|
||||||
|
assert_eq "$LISTEN_FDS" "1"
|
||||||
|
assert_eq "$LISTEN_FDNAMES" "new-file"
|
||||||
|
read -r -u 3 text
|
||||||
|
assert_eq "$text" "New"
|
9
test/units/testsuite-77-socket.service
Normal file
9
test/units/testsuite-77-socket.service
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
[Unit]
|
||||||
|
Description=TEST-77-OPENFILE
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
OpenFile=/tmp/test.sock:socket:read-only
|
||||||
|
ExecStartPre=rm -f /failed /testok
|
||||||
|
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||||
|
Type=oneshot
|
14
test/units/testsuite-77-socket.sh
Executable file
14
test/units/testsuite-77-socket.sh
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# shellcheck source=test/units/assert.sh
|
||||||
|
. "$(dirname "$0")"/assert.sh
|
||||||
|
|
||||||
|
export SYSTEMD_LOG_LEVEL=debug
|
||||||
|
|
||||||
|
assert_eq "$LISTEN_FDS" "1"
|
||||||
|
assert_eq "$LISTEN_FDNAMES" "socket"
|
||||||
|
read -r -u 3 text
|
||||||
|
assert_eq "$text" "Socket"
|
10
test/units/testsuite-77.service
Normal file
10
test/units/testsuite-77.service
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
[Unit]
|
||||||
|
Description=TEST-77-OPENFILE
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
OpenFile=/test-77-open.dat:open:read-only
|
||||||
|
OpenFile=/test-77-file.dat
|
||||||
|
ExecStartPre=rm -f /failed /testok
|
||||||
|
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||||
|
Type=oneshot
|
35
test/units/testsuite-77.sh
Executable file
35
test/units/testsuite-77.sh
Executable file
@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# shellcheck source=test/units/assert.sh
|
||||||
|
. "$(dirname "$0")"/assert.sh
|
||||||
|
|
||||||
|
export SYSTEMD_LOG_LEVEL=debug
|
||||||
|
|
||||||
|
assert_eq "$LISTEN_FDS" "2"
|
||||||
|
assert_eq "$LISTEN_FDNAMES" "open:test-77-file.dat"
|
||||||
|
read -r -u 3 text
|
||||||
|
assert_eq "$text" "Open"
|
||||||
|
read -r -u 4 text
|
||||||
|
assert_eq "$text" "File"
|
||||||
|
|
||||||
|
# Test for socket
|
||||||
|
systemctl start testsuite-77-netcat.service
|
||||||
|
systemctl start testsuite-77-socket.service
|
||||||
|
|
||||||
|
# Tests for D-Bus
|
||||||
|
diff <(systemctl show -p OpenFile testsuite-77) - <<EOF
|
||||||
|
OpenFile=/test-77-open.dat:open:read-only
|
||||||
|
OpenFile=/test-77-file.dat
|
||||||
|
EOF
|
||||||
|
echo "New" > /test-77-new-file.dat
|
||||||
|
systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
|
||||||
|
|
||||||
|
assert_rc 202 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only "$(dirname "$0")"/testsuite-77-run.sh
|
||||||
|
|
||||||
|
assert_rc 0 systemd-run --wait -p OpenFile=/test-77-new-file.dat:new-file:read-only -p OpenFile=/test-77-mssing-file.dat:missing-file:read-only,graceful "$(dirname "$0")"/testsuite-77-run.sh
|
||||||
|
|
||||||
|
# End
|
||||||
|
touch /testok
|
Loading…
Reference in New Issue
Block a user