return move_fd(fd, nfd, false);
}
+static int acquire_path(const char *path, int flags, mode_t mode) {
+ union sockaddr_union sa = {
+ .sa.sa_family = AF_UNIX,
+ };
+ int fd, r;
+
+ assert(path);
+
+ if (IN_SET(flags & O_ACCMODE, O_WRONLY, O_RDWR))
+ flags |= O_CREAT;
+
+ fd = open(path, flags|O_NOCTTY, mode);
+ if (fd >= 0)
+ return fd;
+
+ if (errno != ENXIO) /* ENXIO is returned when we try to open() an AF_UNIX file system socket on Linux */
+ return -errno;
+ if (strlen(path) > sizeof(sa.un.sun_path)) /* Too long, can't be a UNIX socket */
+ return -ENXIO;
+
+ /* So, it appears the specified path could be an AF_UNIX socket. Let's see if we can connect to it. */
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0)
+ return -errno;
+
+ strncpy(sa.un.sun_path, path, sizeof(sa.un.sun_path));
+ if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
+ safe_close(fd);
+ return errno == EINVAL ? -ENXIO : -errno; /* Propagate initial error if we get EINVAL, i.e. we have
+ * indication that his wasn't an AF_UNIX socket after all */
+ }
+
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ r = shutdown(fd, SHUT_WR);
+ else if ((flags & O_ACCMODE) == O_WRONLY)
+ r = shutdown(fd, SHUT_RD);
+ else
+ return fd;
+ if (r < 0) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ return fd;
+}
+
static int fixup_input(
const ExecContext *context,
int socket_fd,
return move_fd(fd, STDIN_FILENO, false);
}
+ case EXEC_INPUT_FILE: {
+ bool rw;
+ int fd;
+
+ assert(context->stdio_file[STDIN_FILENO]);
+
+ rw = (context->std_output == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDOUT_FILENO])) ||
+ (context->std_error == EXEC_OUTPUT_FILE && streq_ptr(context->stdio_file[STDIN_FILENO], context->stdio_file[STDERR_FILENO]));
+
+ fd = acquire_path(context->stdio_file[STDIN_FILENO], rw ? O_RDWR : O_RDONLY, 0666 & ~context->umask);
+ if (fd < 0)
+ return fd;
+
+ return move_fd(fd, STDIN_FILENO, false);
+ }
+
default:
assert_not_reached("Unknown input type");
}
(void) fd_nonblock(named_iofds[fileno], false);
return dup2(named_iofds[fileno], fileno) < 0 ? -errno : fileno;
+ case EXEC_OUTPUT_FILE: {
+ bool rw;
+ int fd;
+
+ assert(context->stdio_file[fileno]);
+
+ rw = context->std_input == EXEC_INPUT_FILE &&
+ streq_ptr(context->stdio_file[fileno], context->stdio_file[STDIN_FILENO]);
+
+ if (rw)
+ return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
+
+ fd = acquire_path(context->stdio_file[fileno], O_WRONLY, 0666 & ~context->umask);
+ if (fd < 0)
+ return fd;
+
+ return move_fd(fd, fileno, false);
+ }
+
default:
assert_not_reached("Unknown error type");
}
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
- for (l = 0; l < 3; l++)
+ for (l = 0; l < 3; l++) {
c->stdio_fdname[l] = mfree(c->stdio_fdname[l]);
+ c->stdio_file[l] = mfree(c->stdio_file[l]);
+ }
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
[EXEC_INPUT_SOCKET] = "socket",
[EXEC_INPUT_NAMED_FD] = "fd",
[EXEC_INPUT_DATA] = "data",
+ [EXEC_INPUT_FILE] = "file",
};
DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput);
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
[EXEC_OUTPUT_SOCKET] = "socket",
[EXEC_OUTPUT_NAMED_FD] = "fd",
+ [EXEC_OUTPUT_FILE] = "file",
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
void *userdata) {
ExecContext *c = data;
- const char *name;
+ Unit *u = userdata;
+ const char *n;
+ ExecInput ei;
int r;
assert(data);
assert(line);
assert(rvalue);
- name = startswith(rvalue, "fd:");
- if (name) {
- /* Strip prefix and validate fd name */
- if (!fdname_is_valid(name)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
- return 0;
+ n = startswith(rvalue, "fd:");
+ if (n) {
+ _cleanup_free_ char *resolved = NULL;
+
+ r = unit_full_printf(u, n, &resolved);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
+
+ if (isempty(resolved))
+ resolved = mfree(resolved);
+ else if (!fdname_is_valid(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
+ return -EINVAL;
}
- r = free_and_strdup(&c->stdio_fdname[STDIN_FILENO], name);
+ free_and_replace(c->stdio_fdname[STDIN_FILENO], resolved);
+
+ ei = EXEC_INPUT_NAMED_FD;
+
+ } else if ((n = startswith(rvalue, "file:"))) {
+ _cleanup_free_ char *resolved = NULL;
+
+ r = unit_full_printf(u, n, &resolved);
if (r < 0)
- return log_oom();
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
- c->std_input = EXEC_INPUT_NAMED_FD;
- } else {
- ExecInput ei;
+ if (!path_is_absolute(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
+ return -EINVAL;
+ }
+
+ if (!path_is_safe(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name: %s", resolved);
+ return -EINVAL;
+ }
+
+ free_and_replace(c->stdio_file[STDIN_FILENO], resolved);
+ ei = EXEC_INPUT_FILE;
+
+ } else {
ei = exec_input_from_string(rvalue);
if (ei < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse input specifier, ignoring: %s", rvalue);
return 0;
}
-
- c->std_input = ei;
}
+ c->std_input = ei;
return 0;
}
}
r = cunescape(rvalue, 0, &unescaped);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text, ignoring: %s", rvalue);
- return 0;
- }
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode C escaped text: %s", rvalue);
r = unit_full_printf(u, unescaped, &resolved);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", unescaped);
- return 0;
- }
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", unescaped);
sz = strlen(resolved);
if (c->stdin_data_size + sz + 1 < c->stdin_data_size || /* check for overflow */
c->stdin_data_size + sz + 1 > EXEC_STDIN_DATA_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
- return 0;
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
+ return -E2BIG;
}
p = realloc(c->stdin_data, c->stdin_data_size + sz + 1);
delete_chars(cleaned, WHITESPACE);
r = unbase64mem(cleaned, (size_t) -1, &p, &sz);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned);
- return 0;
- }
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to decode base64 data, ignoring: %s", cleaned);
assert(sz > 0);
if (c->stdin_data_size + sz < c->stdin_data_size || /* check for overflow */
c->stdin_data_size + sz > EXEC_STDIN_DATA_MAX) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
- return 0;
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Standard input data too large (%zu), maximum of %zu permitted, ignoring.", c->stdin_data_size + sz, (size_t) EXEC_STDIN_DATA_MAX);
+ return -E2BIG;
}
q = realloc(c->stdin_data, c->stdin_data_size + sz);
return 0;
}
-int config_parse_exec_output(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
+int config_parse_exec_output(
+ 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_free_ char *resolved = NULL;
+ const char *n;
ExecContext *c = data;
+ Unit *u = userdata;
ExecOutput eo;
- const char *name;
int r;
assert(data);
assert(lvalue);
assert(rvalue);
- name = startswith(rvalue, "fd:");
- if (name) {
- /* Strip prefix and validate fd name */
- if (!fdname_is_valid(name)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name, ignoring: %s", name);
- return 0;
+ n = startswith(rvalue, "fd:");
+ if (n) {
+ r = unit_full_printf(u, n, &resolved);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
+
+ if (isempty(resolved))
+ resolved = mfree(resolved);
+ else if (!fdname_is_valid(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid file descriptor name: %s", resolved);
+ return -EINVAL;
}
+
eo = EXEC_OUTPUT_NAMED_FD;
+
+ } else if ((n = startswith(rvalue, "file:"))) {
+
+ r = unit_full_printf(u, n, &resolved);
+ if (r < 0)
+ return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s: %m", n);
+
+ if (!path_is_absolute(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires an absolute path name: %s", resolved);
+ return -EINVAL;
+ }
+
+ if (!path_is_safe(resolved)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "file: requires a normalized path name, ignoring: %s", resolved);
+ return -EINVAL;
+ }
+
+ eo = EXEC_OUTPUT_FILE;
+
} else {
eo = exec_output_from_string(rvalue);
- if (eo == _EXEC_OUTPUT_INVALID) {
+ if (eo < 0) {
log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output specifier, ignoring: %s", rvalue);
return 0;
}
}
if (streq(lvalue, "StandardOutput")) {
+ if (eo == EXEC_OUTPUT_NAMED_FD)
+ free_and_replace(c->stdio_fdname[STDOUT_FILENO], resolved);
+ else
+ free_and_replace(c->stdio_file[STDOUT_FILENO], resolved);
+
c->std_output = eo;
- r = free_and_strdup(&c->stdio_fdname[STDOUT_FILENO], name);
- if (r < 0)
- log_oom();
- return r;
- } else if (streq(lvalue, "StandardError")) {
- c->std_error = eo;
- r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], name);
- if (r < 0)
- log_oom();
- return r;
+
} else {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output property, ignoring: %s", lvalue);
- return 0;
+ assert(streq(lvalue, "StandardError"));
+
+ if (eo == EXEC_OUTPUT_NAMED_FD)
+ free_and_replace(c->stdio_fdname[STDERR_FILENO], resolved);
+ else
+ free_and_replace(c->stdio_file[STDERR_FILENO], resolved);
+
+ c->std_error = eo;
}
+
+ return 0;
}
int config_parse_exec_io_class(const char *unit,
r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
goto finish;
+ } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) {
+ const char *n, *appended;
+
+ n = startswith(eq, "fd:");
+ if (n) {
+ appended = strjoina(field, "FileDescriptorName");
+ r = sd_bus_message_append(m, "sv", appended, "s", n);
+
+ } else if ((n = startswith(eq, "file:"))) {
+ appended = strjoina(field, "File");
+ r = sd_bus_message_append(m, "sv", appended, "s", n);
+ } else
+ r = sd_bus_message_append(m, "sv", field, "s", eq);
+
+ goto finish;
+
} else if (streq(field, "StandardInputText")) {
_cleanup_free_ char *unescaped = NULL;
} else if (STR_IN_SET(field,
"User", "Group", "DevicePolicy", "KillMode",
"UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
- "StandardInput", "StandardOutput", "StandardError",
"Description", "Slice", "Type", "WorkingDirectory",
"RootDirectory", "SyslogIdentifier", "ProtectSystem",
"ProtectHome", "SELinuxContext", "Restart", "RootImage",