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

systemd / systemd / 23927985597

02 Apr 2026 07:45PM UTC coverage: 72.362% (+0.02%) from 72.343%
23927985597

push

github

daandemeyer
ci: Drop base64 encoding in claude review workflow

Doesn't seem to work nearly as good as the previous solution which
just told claude not to escape stuff.

319121 of 441004 relevant lines covered (72.36%)

1167673.48 hits per line

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

87.84
/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) {
27,021✔
43
        assert(p);
27,021✔
44

45
        userdb_match_done(&p->match);
27,021✔
46
}
27,021✔
47

48
static int add_nss_service(sd_json_variant **v) {
7,793✔
49
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
15,584✔
50
        sd_id128_t mid;
7,793✔
51
        int r;
7,793✔
52

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

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

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

68
        if (sd_json_variant_by_key(z, "service"))
7,791✔
69
                return 0;
70

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

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

79
        return sd_json_variant_set_field(v, "status", status);
7,549✔
80
}
81

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

90
        assert(ur);
4,823✔
91
        assert(ret);
4,823✔
92

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

110
        stripped->incomplete =
9,646✔
111
                ur->incomplete ||
4,823✔
112
                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
4,823✔
113
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
4,188✔
114

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

120
        return sd_json_buildo(
4,823✔
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) {
41,416✔
127
        assert(link);
41,416✔
128
        assert(ret);
41,416✔
129

130
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
41,416✔
131
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
58✔
132
        else if (streq_ptr(service, "io.systemd.DropIn"))
41,358✔
133
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
31,508✔
134
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
9,850✔
135
                *ret = USERDB_AVOID_MULTIPLEXER;
9,850✔
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) {
8,749✔
143

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

164
        assert(parameters);
8,749✔
165

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

170
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
8,749✔
171
        if (r != 0) /* return value of < 0 means error (as usual); > 0 means 'already processed and replied,
8,749✔
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");
8,749✔
176
        if (r < 0)
8,749✔
177
                return r;
178

179
        if (uid_is_valid(p.uid))
8,749✔
180
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
4,766✔
181
        else if (p.name)
3,983✔
182
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
3,975✔
183
        else {
184
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
8✔
185

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

198
                for (;;) {
189✔
199
                        _cleanup_(user_record_unrefp) UserRecord *z = NULL;
8✔
200

201
                        r = userdb_iterator_get(iterator, &p.match, &z);
197✔
202
                        if (r == -ESRCH)
197✔
203
                                break;
204
                        if (r < 0)
189✔
205
                                return r;
206

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

212
                        r = sd_varlink_reply(link, v);
189✔
213
                        if (r < 0)
189✔
214
                                return r;
215
                }
216

217
                return 0;
8✔
218
        }
219
        if (r == -ESRCH)
8,741✔
220
                return 0;
221
        if (r == -ENOEXEC)
4,634✔
222
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
223
        if (r < 0) {
4,634✔
224
                log_debug_errno(r, "User lookup failed abnormally: %m");
×
225
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
226
        }
227

228
        if ((uid_is_valid(p.uid) && hr->uid != p.uid) ||
4,634✔
229
            (p.name && !user_record_matches_user_name(hr, p.name)))
4,634✔
230
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
231

232
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
8,749✔
233
        r = build_user_json(link, hr, &v);
4,634✔
234
        if (r < 0)
4,634✔
235
                return r;
236

237
        return sd_varlink_reply(link, v);
4,634✔
238
}
239

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

248
        assert(gr);
2,970✔
249
        assert(ret);
2,970✔
250

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

258
        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,970✔
259
        if (trusted)
2,970✔
260
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
261
        else
262
                flags |= USER_RECORD_STRIP_PRIVILEGED;
263

264
        r = group_record_clone(gr, flags, &stripped);
2,970✔
265
        if (r < 0)
2,970✔
266
                return r;
267

268
        stripped->incomplete =
5,940✔
269
                gr->incomplete ||
2,970✔
270
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
2,970✔
271
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
37✔
272

273
        v = sd_json_variant_ref(stripped->json);
2,970✔
274
        r = add_nss_service(&v);
2,970✔
275
        if (r < 0)
2,970✔
276
                return r;
277

278
        return sd_json_buildo(
2,970✔
279
                        ret,
280
                        SD_JSON_BUILD_PAIR_VARIANT("record", v),
281
                        SD_JSON_BUILD_PAIR_BOOLEAN("incomplete", stripped->incomplete));
282
}
283

284
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
18,272✔
285

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

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

306
        assert(parameters);
18,272✔
307

308
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
18,272✔
309
        if (r != 0)
18,272✔
310
                return r;
311

312
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
18,272✔
313
        if (r != 0)
18,272✔
314
                return r;
315

316
        r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
18,272✔
317
        if (r < 0)
18,272✔
318
                return r;
319

320
        if (gid_is_valid(p.gid))
18,272✔
321
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
4,716✔
322
        else if (p.name)
13,556✔
323
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
13,552✔
324
        else {
325
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
4✔
326

327
                r = groupdb_all(&p.match, userdb_flags, &iterator);
4✔
328
                if (IN_SET(r, -ESRCH, -ENOLINK))
4✔
329
                        return 0;
330
                if (r < 0)
4✔
331
                        return r;
332

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

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

342
                        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
121✔
343
                        r = build_group_json(link, z, &v);
121✔
344
                        if (r < 0)
121✔
345
                                return r;
346

347
                        r = sd_varlink_reply(link, v);
121✔
348
                        if (r < 0)
121✔
349
                                return r;
350
                }
351

352
                return 0;
4✔
353
        }
354
        if (r == -ESRCH)
18,268✔
355
                return 0;
356
        if (r == -ENOEXEC)
2,849✔
357
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
358
        if (r < 0) {
2,849✔
359
                log_debug_errno(r, "Group lookup failed abnormally: %m");
×
360
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
361
        }
362

363
        if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
2,849✔
364
            (p.name && !group_record_matches_group_name(g, p.name)))
2,849✔
365
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
366

367
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
18,272✔
368
        r = build_group_json(link, g, &v);
2,849✔
369
        if (r < 0)
2,849✔
370
                return r;
371

372
        return sd_varlink_reply(link, v);
2,849✔
373
}
374

375
typedef struct MembershipLookupParameters {
376
        const char *user_name;
377
        const char *group_name;
378
        const char *service;
379
} MembershipLookupParameters;
380

381
static int vl_method_get_memberships(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
14,395✔
382
        static const sd_json_dispatch_field dispatch_table[] = {
14,395✔
383
                { "userName",  SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, user_name),  SD_JSON_RELAX },
384
                { "groupName", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MembershipLookupParameters, group_name), SD_JSON_RELAX },
385
                { "service",   SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string,       offsetof(MembershipLookupParameters, service),    0             },
386
                {}
387
        };
388

389
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
14,395✔
390
        MembershipLookupParameters p = {};
14,395✔
391
        UserDBFlags userdb_flags;
14,395✔
392
        int r;
14,395✔
393

394
        assert(parameters);
14,395✔
395

396
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
14,395✔
397
        if (r != 0)
14,395✔
398
                return r;
399

400
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
14,395✔
401
        if (r != 0)
14,395✔
402
                return r;
403

404
        r = sd_varlink_set_sentinel(link, "io.systemd.UserDatabase.NoRecordFound");
14,395✔
405
        if (r < 0)
14,395✔
406
                return r;
407

408
        if (p.group_name)
14,395✔
409
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
12,369✔
410
        else if (p.user_name)
2,026✔
411
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
2,020✔
412
        else
413
                r = membershipdb_all(userdb_flags, &iterator);
6✔
414
        if (IN_SET(r, -ESRCH, -ENOLINK))
14,395✔
415
                return 0;
416
        if (r < 0)
131✔
417
                return r;
418

419
        for (;;) {
303✔
420
                _cleanup_free_ char *user_name = NULL, *group_name = NULL;
174✔
421

422
                r = membershipdb_iterator_get(iterator, &user_name, &group_name);
303✔
423
                if (r == -ESRCH)
303✔
424
                        break;
425
                if (r < 0)
174✔
426
                        return r;
427

428
                /* If both group + user are specified do a-posteriori filtering */
429
                if (p.group_name && p.user_name && !streq(group_name, p.group_name))
174✔
430
                        continue;
×
431

432
                r = sd_varlink_replybo(
174✔
433
                                link,
434
                                SD_JSON_BUILD_PAIR_STRING("userName", user_name),
435
                                SD_JSON_BUILD_PAIR_STRING("groupName", group_name));
436
                if (r < 0)
174✔
437
                        return r;
438
        }
439

440
        return 0;
129✔
441
}
442

443
static int process_connection(sd_varlink_server *server, int _fd) {
41,417✔
444
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
41,417✔
445
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
41,417✔
446
        int r;
41,417✔
447

448
        assert(server);
41,417✔
449
        assert(fd >= 0);
41,417✔
450

451
        r = sd_varlink_server_add_connection(server, fd, &vl);
41,417✔
452
        if (r < 0)
41,417✔
453
                return log_error_errno(r, "Failed to add connection: %m");
×
454

455
        TAKE_FD(fd);
41,417✔
456
        vl = sd_varlink_ref(vl);
41,417✔
457

458
        for (;;) {
385,790✔
459
                r = sd_varlink_process(vl);
385,790✔
460
                if (r == -ENOTCONN) {
385,790✔
461
                        log_debug("Connection terminated.");
41,417✔
462
                        break;
463
                }
464
                if (r < 0)
344,373✔
465
                        return log_error_errno(r, "Failed to process connection: %m");
×
466
                if (r > 0)
344,373✔
467
                        continue;
289,913✔
468

469
                r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
54,460✔
470
                if (r < 0)
54,460✔
471
                        return log_error_errno(r, "Failed to wait for connection events: %m");
×
472
                if (r == 0)
54,460✔
473
                        break;
474
        }
475

476
        return 0;
477
}
478

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

486
        log_setup();
655✔
487

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

496
        listen_fd = SD_LISTEN_FDS_START;
655✔
497

498
        r = fd_nonblock(listen_fd, false);
655✔
499
        if (r < 0)
655✔
500
                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
×
501

502
        r = varlink_server_new(&server, 0, NULL);
655✔
503
        if (r < 0)
655✔
504
                return log_error_errno(r, "Failed to allocate varlink server: %m");
×
505

506
        r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
655✔
507
        if (r < 0)
655✔
508
                return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
×
509

510
        r = sd_varlink_server_bind_method_many(
655✔
511
                        server,
512
                        "io.systemd.UserDatabase.GetUserRecord",  vl_method_get_user_record,
513
                        "io.systemd.UserDatabase.GetGroupRecord", vl_method_get_group_record,
514
                        "io.systemd.UserDatabase.GetMemberships", vl_method_get_memberships);
515
        if (r < 0)
655✔
516
                return log_error_errno(r, "Failed to bind methods: %m");
×
517

518
        r = getenv_bool("USERDB_FIXED_WORKER");
655✔
519
        if (r < 0)
655✔
520
                return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
×
521
        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
655✔
522

523
        r = userdb_block_nss_systemd(true);
655✔
524
        if (r < 0)
655✔
525
                return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
×
526

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

533
        start_time = now(CLOCK_MONOTONIC);
655✔
534

535
        for (;;) {
42,333✔
536
                _cleanup_close_ int fd = -EBADF;
655✔
537
                usec_t n;
42,333✔
538

539
                /* Exit the worker in regular intervals, to flush out all memory use */
540
                if (n_iterations++ > ITERATIONS_MAX) {
42,333✔
541
                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
629✔
542
                        break;
543
                }
544

545
                n = now(CLOCK_MONOTONIC);
41,704✔
546
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
83,408✔
547
                        log_debug("Exiting worker, ran for %s, that's enough.",
22✔
548
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
549
                        break;
11✔
550
                }
551

552
                if (last_busy_usec == USEC_INFINITY)
41,693✔
553
                        last_busy_usec = n;
554
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
316✔
555
                        log_debug("Exiting worker, been idle for %s.",
30✔
556
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
557
                        break;
15✔
558
                }
559

560
                (void) rename_process("systemd-userwork: waiting...");
41,678✔
561
                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
41,678✔
562
                (void) rename_process("systemd-userwork: processing...");
41,678✔
563

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

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

577
                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
32,783✔
578
                        if (r < 0)
32,783✔
579
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
580

581
                        if (FLAGS_SET(r, POLLIN)) {
32,783✔
582
                                r = pidref_kill(&parent, SIGUSR2);
128✔
583
                                if (r == -ESRCH)
128✔
584
                                        return log_error_errno(r, "Parent already died?");
×
585
                                if (r < 0)
128✔
586
                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
×
587
                        }
588
                }
589

590
                (void) process_connection(server, TAKE_FD(fd));
41,417✔
591
                last_busy_usec = USEC_INFINITY;
41,417✔
592
        }
593

594
        return 0;
595
}
596

597
DEFINE_MAIN_FUNCTION(run);
655✔
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