core: add OpenFile setting
This commit is contained in:
committed by
Lennart Poettering
parent
81315baa68
commit
cd48e23f6a
@@ -17,6 +17,7 @@
|
||||
#include "fileio.h"
|
||||
#include "locale-util.h"
|
||||
#include "mount-util.h"
|
||||
#include "open-file.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.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_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(
|
||||
sd_bus *bus,
|
||||
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("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),
|
||||
|
||||
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),
|
||||
@@ -532,6 +562,56 @@ static int bus_service_set_transient_property(
|
||||
if (streq(name, "StandardErrorFileDescriptor"))
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+148
-6
@@ -151,11 +151,14 @@ static int shift_fds(int fds[], size_t n_fds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int flags_fds(const int fds[], size_t n_socket_fds, size_t n_storage_fds, bool nonblock) {
|
||||
size_t n_fds;
|
||||
static int flags_fds(
|
||||
const int fds[],
|
||||
size_t n_socket_fds,
|
||||
size_t n_fds,
|
||||
bool nonblock) {
|
||||
|
||||
int r;
|
||||
|
||||
n_fds = n_socket_fds + n_storage_fds;
|
||||
if (n_fds <= 0)
|
||||
return 0;
|
||||
|
||||
@@ -1805,6 +1808,7 @@ static int build_environment(
|
||||
const ExecContext *c,
|
||||
const ExecParameters *p,
|
||||
size_t n_fds,
|
||||
char **fdnames,
|
||||
const char *home,
|
||||
const char *username,
|
||||
const char *shell,
|
||||
@@ -1837,7 +1841,7 @@ static int build_environment(
|
||||
return -ENOMEM;
|
||||
our_env[n_env++] = x;
|
||||
|
||||
joined = strv_join(p->fd_names, ":");
|
||||
joined = strv_join(fdnames, ":");
|
||||
if (!joined)
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
Unit *unit,
|
||||
const ExecCommand *command,
|
||||
@@ -4114,7 +4235,7 @@ static int exec_child(
|
||||
DynamicCreds *dcreds,
|
||||
int socket_fd,
|
||||
const int named_iofds[static 3],
|
||||
int *fds,
|
||||
int *params_fds,
|
||||
size_t n_socket_fds,
|
||||
size_t n_storage_fds,
|
||||
char **files_env,
|
||||
@@ -4154,6 +4275,8 @@ static int exec_child(
|
||||
int secure_bits;
|
||||
_cleanup_free_ gid_t *gids_after_pam = NULL;
|
||||
int ngids_after_pam = 0;
|
||||
_cleanup_free_ int *fds = NULL;
|
||||
_cleanup_strv_free_ char **fdnames = NULL;
|
||||
|
||||
assert(unit);
|
||||
assert(command);
|
||||
@@ -4196,6 +4319,24 @@ static int exec_child(
|
||||
/* In case anything used libc syslog(), close this here, too */
|
||||
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];
|
||||
memcpy_safe(keep_fds, fds, n_fds * sizeof(int));
|
||||
n_keep_fds = n_fds;
|
||||
@@ -4551,6 +4692,7 @@ static int exec_child(
|
||||
context,
|
||||
params,
|
||||
n_fds,
|
||||
fdnames,
|
||||
home,
|
||||
username,
|
||||
shell,
|
||||
@@ -4843,7 +4985,7 @@ static int exec_child(
|
||||
if (r >= 0)
|
||||
r = shift_fds(fds, n_fds);
|
||||
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) {
|
||||
*exit_status = EXIT_FDS;
|
||||
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 "nsflags.h"
|
||||
#include "numa-util.h"
|
||||
#include "open-file.h"
|
||||
#include "path-util.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
@@ -427,6 +428,8 @@ struct ExecParameters {
|
||||
int exec_fd;
|
||||
|
||||
const char *notify_socket;
|
||||
|
||||
LIST_HEAD(OpenFile, open_files);
|
||||
};
|
||||
|
||||
#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.USBFunctionStrings, config_parse_unit_path_printf, 0, offsetof(Service, usb_function_strings)
|
||||
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') }}
|
||||
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "missing_ioprio.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "open-file.h"
|
||||
#include "parse-helpers.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
@@ -6532,3 +6533,39 @@ int config_parse_log_filter_patterns(
|
||||
|
||||
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_tty_size);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_log_filter_patterns);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_open_file);
|
||||
|
||||
/* gperf prototypes */
|
||||
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "load-fragment.h"
|
||||
#include "log.h"
|
||||
#include "manager.h"
|
||||
#include "open-file.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
@@ -360,6 +361,8 @@ static void service_done(Unit *u) {
|
||||
|
||||
assert(s);
|
||||
|
||||
open_file_free_many(&s->open_files);
|
||||
|
||||
s->pid_file = mfree(s->pid_file);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1528,6 +1546,8 @@ static int service_spawn_internal(
|
||||
if (r < 0)
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ typedef struct ServiceFDStore ServiceFDStore;
|
||||
|
||||
#include "exit-status.h"
|
||||
#include "kill.h"
|
||||
#include "open-file.h"
|
||||
#include "path.h"
|
||||
#include "ratelimit.h"
|
||||
#include "socket.h"
|
||||
@@ -215,6 +216,8 @@ struct Service {
|
||||
bool flush_n_restarts;
|
||||
|
||||
OOMPolicy oom_policy;
|
||||
|
||||
LIST_HEAD(OpenFile, open_files);
|
||||
};
|
||||
|
||||
static inline usec_t service_timeout_abort_usec(Service *s) {
|
||||
|
||||
Reference in New Issue
Block a user