]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/exec-util.c
basic/exec-util: use conf_files_list_strv to list executables
[thirdparty/systemd.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
26 #include "alloc-util.h"
27 #include "conf-files.h"
28 #include "exec-util.h"
29 #include "hashmap.h"
30 #include "macro.h"
31 #include "process-util.h"
32 #include "set.h"
33 #include "signal-util.h"
34 #include "stat-util.h"
35 #include "string-util.h"
36 #include "strv.h"
37 #include "util.h"
38
39 /* Put this test here for a lack of better place */
40 assert_cc(EAGAIN == EWOULDBLOCK);
41
42 static int do_spawn(const char *path, char *argv[], pid_t *pid) {
43 pid_t _pid;
44
45 if (null_or_empty_path(path)) {
46 log_debug("%s is empty (a mask).", path);
47 return 0;
48 }
49
50 _pid = fork();
51 if (_pid < 0)
52 return log_error_errno(errno, "Failed to fork: %m");
53 if (_pid == 0) {
54 char *_argv[2];
55
56 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
57
58 if (!argv) {
59 _argv[0] = (char*) path;
60 _argv[1] = NULL;
61 argv = _argv;
62 } else
63 argv[0] = (char*) path;
64
65 execv(path, argv);
66 log_error_errno(errno, "Failed to execute %s: %m", path);
67 _exit(EXIT_FAILURE);
68 }
69
70 log_debug("Spawned %s as " PID_FMT ".", path, _pid);
71 *pid = _pid;
72 return 1;
73 }
74
75 static int do_execute(char **directories, usec_t timeout, char *argv[]) {
76 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
77 _cleanup_strv_free_ char **paths = NULL;
78 char **path;
79 int r;
80
81 /* We fork this all off from a child process so that we can
82 * somewhat cleanly make use of SIGALRM to set a time limit */
83
84 (void) reset_all_signal_handlers();
85 (void) reset_signal_mask();
86
87 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
88
89 r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
90 if (r < 0)
91 return r;
92
93 pids = hashmap_new(NULL);
94 if (!pids)
95 return log_oom();
96
97 STRV_FOREACH(path, paths) {
98 _cleanup_free_ char *t = NULL;
99 pid_t pid;
100
101 t = strdup(*path);
102 if (!t)
103 return log_oom();
104
105 r = do_spawn(t, argv, &pid);
106 if (r <= 0)
107 continue;
108
109 r = hashmap_put(pids, PID_TO_PTR(pid), t);
110 if (r < 0)
111 return log_oom();
112
113 t = NULL;
114 }
115
116 /* Abort execution of this process after the timout. We simply
117 * rely on SIGALRM as default action terminating the process,
118 * and turn on alarm(). */
119
120 if (timeout != USEC_INFINITY)
121 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
122
123 while (!hashmap_isempty(pids)) {
124 _cleanup_free_ char *t = NULL;
125 pid_t pid;
126
127 pid = PTR_TO_PID(hashmap_first_key(pids));
128 assert(pid > 0);
129
130 t = hashmap_remove(pids, PID_TO_PTR(pid));
131 assert(t);
132
133 wait_for_terminate_and_warn(t, pid, true);
134 }
135
136 return 0;
137 }
138
139 void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
140 pid_t executor_pid;
141 int r;
142 char *name;
143 char **dirs = (char**) directories;
144
145 assert(!strv_isempty(dirs));
146
147 name = basename(dirs[0]);
148 assert(!isempty(name));
149
150 /* Executes all binaries in the directories in parallel and waits
151 * for them to finish. Optionally a timeout is applied. If a file
152 * with the same name exists in more than one directory, the
153 * earliest one wins. */
154
155 executor_pid = fork();
156 if (executor_pid < 0) {
157 log_error_errno(errno, "Failed to fork: %m");
158 return;
159
160 } else if (executor_pid == 0) {
161 r = do_execute(dirs, timeout, argv);
162 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
163 }
164
165 wait_for_terminate_and_warn(name, executor_pid, true);
166 }