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

systemd / systemd / 28485131107

30 Jun 2026 03:24PM UTC coverage: 72.877% (+0.07%) from 72.809%
28485131107

push

github

bluca
test: reduce number of disks in TEST-64-UDEV-STORAGE-simultaneous_events on Debian/Ubuntu

This test never finishes in Ubuntu autopkgtest with the current values,
and is currently skipped all together on Debian. When running on either,
reduce the number of disks to make the test more reliable.

341909 of 469157 relevant lines covered (72.88%)

1343417.0 hits per line

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

87.92
/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) {
29,523✔
43
        assert(p);
29,523✔
44

45
        userdb_match_done(&p->match);
29,523✔
46
}
29,523✔
47

48
static int add_nss_service(sd_json_variant **v) {
8,579✔
49
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
17,156✔
50
        sd_id128_t mid;
8,579✔
51
        int r;
8,579✔
52

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

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

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

68
        if (sd_json_variant_by_key(z, "service"))
8,577✔
69
                return 0;
70

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

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

79
        return sd_json_variant_set_field(v, "status", status);
8,371✔
80
}
81

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

90
        assert(ur);
5,558✔
91
        assert(ret);
5,558✔
92

93
        r = sd_varlink_get_peer_uid(link, &peer_uid);
5,558✔
94
        if (r < 0) {
5,558✔
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;
5,558✔
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;
5,710✔
101
        if (trusted)
152✔
102
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
103
        else
104
                flags |= USER_RECORD_STRIP_PRIVILEGED;
105

106
        r = user_record_clone(ur, flags, &stripped);
5,558✔
107
        if (r < 0)
5,558✔
108
                return r;
109

110
        stripped->incomplete =
11,116✔
111
                ur->incomplete ||
5,558✔
112
                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
5,558✔
113
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
4,964✔
114

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

120
        return sd_json_buildo(
5,558✔
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) {
45,566✔
127
        assert(link);
45,566✔
128
        assert(ret);
45,566✔
129

130
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
45,566✔
131
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
57✔
132
        else if (streq_ptr(service, "io.systemd.DropIn"))
45,509✔
133
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
35,471✔
134
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
10,038✔
135
                *ret = USERDB_AVOID_MULTIPLEXER;
10,038✔
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) {
9,583✔
143

144
        static const sd_json_dispatch_field dispatch_table[] = {
9,583✔
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_(user_record_unrefp) UserRecord *hr = NULL;
9,583✔
157
        _cleanup_(lookup_parameters_done) LookupParameters p = {
×
158
                .uid = UID_INVALID,
159
                .match = USERDB_MATCH_NULL,
160
        };
161
        UserDBFlags userdb_flags;
9,583✔
162
        int r;
9,583✔
163

164
        assert(parameters);
9,583✔
165

166
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
9,583✔
167
        if (r != 0)
9,583✔
168
                return r;
169

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

175
        r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
9,583✔
176
        if (r < 0)
9,583✔
177
                return r;
178

179
        /* Prefer lookup by name if both name and UID are specified, so NSS-backed records preserve
180
         * the requested lookup name as an alias before we check the UID below. */
181
        if (p.name)
9,583✔
182
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
4,553✔
183
        else if (uid_is_valid(p.uid))
5,030✔
184
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
5,025✔
185
        else {
186
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
5✔
187

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

200
                for (;;) {
108✔
201
                        _cleanup_(user_record_unrefp) UserRecord *z = NULL;
5✔
202

203
                        r = userdb_iterator_get(iterator, &p.match, &z);
113✔
204
                        if (r == -ESRCH)
113✔
205
                                break;
206
                        if (r < 0)
108✔
207
                                return r;
208

209
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
108✔
210
                        r = build_user_json(link, z, &v);
108✔
211
                        if (r < 0)
108✔
212
                                return r;
213

214
                        r = sd_varlink_reply(link, v);
108✔
215
                        if (r < 0)
108✔
216
                                return r;
217
                }
218

219
                return 0;
5✔
220
        }
221
        if (r == -ESRCH)
9,578✔
222
                return 0;
223
        if (r == -ENOEXEC)
5,450✔
224
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
225
        if (r < 0) {
5,450✔
226
                log_debug_errno(r, "User lookup failed abnormally: %m");
×
227
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
228
        }
229

230
        if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
5,450✔
231
            (p.name && !user_record_matches_user_name(hr, p.name)))
5,450✔
232
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
233

234
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
9,583✔
235
        r = build_user_json(link, hr, &v);
5,450✔
236
        if (r < 0)
5,450✔
237
                return r;
238

239
        return sd_varlink_reply(link, v);
5,450✔
240
}
241

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

250
        assert(gr);
3,021✔
251
        assert(ret);
3,021✔
252

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

260
        flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE|USER_RECORD_PERMISSIVE;
6,042✔
261
        if (trusted)
3,021✔
262
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
263
        else
264
                flags |= USER_RECORD_STRIP_PRIVILEGED;
265

266
        r = group_record_clone(gr, flags, &stripped);
3,021✔
267
        if (r < 0)
3,021✔
268
                return r;
269

270
        stripped->incomplete =
6,042✔
271
                gr->incomplete ||
3,021✔
272
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
3,021✔
273
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
34✔
274

275
        v = sd_json_variant_ref(stripped->json);
3,021✔
276
        r = add_nss_service(&v);
3,021✔
277
        if (r < 0)
3,021✔
278
                return r;
279

280
        return sd_json_buildo(
3,021✔
281
                        ret,
282
                        SD_JSON_BUILD_PAIR_VARIANT("record", v),
283
                        SD_JSON_BUILD_PAIR_BOOLEAN("incomplete", stripped->incomplete));
284
}
285

286
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
19,940✔
287

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

300
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
19,940✔
301
        _cleanup_(lookup_parameters_done) LookupParameters p = {
×
302
                .gid = GID_INVALID,
303
                .match = USERDB_MATCH_NULL,
304
        };
305
        UserDBFlags userdb_flags;
19,940✔
306
        int r;
19,940✔
307

308
        assert(parameters);
19,940✔
309

310
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
19,940✔
311
        if (r != 0)
19,940✔
312
                return r;
313

314
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
19,940✔
315
        if (r != 0)
19,940✔
316
                return r;
317

318
        r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
19,940✔
319
        if (r < 0)
19,940✔
320
                return r;
321

322
        /* Prefer lookup by name if both name and GID are specified, so NSS-backed records preserve
323
         * the requested lookup name as an alias before we check the GID below. */
324
        if (p.name)
19,940✔
325
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
14,944✔
326
        else if (gid_is_valid(p.gid))
4,996✔
327
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
4,995✔
328
        else {
329
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
1✔
330

331
                r = groupdb_all(&p.match, userdb_flags, &iterator);
1✔
332
                if (IN_SET(r, -ESRCH, -ENOLINK))
1✔
333
                        return 0;
334
                if (r < 0)
1✔
335
                        return r;
336

337
                for (;;) {
59✔
338
                        _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
1✔
339

340
                        r = groupdb_iterator_get(iterator, &p.match, &z);
60✔
341
                        if (r == -ESRCH)
60✔
342
                                break;
343
                        if (r < 0)
59✔
344
                                return r;
345

346
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
59✔
347
                        r = build_group_json(link, z, &v);
59✔
348
                        if (r < 0)
59✔
349
                                return r;
350

351
                        r = sd_varlink_reply(link, v);
59✔
352
                        if (r < 0)
59✔
353
                                return r;
354
                }
355

356
                return 0;
1✔
357
        }
358
        if (r == -ESRCH)
19,939✔
359
                return 0;
360
        if (r == -ENOEXEC)
2,962✔
361
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
362
        if (r < 0) {
2,962✔
363
                log_debug_errno(r, "Group lookup failed abnormally: %m");
×
364
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
365
        }
366

367
        if ((gid_is_valid(p.gid) && g->gid != p.gid) ||
2,962✔
368
            (p.name && !group_record_matches_group_name(g, p.name)))
2,962✔
369
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
370

371
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
19,940✔
372
        r = build_group_json(link, g, &v);
2,962✔
373
        if (r < 0)
2,962✔
374
                return r;
375

376
        return sd_varlink_reply(link, v);
2,962✔
377
}
378

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

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

393
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
16,043✔
394
        MembershipLookupParameters p = {};
16,043✔
395
        UserDBFlags userdb_flags;
16,043✔
396
        int r;
16,043✔
397

398
        assert(parameters);
16,043✔
399

400
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
16,043✔
401
        if (r != 0)
16,043✔
402
                return r;
403

404
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
16,043✔
405
        if (r != 0)
16,043✔
406
                return r;
407

408
        r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
16,043✔
409
        if (r < 0)
16,043✔
410
                return r;
411

412
        if (p.group_name)
16,043✔
413
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
13,837✔
414
        else if (p.user_name)
2,206✔
415
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
2,201✔
416
        else
417
                r = membershipdb_all(userdb_flags, &iterator);
5✔
418
        if (IN_SET(r, -ESRCH, -ENOLINK))
16,043✔
419
                return 0;
420
        if (r < 0)
118✔
421
                return r;
422

423
        for (;;) {
280✔
424
                _cleanup_free_ char *user_name = NULL, *group_name = NULL;
164✔
425

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

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

436
                r = sd_varlink_replybo(
164✔
437
                                link,
438
                                SD_JSON_BUILD_PAIR_STRING("userName", user_name),
439
                                SD_JSON_BUILD_PAIR_STRING("groupName", group_name));
440
                if (r < 0)
164✔
441
                        return r;
442
        }
443

444
        return 0;
116✔
445
}
446

447
static int process_connection(sd_varlink_server *server, int _fd) {
45,575✔
448
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
45,575✔
449
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
45,575✔
450
        int r;
45,575✔
451

452
        assert(server);
45,575✔
453
        assert(fd >= 0);
45,575✔
454

455
        r = sd_varlink_server_add_connection(server, fd, &vl);
45,575✔
456
        if (r < 0)
45,575✔
457
                return log_error_errno(r, "Failed to add connection: %m");
×
458

459
        TAKE_FD(fd);
45,575✔
460
        vl = sd_varlink_ref(vl);
45,575✔
461

462
        for (;;) {
420,885✔
463
                r = sd_varlink_process(vl);
420,885✔
464
                if (r == -ENOTCONN) {
420,885✔
465
                        log_debug("Connection terminated.");
45,575✔
466
                        break;
467
                }
468
                if (r < 0)
375,310✔
469
                        return log_error_errno(r, "Failed to process connection: %m");
×
470
                if (r > 0)
375,310✔
471
                        continue;
319,055✔
472

473
                r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
56,255✔
474
                if (r < 0)
56,255✔
475
                        return log_error_errno(r, "Failed to wait for connection events: %m");
×
476
                if (r == 0)
56,255✔
477
                        break;
478
        }
479

480
        return 0;
481
}
482

483
static int run(int argc, char *argv[]) {
718✔
484
        usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
718✔
485
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
718✔
486
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
×
487
        unsigned n_iterations = 0;
718✔
488
        int m, listen_fd, r;
718✔
489

490
        log_setup();
718✔
491

492
        m = sd_listen_fds(false);
718✔
493
        if (m < 0)
718✔
494
                return log_error_errno(m, "Failed to determine number of listening fds: %m");
×
495
        if (m == 0)
718✔
496
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
×
497
        if (m > 1)
718✔
498
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
×
499

500
        listen_fd = SD_LISTEN_FDS_START;
718✔
501

502
        r = fd_nonblock(listen_fd, false);
718✔
503
        if (r < 0)
718✔
504
                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
×
505

506
        r = varlink_server_new(&server, 0, NULL);
718✔
507
        if (r < 0)
718✔
508
                return log_error_errno(r, "Failed to allocate varlink server: %m");
×
509

510
        r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
718✔
511
        if (r < 0)
718✔
512
                return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
×
513

514
        r = sd_varlink_server_bind_method_many(
718✔
515
                        server,
516
                        "io.systemd.UserDatabase.GetUserRecord",  vl_method_get_user_record,
517
                        "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
518
                        "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
519
        if (r < 0)
718✔
520
                return log_error_errno(r, "Failed to bind methods: %m");
×
521

522
        r = getenv_bool("USERDB_FIXED_WORKER");
718✔
523
        if (r < 0)
718✔
524
                return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
×
525
        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
718✔
526

527
        r = userdb_block_nss_systemd(true);
718✔
528
        if (r < 0)
718✔
529
                return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
×
530

531
        r = pidref_set_parent(&parent);
718✔
532
        if (r < 0)
718✔
533
                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
×
534
        if (parent.pid == 1) /* We got reparented away from userdbd? */
718✔
535
                return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
×
536

537
        start_time = now(CLOCK_MONOTONIC);
718✔
538

539
        for (;;) {
46,613✔
540
                _cleanup_close_ int fd = -EBADF;
718✔
541
                usec_t n;
46,613✔
542

543
                /* Exit the worker in regular intervals, to flush out all memory use */
544
                if (n_iterations++ > ITERATIONS_MAX) {
46,613✔
545
                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
692✔
546
                        break;
547
                }
548

549
                n = now(CLOCK_MONOTONIC);
45,921✔
550
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
91,842✔
551
                        log_debug("Exiting worker, ran for %s, that's enough.",
30✔
552
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
553
                        break;
15✔
554
                }
555

556
                if (last_busy_usec == USEC_INFINITY)
45,906✔
557
                        last_busy_usec = n;
558
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
369✔
559
                        log_debug("Exiting worker, been idle for %s.",
22✔
560
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
561
                        break;
11✔
562
                }
563

564
                (void) rename_process("systemd-userwork: waiting...");
45,895✔
565
                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
45,895✔
566
                (void) rename_process("systemd-userwork: processing...");
45,895✔
567

568
                if (fd == -EAGAIN)
45,895✔
569
                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
320✔
570
                                   * after a while, let's check if it's time to exit though. */
571
                if (fd == -EINTR)
45,575✔
572
                        continue; /* Might be that somebody attached via strace, let's just continue in that
×
573
                                   * case */
574
                if (fd < 0)
45,575✔
575
                        return log_error_errno(fd, "Failed to accept() from listening socket: %m");
×
576

577
                if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
45,575✔
578
                        /* We only slept a very short time? If so, let's see if there are more sockets
579
                         * pending, and if so, let's ask our parent for more workers */
580

581
                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
36,864✔
582
                        if (r < 0)
36,864✔
583
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
584

585
                        if (FLAGS_SET(r, POLLIN)) {
36,864✔
586
                                r = pidref_kill(&parent, SIGUSR2);
160✔
587
                                if (r == -ESRCH)
160✔
588
                                        return log_error_errno(r, "Parent already died?");
×
589
                                if (r < 0)
160✔
590
                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
×
591
                        }
592
                }
593

594
                (void) process_connection(server, TAKE_FD(fd));
45,575✔
595
                last_busy_usec = USEC_INFINITY;
45,575✔
596
        }
597

598
        return 0;
599
}
600

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

© 2026 Coveralls, Inc