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