]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/userdb/userdbd-manager.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
10 #include "process-util.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"
18 #define LISTEN_TIMEOUT_USEC (25 * USEC_PER_SEC)
20 static int start_workers ( Manager
* m
, bool explicit_request
);
22 static int on_sigchld ( sd_event_source
* s
, const struct signalfd_siginfo
* si
, void * userdata
) {
23 Manager
* m
= userdata
;
29 siginfo_t siginfo
= {};
32 if ( waitid ( P_ALL
, 0 , & siginfo
, WNOHANG
| WEXITED
) < 0 ) {
36 log_warning_errno ( errno
, "Failed to invoke waitid(): %m" );
39 if ( siginfo
. si_pid
== 0 )
42 if ( set_remove ( m
-> workers_dynamic
, PID_TO_PTR ( siginfo
. si_pid
)))
44 if ( set_remove ( m
-> workers_fixed
, PID_TO_PTR ( siginfo
. si_pid
)))
48 log_warning ( "Weird, got SIGCHLD for unknown child " PID_FMT
", ignoring." , siginfo
. si_pid
);
52 if ( siginfo
. si_code
== CLD_EXITED
) {
53 if ( siginfo
. si_status
== EXIT_SUCCESS
)
54 log_debug ( "Worker " PID_FMT
" exited successfully." , siginfo
. si_pid
);
56 log_warning ( "Worker " PID_FMT
" died with a failure exit status %i, ignoring." , siginfo
. si_pid
, siginfo
. si_status
);
57 } else if ( siginfo
. si_code
== CLD_KILLED
)
58 log_warning ( "Worker " PID_FMT
" was killed by signal %s, ignoring." , siginfo
. si_pid
, signal_to_string ( siginfo
. si_status
));
59 else if ( siginfo
. si_code
== CLD_DUMPED
)
60 log_warning ( "Worker " PID_FMT
" dumped core by signal %s, ignoring." , siginfo
. si_pid
, signal_to_string ( siginfo
. si_status
));
62 log_warning ( "Can't handle SIGCHLD of this type" );
65 ( void ) start_workers ( m
, false ); /* Fill up workers again if we fell below the low watermark */
69 static int on_sigusr2 ( sd_event_source
* s
, const struct signalfd_siginfo
* si
, void * userdata
) {
70 Manager
* m
= userdata
;
75 ( 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 */
79 int manager_new ( Manager
** ret
) {
80 _cleanup_ ( manager_freep
) Manager
* m
= NULL
;
90 . interval
= 5 * USEC_PER_SEC
,
95 r
= sd_event_new (& m
-> event
);
99 r
= sd_event_add_signal ( m
-> event
, NULL
, SIGINT
, NULL
, NULL
);
103 r
= sd_event_add_signal ( m
-> event
, NULL
, SIGTERM
, NULL
, NULL
);
107 ( void ) sd_event_set_watchdog ( m
-> event
, true );
109 m
-> workers_fixed
= set_new ( NULL
);
110 m
-> workers_dynamic
= set_new ( NULL
);
112 if (! m
-> workers_fixed
|| ! m
-> workers_dynamic
)
115 r
= sd_event_add_signal ( m
-> event
, & m
-> sigusr2_event_source
, SIGUSR2
, on_sigusr2
, m
);
119 r
= sd_event_add_signal ( m
-> event
, & m
-> sigchld_event_source
, SIGCHLD
, on_sigchld
, m
);
127 Manager
* manager_free ( Manager
* m
) {
131 set_free ( m
-> workers_fixed
);
132 set_free ( m
-> workers_dynamic
);
134 sd_event_source_disable_unref ( m
-> sigusr2_event_source
);
135 sd_event_source_disable_unref ( m
-> sigchld_event_source
);
137 sd_event_unref ( m
-> event
);
142 static size_t manager_current_workers ( Manager
* m
) {
145 return set_size ( m
-> workers_fixed
) + set_size ( m
-> workers_dynamic
);
148 static int start_one_worker ( Manager
* m
) {
155 fixed
= set_size ( m
-> workers_fixed
) < USERDB_WORKERS_MIN
;
157 r
= safe_fork ( "(sd-worker)" , FORK_RESET_SIGNALS
| FORK_DEATHSIG
| FORK_LOG
, & pid
);
159 return log_error_errno ( r
, "Failed to fork new worker child: %m" );
161 char pids
[ DECIMAL_STR_MAX ( pid_t
)];
166 r
= close_all_fds (& m
-> listen_fd
, 1 );
168 log_error_errno ( r
, "Failed to close fds in child: %m" );
174 if ( m
-> listen_fd
== 3 ) {
175 r
= fd_cloexec ( 3 , false );
177 log_error_errno ( r
, "Failed to turn off O_CLOEXEC for fd 3: %m" );
181 if ( dup2 ( m
-> listen_fd
, 3 ) < 0 ) { /* dup2() creates with O_CLOEXEC off */
182 log_error_errno ( errno
, "Failed to move listen fd to 3: %m" );
186 safe_close ( m
-> listen_fd
);
189 xsprintf ( pids
, PID_FMT
, pid
);
190 if ( setenv ( "LISTEN_PID" , pids
, 1 ) < 0 ) {
191 log_error_errno ( errno
, "Failed to set $LISTEN_PID: %m" );
195 if ( setenv ( "LISTEN_FDS" , "1" , 1 ) < 0 ) {
196 log_error_errno ( errno
, "Failed to set $LISTEN_FDS: %m" );
201 if ( setenv ( "USERDB_FIXED_WORKER" , one_zero ( fixed
), 1 ) < 0 ) {
202 log_error_errno ( errno
, "Failed to set $USERDB_FIXED_WORKER: %m" );
206 /* execl("/home/lennart/projects/systemd/build/systemd-userwork", "systemd-userwork", "xxxxxxxxxxxxxxxx", NULL); /\* With some extra space rename_process() can make use of *\/ */
207 /* 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 *\/ */
209 execl ( SYSTEMD_USERWORK_PATH
, "systemd-userwork" , "xxxxxxxxxxxxxxxx" , NULL
); /* With some extra space rename_process() can make use of */
210 log_error_errno ( errno
, "Failed start worker process: %m" );
215 r
= set_put ( m
-> workers_fixed
, PID_TO_PTR ( pid
));
217 r
= set_put ( m
-> workers_dynamic
, PID_TO_PTR ( pid
));
219 return log_error_errno ( r
, "Failed to add child process to set: %m" );
224 static int start_workers ( Manager
* m
, bool explicit_request
) {
232 n
= manager_current_workers ( m
);
233 if ( n
>= USERDB_WORKERS_MIN
&& (! explicit_request
|| n
>= USERDB_WORKERS_MAX
))
236 if (! ratelimit_below (& m
-> worker_ratelimit
)) {
237 /* If we keep starting workers too often, let's fail the whole daemon, something is wrong */
238 sd_event_exit ( m
-> event
, EXIT_FAILURE
);
240 return log_error_errno ( SYNTHETIC_ERRNO ( EUCLEAN
), "Worker threads requested too frequently, something is wrong." );
243 r
= start_one_worker ( m
);
247 explicit_request
= false ;
253 int manager_startup ( Manager
* m
) {
257 assert ( m
-> listen_fd
< 0 );
259 n
= sd_listen_fds ( false );
261 return log_error_errno ( n
, "Failed to determine number of passed file descriptors: %m" );
263 return log_error_errno ( SYNTHETIC_ERRNO ( EINVAL
), "Expected one listening fd, got %i." , n
);
265 m
-> listen_fd
= SD_LISTEN_FDS_START
;
267 union sockaddr_union sockaddr
= {
268 . un
. sun_family
= AF_UNIX
,
269 . un
. sun_path
= "/run/systemd/userdb/io.systemd.Multiplexer" ,
272 r
= mkdir_p ( "/run/systemd/userdb" , 0755 );
274 return log_error_errno ( r
, "Failed to create /run/systemd/userdb: %m" );
276 m
-> listen_fd
= socket ( AF_UNIX
, SOCK_STREAM
| SOCK_CLOEXEC
, 0 );
277 if ( m
-> listen_fd
< 0 )
278 return log_error_errno ( errno
, "Failed to bind on socket: %m" );
280 ( void ) sockaddr_un_unlink (& sockaddr
. un
);
283 if ( bind ( m
-> listen_fd
, & sockaddr
. sa
, SOCKADDR_UN_LEN ( sockaddr
. un
)) < 0 )
284 return log_error_errno ( errno
, "Failed to bind socket: %m" );
286 r
= symlink_idempotent ( "io.systemd.Multiplexer" ,
287 "/run/systemd/userdb/io.systemd.NameServiceSwitch" , false );
289 return log_error_errno ( r
, "Failed to bind io.systemd.Multiplexer: %m" );
291 r
= symlink_idempotent ( "io.systemd.Multiplexer" ,
292 "/run/systemd/userdb/io.systemd.DropIn" , false );
294 return log_error_errno ( r
, "Failed to bind io.systemd.Multiplexer: %m" );
296 if ( listen ( m
-> listen_fd
, SOMAXCONN
) < 0 )
297 return log_error_errno ( errno
, "Failed to listen on socket: %m" );
300 /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be
302 if ( setsockopt ( m
-> listen_fd
, SOL_SOCKET
, SO_RCVTIMEO
, TIMEVAL_STORE ( LISTEN_TIMEOUT_USEC
), sizeof ( struct timeval
)) < 0 )
303 return log_error_errno ( errno
, "Failed to se SO_RCVTIMEO: %m" );
305 return start_workers ( m
, false );