1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "alloc-util.h"
11 #include "conf-files.h"
13 #include "exec-util.h"
18 #include "process-util.h"
20 #include "signal-util.h"
21 #include "stat-util.h"
22 #include "string-util.h"
24 #include "terminal-util.h"
27 /* Put this test here for a lack of better place */
28 assert_cc(EAGAIN
== EWOULDBLOCK
);
30 static int do_spawn(const char *path
, char *argv
[], int stdout_fd
, pid_t
*pid
) {
35 if (null_or_empty_path(path
)) {
36 log_debug("%s is empty (a mask).", path
);
40 r
= safe_fork("(direxec)", FORK_DEATHSIG
|FORK_LOG
, &_pid
);
47 r
= rearrange_stdio(STDIN_FILENO
, stdout_fd
, STDERR_FILENO
);
53 _argv
[0] = (char*) path
;
57 argv
[0] = (char*) path
;
60 log_error_errno(errno
, "Failed to execute %s: %m", path
);
68 static int do_execute(
71 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
72 void* const callback_args
[_STDOUT_CONSUME_MAX
],
76 _cleanup_hashmap_free_free_ Hashmap
*pids
= NULL
;
77 _cleanup_strv_free_
char **paths
= NULL
;
81 /* We fork this all off from a child process so that we can somewhat cleanly make
82 * use of SIGALRM to set a time limit.
84 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
87 r
= conf_files_list_strv(&paths
, NULL
, NULL
, CONF_FILES_EXECUTABLE
|CONF_FILES_REGULAR
|CONF_FILES_FILTER_MASKED
, (const char* const*) directories
);
92 pids
= hashmap_new(NULL
);
97 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
98 * default action terminating the process, and turn on alarm(). */
100 if (timeout
!= USEC_INFINITY
)
101 alarm(DIV_ROUND_UP(timeout
, USEC_PER_SEC
));
103 STRV_FOREACH(path
, paths
) {
104 _cleanup_free_
char *t
= NULL
;
105 _cleanup_close_
int fd
= -1;
113 fd
= open_serialization_fd(basename(*path
));
115 return log_error_errno(fd
, "Failed to open serialization file: %m");
118 r
= do_spawn(t
, argv
, fd
, &pid
);
123 r
= hashmap_put(pids
, PID_TO_PTR(pid
), t
);
128 r
= wait_for_terminate_and_check(t
, pid
, WAIT_LOG
);
132 if (lseek(fd
, 0, SEEK_SET
) < 0)
133 return log_error_errno(errno
, "Failed to seek on serialization fd: %m");
135 r
= callbacks
[STDOUT_GENERATE
](fd
, callback_args
[STDOUT_GENERATE
]);
138 return log_error_errno(r
, "Failed to process output from %s: %m", *path
);
143 r
= callbacks
[STDOUT_COLLECT
](output_fd
, callback_args
[STDOUT_COLLECT
]);
145 return log_error_errno(r
, "Callback two failed: %m");
148 while (!hashmap_isempty(pids
)) {
149 _cleanup_free_
char *t
= NULL
;
152 pid
= PTR_TO_PID(hashmap_first_key(pids
));
155 t
= hashmap_remove(pids
, PID_TO_PTR(pid
));
158 (void) wait_for_terminate_and_check(t
, pid
, WAIT_LOG
);
164 int execute_directories(
165 const char* const* directories
,
167 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
168 void* const callback_args
[_STDOUT_CONSUME_MAX
],
171 char **dirs
= (char**) directories
;
172 _cleanup_close_
int fd
= -1;
176 assert(!strv_isempty(dirs
));
178 name
= basename(dirs
[0]);
179 assert(!isempty(name
));
182 assert(callback_args
);
183 assert(callbacks
[STDOUT_GENERATE
]);
184 assert(callbacks
[STDOUT_COLLECT
]);
185 assert(callbacks
[STDOUT_CONSUME
]);
187 fd
= open_serialization_fd(name
);
189 return log_error_errno(fd
, "Failed to open serialization file: %m");
192 /* Executes all binaries in the directories serially or in parallel and waits for
193 * them to finish. Optionally a timeout is applied. If a file with the same name
194 * exists in more than one directory, the earliest one wins. */
196 r
= safe_fork("(sd-executor)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
|FORK_WAIT
, NULL
);
200 r
= do_execute(dirs
, timeout
, callbacks
, callback_args
, fd
, argv
);
201 _exit(r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
);
207 if (lseek(fd
, 0, SEEK_SET
) < 0)
208 return log_error_errno(errno
, "Failed to rewind serialization fd: %m");
210 r
= callbacks
[STDOUT_CONSUME
](fd
, callback_args
[STDOUT_CONSUME
]);
213 return log_error_errno(r
, "Failed to parse returned data: %m");
217 static int gather_environment_generate(int fd
, void *arg
) {
218 char ***env
= arg
, **x
, **y
;
219 _cleanup_fclose_
FILE *f
= NULL
;
220 _cleanup_strv_free_
char **new = NULL
;
223 /* Read a series of VAR=value assignments from fd, use them to update the list of
224 * variables in env. Also update the exported environment.
226 * fd is always consumed, even on error.
237 r
= load_env_file_pairs(f
, NULL
, NULL
, &new);
241 STRV_FOREACH_PAIR(x
, y
, new) {
244 if (!env_name_is_valid(*x
)) {
245 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x
);
249 p
= strjoin(*x
, "=", *y
);
253 r
= strv_env_replace(env
, p
);
257 if (setenv(*x
, *y
, true) < 0)
264 static int gather_environment_collect(int fd
, void *arg
) {
266 _cleanup_fclose_
FILE *f
= NULL
;
269 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
279 r
= serialize_environment(f
, *env
);
284 return errno
> 0 ? -errno
: -EIO
;
289 static int gather_environment_consume(int fd
, void *arg
) {
291 _cleanup_fclose_
FILE *f
= NULL
;
295 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
305 FOREACH_LINE(line
, f
, return -EIO
) {
308 k
= deserialize_environment(env
, line
);
310 log_error_errno(k
, "Invalid line \"%s\": %m", line
);
318 const gather_stdout_callback_t gather_environment
[] = {
319 gather_environment_generate
,
320 gather_environment_collect
,
321 gather_environment_consume
,