]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/userdb/userwork.c
presets: Disable by default for initrd presets
[thirdparty/systemd.git] / src / userdb / userwork.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <poll.h>
4
5#include "sd-daemon.h"
6#include "sd-varlink.h"
7
8#include "alloc-util.h"
9#include "argv-util.h"
10#include "env-util.h"
11#include "errno-util.h"
12#include "fd-util.h"
13#include "group-record.h"
14#include "io-util.h"
15#include "json-util.h"
16#include "main-func.h"
17#include "pidref.h"
18#include "string-util.h"
19#include "time-util.h"
20#include "user-record.h"
21#include "user-util.h"
22#include "userdb.h"
23#include "varlink-io.systemd.UserDatabase.h"
24#include "varlink-util.h"
25
26#define ITERATIONS_MAX 64U
27#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
28#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
29#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
30#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
31
32typedef struct LookupParameters {
33 const char *name;
34 union {
35 uid_t uid;
36 gid_t gid;
37 };
38 const char *service;
39 UserDBMatch match;
40} LookupParameters;
41
42static void lookup_parameters_done(LookupParameters *p) {
43 assert(p);
44
45 userdb_match_done(&p->match);
46}
47
48static int add_nss_service(sd_json_variant **v) {
49 _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
50 sd_id128_t mid;
51 int r;
52
53 assert(v);
54
55 /* Patch in service field if it's missing. The assumption here is that this field is unset only for
56 * NSS records */
57
58 if (sd_json_variant_by_key(*v, "service"))
59 return 0;
60
61 r = sd_id128_get_machine(&mid);
62 if (r < 0)
63 return r;
64
65 status = sd_json_variant_ref(sd_json_variant_by_key(*v, "status"));
66 z = sd_json_variant_ref(sd_json_variant_by_key(status, SD_ID128_TO_STRING(mid)));
67
68 if (sd_json_variant_by_key(z, "service"))
69 return 0;
70
71 r = sd_json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
72 if (r < 0)
73 return r;
74
75 r = sd_json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
76 if (r < 0)
77 return r;
78
79 return sd_json_variant_set_field(v, "status", status);
80}
81
82static int build_user_json(sd_varlink *link, UserRecord *ur, sd_json_variant **ret) {
83 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
84 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
85 UserRecordLoadFlags flags;
86 uid_t peer_uid;
87 bool trusted;
88 int r;
89
90 assert(ur);
91 assert(ret);
92
93 r = sd_varlink_get_peer_uid(link, &peer_uid);
94 if (r < 0) {
95 log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
96 trusted = false;
97 } else
98 trusted = peer_uid == 0 || peer_uid == ur->uid;
99
100 flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE;
101 if (trusted)
102 flags |= USER_RECORD_ALLOW_PRIVILEGED;
103 else
104 flags |= USER_RECORD_STRIP_PRIVILEGED;
105
106 r = user_record_clone(ur, flags, &stripped);
107 if (r < 0)
108 return r;
109
110 stripped->incomplete =
111 ur->incomplete ||
112 (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
113 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
114
115 v = sd_json_variant_ref(stripped->json);
116 r = add_nss_service(&v);
117 if (r < 0)
118 return r;
119
120 return sd_json_buildo(
121 ret,
122 SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_VARIANT(v)),
123 SD_JSON_BUILD_PAIR("incomplete", SD_JSON_BUILD_BOOLEAN(stripped->incomplete)));
124}
125
126static int userdb_flags_from_service(sd_varlink *link, const char *service, UserDBFlags *ret) {
127 assert(link);
128 assert(ret);
129
130 if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
131 *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
132 else if (streq_ptr(service, "io.systemd.DropIn"))
133 *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
134 else if (streq_ptr(service, "io.systemd.Multiplexer"))
135 *ret = USERDB_AVOID_MULTIPLEXER;
136 else
137 return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
138
139 return 0;
140}
141
142static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
143
144 static const sd_json_dispatch_field dispatch_table[] = {
145 { "uid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
146 { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
147 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
148 { "fuzzyNames", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(LookupParameters, match.fuzzy_names), 0 },
149 { "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
150 { "uidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_min), 0 },
151 { "uidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.uid_max), 0 },
152 {}
153 };
154
155 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
156 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
157 _cleanup_(lookup_parameters_done) LookupParameters p = {
158 .uid = UID_INVALID,
159 .match = USERDB_MATCH_NULL,
160 };
161 UserDBFlags userdb_flags;
162 int r;
163
164 assert(parameters);
165
166 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
167 if (r != 0)
168 return r;
169
170 r = userdb_flags_from_service(link, p.service, &userdb_flags);
171 if (r != 0) /* return value of < 0 means error (as usual); > 0 means 'already processed and replied,
172 * we are done'; == 0 means 'not processed, caller should process now' */
173 return r;
174
175 if (uid_is_valid(p.uid))
176 r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
177 else if (p.name)
178 r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
179 else {
180 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
181 _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
182
183 r = userdb_all(&p.match, userdb_flags, &iterator);
184 if (IN_SET(r, -ESRCH, -ENOLINK))
185 /* We turn off Varlink lookups in various cases (e.g. in case we only enable DropIn
186 * backend) — this might make userdb_all return ENOLINK (which indicates that varlink
187 * was off and no other suitable source or entries were found). Let's hide this
188 * implementation detail and always return NoRecordFound in this case, since from a
189 * client's perspective it's irrelevant if there was no entry at all or just not on
190 * the service that the query was limited to. */
191 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
192 if (r < 0)
193 return r;
194
195 for (;;) {
196 _cleanup_(user_record_unrefp) UserRecord *z = NULL;
197
198 r = userdb_iterator_get(iterator, &p.match, &z);
199 if (r == -ESRCH)
200 break;
201 if (r < 0)
202 return r;
203
204 if (last) {
205 r = sd_varlink_notify(link, last);
206 if (r < 0)
207 return r;
208
209 last = sd_json_variant_unref(last);
210 }
211
212 r = build_user_json(link, z, &last);
213 if (r < 0)
214 return r;
215 }
216
217 if (!last)
218 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
219
220 return sd_varlink_reply(link, last);
221 }
222 if (r == -ESRCH)
223 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
224 if (r == -ENOEXEC)
225 return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
226 if (r < 0) {
227 log_debug_errno(r, "User lookup failed abnormally: %m");
228 return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
229 }
230
231 if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
232 (p.name && !user_record_matches_user_name(hr, p.name)))
233 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
234
235 r = build_user_json(link, hr, &v);
236 if (r < 0)
237 return r;
238
239 return sd_varlink_reply(link, v);
240}
241
242static int build_group_json(sd_varlink *link, GroupRecord *gr, sd_json_variant **ret) {
243 _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
244 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
245 UserRecordLoadFlags flags;
246 uid_t peer_uid;
247 bool trusted;
248 int r;
249
250 assert(gr);
251 assert(ret);
252
253 r = sd_varlink_get_peer_uid(link, &peer_uid);
254 if (r < 0) {
255 log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
256 trusted = false;
257 } else
258 trusted = peer_uid == 0;
259
260 flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE;
261 if (trusted)
262 flags |= USER_RECORD_ALLOW_PRIVILEGED;
263 else
264 flags |= USER_RECORD_STRIP_PRIVILEGED;
265
266 r = group_record_clone(gr, flags, &stripped);
267 if (r < 0)
268 return r;
269
270 stripped->incomplete =
271 gr->incomplete ||
272 (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
273 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
274
275 v = sd_json_variant_ref(gr->json);
276 r = add_nss_service(&v);
277 if (r < 0)
278 return r;
279
280 return sd_json_buildo(
281 ret,
282 SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_VARIANT(v)),
283 SD_JSON_BUILD_PAIR("incomplete", SD_JSON_BUILD_BOOLEAN(stripped->incomplete)));
284}
285
286static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
287
288 static const sd_json_dispatch_field dispatch_table[] = {
289 { "gid", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
290 { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(LookupParameters, name), SD_JSON_RELAX },
291 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
292 { "fuzzyNames", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(LookupParameters, match.fuzzy_names), 0 },
293 { "dispositionMask", SD_JSON_VARIANT_ARRAY, json_dispatch_dispositions_mask, offsetof(LookupParameters, match.disposition_mask), 0 },
294 { "gidMin", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_min), 0 },
295 { "gidMax", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid, offsetof(LookupParameters, match.gid_max), 0 },
296 {}
297 };
298
299 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
300 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
301 _cleanup_(lookup_parameters_done) LookupParameters p = {
302 .gid = GID_INVALID,
303 .match = USERDB_MATCH_NULL,
304 };
305 UserDBFlags userdb_flags;
306 int r;
307
308 assert(parameters);
309
310 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
311 if (r != 0)
312 return r;
313
314 r = userdb_flags_from_service(link, p.service, &userdb_flags);
315 if (r != 0)
316 return r;
317
318 if (gid_is_valid(p.gid))
319 r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
320 else if (p.name)
321 r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
322 else {
323 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
324 _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
325
326 r = groupdb_all(&p.match, userdb_flags, &iterator);
327 if (IN_SET(r, -ESRCH, -ENOLINK))
328 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
329 if (r < 0)
330 return r;
331
332 for (;;) {
333 _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
334
335 r = groupdb_iterator_get(iterator, &p.match, &z);
336 if (r == -ESRCH)
337 break;
338 if (r < 0)
339 return r;
340
341 if (last) {
342 r = sd_varlink_notify(link, last);
343 if (r < 0)
344 return r;
345
346 last = sd_json_variant_unref(last);
347 }
348
349 r = build_group_json(link, z, &last);
350 if (r < 0)
351 return r;
352 }
353
354 if (!last)
355 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
356
357 return sd_varlink_reply(link, last);
358 }
359 if (r == -ESRCH)
360 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
361 if (r == -ENOEXEC)
362 return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
363 if (r < 0) {
364 log_debug_errno(r, "Group lookup failed abnormally: %m");
365 return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
366 }
367
368 if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
369 (p.name && !group_record_matches_group_name(g, p.name)))
370 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
371
372 r = build_group_json(link, g, &v);
373 if (r < 0)
374 return r;
375
376 return sd_varlink_reply(link, v);
377}
378
379typedef struct MembershipLookupParameters {
380 const char *user_name;
381 const char *group_name;
382 const char *service;
383} MembershipLookupParameters;
384
385static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
386 static const sd_json_dispatch_field dispatch_table[] = {
387 { "userName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, user_name), SD_JSON_RELAX },
388 { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, group_name), SD_JSON_RELAX },
389 { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MembershipLookupParameters, service), 0 },
390 {}
391 };
392
393 _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
394 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
395 MembershipLookupParameters p = {};
396 UserDBFlags userdb_flags;
397 int r;
398
399 assert(parameters);
400
401 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
402 if (r != 0)
403 return r;
404
405 r = userdb_flags_from_service(link, p.service, &userdb_flags);
406 if (r != 0)
407 return r;
408
409 if (p.group_name)
410 r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
411 else if (p.user_name)
412 r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
413 else
414 r = membershipdb_all(userdb_flags, &iterator);
415 if (IN_SET(r, -ESRCH, -ENOLINK))
416 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
417 if (r < 0)
418 return r;
419
420 for (;;) {
421 _cleanup_free_ char *user_name = NULL, *group_name = NULL;
422
423 r = membershipdb_iterator_get(iterator, &user_name, &group_name);
424 if (r == -ESRCH)
425 break;
426 if (r < 0)
427 return r;
428
429 /* If both group + user are specified do a-posteriori filtering */
430 if (p.group_name && p.user_name && !streq(group_name, p.group_name))
431 continue;
432
433 if (last_user_name) {
434 assert(last_group_name);
435
436 r = sd_varlink_notifybo(
437 link,
438 SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)),
439 SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name)));
440 if (r < 0)
441 return r;
442 }
443
444 free_and_replace(last_user_name, user_name);
445 free_and_replace(last_group_name, group_name);
446 }
447
448 if (!last_user_name) {
449 assert(!last_group_name);
450 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
451 }
452
453 assert(last_group_name);
454
455 return sd_varlink_replybo(
456 link,
457 SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)),
458 SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name)));
459}
460
461static int process_connection(sd_varlink_server *server, int _fd) {
462 _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
463 _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
464 int r;
465
466 assert(server);
467 assert(fd >= 0);
468
469 r = sd_varlink_server_add_connection(server, fd, &vl);
470 if (r < 0)
471 return log_error_errno(r, "Failed to add connection: %m");
472
473 TAKE_FD(fd);
474 vl = sd_varlink_ref(vl);
475
476 for (;;) {
477 r = sd_varlink_process(vl);
478 if (r == -ENOTCONN) {
479 log_debug("Connection terminated.");
480 break;
481 }
482 if (r < 0)
483 return log_error_errno(r, "Failed to process connection: %m");
484 if (r > 0)
485 continue;
486
487 r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
488 if (r < 0)
489 return log_error_errno(r, "Failed to wait for connection events: %m");
490 if (r == 0)
491 break;
492 }
493
494 return 0;
495}
496
497static int run(int argc, char *argv[]) {
498 usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
499 _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
500 _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
501 unsigned n_iterations = 0;
502 int m, listen_fd, r;
503
504 log_setup();
505
506 m = sd_listen_fds(false);
507 if (m < 0)
508 return log_error_errno(m, "Failed to determine number of listening fds: %m");
509 if (m == 0)
510 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
511 if (m > 1)
512 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
513
514 listen_fd = SD_LISTEN_FDS_START;
515
516 r = fd_nonblock(listen_fd, false);
517 if (r < 0)
518 return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
519
520 r = varlink_server_new(&server, 0, NULL);
521 if (r < 0)
522 return log_error_errno(r, "Failed to allocate varlink server: %m");
523
524 r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
525 if (r < 0)
526 return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
527
528 r = sd_varlink_server_bind_method_many(
529 server,
530 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
531 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
532 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
533 if (r < 0)
534 return log_error_errno(r, "Failed to bind methods: %m");
535
536 r = getenv_bool("USERDB_FIXED_WORKER");
537 if (r < 0)
538 return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
539 listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
540
541 r = userdb_block_nss_systemd(true);
542 if (r < 0)
543 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
544
545 r = pidref_set_parent(&parent);
546 if (r < 0)
547 return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
548 if (parent.pid == 1) /* We got reparented away from userdbd? */
549 return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
550
551 start_time = now(CLOCK_MONOTONIC);
552
553 for (;;) {
554 _cleanup_close_ int fd = -EBADF;
555 usec_t n;
556
557 /* Exit the worker in regular intervals, to flush out all memory use */
558 if (n_iterations++ > ITERATIONS_MAX) {
559 log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
560 break;
561 }
562
563 n = now(CLOCK_MONOTONIC);
564 if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
565 log_debug("Exiting worker, ran for %s, that's enough.",
566 FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
567 break;
568 }
569
570 if (last_busy_usec == USEC_INFINITY)
571 last_busy_usec = n;
572 else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
573 log_debug("Exiting worker, been idle for %s.",
574 FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
575 break;
576 }
577
578 (void) rename_process("systemd-userwork: waiting...");
579 fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
580 (void) rename_process("systemd-userwork: processing...");
581
582 if (fd == -EAGAIN)
583 continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
584 * after a while, let's check if it's time to exit though. */
585 if (fd == -EINTR)
586 continue; /* Might be that somebody attached via strace, let's just continue in that
587 * case */
588 if (fd < 0)
589 return log_error_errno(fd, "Failed to accept() from listening socket: %m");
590
591 if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
592 /* We only slept a very short time? If so, let's see if there are more sockets
593 * pending, and if so, let's ask our parent for more workers */
594
595 r = fd_wait_for_event(listen_fd, POLLIN, 0);
596 if (r < 0)
597 return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
598
599 if (FLAGS_SET(r, POLLIN)) {
600 r = pidref_kill(&parent, SIGUSR2);
601 if (r == -ESRCH)
602 return log_error_errno(r, "Parent already died?");
603 if (r < 0)
604 return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
605 }
606 }
607
608 (void) process_connection(server, TAKE_FD(fd));
609 last_busy_usec = USEC_INFINITY;
610 }
611
612 return 0;
613}
614
615DEFINE_MAIN_FUNCTION(run);