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