chiark / gitweb /
exec-util: initialize `new` before using it (#7471)
[elogind.git] / src / basic / exec-util.c
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>
25 #include <stdio.h>
26
27 #include "alloc-util.h"
28 #include "conf-files.h"
29 #include "env-util.h"
30 #include "exec-util.h"
31 #include "fd-util.h"
32 #include "fileio.h"
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"
41 #include "terminal-util.h"
42 #include "util.h"
43
44 /* Put this test here for a lack of better place */
45 assert_cc(EAGAIN == EWOULDBLOCK);
46
47 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
48
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
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
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
90 static 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
98         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
99         _cleanup_strv_free_ char **paths = NULL;
100         char **path;
101         int r;
102
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          */
108
109         (void) reset_all_signal_handlers();
110         (void) reset_signal_mask();
111
112         assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
113
114         r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
115         if (r < 0)
116                 return r;
117
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);
129
130         STRV_FOREACH(path, paths) {
131                 _cleanup_free_ char *t = NULL;
132                 _cleanup_close_ int fd = -1;
133 #if 0 /// No "maybe uninitialized" warning in elogind
134                 pid_t pid;
135 #else
136                 pid_t pid = 0;
137 #endif // 0
138
139                 t = strdup(*path);
140                 if (!t)
141                         return log_oom();
142
143                 if (callbacks) {
144                         fd = open_serialization_fd(basename(*path));
145                         if (fd < 0)
146                                 return log_error_errno(fd, "Failed to open serialization file: %m");
147                 }
148
149                 r = do_spawn(t, argv, fd, &pid);
150                 if (r <= 0)
151                         continue;
152
153                 if (pids) {
154                         r = hashmap_put(pids, PID_TO_PTR(pid), t);
155                         if (r < 0)
156                                 return log_oom();
157                         t = NULL;
158                 } else {
159                         r = wait_for_terminate_and_warn(t, pid, true);
160                         if (r < 0)
161                                 continue;
162
163                         if (lseek(fd, 0, SEEK_SET) < 0)
164                                 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
165
166                         r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
167                         fd = -1;
168                         if (r < 0)
169                                 return log_error_errno(r, "Failed to process output from %s: %m", *path);
170                 }
171         }
172
173         if (callbacks) {
174                 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
175                 if (r < 0)
176                         return log_error_errno(r, "Callback two failed: %m");
177         }
178
179         while (!hashmap_isempty(pids)) {
180                 _cleanup_free_ char *t = NULL;
181                 pid_t pid;
182
183                 pid = PTR_TO_PID(hashmap_first_key(pids));
184                 assert(pid > 0);
185
186                 t = hashmap_remove(pids, PID_TO_PTR(pid));
187                 assert(t);
188
189                 wait_for_terminate_and_warn(t, pid, true);
190         }
191
192         return 0;
193 }
194
195 int execute_directories(
196                 const char* const* directories,
197                 usec_t timeout,
198                 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
199                 void* const callback_args[_STDOUT_CONSUME_MAX],
200                 char *argv[]) {
201
202         pid_t executor_pid;
203         char *name;
204         char **dirs = (char**) directories;
205         _cleanup_close_ int fd = -1;
206         int r;
207
208         assert(!strv_isempty(dirs));
209
210         name = basename(dirs[0]);
211         assert(!isempty(name));
212
213         if (callbacks) {
214                 assert(callback_args);
215                 assert(callbacks[STDOUT_GENERATE]);
216                 assert(callbacks[STDOUT_COLLECT]);
217                 assert(callbacks[STDOUT_CONSUME]);
218
219                 fd = open_serialization_fd(name);
220                 if (fd < 0)
221                         return log_error_errno(fd, "Failed to open serialization file: %m");
222         }
223
224         /* Executes all binaries in the directories serially or in parallel and waits for
225          * them to finish. Optionally a timeout is applied. If a file with the same name
226          * exists in more than one directory, the earliest one wins. */
227
228         executor_pid = fork();
229         if (executor_pid < 0)
230                 return log_error_errno(errno, "Failed to fork: %m");
231
232         if (executor_pid == 0) {
233                 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
234                 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
235         }
236
237         r = wait_for_terminate_and_warn(name, executor_pid, true);
238         if (r < 0)
239                 return log_error_errno(r, "Execution failed: %m");
240         if (r > 0) {
241                 /* non-zero return code from child */
242                 log_error("Forker process failed.");
243                 return -EREMOTEIO;
244         }
245
246         if (!callbacks)
247                 return 0;
248
249         if (lseek(fd, 0, SEEK_SET) < 0)
250                 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
251
252         r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
253         fd = -1;
254         if (r < 0)
255                 return log_error_errno(r, "Failed to parse returned data: %m");
256         return 0;
257 }
258
259 #if 0 /// UNNEEDED by elogind
260 static int gather_environment_generate(int fd, void *arg) {
261         char ***env = arg, **x, **y;
262         _cleanup_fclose_ FILE *f = NULL;
263         _cleanup_strv_free_ char **new = NULL;
264         int r;
265
266         /* Read a series of VAR=value assignments from fd, use them to update the list of
267          * variables in env. Also update the exported environment.
268          *
269          * fd is always consumed, even on error.
270          */
271
272         assert(env);
273
274         f = fdopen(fd, "r");
275         if (!f) {
276                 safe_close(fd);
277                 return -errno;
278         }
279
280         r = load_env_file_pairs(f, NULL, NULL, &new);
281         if (r < 0)
282                 return r;
283
284         STRV_FOREACH_PAIR(x, y, new) {
285                 char *p;
286
287                 if (!env_name_is_valid(*x)) {
288                         log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
289                         continue;
290                 }
291
292                 p = strjoin(*x, "=", *y);
293                 if (!p)
294                         return -ENOMEM;
295
296                 r = strv_env_replace(env, p);
297                 if (r < 0)
298                         return r;
299
300                 if (setenv(*x, *y, true) < 0)
301                         return -errno;
302         }
303
304         return r;
305 }
306
307 static int gather_environment_collect(int fd, void *arg) {
308         char ***env = arg;
309         _cleanup_fclose_ FILE *f = NULL;
310         int r;
311
312         /* Write out a series of env=cescape(VAR=value) assignments to fd. */
313
314         assert(env);
315
316         f = fdopen(fd, "w");
317         if (!f) {
318                 safe_close(fd);
319                 return -errno;
320         }
321
322         r = serialize_environment(f, *env);
323         if (r < 0)
324                 return r;
325
326         if (ferror(f))
327                 return errno > 0 ? -errno : -EIO;
328
329         return 0;
330 }
331
332 static int gather_environment_consume(int fd, void *arg) {
333         char ***env = arg;
334         _cleanup_fclose_ FILE *f = NULL;
335         char line[LINE_MAX];
336         int r = 0, k;
337
338         /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
339
340         assert(env);
341
342         f = fdopen(fd, "r");
343         if (!f) {
344                 safe_close(fd);
345                 return -errno;
346         }
347
348         FOREACH_LINE(line, f, return -EIO) {
349                 truncate_nl(line);
350
351                 k = deserialize_environment(env, line);
352                 if (k < 0)
353                         log_error_errno(k, "Invalid line \"%s\": %m", line);
354                 if (k < 0 && r == 0)
355                         r = k;
356         }
357
358         return r;
359 }
360
361 const gather_stdout_callback_t gather_environment[] = {
362         gather_environment_generate,
363         gather_environment_collect,
364         gather_environment_consume,
365 };
366 #endif // 0