1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "alloc-util.h"
11 #include "conf-files.h"
14 #include "errno-util.h"
15 #include "exec-util.h"
20 #include "missing_syscall.h"
21 #include "process-util.h"
22 #include "rlimit-util.h"
23 #include "serialize.h"
25 #include "signal-util.h"
26 #include "stat-util.h"
27 #include "string-table.h"
28 #include "string-util.h"
30 #include "terminal-util.h"
31 #include "tmpfile-util.h"
34 /* Put this test here for a lack of better place */
35 assert_cc(EAGAIN
== EWOULDBLOCK
);
37 static int do_spawn(const char *path
, char *argv
[], int stdout_fd
, pid_t
*pid
) {
41 if (null_or_empty_path(path
)) {
42 log_debug("%s is empty (a mask).", path
);
46 r
= safe_fork("(direxec)", FORK_DEATHSIG
|FORK_LOG
, &_pid
);
53 r
= rearrange_stdio(STDIN_FILENO
, stdout_fd
, STDERR_FILENO
);
58 (void) rlimit_nofile_safe();
61 _argv
[0] = (char*) path
;
65 argv
[0] = (char*) path
;
68 log_error_errno(errno
, "Failed to execute %s: %m", path
);
76 static int do_execute(
79 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
80 void* const callback_args
[_STDOUT_CONSUME_MAX
],
86 _cleanup_hashmap_free_free_ Hashmap
*pids
= NULL
;
87 _cleanup_strv_free_
char **paths
= NULL
;
90 bool parallel_execution
;
92 /* We fork this all off from a child process so that we can somewhat cleanly make
93 * use of SIGALRM to set a time limit.
95 * We attempt to perform parallel execution if configured by the user, however
96 * if `callbacks` is nonnull, execution must be serial.
98 parallel_execution
= FLAGS_SET(flags
, EXEC_DIR_PARALLEL
) && !callbacks
;
100 r
= conf_files_list_strv(&paths
, NULL
, NULL
, CONF_FILES_EXECUTABLE
|CONF_FILES_REGULAR
|CONF_FILES_FILTER_MASKED
, (const char* const*) directories
);
102 return log_error_errno(r
, "Failed to enumerate executables: %m");
104 if (parallel_execution
) {
105 pids
= hashmap_new(NULL
);
110 /* Abort execution of this process after the timeout. We simply rely on SIGALRM as
111 * default action terminating the process, and turn on alarm(). */
113 if (timeout
!= USEC_INFINITY
)
114 alarm(DIV_ROUND_UP(timeout
, USEC_PER_SEC
));
116 STRV_FOREACH(e
, envp
)
118 return log_error_errno(errno
, "Failed to set environment variable: %m");
120 STRV_FOREACH(path
, paths
) {
121 _cleanup_free_
char *t
= NULL
;
122 _cleanup_close_
int fd
= -1;
130 fd
= open_serialization_fd(basename(*path
));
132 return log_error_errno(fd
, "Failed to open serialization file: %m");
135 r
= do_spawn(t
, argv
, fd
, &pid
);
139 if (parallel_execution
) {
140 r
= hashmap_put(pids
, PID_TO_PTR(pid
), t
);
145 r
= wait_for_terminate_and_check(t
, pid
, WAIT_LOG
);
146 if (FLAGS_SET(flags
, EXEC_DIR_IGNORE_ERRORS
)) {
153 if (lseek(fd
, 0, SEEK_SET
) < 0)
154 return log_error_errno(errno
, "Failed to seek on serialization fd: %m");
156 r
= callbacks
[STDOUT_GENERATE
](fd
, callback_args
[STDOUT_GENERATE
]);
159 return log_error_errno(r
, "Failed to process output from %s: %m", *path
);
165 r
= callbacks
[STDOUT_COLLECT
](output_fd
, callback_args
[STDOUT_COLLECT
]);
167 return log_error_errno(r
, "Callback two failed: %m");
170 while (!hashmap_isempty(pids
)) {
171 _cleanup_free_
char *t
= NULL
;
174 pid
= PTR_TO_PID(hashmap_first_key(pids
));
177 t
= hashmap_remove(pids
, PID_TO_PTR(pid
));
180 r
= wait_for_terminate_and_check(t
, pid
, WAIT_LOG
);
181 if (!FLAGS_SET(flags
, EXEC_DIR_IGNORE_ERRORS
) && r
> 0)
188 int execute_directories(
189 const char* const* directories
,
191 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
192 void* const callback_args
[_STDOUT_CONSUME_MAX
],
195 ExecDirFlags flags
) {
197 char **dirs
= (char**) directories
;
198 _cleanup_close_
int fd
= -1;
203 assert(!strv_isempty(dirs
));
205 name
= basename(dirs
[0]);
206 assert(!isempty(name
));
209 assert(callback_args
);
210 assert(callbacks
[STDOUT_GENERATE
]);
211 assert(callbacks
[STDOUT_COLLECT
]);
212 assert(callbacks
[STDOUT_CONSUME
]);
214 fd
= open_serialization_fd(name
);
216 return log_error_errno(fd
, "Failed to open serialization file: %m");
219 /* Executes all binaries in the directories serially or in parallel and waits for
220 * them to finish. Optionally a timeout is applied. If a file with the same name
221 * exists in more than one directory, the earliest one wins. */
223 r
= safe_fork("(sd-executor)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &executor_pid
);
227 r
= do_execute(dirs
, timeout
, callbacks
, callback_args
, fd
, argv
, envp
, flags
);
228 _exit(r
< 0 ? EXIT_FAILURE
: r
);
231 r
= wait_for_terminate_and_check("(sd-executor)", executor_pid
, 0);
234 if (!FLAGS_SET(flags
, EXEC_DIR_IGNORE_ERRORS
) && r
> 0)
240 if (lseek(fd
, 0, SEEK_SET
) < 0)
241 return log_error_errno(errno
, "Failed to rewind serialization fd: %m");
243 r
= callbacks
[STDOUT_CONSUME
](fd
, callback_args
[STDOUT_CONSUME
]);
246 return log_error_errno(r
, "Failed to parse returned data: %m");
250 static int gather_environment_generate(int fd
, void *arg
) {
251 char ***env
= arg
, **x
, **y
;
252 _cleanup_fclose_
FILE *f
= NULL
;
253 _cleanup_strv_free_
char **new = NULL
;
256 /* Read a series of VAR=value assignments from fd, use them to update the list of
257 * variables in env. Also update the exported environment.
259 * fd is always consumed, even on error.
270 r
= load_env_file_pairs(f
, NULL
, &new);
274 STRV_FOREACH_PAIR(x
, y
, new) {
277 if (!env_name_is_valid(*x
)) {
278 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x
);
282 p
= strjoin(*x
, "=", *y
);
286 r
= strv_env_replace(env
, p
);
290 if (setenv(*x
, *y
, true) < 0)
297 static int gather_environment_collect(int fd
, void *arg
) {
298 _cleanup_fclose_
FILE *f
= NULL
;
302 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
312 r
= serialize_strv(f
, "env", *env
);
316 r
= fflush_and_check(f
);
323 static int gather_environment_consume(int fd
, void *arg
) {
324 _cleanup_fclose_
FILE *f
= NULL
;
328 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
339 _cleanup_free_
char *line
= NULL
;
343 k
= read_line(f
, LONG_LINE_MAX
, &line
);
349 v
= startswith(line
, "env=");
351 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line
);
358 k
= deserialize_environment(v
, env
);
360 log_debug_errno(k
, "Invalid serialization line \"%s\": %m", line
);
370 int exec_command_flags_from_strv(char **ex_opts
, ExecCommandFlags
*flags
) {
371 ExecCommandFlags ex_flag
, ret_flags
= 0;
376 STRV_FOREACH(opt
, ex_opts
) {
377 ex_flag
= exec_command_flags_from_string(*opt
);
379 ret_flags
|= ex_flag
;
389 int exec_command_flags_to_strv(ExecCommandFlags flags
, char ***ex_opts
) {
390 _cleanup_strv_free_
char **ret_opts
= NULL
;
391 ExecCommandFlags it
= flags
;
397 for (i
= 0; it
!= 0; it
&= ~(1 << i
), i
++) {
398 if (FLAGS_SET(flags
, (1 << i
))) {
399 str
= exec_command_flags_to_string(1 << i
);
403 r
= strv_extend(&ret_opts
, str
);
409 *ex_opts
= TAKE_PTR(ret_opts
);
414 const gather_stdout_callback_t gather_environment
[] = {
415 gather_environment_generate
,
416 gather_environment_collect
,
417 gather_environment_consume
,
420 static const char* const exec_command_strings
[] = {
421 "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
422 "privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
423 "no-setuid", /* EXEC_COMMAND_NO_SETUID */
424 "ambient", /* EXEC_COMMAND_AMBIENT_MAGIC */
425 "no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
428 const char* exec_command_flags_to_string(ExecCommandFlags i
) {
431 for (idx
= 0; idx
< ELEMENTSOF(exec_command_strings
); idx
++)
433 return exec_command_strings
[idx
];
438 ExecCommandFlags
exec_command_flags_from_string(const char *s
) {
441 idx
= string_table_lookup(exec_command_strings
, ELEMENTSOF(exec_command_strings
), s
);
444 return _EXEC_COMMAND_FLAGS_INVALID
;
449 int fexecve_or_execve(int executable_fd
, const char *executable
, char *const argv
[], char *const envp
[]) {
451 execveat(executable_fd
, "", argv
, envp
, AT_EMPTY_PATH
);
453 if (IN_SET(errno
, ENOSYS
, ENOENT
) || ERRNO_IS_PRIVILEGE(errno
))
454 /* Old kernel or a script or an overzealous seccomp filter? Let's fall back to execve().
456 * fexecve(3): "If fd refers to a script (i.e., it is an executable text file that names a
457 * script interpreter with a first line that begins with the characters #!) and the
458 * close-on-exec flag has been set for fd, then fexecve() fails with the error ENOENT. This
459 * error occurs because, by the time the script interpreter is executed, fd has already been
460 * closed because of the close-on-exec flag. Thus, the close-on-exec flag can't be set on fd
461 * if it refers to a script."
463 * Unfortunately, if we unset close-on-exec, the script will be executed just fine, but (at
464 * least in case of bash) the script name, $0, will be shown as /dev/fd/nnn, which breaks
465 * scripts which make use of $0. Thus, let's fall back to execve() in this case.
468 execve(executable
, argv
, envp
);