]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/exec-util.c
util-lib: split out env file parsing code into env-file.c
[thirdparty/systemd.git] / src / shared / 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"
686d13b9 12#include "env-file.h"
c6e47247 13#include "env-util.h"
89711996 14#include "exec-util.h"
c6e47247
ZJS
15#include "fd-util.h"
16#include "fileio.h"
89711996
ZJS
17#include "hashmap.h"
18#include "macro.h"
19#include "process-util.h"
d68c645b 20#include "serialize.h"
89711996
ZJS
21#include "set.h"
22#include "signal-util.h"
23#include "stat-util.h"
24#include "string-util.h"
25#include "strv.h"
c6e47247 26#include "terminal-util.h"
e4de7287 27#include "tmpfile-util.h"
89711996
ZJS
28#include "util.h"
29
30/* Put this test here for a lack of better place */
31assert_cc(EAGAIN == EWOULDBLOCK);
32
c6e47247
ZJS
33static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
34
cf55fc18 35 pid_t _pid;
4c253ed1 36 int r;
cf55fc18
ZJS
37
38 if (null_or_empty_path(path)) {
39 log_debug("%s is empty (a mask).", path);
40 return 0;
41 }
42
b6e1fff1 43 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
4c253ed1 44 if (r < 0)
b6e1fff1 45 return r;
4c253ed1 46 if (r == 0) {
cf55fc18
ZJS
47 char *_argv[2];
48
c6e47247 49 if (stdout_fd >= 0) {
2b33ab09
LP
50 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
51 if (r < 0)
3554ef51 52 _exit(EXIT_FAILURE);
c6e47247
ZJS
53 }
54
cf55fc18
ZJS
55 if (!argv) {
56 _argv[0] = (char*) path;
57 _argv[1] = NULL;
58 argv = _argv;
59 } else
60 argv[0] = (char*) path;
61
62 execv(path, argv);
63 log_error_errno(errno, "Failed to execute %s: %m", path);
64 _exit(EXIT_FAILURE);
65 }
66
cf55fc18
ZJS
67 *pid = _pid;
68 return 1;
69}
70
c6e47247
ZJS
71static int do_execute(
72 char **directories,
73 usec_t timeout,
74 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
75 void* const callback_args[_STDOUT_CONSUME_MAX],
76 int output_fd,
78ec1bb4
DJL
77 char *argv[],
78 char *envp[]) {
c6e47247 79
89711996 80 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65 81 _cleanup_strv_free_ char **paths = NULL;
78ec1bb4 82 char **path, **e;
2e4cfe65 83 int r;
89711996 84
c6e47247
ZJS
85 /* We fork this all off from a child process so that we can somewhat cleanly make
86 * use of SIGALRM to set a time limit.
87 *
88 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
89 */
89711996 90
3e36211b 91 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65 92 if (r < 0)
653d4695 93 return log_error_errno(r, "Failed to enumerate executables: %m");
2e4cfe65 94
c6e47247
ZJS
95 if (!callbacks) {
96 pids = hashmap_new(NULL);
97 if (!pids)
98 return log_oom();
99 }
100
101 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
102 * default action terminating the process, and turn on alarm(). */
103
104 if (timeout != USEC_INFINITY)
be6b0c21 105 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 106
78ec1bb4 107 STRV_FOREACH(e, envp)
8f7329ac 108 if (putenv(*e) != 0)
ed689f78 109 return log_error_errno(errno, "Failed to set environment variable: %m");
78ec1bb4 110
2e4cfe65
ZJS
111 STRV_FOREACH(path, paths) {
112 _cleanup_free_ char *t = NULL;
c6e47247 113 _cleanup_close_ int fd = -1;
2e4cfe65 114 pid_t pid;
89711996 115
2e4cfe65
ZJS
116 t = strdup(*path);
117 if (!t)
118 return log_oom();
89711996 119
c6e47247
ZJS
120 if (callbacks) {
121 fd = open_serialization_fd(basename(*path));
122 if (fd < 0)
123 return log_error_errno(fd, "Failed to open serialization file: %m");
124 }
125
126 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
127 if (r <= 0)
128 continue;
89711996 129
c6e47247
ZJS
130 if (pids) {
131 r = hashmap_put(pids, PID_TO_PTR(pid), t);
132 if (r < 0)
133 return log_oom();
134 t = NULL;
135 } else {
7d4904fe 136 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
c6e47247
ZJS
137 if (r < 0)
138 continue;
139
140 if (lseek(fd, 0, SEEK_SET) < 0)
141 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
142
143 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
144 fd = -1;
145 if (r < 0)
146 return log_error_errno(r, "Failed to process output from %s: %m", *path);
147 }
89711996
ZJS
148 }
149
c6e47247
ZJS
150 if (callbacks) {
151 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
152 if (r < 0)
153 return log_error_errno(r, "Callback two failed: %m");
154 }
89711996
ZJS
155
156 while (!hashmap_isempty(pids)) {
2e4cfe65 157 _cleanup_free_ char *t = NULL;
89711996
ZJS
158 pid_t pid;
159
160 pid = PTR_TO_PID(hashmap_first_key(pids));
161 assert(pid > 0);
162
2e4cfe65
ZJS
163 t = hashmap_remove(pids, PID_TO_PTR(pid));
164 assert(t);
89711996 165
7d4904fe 166 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
89711996
ZJS
167 }
168
169 return 0;
170}
171
c6e47247
ZJS
172int execute_directories(
173 const char* const* directories,
174 usec_t timeout,
175 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
176 void* const callback_args[_STDOUT_CONSUME_MAX],
78ec1bb4
DJL
177 char *argv[],
178 char *envp[]) {
c6e47247 179
89711996 180 char **dirs = (char**) directories;
c6e47247 181 _cleanup_close_ int fd = -1;
1f5d1e02 182 char *name;
c6e47247 183 int r;
89711996
ZJS
184
185 assert(!strv_isempty(dirs));
186
187 name = basename(dirs[0]);
188 assert(!isempty(name));
189
c6e47247
ZJS
190 if (callbacks) {
191 assert(callback_args);
192 assert(callbacks[STDOUT_GENERATE]);
193 assert(callbacks[STDOUT_COLLECT]);
194 assert(callbacks[STDOUT_CONSUME]);
195
196 fd = open_serialization_fd(name);
197 if (fd < 0)
198 return log_error_errno(fd, "Failed to open serialization file: %m");
199 }
200
201 /* Executes all binaries in the directories serially or in parallel and waits for
202 * them to finish. Optionally a timeout is applied. If a file with the same name
203 * exists in more than one directory, the earliest one wins. */
89711996 204
1f5d1e02 205 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
4c253ed1 206 if (r < 0)
b6e1fff1 207 return r;
4c253ed1 208 if (r == 0) {
78ec1bb4 209 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
89711996
ZJS
210 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
211 }
212
c6e47247
ZJS
213 if (!callbacks)
214 return 0;
215
216 if (lseek(fd, 0, SEEK_SET) < 0)
217 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
218
219 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
220 fd = -1;
221 if (r < 0)
222 return log_error_errno(r, "Failed to parse returned data: %m");
223 return 0;
89711996 224}
3303d1b2
ZJS
225
226static int gather_environment_generate(int fd, void *arg) {
227 char ***env = arg, **x, **y;
228 _cleanup_fclose_ FILE *f = NULL;
3f199740 229 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
230 int r;
231
232 /* Read a series of VAR=value assignments from fd, use them to update the list of
233 * variables in env. Also update the exported environment.
234 *
235 * fd is always consumed, even on error.
236 */
237
238 assert(env);
239
240 f = fdopen(fd, "r");
241 if (!f) {
242 safe_close(fd);
243 return -errno;
244 }
245
aa8fbc74 246 r = load_env_file_pairs(f, NULL, &new);
3303d1b2
ZJS
247 if (r < 0)
248 return r;
249
250 STRV_FOREACH_PAIR(x, y, new) {
251 char *p;
252
184d1904
ZJS
253 if (!env_name_is_valid(*x)) {
254 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
255 continue;
256 }
257
3303d1b2
ZJS
258 p = strjoin(*x, "=", *y);
259 if (!p)
260 return -ENOMEM;
261
262 r = strv_env_replace(env, p);
263 if (r < 0)
264 return r;
265
266 if (setenv(*x, *y, true) < 0)
267 return -errno;
268 }
269
270 return r;
271}
272
273static int gather_environment_collect(int fd, void *arg) {
3303d1b2 274 _cleanup_fclose_ FILE *f = NULL;
c93d527f 275 char ***env = arg;
3303d1b2
ZJS
276 int r;
277
278 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
279
280 assert(env);
281
282 f = fdopen(fd, "w");
283 if (!f) {
284 safe_close(fd);
285 return -errno;
286 }
287
d68c645b 288 r = serialize_strv(f, "env", *env);
3303d1b2
ZJS
289 if (r < 0)
290 return r;
291
c93d527f
LP
292 r = fflush_and_check(f);
293 if (r < 0)
294 return r;
3303d1b2
ZJS
295
296 return 0;
297}
298
299static int gather_environment_consume(int fd, void *arg) {
3303d1b2 300 _cleanup_fclose_ FILE *f = NULL;
d68c645b
LP
301 char ***env = arg;
302 int r = 0;
3303d1b2
ZJS
303
304 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
305
306 assert(env);
307
d68c645b 308 f = fdopen(fd, "re");
3303d1b2
ZJS
309 if (!f) {
310 safe_close(fd);
311 return -errno;
312 }
313
d68c645b
LP
314 for (;;) {
315 _cleanup_free_ char *line = NULL;
316 const char *v;
317 int k;
3303d1b2 318
d68c645b 319 k = read_line(f, LONG_LINE_MAX, &line);
3303d1b2 320 if (k < 0)
d68c645b
LP
321 return k;
322 if (k == 0)
323 break;
324
325 v = startswith(line, "env=");
326 if (!v) {
327 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
328 if (r == 0)
329 r = -EINVAL;
330
331 continue;
332 }
333
334 k = deserialize_environment(v, env);
335 if (k < 0) {
336 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
337
338 if (r == 0)
339 r = k;
340 }
3303d1b2
ZJS
341 }
342
343 return r;
344}
345
346const gather_stdout_callback_t gather_environment[] = {
347 gather_environment_generate,
348 gather_environment_collect,
349 gather_environment_consume,
350};