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