]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/userdb/userdbd-manager.c
treewide: drop "RUN_" from "RUN_WITH_UMASK"
[thirdparty/systemd.git] / src / userdb / userdbd-manager.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d093b62c
LP
2
3#include <sys/wait.h>
4
5#include "sd-daemon.h"
6
7#include "fd-util.h"
8#include "fs-util.h"
9#include "mkdir.h"
10#include "process-util.h"
11#include "set.h"
12#include "signal-util.h"
13#include "socket-util.h"
14#include "stdio-util.h"
15#include "umask-util.h"
16#include "userdbd-manager.h"
17
18#define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
19
20static int start_workers(Manager *m, bool explicit_request);
21
22static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
99534007 23 Manager *m = ASSERT_PTR(userdata);
d093b62c
LP
24
25 assert(s);
d093b62c
LP
26
27 for (;;) {
28 siginfo_t siginfo = {};
29 bool removed = false;
30
31 if (waitid(P_ALL, 0, &siginfo, WNOHANG|WEXITED) < 0) {
32 if (errno == ECHILD)
33 break;
34
35 log_warning_errno(errno, "Failed to invoke waitid(): %m");
36 break;
37 }
38 if (siginfo.si_pid == 0)
39 break;
40
41 if (set_remove(m->workers_dynamic, PID_TO_PTR(siginfo.si_pid)))
42 removed = true;
43 if (set_remove(m->workers_fixed, PID_TO_PTR(siginfo.si_pid)))
44 removed = true;
45
46 if (!removed) {
47 log_warning("Weird, got SIGCHLD for unknown child " PID_FMT ", ignoring.", siginfo.si_pid);
48 continue;
49 }
50
51 if (siginfo.si_code == CLD_EXITED) {
52 if (siginfo.si_status == EXIT_SUCCESS)
53 log_debug("Worker " PID_FMT " exited successfully.", siginfo.si_pid);
54 else
55 log_warning("Worker " PID_FMT " died with a failure exit status %i, ignoring.", siginfo.si_pid, siginfo.si_status);
56 } else if (siginfo.si_code == CLD_KILLED)
57 log_warning("Worker " PID_FMT " was killed by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
58 else if (siginfo.si_code == CLD_DUMPED)
59 log_warning("Worker " PID_FMT " dumped core by signal %s, ignoring.", siginfo.si_pid, signal_to_string(siginfo.si_status));
60 else
61 log_warning("Can't handle SIGCHLD of this type");
62 }
63
64 (void) start_workers(m, false); /* Fill up workers again if we fell below the low watermark */
65 return 0;
66}
67
68static int on_sigusr2(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
99534007 69 Manager *m = ASSERT_PTR(userdata);
d093b62c
LP
70
71 assert(s);
d093b62c
LP
72
73 (void) start_workers(m, true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
74 return 0;
75}
76
77int manager_new(Manager **ret) {
b44b735a 78 _cleanup_(manager_freep) Manager *m = NULL;
d093b62c
LP
79 int r;
80
81 m = new(Manager, 1);
82 if (!m)
83 return -ENOMEM;
84
85 *m = (Manager) {
86 .listen_fd = -1,
87 .worker_ratelimit = {
88 .interval = 5 * USEC_PER_SEC,
89 .burst = 50,
90 },
91 };
92
93 r = sd_event_new(&m->event);
94 if (r < 0)
95 return r;
96
97 r = sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL);
98 if (r < 0)
99 return r;
100
101 r = sd_event_add_signal(m->event, NULL, SIGTERM, NULL, NULL);
102 if (r < 0)
103 return r;
104
105 (void) sd_event_set_watchdog(m->event, true);
106
107 m->workers_fixed = set_new(NULL);
108 m->workers_dynamic = set_new(NULL);
109
110 if (!m->workers_fixed || !m->workers_dynamic)
111 return -ENOMEM;
112
113 r = sd_event_add_signal(m->event, &m->sigusr2_event_source, SIGUSR2, on_sigusr2, m);
114 if (r < 0)
115 return r;
116
117 r = sd_event_add_signal(m->event, &m->sigchld_event_source, SIGCHLD, on_sigchld, m);
118 if (r < 0)
119 return r;
120
121 *ret = TAKE_PTR(m);
122 return 0;
123}
124
125Manager* manager_free(Manager *m) {
126 if (!m)
127 return NULL;
128
129 set_free(m->workers_fixed);
130 set_free(m->workers_dynamic);
131
132 sd_event_source_disable_unref(m->sigusr2_event_source);
133 sd_event_source_disable_unref(m->sigchld_event_source);
134
135 sd_event_unref(m->event);
136
137 return mfree(m);
138}
139
140static size_t manager_current_workers(Manager *m) {
141 assert(m);
142
143 return set_size(m->workers_fixed) + set_size(m->workers_dynamic);
144}
145
146static int start_one_worker(Manager *m) {
147 bool fixed;
148 pid_t pid;
149 int r;
150
151 assert(m);
152
153 fixed = set_size(m->workers_fixed) < USERDB_WORKERS_MIN;
154
155 r = safe_fork("(sd-worker)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
156 if (r < 0)
157 return log_error_errno(r, "Failed to fork new worker child: %m");
158 if (r == 0) {
159 char pids[DECIMAL_STR_MAX(pid_t)];
160 /* Child */
161
162 log_close();
163
164 r = close_all_fds(&m->listen_fd, 1);
165 if (r < 0) {
166 log_error_errno(r, "Failed to close fds in child: %m");
167 _exit(EXIT_FAILURE);
168 }
169
170 log_open();
171
172 if (m->listen_fd == 3) {
173 r = fd_cloexec(3, false);
174 if (r < 0) {
175 log_error_errno(r, "Failed to turn off O_CLOEXEC for fd 3: %m");
176 _exit(EXIT_FAILURE);
177 }
178 } else {
179 if (dup2(m->listen_fd, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
180 log_error_errno(errno, "Failed to move listen fd to 3: %m");
181 _exit(EXIT_FAILURE);
182 }
183
184 safe_close(m->listen_fd);
185 }
186
187 xsprintf(pids, PID_FMT, pid);
188 if (setenv("LISTEN_PID", pids, 1) < 0) {
189 log_error_errno(errno, "Failed to set $LISTEN_PID: %m");
190 _exit(EXIT_FAILURE);
191 }
192
193 if (setenv("LISTEN_FDS", "1", 1) < 0) {
194 log_error_errno(errno, "Failed to set $LISTEN_FDS: %m");
195 _exit(EXIT_FAILURE);
196 }
197
198
199 if (setenv("USERDB_FIXED_WORKER", one_zero(fixed), 1) < 0) {
200 log_error_errno(errno, "Failed to set $USERDB_FIXED_WORKER: %m");
201 _exit(EXIT_FAILURE);
202 }
203
204 /* execl("/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
205 /* execl("/usr/bin/valgrind", "valgrind", "/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
206
207 execl(SYSTEMD_USERWORK_PATH, "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /* With some extra space rename_process() can make use of */
208 log_error_errno(errno, "Failed start worker process: %m");
209 _exit(EXIT_FAILURE);
210 }
211
212 if (fixed)
213 r = set_put(m->workers_fixed, PID_TO_PTR(pid));
214 else
215 r = set_put(m->workers_dynamic, PID_TO_PTR(pid));
216 if (r < 0)
217 return log_error_errno(r, "Failed to add child process to set: %m");
218
219 return 0;
220}
221
222static int start_workers(Manager *m, bool explicit_request) {
223 int r;
224
225 assert(m);
226
227 for (;;) {
228 size_t n;
229
230 n = manager_current_workers(m);
231 if (n >= USERDB_WORKERS_MIN && (!explicit_request || n >= USERDB_WORKERS_MAX))
232 break;
233
234 if (!ratelimit_below(&m->worker_ratelimit)) {
235 /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
236 sd_event_exit(m->event, EXIT_FAILURE);
237
238 return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Worker threads requested too frequently, something is wrong.");
239 }
240
241 r = start_one_worker(m);
242 if (r < 0)
243 return r;
244
245 explicit_request = false;
246 }
247
248 return 0;
249}
250
251int manager_startup(Manager *m) {
d093b62c
LP
252 int n, r;
253
254 assert(m);
255 assert(m->listen_fd < 0);
256
257 n = sd_listen_fds(false);
258 if (n < 0)
259 return log_error_errno(n, "Failed to determine number of passed file descriptors: %m");
260 if (n > 1)
261 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected one listening fd, got %i.", n);
262 if (n == 1)
263 m->listen_fd = SD_LISTEN_FDS_START;
264 else {
425d925f
ZJS
265 union sockaddr_union sockaddr = {
266 .un.sun_family = AF_UNIX,
bbfb8c87 267 .un.sun_path = "/run/systemd/userdb/io.systemd.Multiplexer",
425d925f 268 };
d093b62c
LP
269
270 r = mkdir_p("/run/systemd/userdb", 0755);
271 if (r < 0)
272 return log_error_errno(r, "Failed to create /run/systemd/userdb: %m");
273
274 m->listen_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0);
275 if (m->listen_fd < 0)
276 return log_error_errno(errno, "Failed to bind on socket: %m");
277
278 (void) sockaddr_un_unlink(&sockaddr.un);
279
2053593f 280 WITH_UMASK(0000)
d093b62c
LP
281 if (bind(m->listen_fd, &sockaddr.sa, SOCKADDR_UN_LEN(sockaddr.un)) < 0)
282 return log_error_errno(errno, "Failed to bind socket: %m");
283
bbfb8c87
LP
284 r = symlink_idempotent("io.systemd.Multiplexer",
285 "/run/systemd/userdb/io.systemd.NameServiceSwitch", false);
d093b62c
LP
286 if (r < 0)
287 return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
288
8fbb1941
LP
289 r = symlink_idempotent("io.systemd.Multiplexer",
290 "/run/systemd/userdb/io.systemd.DropIn", false);
291 if (r < 0)
292 return log_error_errno(r, "Failed to bind io.systemd.Multiplexer: %m");
293
d093b62c
LP
294 if (listen(m->listen_fd, SOMAXCONN) < 0)
295 return log_error_errno(errno, "Failed to listen on socket: %m");
296 }
297
298 /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
299 * GC'ed on idle */
52bb308c 300 if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0)
d093b62c
LP
301 return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m");
302
303 return start_workers(m, false);
304}