• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 20447389715

21 Dec 2025 07:31PM UTC coverage: 72.37% (-0.1%) from 72.5%
20447389715

push

github

DaanDeMeyer
mkosi: Use initrd as exitrd

Let's speed up image builds by avoiding building
an exitrd and instead reusing the initrd image for
the same purpose.

308584 of 426400 relevant lines covered (72.37%)

1134231.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.9
/src/userdb/userwork.c
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

32
typedef 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

42
static void lookup_parameters_done(LookupParameters *p) {
24,630✔
43
        assert(p);
24,630✔
44

45
        userdb_match_done(&p->match);
24,630✔
46
}
24,630✔
47

48
static int add_nss_service(sd_json_variant **v) {
6,174✔
49
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
12,346✔
50
        sd_id128_t mid;
6,174✔
51
        int r;
6,174✔
52

53
        assert(v);
6,174✔
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"))
6,174✔
59
                return 0;
60

61
        r = sd_id128_get_machine(&mid);
6,172✔
62
        if (r < 0)
6,172✔
63
                return r;
64

65
        status = sd_json_variant_ref(sd_json_variant_by_key(*v, "status"));
6,172✔
66
        z = sd_json_variant_ref(sd_json_variant_by_key(status, SD_ID128_TO_STRING(mid)));
6,172✔
67

68
        if (sd_json_variant_by_key(z, "service"))
6,172✔
69
                return 0;
70

71
        r = sd_json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
5,966✔
72
        if (r < 0)
5,966✔
73
                return r;
74

75
        r = sd_json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
5,966✔
76
        if (r < 0)
5,966✔
77
                return r;
78

79
        return sd_json_variant_set_field(v, "status", status);
5,966✔
80
}
81

82
static int build_user_json(sd_varlink *link, UserRecord *ur, sd_json_variant **ret) {
3,595✔
83
        _cleanup_(user_record_unrefp) UserRecord *stripped = NULL;
×
84
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
3,595✔
85
        UserRecordLoadFlags flags;
3,595✔
86
        uid_t peer_uid;
3,595✔
87
        bool trusted;
3,595✔
88
        int r;
3,595✔
89

90
        assert(ur);
3,595✔
91
        assert(ret);
3,595✔
92

93
        r = sd_varlink_get_peer_uid(link, &peer_uid);
3,595✔
94
        if (r < 0) {
3,595✔
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;
3,595✔
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);
3,595✔
107
        if (r < 0)
3,595✔
108
                return r;
109

110
        stripped->incomplete =
7,190✔
111
                ur->incomplete ||
3,595✔
112
                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
3,595✔
113
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
3,053✔
114

115
        v = sd_json_variant_ref(stripped->json);
3,595✔
116
        r = add_nss_service(&v);
3,595✔
117
        if (r < 0)
3,595✔
118
                return r;
119

120
        return sd_json_buildo(
3,595✔
121
                        ret,
122
                        SD_JSON_BUILD_PAIR_VARIANT("record", v),
123
                        SD_JSON_BUILD_PAIR_BOOLEAN("incomplete", stripped->incomplete));
124
}
125

126
static int userdb_flags_from_service(sd_varlink *link, const char *service, UserDBFlags *ret) {
37,733✔
127
        assert(link);
37,733✔
128
        assert(ret);
37,733✔
129

130
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
37,733✔
131
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
54✔
132
        else if (streq_ptr(service, "io.systemd.DropIn"))
37,679✔
133
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
28,175✔
134
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
9,504✔
135
                *ret = USERDB_AVOID_MULTIPLEXER;
9,504✔
136
        else
137
                return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
×
138

139
        return 0;
140
}
141

142
static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
7,390✔
143

144
        static const sd_json_dispatch_field dispatch_table[] = {
7,390✔
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
                { "uuid",            SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,              offsetof(LookupParameters, match.uuid),             0             },
153
                {}
154
        };
155

156
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
7,390✔
157
        _cleanup_(user_record_unrefp) UserRecord *hr = NULL;
7,390✔
158
        _cleanup_(lookup_parameters_done) LookupParameters p = {
7,390✔
159
                .uid = UID_INVALID,
160
                .match = USERDB_MATCH_NULL,
161
        };
162
        UserDBFlags userdb_flags;
7,390✔
163
        int r;
7,390✔
164

165
        assert(parameters);
7,390✔
166

167
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
7,390✔
168
        if (r != 0)
7,390✔
169
                return r;
170

171
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
7,390✔
172
        if (r != 0) /* return value of < 0 means error (as usual); > 0 means 'already processed and replied,
7,390✔
173
                     * we are done'; == 0 means 'not processed, caller should process now' */
174
                return r;
175

176
        if (uid_is_valid(p.uid))
7,390✔
177
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
4,246✔
178
        else if (p.name)
3,144✔
179
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
3,138✔
180
        else {
181
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
182
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
6✔
183

184
                r = userdb_all(&p.match, userdb_flags, &iterator);
6✔
185
                if (IN_SET(r, -ESRCH, -ENOLINK))
6✔
186
                        /* We turn off Varlink lookups in various cases (e.g. in case we only enable DropIn
187
                         * backend) — this might make userdb_all return ENOLINK (which indicates that varlink
188
                         * was off and no other suitable source or entries were found). Let's hide this
189
                         * implementation detail and always return NoRecordFound in this case, since from a
190
                         * client's perspective it's irrelevant if there was no entry at all or just not on
191
                         * the service that the query was limited to. */
192
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
×
193
                if (r < 0)
6✔
194
                        return r;
195

196
                for (;;) {
109✔
197
                        _cleanup_(user_record_unrefp) UserRecord *z = NULL;
115✔
198

199
                        r = userdb_iterator_get(iterator, &p.match, &z);
115✔
200
                        if (r == -ESRCH)
115✔
201
                                break;
202
                        if (r < 0)
109✔
203
                                return r;
204

205
                        if (last) {
109✔
206
                                r = sd_varlink_notify(link, last);
104✔
207
                                if (r < 0)
104✔
208
                                        return r;
209

210
                                last = sd_json_variant_unref(last);
104✔
211
                        }
212

213
                        r = build_user_json(link, z, &last);
109✔
214
                        if (r < 0)
109✔
215
                                return r;
216
                }
217

218
                if (!last)
6✔
219
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
1✔
220

221
                return sd_varlink_reply(link, last);
5✔
222
        }
223
        if (r == -ESRCH)
7,384✔
224
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
3,898✔
225
        if (r == -ENOEXEC)
3,486✔
226
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
227
        if (r < 0) {
3,486✔
228
                log_debug_errno(r, "User lookup failed abnormally: %m");
×
229
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
230
        }
231

232
        if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
3,486✔
233
            (p.name && !user_record_matches_user_name(hr, p.name)))
3,486✔
234
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
235

236
        r = build_user_json(link, hr, &v);
3,486✔
237
        if (r < 0)
3,486✔
238
                return r;
239

240
        return sd_varlink_reply(link, v);
3,486✔
241
}
242

243
static int build_group_json(sd_varlink *link, GroupRecord *gr, sd_json_variant **ret) {
2,579✔
244
        _cleanup_(group_record_unrefp) GroupRecord *stripped = NULL;
×
245
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
2,579✔
246
        UserRecordLoadFlags flags;
2,579✔
247
        uid_t peer_uid;
2,579✔
248
        bool trusted;
2,579✔
249
        int r;
2,579✔
250

251
        assert(gr);
2,579✔
252
        assert(ret);
2,579✔
253

254
        r = sd_varlink_get_peer_uid(link, &peer_uid);
2,579✔
255
        if (r < 0) {
2,579✔
256
                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
×
257
                trusted = false;
258
        } else
259
                trusted = peer_uid == 0;
2,579✔
260

261
        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;
2,579✔
262
        if (trusted)
2,579✔
263
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
264
        else
265
                flags |= USER_RECORD_STRIP_PRIVILEGED;
266

267
        r = group_record_clone(gr, flags, &stripped);
2,579✔
268
        if (r < 0)
2,579✔
269
                return r;
270

271
        stripped->incomplete =
5,158✔
272
                gr->incomplete ||
2,579✔
273
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
2,579✔
274
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
35✔
275

276
        v = sd_json_variant_ref(gr->json);
2,579✔
277
        r = add_nss_service(&v);
2,579✔
278
        if (r < 0)
2,579✔
279
                return r;
280

281
        return sd_json_buildo(
2,579✔
282
                        ret,
283
                        SD_JSON_BUILD_PAIR_VARIANT("record", v),
284
                        SD_JSON_BUILD_PAIR_BOOLEAN("incomplete", stripped->incomplete));
285
}
286

287
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
17,240✔
288

289
        static const sd_json_dispatch_field dispatch_table[] = {
17,240✔
290
                { "gid",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, gid),                    0             },
291
                { "groupName",       SD_JSON_VARIANT_STRING,        json_dispatch_const_user_group_name, offsetof(LookupParameters, name),                   SD_JSON_RELAX },
292
                { "service",         SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,       offsetof(LookupParameters, service),                0             },
293
                { "fuzzyNames",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,               offsetof(LookupParameters, match.fuzzy_names),      0             },
294
                { "dispositionMask", SD_JSON_VARIANT_ARRAY,         json_dispatch_dispositions_mask,     offsetof(LookupParameters, match.disposition_mask), 0             },
295
                { "gidMin",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.gid_min),          0             },
296
                { "gidMax",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.gid_max),          0             },
297
                { "uuid",            SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,              offsetof(LookupParameters, match.uuid),             0             },
298
                {}
299
        };
300

301
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
17,240✔
302
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
17,240✔
303
        _cleanup_(lookup_parameters_done) LookupParameters p = {
17,240✔
304
                .gid = GID_INVALID,
305
                .match = USERDB_MATCH_NULL,
306
        };
307
        UserDBFlags userdb_flags;
17,240✔
308
        int r;
17,240✔
309

310
        assert(parameters);
17,240✔
311

312
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
17,240✔
313
        if (r != 0)
17,240✔
314
                return r;
315

316
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
17,240✔
317
        if (r != 0)
17,240✔
318
                return r;
319

320
        if (gid_is_valid(p.gid))
17,240✔
321
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
4,432✔
322
        else if (p.name)
12,808✔
323
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
12,806✔
324
        else {
325
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
326
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
2✔
327

328
                r = groupdb_all(&p.match, userdb_flags, &iterator);
2✔
329
                if (IN_SET(r, -ESRCH, -ENOLINK))
2✔
330
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
×
331
                if (r < 0)
2✔
332
                        return r;
333

334
                for (;;) {
119✔
335
                        _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
121✔
336

337
                        r = groupdb_iterator_get(iterator, &p.match, &z);
121✔
338
                        if (r == -ESRCH)
121✔
339
                                break;
340
                        if (r < 0)
119✔
341
                                return r;
342

343
                        if (last) {
119✔
344
                                r = sd_varlink_notify(link, last);
117✔
345
                                if (r < 0)
117✔
346
                                        return r;
347

348
                                last = sd_json_variant_unref(last);
117✔
349
                        }
350

351
                        r = build_group_json(link, z, &last);
119✔
352
                        if (r < 0)
119✔
353
                                return r;
354
                }
355

356
                if (!last)
2✔
357
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
×
358

359
                return sd_varlink_reply(link, last);
2✔
360
        }
361
        if (r == -ESRCH)
17,238✔
362
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
14,778✔
363
        if (r == -ENOEXEC)
2,460✔
364
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
365
        if (r < 0) {
2,460✔
366
                log_debug_errno(r, "Group lookup failed abnormally: %m");
×
367
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
368
        }
369

370
        if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
2,460✔
371
            (p.name && !group_record_matches_group_name(g, p.name)))
2,460✔
372
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
373

374
        r = build_group_json(link, g, &v);
2,460✔
375
        if (r < 0)
2,460✔
376
                return r;
377

378
        return sd_varlink_reply(link, v);
2,460✔
379
}
380

381
typedef struct MembershipLookupParameters {
382
        const char *user_name;
383
        const char *group_name;
384
        const char *service;
385
} MembershipLookupParameters;
386

387
static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
13,103✔
388
        static const sd_json_dispatch_field dispatch_table[] = {
13,103✔
389
                { "userName",  SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, user_name),  SD_JSON_RELAX },
390
                { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, group_name), SD_JSON_RELAX },
391
                { "service",   SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string,       offsetof(MembershipLookupParameters, service),    0             },
392
                {}
393
        };
394

395
        _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
13,103✔
396
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
13,103✔
397
        MembershipLookupParameters p = {};
13,103✔
398
        UserDBFlags userdb_flags;
13,103✔
399
        int r;
13,103✔
400

401
        assert(parameters);
13,103✔
402

403
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
13,103✔
404
        if (r != 0)
13,103✔
405
                return r;
406

407
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
13,103✔
408
        if (r != 0)
13,103✔
409
                return r;
410

411
        if (p.group_name)
13,103✔
412
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
11,592✔
413
        else if (p.user_name)
1,511✔
414
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
1,511✔
415
        else
416
                r = membershipdb_all(userdb_flags, &iterator);
×
417
        if (IN_SET(r, -ESRCH, -ENOLINK))
13,103✔
418
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
12,996✔
419
        if (r < 0)
107✔
420
                return r;
421

422
        for (;;) {
201✔
423
                _cleanup_free_ char *user_name = NULL, *group_name = NULL;
×
424

425
                r = membershipdb_iterator_get(iterator, &user_name, &group_name);
201✔
426
                if (r == -ESRCH)
201✔
427
                        break;
428
                if (r < 0)
94✔
429
                        return r;
430

431
                /* If both group + user are specified do a-posteriori filtering */
432
                if (p.group_name && p.user_name && !streq(group_name, p.group_name))
94✔
433
                        continue;
×
434

435
                if (last_user_name) {
94✔
436
                        assert(last_group_name);
×
437

438
                        r = sd_varlink_notifybo(
×
439
                                        link,
440
                                        SD_JSON_BUILD_PAIR_STRING("userName", last_user_name),
441
                                        SD_JSON_BUILD_PAIR_STRING("groupName", last_group_name));
442
                        if (r < 0)
×
443
                                return r;
444
                }
445

446
                free_and_replace(last_user_name, user_name);
94✔
447
                free_and_replace(last_group_name, group_name);
94✔
448
        }
449

450
        if (!last_user_name) {
107✔
451
                assert(!last_group_name);
13✔
452
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
13✔
453
        }
454

455
        assert(last_group_name);
94✔
456

457
        return sd_varlink_replybo(
94✔
458
                        link,
459
                        SD_JSON_BUILD_PAIR_STRING("userName", last_user_name),
460
                        SD_JSON_BUILD_PAIR_STRING("groupName", last_group_name));
461
}
462

463
static int process_connection(sd_varlink_server *server, int _fd) {
37,735✔
464
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
37,735✔
465
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
37,735✔
466
        int r;
37,735✔
467

468
        assert(server);
37,735✔
469
        assert(fd >= 0);
37,735✔
470

471
        r = sd_varlink_server_add_connection(server, fd, &vl);
37,735✔
472
        if (r < 0)
37,735✔
473
                return log_error_errno(r, "Failed to add connection: %m");
×
474

475
        TAKE_FD(fd);
37,735✔
476
        vl = sd_varlink_ref(vl);
37,735✔
477

478
        for (;;) {
351,758✔
479
                r = sd_varlink_process(vl);
351,758✔
480
                if (r == -ENOTCONN) {
351,758✔
481
                        log_debug("Connection terminated.");
37,735✔
482
                        break;
483
                }
484
                if (r < 0)
314,023✔
485
                        return log_error_errno(r, "Failed to process connection: %m");
×
486
                if (r > 0)
314,023✔
487
                        continue;
264,137✔
488

489
                r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
49,886✔
490
                if (r < 0)
49,886✔
491
                        return log_error_errno(r, "Failed to wait for connection events: %m");
×
492
                if (r == 0)
49,886✔
493
                        break;
494
        }
495

496
        return 0;
497
}
498

499
static int run(int argc, char *argv[]) {
599✔
500
        usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
599✔
501
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
599✔
502
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
×
503
        unsigned n_iterations = 0;
599✔
504
        int m, listen_fd, r;
599✔
505

506
        log_setup();
599✔
507

508
        m = sd_listen_fds(false);
599✔
509
        if (m < 0)
599✔
510
                return log_error_errno(m, "Failed to determine number of listening fds: %m");
×
511
        if (m == 0)
599✔
512
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
×
513
        if (m > 1)
599✔
514
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
×
515

516
        listen_fd = SD_LISTEN_FDS_START;
599✔
517

518
        r = fd_nonblock(listen_fd, false);
599✔
519
        if (r < 0)
599✔
520
                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
×
521

522
        r = varlink_server_new(&server, 0, NULL);
599✔
523
        if (r < 0)
599✔
524
                return log_error_errno(r, "Failed to allocate varlink server: %m");
×
525

526
        r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
599✔
527
        if (r < 0)
599✔
528
                return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
×
529

530
        r = sd_varlink_server_bind_method_many(
599✔
531
                        server,
532
                        "io.systemd.UserDatabase.GetUserRecord",  vl_method_get_user_record,
533
                        "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
534
                        "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
535
        if (r < 0)
599✔
536
                return log_error_errno(r, "Failed to bind methods: %m");
×
537

538
        r = getenv_bool("USERDB_FIXED_WORKER");
599✔
539
        if (r < 0)
599✔
540
                return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
×
541
        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
599✔
542

543
        r = userdb_block_nss_systemd(true);
599✔
544
        if (r < 0)
599✔
545
                return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
×
546

547
        r = pidref_set_parent(&parent);
599✔
548
        if (r < 0)
599✔
549
                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
×
550
        if (parent.pid == 1) /* We got reparented away from userdbd? */
599✔
551
                return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
×
552

553
        start_time = now(CLOCK_MONOTONIC);
599✔
554

555
        for (;;) {
38,612✔
556
                _cleanup_close_ int fd = -EBADF;
599✔
557
                usec_t n;
38,612✔
558

559
                /* Exit the worker in regular intervals, to flush out all memory use */
560
                if (n_iterations++ > ITERATIONS_MAX) {
38,612✔
561
                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
572✔
562
                        break;
563
                }
564

565
                n = now(CLOCK_MONOTONIC);
38,040✔
566
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
76,080✔
567
                        log_debug("Exiting worker, ran for %s, that's enough.",
22✔
568
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
569
                        break;
11✔
570
                }
571

572
                if (last_busy_usec == USEC_INFINITY)
38,029✔
573
                        last_busy_usec = n;
574
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
338✔
575
                        log_debug("Exiting worker, been idle for %s.",
32✔
576
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
577
                        break;
16✔
578
                }
579

580
                (void) rename_process("systemd-userwork: waiting...");
38,013✔
581
                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
38,013✔
582
                (void) rename_process("systemd-userwork: processing...");
38,013✔
583

584
                if (fd == -EAGAIN)
38,013✔
585
                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
278✔
586
                                   * after a while, let's check if it's time to exit though. */
587
                if (fd == -EINTR)
37,735✔
588
                        continue; /* Might be that somebody attached via strace, let's just continue in that
×
589
                                   * case */
590
                if (fd < 0)
37,735✔
591
                        return log_error_errno(fd, "Failed to accept() from listening socket: %m");
×
592

593
                if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
37,735✔
594
                        /* We only slept a very short time? If so, let's see if there are more sockets
595
                         * pending, and if so, let's ask our parent for more workers */
596

597
                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
29,841✔
598
                        if (r < 0)
29,841✔
599
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
600

601
                        if (FLAGS_SET(r, POLLIN)) {
29,841✔
602
                                r = pidref_kill(&parent, SIGUSR2);
168✔
603
                                if (r == -ESRCH)
168✔
604
                                        return log_error_errno(r, "Parent already died?");
×
605
                                if (r < 0)
168✔
606
                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
×
607
                        }
608
                }
609

610
                (void) process_connection(server, TAKE_FD(fd));
37,735✔
611
                last_busy_usec = USEC_INFINITY;
37,735✔
612
        }
613

614
        return 0;
615
}
616

617
DEFINE_MAIN_FUNCTION(run);
599✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc