]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/userdb/userwork.c
Merge pull request #18007 from fw-strlen/ipv6_masq_and_dnat
[thirdparty/systemd.git] / src / userdb / userwork.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <poll.h>
4 #include <sys/wait.h>
5
6 #include "sd-daemon.h"
7
8 #include "env-util.h"
9 #include "fd-util.h"
10 #include "group-record.h"
11 #include "io-util.h"
12 #include "main-func.h"
13 #include "process-util.h"
14 #include "strv.h"
15 #include "time-util.h"
16 #include "user-record-nss.h"
17 #include "user-record.h"
18 #include "user-util.h"
19 #include "userdb.h"
20 #include "varlink.h"
21
22 #define ITERATIONS_MAX 64U
23 #define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
24 #define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
25 #define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
26 #define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
27
28 typedef struct LookupParameters {
29 const char *user_name;
30 const char *group_name;
31 union {
32 uid_t uid;
33 gid_t gid;
34 };
35 const char *service;
36 } LookupParameters;
37
38 static int add_nss_service(JsonVariant **v) {
39 _cleanup_(json_variant_unrefp) JsonVariant *status = NULL, *z = NULL;
40 char buf[SD_ID128_STRING_MAX];
41 sd_id128_t mid;
42 int r;
43
44 assert(v);
45
46 /* Patch in service field if it's missing. The assumption here is that this field is unset only for
47 * NSS records */
48
49 if (json_variant_by_key(*v, "service"))
50 return 0;
51
52 r = sd_id128_get_machine(&mid);
53 if (r < 0)
54 return r;
55
56 status = json_variant_ref(json_variant_by_key(*v, "status"));
57 z = json_variant_ref(json_variant_by_key(status, sd_id128_to_string(mid, buf)));
58
59 if (json_variant_by_key(z, "service"))
60 return 0;
61
62 r = json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
63 if (r < 0)
64 return r;
65
66 r = json_variant_set_field(&status, buf, z);
67 if (r < 0)
68 return r;
69
70 return json_variant_set_field(v, "status", status);
71 }
72
73 static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
74 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
75 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
76 UserRecordLoadFlags flags;
77 uid_t peer_uid;
78 bool trusted;
79 int r;
80
81 assert(ur);
82 assert(ret);
83
84 r = varlink_get_peer_uid(link, &peer_uid);
85 if (r < 0) {
86 log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
87 trusted = false;
88 } else
89 trusted = peer_uid == 0 || peer_uid == ur->uid;
90
91 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;
92 if (trusted)
93 flags |= USER_RECORD_ALLOW_PRIVILEGED;
94 else
95 flags |= USER_RECORD_STRIP_PRIVILEGED;
96
97 r = user_record_clone(ur, flags, &stripped);
98 if (r < 0)
99 return r;
100
101 stripped->incomplete =
102 ur->incomplete ||
103 (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
104 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
105
106 v = json_variant_ref(stripped->json);
107 r = add_nss_service(&v);
108 if (r < 0)
109 return r;
110
111 return json_build(ret, JSON_BUILD_OBJECT(
112 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
113 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
114 }
115
116 static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
117
118 static const JsonDispatch dispatch_table[] = {
119 { "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
120 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
121 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
122 {}
123 };
124
125 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
126 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
127 LookupParameters p = {
128 .uid = UID_INVALID,
129 };
130 int r;
131
132 assert(parameters);
133
134 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
135 if (r < 0)
136 return r;
137
138 if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
139 if (uid_is_valid(p.uid))
140 r = nss_user_record_by_uid(p.uid, true, &hr);
141 else if (p.user_name)
142 r = nss_user_record_by_name(p.user_name, true, &hr);
143 else {
144 _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
145
146 setpwent();
147
148 for (;;) {
149 _cleanup_(user_record_unrefp) UserRecord *z = NULL;
150 _cleanup_free_ char *sbuf = NULL;
151 struct passwd *pw;
152 struct spwd spwd;
153
154 errno = 0;
155 pw = getpwent();
156 if (!pw) {
157 if (errno != 0)
158 log_debug_errno(errno, "Failure while iterating through NSS user database, ignoring: %m");
159
160 break;
161 }
162
163 r = nss_spwd_for_passwd(pw, &spwd, &sbuf);
164 if (r < 0)
165 log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
166
167 r = nss_passwd_to_user_record(pw, NULL, &z);
168 if (r < 0) {
169 endpwent();
170 return r;
171 }
172
173 if (last) {
174 r = varlink_notify(link, last);
175 if (r < 0) {
176 endpwent();
177 return r;
178 }
179
180 last = json_variant_unref(last);
181 }
182
183 r = build_user_json(link, z, &last);
184 if (r < 0) {
185 endpwent();
186 return r;
187 }
188 }
189
190 endpwent();
191
192 if (!last)
193 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
194
195 return varlink_reply(link, last);
196 }
197
198 } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
199
200 if (uid_is_valid(p.uid))
201 r = userdb_by_uid(p.uid, USERDB_AVOID_MULTIPLEXER, &hr);
202 else if (p.user_name)
203 r = userdb_by_name(p.user_name, USERDB_AVOID_MULTIPLEXER, &hr);
204 else {
205 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
206 _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
207
208 r = userdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
209 if (r < 0)
210 return r;
211
212 for (;;) {
213 _cleanup_(user_record_unrefp) UserRecord *z = NULL;
214
215 r = userdb_iterator_get(iterator, &z);
216 if (r == -ESRCH)
217 break;
218 if (r < 0)
219 return r;
220
221 if (last) {
222 r = varlink_notify(link, last);
223 if (r < 0)
224 return r;
225
226 last = json_variant_unref(last);
227 }
228
229 r = build_user_json(link, z, &last);
230 if (r < 0)
231 return r;
232 }
233
234 if (!last)
235 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
236
237 return varlink_reply(link, last);
238 }
239 } else
240 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
241 if (r == -ESRCH)
242 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
243 if (r < 0) {
244 log_debug_errno(r, "User lookup failed abnormally: %m");
245 return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
246 }
247
248 if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
249 (p.user_name && !streq(hr->user_name, p.user_name)))
250 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
251
252 r = build_user_json(link, hr, &v);
253 if (r < 0)
254 return r;
255
256 return varlink_reply(link, v);
257 }
258
259 static int build_group_json(Varlink *link, GroupRecord *gr, JsonVariant **ret) {
260 _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
261 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
262 UserRecordLoadFlags flags;
263 uid_t peer_uid;
264 bool trusted;
265 int r;
266
267 assert(gr);
268 assert(ret);
269
270 r = varlink_get_peer_uid(link, &peer_uid);
271 if (r < 0) {
272 log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
273 trusted = false;
274 } else
275 trusted = peer_uid == 0;
276
277 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;
278 if (trusted)
279 flags |= USER_RECORD_ALLOW_PRIVILEGED;
280 else
281 flags |= USER_RECORD_STRIP_PRIVILEGED;
282
283 r = group_record_clone(gr, flags, &stripped);
284 if (r < 0)
285 return r;
286
287 stripped->incomplete =
288 gr->incomplete ||
289 (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
290 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
291
292 v = json_variant_ref(gr->json);
293 r = add_nss_service(&v);
294 if (r < 0)
295 return r;
296
297 return json_build(ret, JSON_BUILD_OBJECT(
298 JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(v)),
299 JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
300 }
301
302 static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
303
304 static const JsonDispatch dispatch_table[] = {
305 { "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
306 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
307 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
308 {}
309 };
310
311 _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
312 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
313 LookupParameters p = {
314 .gid = GID_INVALID,
315 };
316 int r;
317
318 assert(parameters);
319
320 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
321 if (r < 0)
322 return r;
323
324 if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
325
326 if (gid_is_valid(p.gid))
327 r = nss_group_record_by_gid(p.gid, true, &g);
328 else if (p.group_name)
329 r = nss_group_record_by_name(p.group_name, true, &g);
330 else {
331 _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
332
333 setgrent();
334
335 for (;;) {
336 _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
337 _cleanup_free_ char *sbuf = NULL;
338 struct group *grp;
339 struct sgrp sgrp;
340
341 errno = 0;
342 grp = getgrent();
343 if (!grp) {
344 if (errno != 0)
345 log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
346
347 break;
348 }
349
350 r = nss_sgrp_for_group(grp, &sgrp, &sbuf);
351 if (r < 0)
352 log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", grp->gr_name);
353
354 r = nss_group_to_group_record(grp, r >= 0 ? &sgrp : NULL, &z);
355 if (r < 0) {
356 endgrent();
357 return r;
358 }
359
360 if (last) {
361 r = varlink_notify(link, last);
362 if (r < 0) {
363 endgrent();
364 return r;
365 }
366
367 last = json_variant_unref(last);
368 }
369
370 r = build_group_json(link, z, &last);
371 if (r < 0) {
372 endgrent();
373 return r;
374 }
375 }
376
377 endgrent();
378
379 if (!last)
380 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
381
382 return varlink_reply(link, last);
383 }
384
385 } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
386
387 if (gid_is_valid(p.gid))
388 r = groupdb_by_gid(p.gid, USERDB_AVOID_MULTIPLEXER, &g);
389 else if (p.group_name)
390 r = groupdb_by_name(p.group_name, USERDB_AVOID_MULTIPLEXER, &g);
391 else {
392 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
393 _cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
394
395 r = groupdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
396 if (r < 0)
397 return r;
398
399 for (;;) {
400 _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
401
402 r = groupdb_iterator_get(iterator, &z);
403 if (r == -ESRCH)
404 break;
405 if (r < 0)
406 return r;
407
408 if (last) {
409 r = varlink_notify(link, last);
410 if (r < 0)
411 return r;
412
413 last = json_variant_unref(last);
414 }
415
416 r = build_group_json(link, z, &last);
417 if (r < 0)
418 return r;
419 }
420
421 if (!last)
422 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
423
424 return varlink_reply(link, last);
425 }
426 } else
427 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
428 if (r == -ESRCH)
429 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
430 if (r < 0) {
431 log_debug_errno(r, "Group lookup failed abnormally: %m");
432 return varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
433 }
434
435 if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
436 (p.group_name && !streq(g->group_name, p.group_name)))
437 return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
438
439 r = build_group_json(link, g, &v);
440 if (r < 0)
441 return r;
442
443 return varlink_reply(link, v);
444 }
445
446 static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
447 static const JsonDispatch dispatch_table[] = {
448 { "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), 0 },
449 { "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), 0 },
450 { "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
451 {}
452 };
453
454 LookupParameters p = {};
455 int r;
456
457 assert(parameters);
458
459 r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
460 if (r < 0)
461 return r;
462
463 if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
464
465 if (p.group_name) {
466 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
467 const char *last = NULL;
468 char **i;
469
470 r = nss_group_record_by_name(p.group_name, true, &g);
471 if (r == -ESRCH)
472 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
473 if (r < 0)
474 return r;
475
476 STRV_FOREACH(i, g->members) {
477
478 if (p.user_name && !streq_ptr(p.user_name, *i))
479 continue;
480
481 if (last) {
482 r = varlink_notifyb(link, JSON_BUILD_OBJECT(
483 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
484 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
485 if (r < 0)
486 return r;
487 }
488
489 last = *i;
490 }
491
492 if (!last)
493 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
494
495 return varlink_replyb(link, JSON_BUILD_OBJECT(
496 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
497 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
498 } else {
499 _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
500
501 setgrent();
502
503 for (;;) {
504 struct group *grp;
505 const char* two[2], **users, **i;
506
507 errno = 0;
508 grp = getgrent();
509 if (!grp) {
510 if (errno != 0)
511 log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
512
513 break;
514 }
515
516 if (p.user_name) {
517 if (!strv_contains(grp->gr_mem, p.user_name))
518 continue;
519
520 two[0] = p.user_name;
521 two[1] = NULL;
522
523 users = two;
524 } else
525 users = (const char**) grp->gr_mem;
526
527 STRV_FOREACH(i, users) {
528
529 if (last_user_name) {
530 assert(last_group_name);
531
532 r = varlink_notifyb(link, JSON_BUILD_OBJECT(
533 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
534 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
535 if (r < 0) {
536 endgrent();
537 return r;
538 }
539
540 free(last_user_name);
541 free(last_group_name);
542 }
543
544 last_user_name = strdup(*i);
545 last_group_name = strdup(grp->gr_name);
546 if (!last_user_name || !last_group_name) {
547 endgrent();
548 return -ENOMEM;
549 }
550 }
551 }
552
553 endgrent();
554
555 if (!last_user_name) {
556 assert(!last_group_name);
557 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
558 }
559
560 assert(last_group_name);
561
562 return varlink_replyb(link, JSON_BUILD_OBJECT(
563 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
564 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
565 }
566
567 } else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
568
569 _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
570 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
571
572 if (p.group_name)
573 r = membershipdb_by_group(p.group_name, USERDB_AVOID_MULTIPLEXER, &iterator);
574 else if (p.user_name)
575 r = membershipdb_by_user(p.user_name, USERDB_AVOID_MULTIPLEXER, &iterator);
576 else
577 r = membershipdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
578 if (r < 0)
579 return r;
580
581 for (;;) {
582 _cleanup_free_ char *user_name = NULL, *group_name = NULL;
583
584 r = membershipdb_iterator_get(iterator, &user_name, &group_name);
585 if (r == -ESRCH)
586 break;
587 if (r < 0)
588 return r;
589
590 /* If both group + user are specified do a-posteriori filtering */
591 if (p.group_name && p.user_name && !streq(group_name, p.group_name))
592 continue;
593
594 if (last_user_name) {
595 assert(last_group_name);
596
597 r = varlink_notifyb(link, JSON_BUILD_OBJECT(
598 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
599 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
600 if (r < 0)
601 return r;
602
603 free(last_user_name);
604 free(last_group_name);
605 }
606
607 last_user_name = TAKE_PTR(user_name);
608 last_group_name = TAKE_PTR(group_name);
609 }
610
611 if (!last_user_name) {
612 assert(!last_group_name);
613 return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
614 }
615
616 assert(last_group_name);
617
618 return varlink_replyb(link, JSON_BUILD_OBJECT(
619 JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
620 JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
621 }
622
623 return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
624 }
625
626 static int process_connection(VarlinkServer *server, int fd) {
627 _cleanup_(varlink_close_unrefp) Varlink *vl = NULL;
628 int r;
629
630 r = varlink_server_add_connection(server, fd, &vl);
631 if (r < 0) {
632 fd = safe_close(fd);
633 return log_error_errno(r, "Failed to add connection: %m");
634 }
635
636 vl = varlink_ref(vl);
637
638 for (;;) {
639 r = varlink_process(vl);
640 if (r == -ENOTCONN) {
641 log_debug("Connection terminated.");
642 break;
643 }
644 if (r < 0)
645 return log_error_errno(r, "Failed to process connection: %m");
646 if (r > 0)
647 continue;
648
649 r = varlink_wait(vl, CONNECTION_IDLE_USEC);
650 if (r < 0)
651 return log_error_errno(r, "Failed to wait for connection events: %m");
652 if (r == 0)
653 break;
654 }
655
656 return 0;
657 }
658
659 static int run(int argc, char *argv[]) {
660 usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
661 _cleanup_(varlink_server_unrefp) VarlinkServer *server = NULL;
662 unsigned n_iterations = 0;
663 int m, listen_fd, r;
664
665 log_setup();
666
667 m = sd_listen_fds(false);
668 if (m < 0)
669 return log_error_errno(m, "Failed to determine number of listening fds: %m");
670 if (m == 0)
671 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
672 if (m > 1)
673 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
674
675 listen_fd = SD_LISTEN_FDS_START;
676
677 r = fd_nonblock(listen_fd, false);
678 if (r < 0)
679 return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
680
681 r = varlink_server_new(&server, 0);
682 if (r < 0)
683 return log_error_errno(r, "Failed to allocate server: %m");
684
685 r = varlink_server_bind_method_many(
686 server,
687 "io.systemd.UserDatabase.GetUserRecord", vl_method_get_user_record,
688 "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
689 "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
690 if (r < 0)
691 return log_error_errno(r, "Failed to bind methods: %m");
692
693 r = getenv_bool("USERDB_FIXED_WORKER");
694 if (r < 0)
695 return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
696 listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
697
698 r = userdb_block_nss_systemd(true);
699 if (r < 0)
700 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
701
702 start_time = now(CLOCK_MONOTONIC);
703
704 for (;;) {
705 _cleanup_close_ int fd = -1;
706 usec_t n;
707
708 /* Exit the worker in regular intervals, to flush out all memory use */
709 if (n_iterations++ > ITERATIONS_MAX) {
710 log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
711 break;
712 }
713
714 n = now(CLOCK_MONOTONIC);
715 if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
716 char buf[FORMAT_TIMESPAN_MAX];
717 log_debug("Exiting worker, ran for %s, that's enough.",
718 format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, start_time), 0));
719 break;
720 }
721
722 if (last_busy_usec == USEC_INFINITY)
723 last_busy_usec = n;
724 else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
725 char buf[FORMAT_TIMESPAN_MAX];
726 log_debug("Exiting worker, been idle for %s.",
727 format_timespan(buf, sizeof(buf), usec_sub_unsigned(n, last_busy_usec), 0));
728 break;
729 }
730
731 (void) rename_process("systemd-userwork: waiting...");
732
733 fd = accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
734 if (fd < 0)
735 fd = -errno;
736
737 (void) rename_process("systemd-userwork: processing...");
738
739 if (fd == -EAGAIN)
740 continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
741 * after a while, let's check if it's time to exit though. */
742 if (fd == -EINTR)
743 continue; /* Might be that somebody attached via strace, let's just continue in that
744 * case */
745 if (fd < 0)
746 return log_error_errno(fd, "Failed to accept() from listening socket: %m");
747
748 if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
749 /* We only slept a very short time? If so, let's see if there are more sockets
750 * pending, and if so, let's ask our parent for more workers */
751
752 r = fd_wait_for_event(listen_fd, POLLIN, 0);
753 if (r < 0)
754 return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
755
756 if (FLAGS_SET(r, POLLIN)) {
757 pid_t parent;
758
759 parent = getppid();
760 if (parent <= 1)
761 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Parent already died?");
762
763 if (kill(parent, SIGUSR2) < 0)
764 return log_error_errno(errno, "Failed to kill our own parent.");
765 }
766 }
767
768 (void) process_connection(server, TAKE_FD(fd));
769 last_busy_usec = USEC_INFINITY;
770 }
771
772 return 0;
773 }
774
775 DEFINE_MAIN_FUNCTION(run);