Skip to content

Commit

Permalink
Add BinaryDirectory parameter to specify a directory where binaries e…
Browse files Browse the repository at this point in the history
…xecuted by Exec*= should be found

Closes systemd#6308
  • Loading branch information
alexlzhu authored and alexlzhu committed Jul 21, 2021
1 parent a607df2 commit 4e07477
Show file tree
Hide file tree
Showing 15 changed files with 87 additions and 12 deletions.
11 changes: 11 additions & 0 deletions man/systemd.exec.xml
Expand Up @@ -89,6 +89,17 @@

<variablelist class='unit-directives'>

<varlistentry>
<term><varname>BinaryDirectory=</varname></term>

<listitem><para>Takes a directory path relative to which the executable used by
the Exec*= (e.g. ExecStart=, ExecStop=, etc.) properties can be specified.
The name of the executable has to be non standard.
At the moment BinaryDirectory= will not take precedence over the default paths set at compilation time.
Thus it is not recommended to use this when calling standard Linux binaries.
</para></listitem>
</varlistentry>

<varlistentry>
<term><varname>WorkingDirectory=</varname></term>

Expand Down
2 changes: 1 addition & 1 deletion src/analyze/analyze-verify.c
Expand Up @@ -124,7 +124,7 @@ int verify_executable(Unit *u, const ExecCommand *exec) {
if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE)
return 0;

r = find_executable_full(exec->path, false, NULL, NULL);
r = find_executable_full(exec->path, NULL, false, NULL, NULL);
if (r < 0)
return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path);

Expand Down
6 changes: 5 additions & 1 deletion src/basic/path-util.c
Expand Up @@ -637,7 +637,7 @@ static int check_x_access(const char *path, int *ret_fd) {
return 0;
}

int find_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd) {
int find_executable_full(const char *name, char *binary_directory, bool use_path_envvar, char **ret_filename, int *ret_fd) {
int last_error, r;
const char *p = NULL;

Expand Down Expand Up @@ -669,6 +669,10 @@ int find_executable_full(const char *name, bool use_path_envvar, char **ret_file
if (!p)
p = DEFAULT_PATH;

if (binary_directory) {
p = binary_directory;
}

last_error = -ENOENT;

/* Resolve a single-component name to a full path */
Expand Down
4 changes: 2 additions & 2 deletions src/basic/path-util.h
Expand Up @@ -99,9 +99,9 @@ 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_executable_full(const char *name, bool use_path_envvar, char **ret_filename, int *ret_fd);
int find_executable_full(const char *name, char *binary_directory, bool use_path_envvar, char **ret_filename, int *ret_fd);
static inline int find_executable(const char *name, char **ret_filename) {
return find_executable_full(name, true, ret_filename, NULL);
return find_executable_full(name, NULL, true, ret_filename, NULL);
}

bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);
Expand Down
16 changes: 16 additions & 0 deletions src/core/dbus-execute.c
Expand Up @@ -1090,6 +1090,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("LimitRTPRIOSoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTPRIO]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LimitRTTIME", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BinaryDirectory", "s", NULL, offsetof(ExecContext, binary_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST),
Expand Down Expand Up @@ -2635,6 +2636,21 @@ int bus_exec_context_set_transient_property(

return 1;

} else if (streq(name, "BinaryDirectory")) {
const char *s;

r = sd_bus_message_read(message, "s", &s);
if (r < 0)
return r;

if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
r = free_and_strdup(&c->binary_directory, empty_to_null(s));
if (r < 0)
return r;
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "BinaryDirectory=%s%s", "", s);
}

return 1;
} else if (streq(name, "WorkingDirectory")) {
const char *s;
bool missing_ok;
Expand Down
4 changes: 3 additions & 1 deletion src/core/execute.c
Expand Up @@ -4316,7 +4316,7 @@ static int exec_child(

_cleanup_free_ char *executable = NULL;
_cleanup_close_ int executable_fd = -1;
r = find_executable_full(command->path, false, &executable, &executable_fd);
r = find_executable_full(command->path, context->binary_directory, false, &executable, &executable_fd);
if (r < 0) {
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
log_unit_struct_errno(unit, LOG_INFO, r,
Expand Down Expand Up @@ -5260,6 +5260,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {

fprintf(f,
"%sUMask: %04o\n"
"%sBinaryDirectory: %s\n"
"%sWorkingDirectory: %s\n"
"%sRootDirectory: %s\n"
"%sNonBlocking: %s\n"
Expand All @@ -5284,6 +5285,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
"%sProtectProc: %s\n"
"%sProcSubset: %s\n",
prefix, c->umask,
prefix, empty_to_root(c->binary_directory),
prefix, empty_to_root(c->working_directory),
prefix, empty_to_root(c->root_directory),
prefix, yes_no(c->non_blocking),
Expand Down
2 changes: 1 addition & 1 deletion src/core/execute.h
Expand Up @@ -167,7 +167,7 @@ struct ExecContext {
char **unset_environment;

struct rlimit *rlimit[_RLIMIT_MAX];
char *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
char *binary_directory, *working_directory, *root_directory, *root_image, *root_verity, *root_hash_path, *root_hash_sig_path;
void *root_hash, *root_hash_sig;
size_t root_hash_size, root_hash_sig_size;
LIST_HEAD(MountOptions, root_image_options);
Expand Down
1 change: 1 addition & 0 deletions src/core/load-fragment-gperf.gperf.in
Expand Up @@ -2,6 +2,7 @@

{%- macro EXEC_CONTEXT_CONFIG_ITEMS(type) -%}
{# Define the context options only once #}
{{type}}.BinaryDirectory, config_parse_unit_path_printf, 0, offsetof({{type}}, exec_context.binary_directory)
{{type}}.WorkingDirectory, config_parse_working_directory, 0, offsetof({{type}}, exec_context)
{{type}}.RootDirectory, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_directory)
{{type}}.RootImage, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_image)
Expand Down
1 change: 1 addition & 0 deletions src/nspawn/nspawn-gperf.gperf
Expand Up @@ -31,6 +31,7 @@ Exec.DropCapability, config_parse_capability, 0, of
Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal)
Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality)
Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id)
Exec.BinaryDirectory, config_parse_path, 0, offsetof(Settings, binary_directory)
Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory)
Exec.PivotRoot, config_parse_pivot_root, 0, 0
Exec.PrivateUsers, config_parse_private_users, 0, 0
Expand Down
4 changes: 3 additions & 1 deletion src/nspawn/nspawn-settings.h
Expand Up @@ -127,7 +127,8 @@ typedef enum SettingsMask {
SETTING_CONSOLE_MODE = UINT64_C(1) << 29,
SETTING_CREDENTIALS = UINT64_C(1) << 30,
SETTING_BIND_USER = UINT64_C(1) << 31,
SETTING_RLIMIT_FIRST = UINT64_C(1) << 32, /* we define one bit per resource limit here */
SETTING_BINARY_DIRECTORY = UINT64_C(1) << 32,
SETTING_RLIMIT_FIRST = UINT64_C(1) << 33, /* we define one bit per resource limit here */
SETTING_RLIMIT_LAST = UINT64_C(1) << (32 + _RLIMIT_MAX - 1),
_SETTINGS_MASK_ALL = (UINT64_C(1) << (32 + _RLIMIT_MAX)) -1,
_SETTING_FORCE_ENUM_WIDTH = UINT64_MAX
Expand Down Expand Up @@ -171,6 +172,7 @@ typedef struct Settings {
int kill_signal;
unsigned long personality;
sd_id128_t machine_id;
char *binary_directory;
char *working_directory;
char *pivot_root_new;
char *pivot_root_old;
Expand Down
1 change: 1 addition & 0 deletions src/run/run.c
Expand Up @@ -1718,6 +1718,7 @@ static int run(int argc, char* argv[]) {
if (!strv_isempty(arg_cmdline) &&
arg_transport == BUS_TRANSPORT_LOCAL &&
!strv_find_startswith(arg_property, "RootDirectory=") &&
!strv_find_startswith(arg_property, "BinaryDirectory=") &&
!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,
Expand Down
1 change: 1 addition & 0 deletions src/shared/bus-unit-util.c
Expand Up @@ -913,6 +913,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
"UtmpMode",
"PAMName",
"TTYPath",
"BinaryDirectory",
"WorkingDirectory",
"RootDirectory",
"SyslogIdentifier",
Expand Down
13 changes: 13 additions & 0 deletions src/test/test-execute.c
Expand Up @@ -6,6 +6,7 @@

#include "capability-util.h"
#include "cpu-set-util.h"
#include "copy.h"
#include "errno-list.h"
#include "fileio.h"
#include "fs-util.h"
Expand All @@ -21,6 +22,7 @@
#include "service.h"
#include "stat-util.h"
#include "tests.h"
#include "tmpfile-util.h"
#include "unit.h"
#include "user-util.h"
#include "util.h"
Expand Down Expand Up @@ -265,6 +267,16 @@ static void test_exec_workingdirectory(Manager *m) {
(void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
}

static void test_exec_binarydirectory(Manager *m) {
assert_se(mkdir_p("/tmp/test-exec_binarydirectory", 0755) >= 0);

copy_file("/bin/ls", "/tmp/test-exec_binarydirectory/ls_temp", 0, 0777, 0, 0, COPY_REPLACE);

test(m, "exec-binarydirectory.service", 0, CLD_EXITED);

(void) rm_rf("/tmp/test-exec_binarydirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
}

static void test_exec_personality(Manager *m) {
#if defined(__x86_64__)
test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
Expand Down Expand Up @@ -896,6 +908,7 @@ int main(int argc, char *argv[]) {
entry(test_exec_unsetenvironment),
entry(test_exec_user),
entry(test_exec_workingdirectory),
entry(test_exec_binarydirectory),
{},
};
static const test_entry system_tests[] = {
Expand Down
29 changes: 24 additions & 5 deletions src/test/test-path-util.c
Expand Up @@ -6,6 +6,7 @@
#include "alloc-util.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
#include "path-util.h"
#include "process-util.h"
Expand All @@ -14,6 +15,7 @@
#include "string-util.h"
#include "strv.h"
#include "tests.h"
#include "tmpfile-util.h"
#include "util.h"

static void test_print_paths(void) {
Expand Down Expand Up @@ -201,15 +203,18 @@ static void test_path_equal_root(void) {

static void test_find_executable_full(void) {
char *p;
char delim = '/';
char* testFileName;
int fd;

log_info("/* %s */", __func__);

assert_se(find_executable_full("sh", true, &p, NULL) == 0);
assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);

assert_se(find_executable_full("sh", false, &p, NULL) == 0);
assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);
Expand All @@ -221,18 +226,32 @@ static void test_find_executable_full(void) {

assert_se(unsetenv("PATH") == 0);

assert_se(find_executable_full("sh", true, &p, NULL) == 0);
assert_se(find_executable_full("sh", NULL, true, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);

assert_se(find_executable_full("sh", false, &p, NULL) == 0);
assert_se(find_executable_full("sh", NULL, false, &p, NULL) == 0);
puts(p);
assert_se(streq(basename(p), "sh"));
free(p);

if (oldpath)
assert_se(setenv("PATH", oldpath, true) >= 0);

_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-XXXXXX";
fd = mkostemp_safe(fn);
fchmod(fd, 0755);

testFileName = strchr(strchr(fn, delim), delim);

assert_se(find_executable_full(testFileName, "/tmp", false, &p, NULL) == 0);
puts(p);
assert_se(streq(p, fn));
free(p);

unlink(fn);
assert_se(find_executable_full(testFileName, "/tmp", false, &p, NULL) != 0);
}

static void test_find_executable(const char *self) {
Expand Down Expand Up @@ -277,7 +296,7 @@ static void test_find_executable_exec_one(const char *path) {
pid_t pid;
int r;

r = find_executable_full(path, false, &t, &fd);
r = find_executable_full(path, NULL, false, &t, &fd);

log_info_errno(r, "%s: %s → %s: %d/%m", __func__, path, t ?: "-", fd);

Expand Down
4 changes: 4 additions & 0 deletions test/test-execute/exec-binarydirectory.service
@@ -0,0 +1,4 @@
[Service]
ExecStart=ls_temp
Type=oneshot
BinaryDirectory=/tmp/test-exec_binarydirectory

0 comments on commit 4e07477

Please sign in to comment.