]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/userdb/userwork.c
ci: enable arm64 runner for build/unit jobs
[thirdparty/systemd.git] / src / userdb / userwork.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
d093b62c
LP
2
3#include <poll.h>
d093b62c
LP
4
5#include "sd-daemon.h"
25ff515b 6#include "sd-varlink.h"
d093b62c 7
2a5559c2 8#include "alloc-util.h"
76d62b63 9#include "argv-util.h"
d093b62c 10#include "env-util.h"
76d62b63 11#include "errno-util.h"
d093b62c 12#include "fd-util.h"
d093b62c 13#include "group-record.h"
0f2d351f 14#include "io-util.h"
0376ef36 15#include "json-util.h"
d093b62c 16#include "main-func.h"
2a5559c2
DDM
17#include "pidref.h"
18#include "string-util.h"
d093b62c 19#include "time-util.h"
d093b62c
LP
20#include "user-record.h"
21#include "user-util.h"
22#include "userdb.h"
abef4a7b 23#include "varlink-io.systemd.UserDatabase.h"
fd409ff0 24#include "varlink-util.h"
d093b62c
LP
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 {
45e587d8 33 const char *name;
d093b62c
LP
34 union {
35 uid_t uid;
36 gid_t gid;
37 };
38 const char *service;
5ec4933d 39 UserDBMatch match;
d093b62c
LP
40} LookupParameters;
41
5ec4933d
LP
42static void lookup_parameters_done(LookupParameters *p) {
43 assert(p);
44
45 userdb_match_done(&p->match);
46}
47
309a747f
LP
48static int add_nss_service(sd_json_variant **v) {
49 _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
d093b62c
LP
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
309a747f 58 if (sd_json_variant_by_key(*v, "service"))
d093b62c
LP
59 return 0;
60
61 r = sd_id128_get_machine(&mid);
62 if (r < 0)
63 return r;
64
309a747f
LP
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)));
d093b62c 67
309a747f 68 if (sd_json_variant_by_key(z, "service"))
d093b62c
LP
69 return 0;
70
309a747f 71 r = sd_json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
d093b62c
LP
72 if (r < 0)
73 return r;
74
309a747f 75 r = sd_json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
d093b62c
LP
76 if (r < 0)
77 return r;
78
309a747f 79 return sd_json_variant_set_field(v, "status", status);
d093b62c
LP
80}
81
25ff515b 82static int build_user_json(sd_varlink *link, UserRecord *ur, sd_json_variant **ret) {
d093b62c 83 _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
309a747f 84 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d093b62c
LP
85 UserRecordLoadFlags flags;
86 uid_t peer_uid;
87 bool trusted;
88 int r;
89
90 assert(ur);
91 assert(ret);
92
25ff515b 93 r = sd_varlink_get_peer_uid(link, &peer_uid);
d093b62c
LP
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
bfc0cc1a 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;
d093b62c
LP
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
309a747f 115 v = sd_json_variant_ref(stripped->json);
d093b62c
LP
116 r = add_nss_service(&v);
117 if (r < 0)
118 return r;
119
be5bee2a
LP
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)));
d093b62c
LP
124}
125
25ff515b 126static int userdb_flags_from_service(sd_varlink *link, const char *service, UserDBFlags *ret) {
134ff8f4 127 assert(link);
134ff8f4
LP
128 assert(ret);
129
130 if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
131 *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
cf7c7512 132 else if (streq_ptr(service, "io.systemd.DropIn"))
8fbb1941 133 *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
134ff8f4
LP
134 else if (streq_ptr(service, "io.systemd.Multiplexer"))
135 *ret = USERDB_AVOID_MULTIPLEXER;
136 else
25ff515b 137 return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
134ff8f4
LP
138
139 return 0;
140}
141
25ff515b 142static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
d093b62c 143
309a747f 144 static const sd_json_dispatch_field dispatch_table[] = {
5ec4933d
LP
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 },
d093b62c
LP
152 {}
153 };
154
309a747f 155 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d093b62c 156 _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
5ec4933d 157 _cleanup_(lookup_parameters_done) LookupParameters p = {
d093b62c 158 .uid = UID_INVALID,
5ec4933d 159 .match = USERDB_MATCH_NULL,
d093b62c 160 };
134ff8f4 161 UserDBFlags userdb_flags;
d093b62c
LP
162 int r;
163
164 assert(parameters);
165
25ff515b 166 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
f1b622a0 167 if (r != 0)
d093b62c
LP
168 return r;
169
134ff8f4 170 r = userdb_flags_from_service(link, p.service, &userdb_flags);
18e94a40
LP
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' */
134ff8f4 173 return r;
d093b62c 174
134ff8f4 175 if (uid_is_valid(p.uid))
5ec4933d 176 r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
45e587d8 177 else if (p.name)
5ec4933d 178 r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
134ff8f4
LP
179 else {
180 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
309a747f 181 _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
d093b62c 182
5ec4933d 183 r = userdb_all(&p.match, userdb_flags, &iterator);
e908961d
LP
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. */
25ff515b 191 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
134ff8f4
LP
192 if (r < 0)
193 return r;
d093b62c 194
134ff8f4
LP
195 for (;;) {
196 _cleanup_(user_record_unrefp) UserRecord *z = NULL;
d093b62c 197
5ec4933d 198 r = userdb_iterator_get(iterator, &p.match, &z);
134ff8f4
LP
199 if (r == -ESRCH)
200 break;
201 if (r < 0)
202 return r;
d093b62c 203
134ff8f4 204 if (last) {
25ff515b 205 r = sd_varlink_notify(link, last);
d093b62c 206 if (r < 0)
d093b62c 207 return r;
d093b62c 208
309a747f 209 last = sd_json_variant_unref(last);
d093b62c
LP
210 }
211
134ff8f4 212 r = build_user_json(link, z, &last);
d093b62c
LP
213 if (r < 0)
214 return r;
134ff8f4 215 }
d093b62c 216
134ff8f4 217 if (!last)
25ff515b 218 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
d093b62c 219
25ff515b 220 return sd_varlink_reply(link, last);
134ff8f4 221 }
d093b62c 222 if (r == -ESRCH)
25ff515b 223 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
5ec4933d
LP
224 if (r == -ENOEXEC)
225 return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
d093b62c
LP
226 if (r < 0) {
227 log_debug_errno(r, "User lookup failed abnormally: %m");
25ff515b 228 return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
d093b62c
LP
229 }
230
231 if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
45e587d8 232 (p.name && !user_record_matches_user_name(hr, p.name)))
25ff515b 233 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
d093b62c
LP
234
235 r = build_user_json(link, hr, &v);
236 if (r < 0)
237 return r;
238
25ff515b 239 return sd_varlink_reply(link, v);
d093b62c
LP
240}
241
25ff515b 242static int build_group_json(sd_varlink *link, GroupRecord *gr, sd_json_variant **ret) {
d093b62c 243 _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
309a747f 244 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d093b62c
LP
245 UserRecordLoadFlags flags;
246 uid_t peer_uid;
247 bool trusted;
248 int r;
249
250 assert(gr);
251 assert(ret);
252
25ff515b 253 r = sd_varlink_get_peer_uid(link, &peer_uid);
d093b62c
LP
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
bfc0cc1a 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;
d093b62c
LP
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
309a747f 275 v = sd_json_variant_ref(gr->json);
d093b62c
LP
276 r = add_nss_service(&v);
277 if (r < 0)
278 return r;
279
be5bee2a
LP
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)));
d093b62c
LP
284}
285
25ff515b 286static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
d093b62c 287
309a747f 288 static const sd_json_dispatch_field dispatch_table[] = {
5ec4933d
LP
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 },
d093b62c
LP
296 {}
297 };
298
309a747f 299 _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
d093b62c 300 _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
5ec4933d 301 _cleanup_(lookup_parameters_done) LookupParameters p = {
d093b62c 302 .gid = GID_INVALID,
5ec4933d 303 .match = USERDB_MATCH_NULL,
d093b62c 304 };
134ff8f4 305 UserDBFlags userdb_flags;
d093b62c
LP
306 int r;
307
308 assert(parameters);
309
25ff515b 310 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
f1b622a0 311 if (r != 0)
d093b62c
LP
312 return r;
313
134ff8f4 314 r = userdb_flags_from_service(link, p.service, &userdb_flags);
18e94a40 315 if (r != 0)
134ff8f4 316 return r;
d093b62c 317
134ff8f4 318 if (gid_is_valid(p.gid))
5ec4933d 319 r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
45e587d8 320 else if (p.name)
5ec4933d 321 r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
134ff8f4
LP
322 else {
323 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
309a747f 324 _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
d093b62c 325
5ec4933d 326 r = groupdb_all(&p.match, userdb_flags, &iterator);
e908961d 327 if (IN_SET(r, -ESRCH, -ENOLINK))
25ff515b 328 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
134ff8f4
LP
329 if (r < 0)
330 return r;
d093b62c 331
134ff8f4
LP
332 for (;;) {
333 _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
d093b62c 334
5ec4933d 335 r = groupdb_iterator_get(iterator, &p.match, &z);
134ff8f4
LP
336 if (r == -ESRCH)
337 break;
338 if (r < 0)
339 return r;
d093b62c 340
134ff8f4 341 if (last) {
25ff515b 342 r = sd_varlink_notify(link, last);
d093b62c 343 if (r < 0)
d093b62c 344 return r;
d093b62c 345
309a747f 346 last = sd_json_variant_unref(last);
d093b62c
LP
347 }
348
134ff8f4 349 r = build_group_json(link, z, &last);
d093b62c
LP
350 if (r < 0)
351 return r;
134ff8f4 352 }
d093b62c 353
134ff8f4 354 if (!last)
25ff515b 355 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
d093b62c 356
25ff515b 357 return sd_varlink_reply(link, last);
134ff8f4 358 }
d093b62c 359 if (r == -ESRCH)
25ff515b 360 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
5ec4933d
LP
361 if (r == -ENOEXEC)
362 return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
d093b62c
LP
363 if (r < 0) {
364 log_debug_errno(r, "Group lookup failed abnormally: %m");
25ff515b 365 return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
d093b62c
LP
366 }
367
368 if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
45e587d8 369 (p.name && !group_record_matches_group_name(g, p.name)))
25ff515b 370 return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
d093b62c
LP
371
372 r = build_group_json(link, g, &v);
373 if (r < 0)
374 return r;
375
25ff515b 376 return sd_varlink_reply(link, v);
d093b62c
LP
377}
378
45e587d8
LP
379typedef struct MembershipLookupParameters {
380 const char *user_name;
381 const char *group_name;
382 const char *service;
383} MembershipLookupParameters;
384
25ff515b 385static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
309a747f 386 static const sd_json_dispatch_field dispatch_table[] = {
45e587d8
LP
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 },
d093b62c
LP
390 {}
391 };
392
134ff8f4
LP
393 _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
394 _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
45e587d8 395 MembershipLookupParameters p = {};
134ff8f4 396 UserDBFlags userdb_flags;
d093b62c
LP
397 int r;
398
399 assert(parameters);
400
25ff515b 401 r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
f1b622a0 402 if (r != 0)
d093b62c
LP
403 return r;
404
134ff8f4 405 r = userdb_flags_from_service(link, p.service, &userdb_flags);
18e94a40 406 if (r != 0)
134ff8f4 407 return r;
d093b62c 408
134ff8f4
LP
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);
e908961d 415 if (IN_SET(r, -ESRCH, -ENOLINK))
25ff515b 416 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
134ff8f4
LP
417 if (r < 0)
418 return r;
d093b62c 419
134ff8f4
LP
420 for (;;) {
421 _cleanup_free_ char *user_name = NULL, *group_name = NULL;
d093b62c 422
134ff8f4
LP
423 r = membershipdb_iterator_get(iterator, &user_name, &group_name);
424 if (r == -ESRCH)
425 break;
d093b62c
LP
426 if (r < 0)
427 return r;
428
134ff8f4
LP
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;
d093b62c 432
134ff8f4
LP
433 if (last_user_name) {
434 assert(last_group_name);
435
25ff515b 436 r = sd_varlink_notifybo(
be5bee2a
LP
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)));
d093b62c
LP
440 if (r < 0)
441 return r;
d093b62c
LP
442 }
443
6ac65492
YW
444 free_and_replace(last_user_name, user_name);
445 free_and_replace(last_group_name, group_name);
134ff8f4 446 }
d093b62c 447
134ff8f4
LP
448 if (!last_user_name) {
449 assert(!last_group_name);
25ff515b 450 return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
d093b62c
LP
451 }
452
134ff8f4
LP
453 assert(last_group_name);
454
25ff515b 455 return sd_varlink_replybo(
be5bee2a
LP
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)));
d093b62c
LP
459}
460
25ff515b 461static int process_connection(sd_varlink_server *server, int _fd) {
d562667f 462 _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
25ff515b 463 _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
d093b62c
LP
464 int r;
465
25ff515b
LP
466 assert(server);
467 assert(fd >= 0);
468
469 r = sd_varlink_server_add_connection(server, fd, &vl);
523121d5 470 if (r < 0)
d093b62c 471 return log_error_errno(r, "Failed to add connection: %m");
d093b62c 472
523121d5 473 TAKE_FD(fd);
25ff515b 474 vl = sd_varlink_ref(vl);
d093b62c
LP
475
476 for (;;) {
25ff515b 477 r = sd_varlink_process(vl);
d093b62c
LP
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
25ff515b 487 r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
d093b62c
LP
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;
25ff515b 499 _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
7c695bea 500 _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
d093b62c
LP
501 unsigned n_iterations = 0;
502 int m, listen_fd, r;
503
d2acb93d 504 log_setup();
d093b62c
LP
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
fd409ff0 520 r = varlink_server_new(&server, 0, NULL);
d093b62c 521 if (r < 0)
fd409ff0 522 return log_error_errno(r, "Failed to allocate varlink server: %m");
d093b62c 523
25ff515b 524 r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
abef4a7b
LP
525 if (r < 0)
526 return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
527
25ff515b 528 r = sd_varlink_server_bind_method_many(
d093b62c
LP
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
037b0a47
LP
541 r = userdb_block_nss_systemd(true);
542 if (r < 0)
d093b62c
LP
543 return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
544
7c695bea
LP
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
d093b62c
LP
551 start_time = now(CLOCK_MONOTONIC);
552
553 for (;;) {
254d1313 554 _cleanup_close_ int fd = -EBADF;
d093b62c
LP
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)) {
e14db350 565 log_debug("Exiting worker, ran for %s, that's enough.",
5291f26d 566 FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
d093b62c
LP
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)) {
40fd0a77 573 log_debug("Exiting worker, been idle for %s.",
5291f26d 574 FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
d093b62c
LP
575 break;
576 }
577
578 (void) rename_process("systemd-userwork: waiting...");
7c248223 579 fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
d093b62c
LP
580 (void) rename_process("systemd-userwork: processing...");
581
582 if (fd == -EAGAIN)
e14db350 583 continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
d093b62c
LP
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)) {
d093b62c
LP
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
0f2d351f
LP
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");
d093b62c 598
0f2d351f 599 if (FLAGS_SET(r, POLLIN)) {
7c695bea
LP
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");
d093b62c
LP
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);