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