1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "common-signal.h"
11 #include "process-util.h"
13 #include "signal-util.h"
14 #include "socket-util.h"
15 #include "stdio-util.h"
16 #include "umask-util.h"
17 #include "userdbd-manager.h"
19 #define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
21 static int start_workers(Manager
*m
, bool explicit_request
);
23 static int on_worker_exit(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
24 Manager
*m
= ASSERT_PTR(userdata
);
28 assert_se(!set_remove(m
->workers_dynamic
, s
) != !set_remove(m
->workers_fixed
, s
));
29 sd_event_source_disable_unref(s
);
31 if (si
->si_code
== CLD_EXITED
) {
32 if (si
->si_status
== EXIT_SUCCESS
)
33 log_debug("Worker " PID_FMT
" exited successfully.", si
->si_pid
);
35 log_warning("Worker " PID_FMT
" died with a failure exit status %i, ignoring.", si
->si_pid
, si
->si_status
);
36 } else if (si
->si_code
== CLD_KILLED
)
37 log_warning("Worker " PID_FMT
" was killed by signal %s, ignoring.", si
->si_pid
, signal_to_string(si
->si_status
));
38 else if (si
->si_code
== CLD_DUMPED
)
39 log_warning("Worker " PID_FMT
" dumped core by signal %s, ignoring.", si
->si_pid
, signal_to_string(si
->si_status
));
41 log_warning("Can't handle SIGCHLD of this type");
43 (void) start_workers(m
, /* explicit_request= */ false); /* Fill up workers again if we fell below the low watermark */
47 static int on_sigusr2(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
48 Manager
*m
= ASSERT_PTR(userdata
);
52 (void) start_workers(m
, /* explicit_request=*/ true); /* Workers told us there's more work, let's add one more worker as long as we are below the high watermark */
56 static int on_deferred_start_worker(sd_event_source
*s
, uint64_t usec
, void *userdata
) {
57 Manager
*m
= ASSERT_PTR(userdata
);
61 m
->deferred_start_worker_event_source
= sd_event_source_unref(m
->deferred_start_worker_event_source
);
63 (void) start_workers(m
, /* explicit_request=*/ false);
67 DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(
68 event_source_hash_ops
,
70 (void (*)(const sd_event_source
*, struct siphash
*)) trivial_hash_func
,
71 (int (*)(const sd_event_source
*, const sd_event_source
*)) trivial_compare_func
,
72 sd_event_source_disable_unref
);
74 int manager_new(Manager
**ret
) {
75 _cleanup_(manager_freep
) Manager
*m
= NULL
;
85 .interval
= 2 * USEC_PER_SEC
,
90 r
= sd_event_new(&m
->event
);
94 r
= sd_event_set_signal_exit(m
->event
, true);
98 r
= sd_event_add_signal(m
->event
, NULL
, (SIGRTMIN
+18)|SD_EVENT_SIGNAL_PROCMASK
, sigrtmin18_handler
, NULL
);
102 r
= sd_event_add_memory_pressure(m
->event
, NULL
, NULL
, NULL
);
104 log_debug_errno(r
, "Failed allocate memory pressure event source, ignoring: %m");
106 r
= sd_event_set_watchdog(m
->event
, true);
108 log_debug_errno(r
, "Failed to enable watchdog handling, ignoring: %m");
110 r
= sd_event_add_signal(m
->event
, NULL
, SIGUSR2
|SD_EVENT_SIGNAL_PROCMASK
, on_sigusr2
, m
);
118 Manager
* manager_free(Manager
*m
) {
122 set_free(m
->workers_fixed
);
123 set_free(m
->workers_dynamic
);
125 m
->deferred_start_worker_event_source
= sd_event_source_unref(m
->deferred_start_worker_event_source
);
127 sd_event_unref(m
->event
);
132 static size_t manager_current_workers(Manager
*m
) {
135 return set_size(m
->workers_fixed
) + set_size(m
->workers_dynamic
);
138 static int start_one_worker(Manager
*m
) {
139 _cleanup_(sd_event_source_disable_unrefp
) sd_event_source
*source
= NULL
;
146 fixed
= set_size(m
->workers_fixed
) < USERDB_WORKERS_MIN
;
150 /* stdio_fds= */ NULL
,
152 FORK_RESET_SIGNALS
|FORK_DEATHSIG_SIGTERM
|FORK_REOPEN_LOG
|FORK_LOG
|FORK_CLOSE_ALL_FDS
,
155 return log_error_errno(r
, "Failed to fork new worker child: %m");
157 char pids
[DECIMAL_STR_MAX(pid_t
)];
160 if (m
->listen_fd
== 3) {
161 r
= fd_cloexec(3, false);
163 log_error_errno(r
, "Failed to turn off O_CLOEXEC for fd 3: %m");
167 if (dup2(m
->listen_fd
, 3) < 0) { /* dup2() creates with O_CLOEXEC off */
168 log_error_errno(errno
, "Failed to move listen fd to 3: %m");
172 safe_close(m
->listen_fd
);
175 xsprintf(pids
, PID_FMT
, pid
);
176 if (setenv("LISTEN_PID", pids
, 1) < 0) {
177 log_error_errno(errno
, "Failed to set $LISTEN_PID: %m");
181 if (setenv("LISTEN_FDS", "1", 1) < 0) {
182 log_error_errno(errno
, "Failed to set $LISTEN_FDS: %m");
187 if (setenv("USERDB_FIXED_WORKER", one_zero(fixed
), 1) < 0) {
188 log_error_errno(errno
, "Failed to set $USERDB_FIXED_WORKER: %m");
192 /* execl("/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
193 /* 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 *\/ */
195 execl(SYSTEMD_USERWORK_PATH
, "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL
); /* With some extra space rename_process() can make use of */
196 log_error_errno(errno
, "Failed start worker process: %m");
200 r
= sd_event_add_child(m
->event
, &source
, pid
, WEXITED
, on_worker_exit
, m
);
202 return log_error_errno(r
, "Failed to watch child " PID_FMT
": %m", pid
);
205 fixed
? &m
->workers_fixed
: &m
->workers_dynamic
,
206 &event_source_hash_ops
,
209 return log_error_errno(r
, "Failed to add child process to set: %m");
216 static int start_workers(Manager
*m
, bool explicit_request
) {
224 n
= manager_current_workers(m
);
225 if (n
>= USERDB_WORKERS_MIN
&& (!explicit_request
|| n
>= USERDB_WORKERS_MAX
))
228 if (!ratelimit_below(&m
->worker_ratelimit
)) {
230 /* If we keep starting workers too often but none sticks, let's fail the whole
231 * daemon, something is wrong */
233 sd_event_exit(m
->event
, EXIT_FAILURE
);
234 return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN
), "Worker threads requested too frequently, but worker count is zero, something is wrong.");
237 /* Otherwise, let's stop spawning more for a while. */
238 log_warning("Worker threads requested too frequently, not starting new ones for a while.");
240 if (!m
->deferred_start_worker_event_source
) {
241 r
= sd_event_add_time(
243 &m
->deferred_start_worker_event_source
,
245 ratelimit_end(&m
->worker_ratelimit
),
246 /* accuracy_usec= */ 0,
247 on_deferred_start_worker
,
250 return log_error_errno(r
, "Failed to allocate deferred start worker event source: %m");
256 r
= start_one_worker(m
);
260 explicit_request
= false;
266 int manager_startup(Manager
*m
) {
270 assert(m
->listen_fd
< 0);
272 n
= sd_listen_fds(false);
274 return log_error_errno(n
, "Failed to determine number of passed file descriptors: %m");
276 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Expected one listening fd, got %i.", n
);
278 m
->listen_fd
= SD_LISTEN_FDS_START
;
280 static const union sockaddr_union sockaddr
= {
281 .un
.sun_family
= AF_UNIX
,
282 .un
.sun_path
= "/run/systemd/userdb/io.systemd.Multiplexer",
285 r
= mkdir_p("/run/systemd/userdb", 0755);
287 return log_error_errno(r
, "Failed to create /run/systemd/userdb: %m");
289 m
->listen_fd
= socket(AF_UNIX
, SOCK_STREAM
|SOCK_CLOEXEC
, 0);
290 if (m
->listen_fd
< 0)
291 return log_error_errno(errno
, "Failed to bind on socket: %m");
293 (void) sockaddr_un_unlink(&sockaddr
.un
);
296 if (bind(m
->listen_fd
, &sockaddr
.sa
, SOCKADDR_UN_LEN(sockaddr
.un
)) < 0)
297 return log_error_errno(errno
, "Failed to bind socket: %m");
299 r
= symlink_idempotent("io.systemd.Multiplexer",
300 "/run/systemd/userdb/io.systemd.NameServiceSwitch", false);
302 return log_error_errno(r
, "Failed to bind io.systemd.Multiplexer: %m");
304 r
= symlink_idempotent("io.systemd.Multiplexer",
305 "/run/systemd/userdb/io.systemd.DropIn", false);
307 return log_error_errno(r
, "Failed to bind io.systemd.Multiplexer: %m");
309 if (listen(m
->listen_fd
, SOMAXCONN_DELUXE
) < 0)
310 return log_error_errno(errno
, "Failed to listen on socket: %m");
313 /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
315 if (setsockopt(m
->listen_fd
, SOL_SOCKET
, SO_RCVTIMEO
, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC
), sizeof(struct timeval
)) < 0)
316 return log_error_errno(errno
, "Failed to se SO_RCVTIMEO: %m");
318 return start_workers(m
, /* explicit_request= */ false);