1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-12-22 13:33:56 +03:00

Merge pull request #17143 from keszybz/late-exec-resolution-alt

Late exec resolution (subset)
This commit is contained in:
Lennart Poettering 2020-09-24 09:38:36 +02:00 committed by GitHub
commit bcaf20dc38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 366 additions and 310 deletions

View File

@ -729,8 +729,7 @@ int fd_duplicate_data_fd(int fd) {
if (f != 0)
return -errno;
safe_close(copy_fd);
copy_fd = TAKE_FD(tmp_fd);
CLOSE_AND_REPLACE(copy_fd, tmp_fd);
remains = mfree(remains);
remains_size = 0;
@ -863,8 +862,7 @@ int rearrange_stdio(int original_input_fd, int original_output_fd, int original_
goto finish;
}
safe_close(null_fd);
null_fd = copy;
CLOSE_AND_REPLACE(null_fd, copy);
}
}

View File

@ -93,6 +93,16 @@ static inline int make_null_stdio(void) {
_fd_; \
})
/* Like free_and_replace(), but for file descriptors */
#define CLOSE_AND_REPLACE(a, b) \
({ \
int *_fdp_ = &(a); \
safe_close(*_fdp_); \
*_fdp_ = TAKE_FD(b); \
0; \
})
int fd_reopen(int fd, int flags);
int read_nr_open(void);

View File

@ -28,14 +28,6 @@
#include "time-util.h"
#include "utf8.h"
bool path_is_absolute(const char *p) {
return p[0] == '/';
}
bool is_path(const char *p) {
return !!strchr(p, '/');
}
int path_split_and_make_absolute(const char *p, char ***ret) {
char **l;
int r;
@ -593,9 +585,9 @@ char* path_join_internal(const char *first, ...) {
return joined;
}
int find_binary(const char *name, char **ret) {
int find_executable_full(const char *name, bool use_path_envvar, char **ret) {
int last_error, r;
const char *p;
const char *p = NULL;
assert(name);
@ -612,11 +604,10 @@ int find_binary(const char *name, char **ret) {
return 0;
}
/**
* Plain getenv, not secure_getenv, because we want
* to actually allow the user to pick the binary.
*/
p = getenv("PATH");
if (use_path_envvar)
/* Plain getenv, not secure_getenv, because we want to actually allow the user to pick the
* binary. */
p = getenv("PATH");
if (!p)
p = DEFAULT_PATH;
@ -649,9 +640,7 @@ int find_binary(const char *name, char **ret) {
if (access(with_dash, X_OK) >= 0)
continue;
/**
* We can't just `continue` inverting this case, since we need to update last_error.
*/
/* We can't just `continue` inverting this case, since we need to update last_error. */
if (errno == ENOTDIR) {
/* Found it! */
if (ret)
@ -704,18 +693,17 @@ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool upd
return changed;
}
static int binary_is_good(const char *binary) {
static int executable_is_good(const char *executable) {
_cleanup_free_ char *p = NULL, *d = NULL;
int r;
r = find_binary(binary, &p);
r = find_executable(executable, &p);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
/* An fsck that is linked to /bin/true is a non-existent
* fsck */
/* An fsck that is linked to /bin/true is a non-existent fsck */
r = readlink_malloc(p, &d);
if (r == -EINVAL) /* not a symlink */
@ -738,7 +726,7 @@ int fsck_exists(const char *fstype) {
return -EINVAL;
checker = strjoina("fsck.", fstype);
return binary_is_good(checker);
return executable_is_good(checker);
}
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {

View File

@ -42,9 +42,17 @@
# define DEFAULT_USER_PATH DEFAULT_PATH
#endif
bool is_path(const char *p) _pure_;
static inline bool is_path(const char *p) {
assert(p);
return strchr(p, '/');
}
static inline bool path_is_absolute(const char *p) {
assert(p);
return p[0] == '/';
}
int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
@ -80,7 +88,10 @@ int path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *root);
char** path_strv_resolve_uniq(char **l, const char *root);
int find_binary(const char *name, char **filename);
int find_executable_full(const char *name, bool use_path_envvar, char **ret);
static inline int find_executable(const char *name, char **ret) {
return find_executable_full(name, true, ret);
}
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);

View File

@ -1354,8 +1354,10 @@ int bus_set_transient_exec_command(
if (r < 0)
return r;
if (!path_is_absolute(path))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
if (!path_is_absolute(path) && !filename_is_valid(path))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"\"%s\" is neither a valid executable name nor an absolute path",
path);
r = sd_bus_message_read_strv(message, &argv);
if (r < 0)

View File

@ -2881,12 +2881,11 @@ static int setup_credentials(
#if ENABLE_SMACK
static int setup_smack(
const ExecContext *context,
const ExecCommand *command) {
const char *executable) {
int r;
assert(context);
assert(command);
assert(executable);
if (context->smack_process_label) {
r = mac_smack_apply_pid(0, context->smack_process_label);
@ -2897,7 +2896,7 @@ static int setup_smack(
else {
_cleanup_free_ char *exec_label = NULL;
r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
r = mac_smack_read(executable, SMACK_ATTR_EXEC, &exec_label);
if (r < 0 && !IN_SET(r, -ENODATA, -EOPNOTSUPP))
return r;
@ -3092,7 +3091,7 @@ static bool insist_on_sandboxing(
static int apply_mount_namespace(
const Unit *u,
const ExecCommand *command,
ExecCommandFlags command_flags,
const ExecContext *context,
const ExecParameters *params,
const ExecRuntime *runtime,
@ -3121,7 +3120,7 @@ static int apply_mount_namespace(
if (r < 0)
return r;
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED);
needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command_flags & EXEC_COMMAND_FULLY_PRIVILEGED);
if (needs_sandboxing) {
/* The runtime struct only contains the parent of the private /tmp,
* which is non-accessible to world users. Inside of it there's a /tmp
@ -3700,11 +3699,11 @@ static int exec_child(
return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
}
if (!context->same_pgrp)
if (setsid() < 0) {
*exit_status = EXIT_SETSID;
return log_unit_error_errno(unit, errno, "Failed to create new process session: %m");
}
if (!context->same_pgrp &&
setsid() < 0) {
*exit_status = EXIT_SETSID;
return log_unit_error_errno(unit, errno, "Failed to create new process session: %m");
}
exec_context_tty_reset(context, params);
@ -3725,8 +3724,8 @@ static int exec_child(
return 0;
}
*exit_status = EXIT_CONFIRM;
log_unit_error(unit, "Execution cancelled by the user");
return -ECANCELED;
return log_unit_error_errno(unit, SYNTHETIC_ERRNO(ECANCELED),
"Execution cancelled by the user");
}
}
@ -4085,47 +4084,33 @@ static int exec_child(
}
}
if (needs_setuid) {
if (needs_setuid && context->pam_name && username) {
/* Let's call into PAM after we set up our own idea of resource limits to that pam_limits
* wins here. (See above.) */
if (context->pam_name && username) {
r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_PAM;
return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
}
r = setup_pam(context->pam_name, username, uid, gid, context->tty_path, &accum_env, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_PAM;
return log_unit_error_errno(unit, r, "Failed to set up PAM session: %m");
}
ngids_after_pam = getgroups_alloc(&gids_after_pam);
if (ngids_after_pam < 0) {
*exit_status = EXIT_MEMORY;
return log_unit_error_errno(unit, ngids_after_pam, "Failed to obtain groups after setting up PAM: %m");
}
ngids_after_pam = getgroups_alloc(&gids_after_pam);
if (ngids_after_pam < 0) {
*exit_status = EXIT_MEMORY;
return log_unit_error_errno(unit, ngids_after_pam, "Failed to obtain groups after setting up PAM: %m");
}
}
if (needs_sandboxing) {
#if HAVE_SELINUX
if (use_selinux && params->selinux_context_net && socket_fd >= 0) {
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
}
}
#endif
if (needs_sandboxing && context->private_users && !have_effective_cap(CAP_SYS_ADMIN)) {
/* If we're unprivileged, set up the user namespace first to enable use of the other namespaces.
* Users with CAP_SYS_ADMIN can set up user namespaces last because they will be able to
* set up the all of the other namespaces (i.e. network, mount, UTS) without a user namespace. */
if (context->private_users && !have_effective_cap(CAP_SYS_ADMIN)) {
userns_set_up = true;
r = setup_private_users(saved_uid, saved_gid, uid, gid);
if (r < 0) {
*exit_status = EXIT_USER;
return log_unit_error_errno(unit, r, "Failed to set up user namespacing for unprivileged user: %m");
}
userns_set_up = true;
r = setup_private_users(saved_uid, saved_gid, uid, gid);
if (r < 0) {
*exit_status = EXIT_USER;
return log_unit_error_errno(unit, r, "Failed to set up user namespacing for unprivileged user: %m");
}
}
@ -4152,7 +4137,7 @@ static int exec_child(
if (needs_mount_namespace) {
_cleanup_free_ char *error_path = NULL;
r = apply_mount_namespace(unit, command, context, params, runtime, &error_path);
r = apply_mount_namespace(unit, command->flags, context, params, runtime, &error_path);
if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return log_unit_error_errno(unit, r, "Failed to set up mount namespacing%s%s: %m",
@ -4206,6 +4191,43 @@ static int exec_child(
}
}
/* Now that the mount namespace has been set up and privileges adjusted, let's look for the thing we
* shall execute. */
_cleanup_free_ char *executable = NULL;
r = find_executable_full(command->path, false, &executable);
if (r < 0) {
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
log_struct_errno(LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit),
LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m",
command->path),
"EXECUTABLE=%s", command->path);
return 0;
}
*exit_status = EXIT_EXEC;
return log_struct_errno(LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit),
LOG_UNIT_MESSAGE(unit, "Failed to locate executable %s: %m",
command->path),
"EXECUTABLE=%s", command->path);
}
#if HAVE_SELINUX
if (needs_sandboxing && use_selinux && params->selinux_context_net && socket_fd >= 0) {
r = mac_selinux_get_child_mls_label(socket_fd, executable, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return log_unit_error_errno(unit, r, "Failed to determine SELinux context: %m");
}
}
#endif
/* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are
* more aggressive this time since socket_fd and the netns fds we don't need anymore. We do keep the exec_fd
* however if we have it as we want to keep it open until the final execve(). */
@ -4225,8 +4247,7 @@ static int exec_child(
return log_unit_error_errno(unit, errno, "Couldn't move exec fd up: %m");
}
safe_close(exec_fd);
exec_fd = moved_fd;
CLOSE_AND_REPLACE(exec_fd, moved_fd);
} else {
/* This fd should be FD_CLOEXEC already, but let's make sure. */
r = fd_cloexec(exec_fd, true);
@ -4279,7 +4300,7 @@ static int exec_child(
/* LSM Smack needs the capability CAP_MAC_ADMIN to change the current execution security context of the
* process. This is the latest place before dropping capabilities. Other MAC context are set later. */
if (use_smack) {
r = setup_smack(context, command);
r = setup_smack(context, executable);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
return log_unit_error_errno(unit, r, "Failed to set SMACK process label: %m");
@ -4531,7 +4552,7 @@ static int exec_child(
line = exec_command_line(final_argv);
if (line)
log_struct(LOG_DEBUG,
"EXECUTABLE=%s", command->path,
"EXECUTABLE=%s", executable,
LOG_UNIT_MESSAGE(unit, "Executing: %s", line),
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit));
@ -4549,7 +4570,7 @@ static int exec_child(
}
}
execve(command->path, final_argv, accum_env);
execve(executable, final_argv, accum_env);
r = -errno;
if (exec_fd >= 0) {
@ -4564,19 +4585,8 @@ static int exec_child(
}
}
if (r == -ENOENT && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
log_struct_errno(LOG_INFO, r,
"MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR,
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit),
LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m",
command->path),
"EXECUTABLE=%s", command->path);
return 0;
}
*exit_status = EXIT_EXEC;
return log_unit_error_errno(unit, r, "Failed to execute command: %m");
return log_unit_error_errno(unit, r, "Failed to execute %s: %m", executable);
}
static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l);
@ -4638,13 +4648,16 @@ int exec_spawn(Unit *unit,
if (!line)
return log_oom();
/* fork with up-to-date SELinux label database, so the child inherits the up-to-date db
and, until the next SELinux policy changes, we safe further reloads in future children */
/* Fork with up-to-date SELinux label database, so the child inherits the up-to-date db
and, until the next SELinux policy changes, we save further reloads in future children. */
mac_selinux_maybe_reload();
log_struct(LOG_DEBUG,
LOG_UNIT_MESSAGE(unit, "About to execute: %s", line),
"EXECUTABLE=%s", command->path,
LOG_UNIT_MESSAGE(unit, "About to execute %s", line),
"EXECUTABLE=%s", command->path, /* We won't know the real executable path until we create
the mount namespace in the child, but we want to log
from the parent, so we need to use the (possibly
inaccurate) path here. */
LOG_UNIT_ID(unit),
LOG_UNIT_INVOCATION_ID(unit));

View File

@ -721,6 +721,12 @@ int config_parse_exec(
if (r <= 0)
return 0;
/* A lone ";" is a separator. Let's make sure we don't treat it as an executable name. */
if (streq(firstword, ";")) {
semicolon = true;
continue;
}
f = firstword;
for (;;) {
/* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't
@ -783,38 +789,11 @@ int config_parse_exec(
return ignore ? 0 : -ENOEXEC;
}
if (!path_is_absolute(path)) {
const char *prefix;
bool found = false;
if (!filename_is_valid(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Neither a valid executable name nor an absolute path%s: %s",
ignore ? ", ignoring" : "", path);
return ignore ? 0 : -ENOEXEC;
}
/* Resolve a single-component name to a full path */
NULSTR_FOREACH(prefix, DEFAULT_PATH_NULSTR) {
_cleanup_free_ char *fullpath = NULL;
fullpath = path_join(prefix, path);
if (!fullpath)
return log_oom();
if (access(fullpath, F_OK) >= 0) {
free_and_replace(path, fullpath);
found = true;
break;
}
}
if (!found) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Executable \"%s\" not found in path \"%s\"%s",
path, DEFAULT_PATH, ignore ? ", ignoring" : "");
return ignore ? 0 : -ENOEXEC;
}
if (!path_is_absolute(path) && !filename_is_valid(path)) {
log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
"Neither a valid executable name nor an absolute path%s: %s",
ignore ? ", ignoring" : "", path);
return ignore ? 0 : -ENOEXEC;
}
if (!separate_argv0) {

View File

@ -1973,10 +1973,9 @@ int manager_load_unit_prepare(
assert(m);
assert(_ret);
/* This will prepare the unit for loading, but not actually
* load anything from disk. */
/* This will prepare the unit for loading, but not actually load anything from disk. */
if (path && !is_path(path))
if (path && !path_is_absolute(path))
return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
if (!name) {

View File

@ -185,9 +185,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) {
(void) unlink(i->temp_path);
free_and_replace(i->temp_path, t);
safe_close(i->output_fd);
i->output_fd = TAKE_FD(converted_fd);
CLOSE_AND_REPLACE(i->output_fd, converted_fd);
return 1;
}

View File

@ -253,9 +253,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) {
(void) unlink(i->temp_path);
free_and_replace(i->temp_path, t);
safe_close(i->raw_job->disk_fd);
i->raw_job->disk_fd = TAKE_FD(converted_fd);
CLOSE_AND_REPLACE(i->raw_job->disk_fd, converted_fd);
return 1;
}

View File

@ -176,7 +176,7 @@ _unused_ static void test_compress_stream(const char *compression,
_cleanup_free_ char *cmd = NULL, *cmd2 = NULL;
struct stat st = {};
r = find_binary(cat, NULL);
r = find_executable(cat, NULL);
if (r < 0) {
log_error_errno(r, "Skipping %s, could not find %s binary: %m", __func__, cat);
return;

View File

@ -471,8 +471,7 @@ int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
if (r < 0)
return r;
safe_close(acd->fd);
acd->fd = r;
CLOSE_AND_REPLACE(acd->fd, r);
acd->defend_window = 0;
if (reset_conflicts)

View File

@ -195,8 +195,7 @@ static int session_device_start(SessionDevice *sd) {
/* For evdev devices, the file descriptor might be left uninitialized. This might happen while resuming
* into a session and logind has been restarted right before. */
safe_close(sd->fd);
sd->fd = r;
CLOSE_AND_REPLACE(sd->fd, r);
break;
case DEVICE_TYPE_UNKNOWN:

View File

@ -1720,12 +1720,16 @@ static int run(int argc, char* argv[]) {
if (r <= 0)
return r;
if (!strv_isempty(arg_cmdline) && arg_transport == BUS_TRANSPORT_LOCAL) {
if (!strv_isempty(arg_cmdline) &&
arg_transport == BUS_TRANSPORT_LOCAL &&
!strv_find_startswith(arg_property, "RootDirectory=") &&
!strv_find_startswith(arg_property, "RootImage=")) {
/* Patch in an absolute path to fail early for user convenience, but only when we can do it
* (i.e. we will be running from the same file system). This also uses the user's $PATH,
* while we use a fixed search path in the manager. */
_cleanup_free_ char *command = NULL;
/* Patch in an absolute path */
r = find_binary(arg_cmdline[0], &command);
r = find_executable(arg_cmdline[0], &command);
if (r < 0)
return log_error_errno(r, "Failed to find executable %s: %m", arg_cmdline[0]);

View File

@ -20,7 +20,7 @@ int mkfs_exists(const char *fstype) {
if (!filename_is_valid(mkfs)) /* refuse file system types with slashes and similar */
return -EINVAL;
r = find_binary(mkfs, NULL);
r = find_executable(mkfs, NULL);
if (r == -ENOENT)
return false;
if (r < 0)
@ -44,7 +44,7 @@ int make_filesystem(
assert(label);
if (streq(fstype, "swap")) {
r = find_binary("mkswap", &mkfs);
r = find_executable("mkswap", &mkfs);
if (r == -ENOENT)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkswap binary not available.");
if (r < 0)

View File

@ -71,7 +71,8 @@ static void wait_for_service_finish(Manager *m, Unit *unit) {
}
}
static void check_main_result(const char *func, Manager *m, Unit *unit, int status_expected, int code_expected) {
static void check_main_result(const char *file, unsigned line, const char *func,
Manager *m, Unit *unit, int status_expected, int code_expected) {
Service *service = NULL;
assert_se(m);
@ -83,21 +84,23 @@ static void check_main_result(const char *func, Manager *m, Unit *unit, int stat
exec_status_dump(&service->main_exec_status, stdout, "\t");
if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
log_error("%s: %s: exit code %d, expected %d",
func, unit->id,
log_error("%s:%u:%s %s: exit code %d, expected %d",
file, line, func,
unit->id,
service->main_exec_status.code, code_expected);
abort();
}
if (service->main_exec_status.status != status_expected) {
log_error("%s: %s: exit status %d, expected %d",
func, unit->id,
log_error("%s:%u:%s: %s: exit status %d, expected %d",
file, line, func, unit->id,
service->main_exec_status.status, status_expected);
abort();
}
}
static void check_service_result(const char *func, Manager *m, Unit *unit, ServiceResult result_expected) {
static void check_service_result(const char *file, unsigned line, const char *func,
Manager *m, Unit *unit, ServiceResult result_expected) {
Service *service = NULL;
assert_se(m);
@ -108,8 +111,9 @@ static void check_service_result(const char *func, Manager *m, Unit *unit, Servi
service = SERVICE(unit);
if (service->result != result_expected) {
log_error("%s: %s: service end result %s, expected %s",
func, unit->id,
log_error("%s:%u:%s: %s: service end result %s, expected %s",
file, line, func,
unit->id,
service_result_to_string(service->result),
service_result_to_string(result_expected));
abort();
@ -198,31 +202,37 @@ static bool is_inaccessible_available(void) {
return true;
}
static void test(const char *func, Manager *m, const char *unit_name, int status_expected, int code_expected) {
static void _test(const char *file, unsigned line, const char *func,
Manager *m, const char *unit_name, int status_expected, int code_expected) {
Unit *unit;
assert_se(unit_name);
assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
assert_se(unit_start(unit) >= 0);
check_main_result(func, m, unit, status_expected, code_expected);
check_main_result(file, line, func, m, unit, status_expected, code_expected);
}
#define test(m, unit_name, status_expected, code_expected) \
_test(PROJECT_FILE, __LINE__, __func__, m, unit_name, status_expected, code_expected)
static void test_service(const char *func, Manager *m, const char *unit_name, ServiceResult result_expected) {
static void _test_service(const char *file, unsigned line, const char *func,
Manager *m, const char *unit_name, ServiceResult result_expected) {
Unit *unit;
assert_se(unit_name);
assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0);
assert_se(unit_start(unit) >= 0);
check_service_result(func, m, unit, result_expected);
check_service_result(file, line, func, m, unit, result_expected);
}
#define test_service(m, unit_name, result_expected) \
_test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
static void test_exec_bindpaths(Manager *m) {
assert_se(mkdir_p("/tmp/test-exec-bindpaths", 0755) >= 0);
assert_se(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755) >= 0);
test(__func__, m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
(void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
@ -239,8 +249,8 @@ static void test_exec_cpuaffinity(Manager *m) {
return;
}
test(__func__, m, "exec-cpuaffinity1.service", 0, CLD_EXITED);
test(__func__, m, "exec-cpuaffinity2.service", 0, CLD_EXITED);
test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED);
test(m, "exec-cpuaffinity2.service", 0, CLD_EXITED);
if (!CPU_ISSET_S(1, c.allocated, c.set) ||
!CPU_ISSET_S(2, c.allocated, c.set)) {
@ -248,53 +258,53 @@ static void test_exec_cpuaffinity(Manager *m) {
return;
}
test(__func__, m, "exec-cpuaffinity3.service", 0, CLD_EXITED);
test(m, "exec-cpuaffinity3.service", 0, CLD_EXITED);
}
static void test_exec_workingdirectory(Manager *m) {
assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0);
test(__func__, m, "exec-workingdirectory.service", 0, CLD_EXITED);
test(__func__, m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
(void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
}
static void test_exec_personality(Manager *m) {
#if defined(__x86_64__)
test(__func__, m, "exec-personality-x86-64.service", 0, CLD_EXITED);
test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
#elif defined(__s390__)
test(__func__, m, "exec-personality-s390.service", 0, CLD_EXITED);
test(m, "exec-personality-s390.service", 0, CLD_EXITED);
#elif defined(__powerpc64__)
# if __BYTE_ORDER == __BIG_ENDIAN
test(__func__, m, "exec-personality-ppc64.service", 0, CLD_EXITED);
test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
# else
test(__func__, m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
# endif
#elif defined(__aarch64__)
test(__func__, m, "exec-personality-aarch64.service", 0, CLD_EXITED);
test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
#elif defined(__i386__)
test(__func__, m, "exec-personality-x86.service", 0, CLD_EXITED);
test(m, "exec-personality-x86.service", 0, CLD_EXITED);
#else
log_notice("Unknown personality, skipping %s", __func__);
#endif
}
static void test_exec_ignoresigpipe(Manager *m) {
test(__func__, m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
test(__func__, m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
}
static void test_exec_privatetmp(Manager *m) {
assert_se(touch("/tmp/test-exec_privatetmp") >= 0);
test(__func__, m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-privatetmp-no.service", 0, CLD_EXITED);
test(__func__, m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
test(m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
unlink("/tmp/test-exec_privatetmp");
}
@ -311,23 +321,23 @@ static void test_exec_privatedevices(Manager *m) {
return;
}
test(__func__, m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-privatedevices-no.service", 0, CLD_EXITED);
test(__func__, m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
/* We use capsh to test if the capabilities are
* properly set, so be sure that it exists */
r = find_binary("capsh", NULL);
r = find_executable("capsh", NULL);
if (r < 0) {
log_notice_errno(r, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__);
return;
}
test(__func__, m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
test(__func__, m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
test(__func__, m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
test(__func__, m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED);
test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED);
}
static void test_exec_protecthome(Manager *m) {
@ -336,7 +346,7 @@ static void test_exec_protecthome(Manager *m) {
return;
}
test(__func__, m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED);
test(m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED);
}
static void test_exec_protectkernelmodules(Manager *m) {
@ -351,29 +361,29 @@ static void test_exec_protectkernelmodules(Manager *m) {
return;
}
r = find_binary("capsh", NULL);
r = find_executable("capsh", NULL);
if (r < 0) {
log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
return;
}
test(__func__, m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
test(__func__, m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
test(__func__, m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED);
test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_readonlypaths(Manager *m) {
test(__func__, m, "exec-readonlypaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-readonlypaths-simple.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
if (path_is_read_only_fs("/var") > 0) {
log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
return;
}
test(__func__, m, "exec-readonlypaths.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-readonlypaths.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_readwritepaths(Manager *m) {
@ -383,7 +393,7 @@ static void test_exec_readwritepaths(Manager *m) {
return;
}
test(__func__, m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_inaccessiblepaths(Manager *m) {
@ -393,22 +403,22 @@ static void test_exec_inaccessiblepaths(Manager *m) {
return;
}
test(__func__, m, "exec-inaccessiblepaths-sys.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-inaccessiblepaths-sys.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
if (path_is_read_only_fs("/") > 0) {
log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
return;
}
test(__func__, m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
}
static void test_exec_temporaryfilesystem(Manager *m) {
test(__func__, m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
}
static void test_exec_systemcallfilter(Manager *m) {
@ -420,22 +430,22 @@ static void test_exec_systemcallfilter(Manager *m) {
return;
}
test(__func__, m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
test(__func__, m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
r = find_binary("python3", NULL);
r = find_executable("python3", NULL);
if (r < 0) {
log_notice_errno(r, "Skipping remaining tests in %s, could not find python3 binary: %m", __func__);
return;
}
test(__func__, m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
test(__func__, m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
test(m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
test(m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
#endif
}
@ -448,14 +458,14 @@ static void test_exec_systemcallerrornumber(Manager *m) {
return;
}
r = find_binary("python3", NULL);
r = find_executable("python3", NULL);
if (r < 0) {
log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__);
return;
}
test(__func__, m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
test(__func__, m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
#endif
}
@ -466,13 +476,13 @@ static void test_exec_restrictnamespaces(Manager *m) {
return;
}
test(__func__, m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(__func__, m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
test(m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED);
test(m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
test(m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
#endif
}
@ -488,7 +498,7 @@ static void test_exec_systemcallfilter_system(Manager *m) {
return;
}
test(__func__, m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@ -500,12 +510,12 @@ static void test_exec_systemcallfilter_system(Manager *m) {
return;
}
test(__func__, m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
#endif
}
static void test_exec_user(Manager *m) {
test(__func__, m, "exec-user.service", 0, CLD_EXITED);
test(m, "exec-user.service", 0, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@ -517,11 +527,11 @@ static void test_exec_user(Manager *m) {
return;
}
test(__func__, m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
}
static void test_exec_group(Manager *m) {
test(__func__, m, "exec-group.service", 0, CLD_EXITED);
test(m, "exec-group.service", 0, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@ -533,16 +543,16 @@ static void test_exec_group(Manager *m) {
return;
}
test(__func__, m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
}
static void test_exec_supplementarygroups(Manager *m) {
test(__func__, m, "exec-supplementarygroups.service", 0, CLD_EXITED);
test(__func__, m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
test(__func__, m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
test(__func__, m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
test(__func__, m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
test(__func__, m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED);
test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED);
}
static char* private_directory_bad(Manager *m) {
@ -569,14 +579,14 @@ static void test_exec_dynamicuser(Manager *m) {
return;
}
test(__func__, m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
if (check_user_has_group_with_same_name("adm"))
test(__func__, m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-fixeduser-adm.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
if (check_user_has_group_with_same_name("games"))
test(__func__, m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-statedir.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
(void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
@ -587,25 +597,25 @@ static void test_exec_dynamicuser(Manager *m) {
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
test(m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
(void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
(void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
test(__func__, m, "exec-dynamicuser-runtimedirectory1.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-runtimedirectory2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(__func__, m, "exec-dynamicuser-runtimedirectory3.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-runtimedirectory1.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-runtimedirectory2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
test(m, "exec-dynamicuser-runtimedirectory3.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
}
static void test_exec_environment(Manager *m) {
test(__func__, m, "exec-environment-no-substitute.service", 0, CLD_EXITED);
test(__func__, m, "exec-environment.service", 0, CLD_EXITED);
test(__func__, m, "exec-environment-multiple.service", 0, CLD_EXITED);
test(__func__, m, "exec-environment-empty.service", 0, CLD_EXITED);
test(m, "exec-environment-no-substitute.service", 0, CLD_EXITED);
test(m, "exec-environment.service", 0, CLD_EXITED);
test(m, "exec-environment-multiple.service", 0, CLD_EXITED);
test(m, "exec-environment-empty.service", 0, CLD_EXITED);
}
static void test_exec_environmentfile(Manager *m) {
@ -625,7 +635,7 @@ static void test_exec_environmentfile(Manager *m) {
r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
assert_se(r == 0);
test(__func__, m, "exec-environmentfile.service", 0, CLD_EXITED);
test(m, "exec-environmentfile.service", 0, CLD_EXITED);
(void) unlink("/tmp/test-exec_environmentfile.conf");
}
@ -647,26 +657,26 @@ static void test_exec_passenvironment(Manager *m) {
assert_se(setenv("VAR3", "$word 5 6", 1) == 0);
assert_se(setenv("VAR4", "new\nline", 1) == 0);
assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0);
test(__func__, m, "exec-passenvironment.service", 0, CLD_EXITED);
test(__func__, m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
test(__func__, m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
test(m, "exec-passenvironment.service", 0, CLD_EXITED);
test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
assert_se(unsetenv("VAR1") == 0);
assert_se(unsetenv("VAR2") == 0);
assert_se(unsetenv("VAR3") == 0);
assert_se(unsetenv("VAR4") == 0);
assert_se(unsetenv("VAR5") == 0);
test(__func__, m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
}
static void test_exec_umask(Manager *m) {
test(__func__, m, "exec-umask-default.service", 0, CLD_EXITED);
test(__func__, m, "exec-umask-0177.service", 0, CLD_EXITED);
test(m, "exec-umask-default.service", 0, CLD_EXITED);
test(m, "exec-umask-0177.service", 0, CLD_EXITED);
}
static void test_exec_runtimedirectory(Manager *m) {
test(__func__, m, "exec-runtimedirectory.service", 0, CLD_EXITED);
test(__func__, m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
test(__func__, m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@ -678,13 +688,13 @@ static void test_exec_runtimedirectory(Manager *m) {
return;
}
test(__func__, m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED);
}
static void test_exec_capabilityboundingset(Manager *m) {
int r;
r = find_binary("capsh", NULL);
r = find_executable("capsh", NULL);
if (r < 0) {
log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
return;
@ -697,14 +707,14 @@ static void test_exec_capabilityboundingset(Manager *m) {
return;
}
test(__func__, m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
test(__func__, m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
test(__func__, m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
test(__func__, m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
}
static void test_exec_basic(Manager *m) {
test(__func__, m, "exec-basic.service", 0, CLD_EXITED);
test(m, "exec-basic.service", 0, CLD_EXITED);
}
static void test_exec_ambientcapabilities(Manager *m) {
@ -726,8 +736,8 @@ static void test_exec_ambientcapabilities(Manager *m) {
return;
}
test(__func__, m, "exec-ambientcapabilities.service", 0, CLD_EXITED);
test(__func__, m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED);
test(m, "exec-ambientcapabilities.service", 0, CLD_EXITED);
test(m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED);
if (!check_nobody_user_and_group()) {
log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
@ -739,71 +749,71 @@ static void test_exec_ambientcapabilities(Manager *m) {
return;
}
test(__func__, m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
test(__func__, m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
test(m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
test(m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
}
static void test_exec_privatenetwork(Manager *m) {
int r;
r = find_binary("ip", NULL);
r = find_executable("ip", NULL);
if (r < 0) {
log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
return;
}
test(__func__, m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED);
}
static void test_exec_oomscoreadjust(Manager *m) {
test(__func__, m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
if (detect_container() > 0) {
log_notice("Testing in container, skipping remaining tests in %s", __func__);
return;
}
test(__func__, m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED);
}
static void test_exec_ioschedulingclass(Manager *m) {
test(__func__, m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
test(__func__, m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
test(__func__, m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
if (detect_container() > 0) {
log_notice("Testing in container, skipping remaining tests in %s", __func__);
return;
}
test(__func__, m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED);
}
static void test_exec_unsetenvironment(Manager *m) {
test(__func__, m, "exec-unsetenvironment.service", 0, CLD_EXITED);
test(m, "exec-unsetenvironment.service", 0, CLD_EXITED);
}
static void test_exec_specifier(Manager *m) {
test(__func__, m, "exec-specifier.service", 0, CLD_EXITED);
test(__func__, m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
test(__func__, m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
test(m, "exec-specifier.service", 0, CLD_EXITED);
test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
}
static void test_exec_standardinput(Manager *m) {
test(__func__, m, "exec-standardinput-data.service", 0, CLD_EXITED);
test(__func__, m, "exec-standardinput-file.service", 0, CLD_EXITED);
test(__func__, m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
test(m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
}
static void test_exec_standardoutput(Manager *m) {
test(__func__, m, "exec-standardoutput-file.service", 0, CLD_EXITED);
test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
}
static void test_exec_standardoutput_append(Manager *m) {
test(__func__, m, "exec-standardoutput-append.service", 0, CLD_EXITED);
test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
}
static void test_exec_condition(Manager *m) {
test_service(__func__, m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
test_service(__func__, m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
test_service(m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
}
typedef struct test_entry {

View File

@ -202,12 +202,11 @@ static void test_config_parse_exec(void) {
"-@/RValue argv0 r1 ; ; "
"/goo/goo boo",
&c, u);
assert_se(r == -ENOEXEC);
assert_se(r >= 0);
c1 = c1->command_next;
check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
/* second command fails because the executable name is ";" */
assert_se(c1->command_next == NULL);
c1 = c1->command_next;
check_execcommand(c1, "/goo/goo", "/goo/goo", "boo", NULL, false);
log_info("/* trailing semicolon */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,

View File

@ -164,31 +164,76 @@ static void test_path_equal_root(void) {
assert_se(!path_equal_or_files_same("/", "/.../", AT_SYMLINK_NOFOLLOW));
}
static void test_find_binary(const char *self) {
static void test_find_executable_full(void) {
char *p;
log_info("/* %s */", __func__);
assert_se(find_binary("/bin/sh", &p) == 0);
assert_se(find_executable_full("sh", true, &p) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
assert_se(find_executable_full("sh", false, &p) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
_cleanup_free_ char *oldpath = NULL;
p = getenv("PATH");
if (p)
assert_se(oldpath = strdup(p));
assert_se(unsetenv("PATH") >= 0);
assert_se(find_executable_full("sh", true, &p) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
assert_se(find_executable_full("sh", false, &p) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
if (oldpath)
assert_se(setenv("PATH", oldpath, true) >= 0);
}
static void test_find_executable(const char *self) {
char *p;
log_info("/* %s */", __func__);
assert_se(find_executable("/bin/sh", &p) == 0);
puts(p);
assert_se(path_equal(p, "/bin/sh"));
free(p);
assert_se(find_binary(self, &p) == 0);
assert_se(find_executable(self, &p) == 0);
puts(p);
/* libtool might prefix the binary name with "lt-" */
assert_se(endswith(p, "/lt-test-path-util") || endswith(p, "/test-path-util"));
assert_se(endswith(p, "/test-path-util"));
assert_se(path_is_absolute(p));
free(p);
assert_se(find_binary("sh", &p) == 0);
assert_se(find_executable("sh", &p) == 0);
puts(p);
assert_se(endswith(p, "/sh"));
assert_se(path_is_absolute(p));
free(p);
assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT);
assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT);
assert_se(find_executable("/bin/touch", &p) == 0);
assert_se(streq(p, "/bin/touch"));
free(p);
assert_se(find_executable("touch", &p) == 0);
assert_se(path_is_absolute(p));
assert_se(streq(basename(p), "touch"));
free(p);
assert_se(find_executable("xxxx-xxxx", &p) == -ENOENT);
assert_se(find_executable("/some/dir/xxxx-xxxx", &p) == -ENOENT);
assert_se(find_executable("/proc/filesystems", &p) == -EACCES);
}
static void test_prefixes(void) {
@ -537,6 +582,9 @@ static void test_filename_is_valid(void) {
assert_se(!filename_is_valid("/"));
assert_se(!filename_is_valid("."));
assert_se(!filename_is_valid(".."));
assert_se(!filename_is_valid("bar/foo"));
assert_se(!filename_is_valid("bar/foo/"));
assert_se(!filename_is_valid("bar//"));
for (i=0; i<FILENAME_MAX+1; i++)
foo[i] = 'a';
@ -667,7 +715,8 @@ int main(int argc, char **argv) {
test_print_paths();
test_path();
test_path_equal_root();
test_find_binary(argv[0]);
test_find_executable_full();
test_find_executable(argv[0]);
test_prefixes();
test_path_join();
test_fsck_exists();

View File

@ -405,7 +405,7 @@ int xdg_autostart_format_exec_start(
/* This is the executable, find it in $PATH */
first_arg = false;
r = find_binary(c, &executable);
r = find_executable(c, &executable);
if (r < 0)
return log_info_errno(r, "Exec binary '%s' does not exist: %m", c);
@ -481,7 +481,7 @@ static int xdg_autostart_generate_desktop_condition(
if (!isempty(condition)) {
_cleanup_free_ char *gnome_autostart_condition_path = NULL, *e_autostart_condition = NULL;
r = find_binary(test_binary, &gnome_autostart_condition_path);
r = find_executable(test_binary, &gnome_autostart_condition_path);
if (r < 0) {
log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r,
"%s not found: %m", test_binary);
@ -536,10 +536,10 @@ int xdg_autostart_service_generate_unit(
/*
* The TryExec key cannot be checked properly from the systemd unit,
* it is trivial to check using find_binary though.
* it is trivial to check using find_executable though.
*/
if (service->try_exec) {
r = find_binary(service->try_exec, NULL);
r = find_executable(service->try_exec, NULL);
if (r < 0) {
log_full_errno(r == -ENOENT ? LOG_INFO : LOG_WARNING, r,
"Not generating service for XDG autostart %s, could not find TryExec= binary %s: %m",