]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/exec-util.c
util: introduce memcmp_safe()
[thirdparty/systemd.git] / src / basic / exec-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89711996
ZJS
2
3#include <dirent.h>
4#include <errno.h>
5#include <sys/prctl.h>
6#include <sys/types.h>
7#include <unistd.h>
c6e47247 8#include <stdio.h>
89711996
ZJS
9
10#include "alloc-util.h"
2e4cfe65 11#include "conf-files.h"
c6e47247 12#include "env-util.h"
89711996 13#include "exec-util.h"
c6e47247
ZJS
14#include "fd-util.h"
15#include "fileio.h"
89711996
ZJS
16#include "hashmap.h"
17#include "macro.h"
18#include "process-util.h"
19#include "set.h"
20#include "signal-util.h"
21#include "stat-util.h"
22#include "string-util.h"
23#include "strv.h"
c6e47247 24#include "terminal-util.h"
89711996
ZJS
25#include "util.h"
26
27/* Put this test here for a lack of better place */
28assert_cc(EAGAIN == EWOULDBLOCK);
29
c6e47247
ZJS
30static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
31
cf55fc18 32 pid_t _pid;
4c253ed1 33 int r;
cf55fc18
ZJS
34
35 if (null_or_empty_path(path)) {
36 log_debug("%s is empty (a mask).", path);
37 return 0;
38 }
39
b6e1fff1 40 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
4c253ed1 41 if (r < 0)
b6e1fff1 42 return r;
4c253ed1 43 if (r == 0) {
cf55fc18
ZJS
44 char *_argv[2];
45
c6e47247 46 if (stdout_fd >= 0) {
2b33ab09
LP
47 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
48 if (r < 0)
3554ef51 49 _exit(EXIT_FAILURE);
c6e47247
ZJS
50 }
51
cf55fc18
ZJS
52 if (!argv) {
53 _argv[0] = (char*) path;
54 _argv[1] = NULL;
55 argv = _argv;
56 } else
57 argv[0] = (char*) path;
58
59 execv(path, argv);
60 log_error_errno(errno, "Failed to execute %s: %m", path);
61 _exit(EXIT_FAILURE);
62 }
63
cf55fc18
ZJS
64 *pid = _pid;
65 return 1;
66}
67
c6e47247
ZJS
68static int do_execute(
69 char **directories,
70 usec_t timeout,
71 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
72 void* const callback_args[_STDOUT_CONSUME_MAX],
73 int output_fd,
74 char *argv[]) {
75
89711996 76 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65
ZJS
77 _cleanup_strv_free_ char **paths = NULL;
78 char **path;
79 int r;
89711996 80
c6e47247
ZJS
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.
83 *
84 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
85 */
89711996 86
3e36211b 87 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65
ZJS
88 if (r < 0)
89 return r;
90
c6e47247
ZJS
91 if (!callbacks) {
92 pids = hashmap_new(NULL);
93 if (!pids)
94 return log_oom();
95 }
96
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(). */
99
100 if (timeout != USEC_INFINITY)
be6b0c21 101 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 102
2e4cfe65
ZJS
103 STRV_FOREACH(path, paths) {
104 _cleanup_free_ char *t = NULL;
c6e47247 105 _cleanup_close_ int fd = -1;
2e4cfe65 106 pid_t pid;
89711996 107
2e4cfe65
ZJS
108 t = strdup(*path);
109 if (!t)
110 return log_oom();
89711996 111
c6e47247
ZJS
112 if (callbacks) {
113 fd = open_serialization_fd(basename(*path));
114 if (fd < 0)
115 return log_error_errno(fd, "Failed to open serialization file: %m");
116 }
117
118 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
119 if (r <= 0)
120 continue;
89711996 121
c6e47247
ZJS
122 if (pids) {
123 r = hashmap_put(pids, PID_TO_PTR(pid), t);
124 if (r < 0)
125 return log_oom();
126 t = NULL;
127 } else {
7d4904fe 128 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
c6e47247
ZJS
129 if (r < 0)
130 continue;
131
132 if (lseek(fd, 0, SEEK_SET) < 0)
133 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
134
135 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
136 fd = -1;
137 if (r < 0)
138 return log_error_errno(r, "Failed to process output from %s: %m", *path);
139 }
89711996
ZJS
140 }
141
c6e47247
ZJS
142 if (callbacks) {
143 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
144 if (r < 0)
145 return log_error_errno(r, "Callback two failed: %m");
146 }
89711996
ZJS
147
148 while (!hashmap_isempty(pids)) {
2e4cfe65 149 _cleanup_free_ char *t = NULL;
89711996
ZJS
150 pid_t pid;
151
152 pid = PTR_TO_PID(hashmap_first_key(pids));
153 assert(pid > 0);
154
2e4cfe65
ZJS
155 t = hashmap_remove(pids, PID_TO_PTR(pid));
156 assert(t);
89711996 157
7d4904fe 158 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
89711996
ZJS
159 }
160
161 return 0;
162}
163
c6e47247
ZJS
164int execute_directories(
165 const char* const* directories,
166 usec_t timeout,
167 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
168 void* const callback_args[_STDOUT_CONSUME_MAX],
169 char *argv[]) {
170
89711996 171 char **dirs = (char**) directories;
c6e47247 172 _cleanup_close_ int fd = -1;
1f5d1e02 173 char *name;
c6e47247 174 int r;
89711996
ZJS
175
176 assert(!strv_isempty(dirs));
177
178 name = basename(dirs[0]);
179 assert(!isempty(name));
180
c6e47247
ZJS
181 if (callbacks) {
182 assert(callback_args);
183 assert(callbacks[STDOUT_GENERATE]);
184 assert(callbacks[STDOUT_COLLECT]);
185 assert(callbacks[STDOUT_CONSUME]);
186
187 fd = open_serialization_fd(name);
188 if (fd < 0)
189 return log_error_errno(fd, "Failed to open serialization file: %m");
190 }
191
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. */
89711996 195
1f5d1e02 196 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
4c253ed1 197 if (r < 0)
b6e1fff1 198 return r;
4c253ed1 199 if (r == 0) {
c6e47247 200 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
89711996
ZJS
201 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
202 }
203
c6e47247
ZJS
204 if (!callbacks)
205 return 0;
206
207 if (lseek(fd, 0, SEEK_SET) < 0)
208 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
209
210 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
211 fd = -1;
212 if (r < 0)
213 return log_error_errno(r, "Failed to parse returned data: %m");
214 return 0;
89711996 215}
3303d1b2
ZJS
216
217static int gather_environment_generate(int fd, void *arg) {
218 char ***env = arg, **x, **y;
219 _cleanup_fclose_ FILE *f = NULL;
3f199740 220 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
221 int r;
222
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.
225 *
226 * fd is always consumed, even on error.
227 */
228
229 assert(env);
230
231 f = fdopen(fd, "r");
232 if (!f) {
233 safe_close(fd);
234 return -errno;
235 }
236
237 r = load_env_file_pairs(f, NULL, NULL, &new);
238 if (r < 0)
239 return r;
240
241 STRV_FOREACH_PAIR(x, y, new) {
242 char *p;
243
184d1904
ZJS
244 if (!env_name_is_valid(*x)) {
245 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
246 continue;
247 }
248
3303d1b2
ZJS
249 p = strjoin(*x, "=", *y);
250 if (!p)
251 return -ENOMEM;
252
253 r = strv_env_replace(env, p);
254 if (r < 0)
255 return r;
256
257 if (setenv(*x, *y, true) < 0)
258 return -errno;
259 }
260
261 return r;
262}
263
264static int gather_environment_collect(int fd, void *arg) {
265 char ***env = arg;
266 _cleanup_fclose_ FILE *f = NULL;
267 int r;
268
269 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
270
271 assert(env);
272
273 f = fdopen(fd, "w");
274 if (!f) {
275 safe_close(fd);
276 return -errno;
277 }
278
279 r = serialize_environment(f, *env);
280 if (r < 0)
281 return r;
282
283 if (ferror(f))
284 return errno > 0 ? -errno : -EIO;
285
286 return 0;
287}
288
289static int gather_environment_consume(int fd, void *arg) {
290 char ***env = arg;
291 _cleanup_fclose_ FILE *f = NULL;
292 char line[LINE_MAX];
293 int r = 0, k;
294
295 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
296
297 assert(env);
298
299 f = fdopen(fd, "r");
300 if (!f) {
301 safe_close(fd);
302 return -errno;
303 }
304
305 FOREACH_LINE(line, f, return -EIO) {
306 truncate_nl(line);
307
308 k = deserialize_environment(env, line);
309 if (k < 0)
310 log_error_errno(k, "Invalid line \"%s\": %m", line);
311 if (k < 0 && r == 0)
312 r = k;
313 }
314
315 return r;
316}
317
318const gather_stdout_callback_t gather_environment[] = {
319 gather_environment_generate,
320 gather_environment_collect,
321 gather_environment_consume,
322};