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

systemd / systemd / 14526975710

17 Apr 2025 09:06PM UTC coverage: 72.13% (+0.01%) from 72.117%
14526975710

push

github

yuwata
rules: Make ADB and fastboot work out-of-the-box

https://android.googlesource.com/platform/packages/modules/adb/+/d0db47dcd/adb.h#199
https://android.googlesource.com/platform/system/core/+/7199051aa/fastboot/fastboot.cpp#244

297093 of 411885 relevant lines covered (72.13%)

687643.53 hits per line

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

87.5
/src/userdb/userwork.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <poll.h>
4
#include <sys/wait.h>
5

6
#include "sd-daemon.h"
7
#include "sd-varlink.h"
8

9
#include "env-util.h"
10
#include "fd-util.h"
11
#include "group-record.h"
12
#include "io-util.h"
13
#include "json-util.h"
14
#include "main-func.h"
15
#include "process-util.h"
16
#include "strv.h"
17
#include "time-util.h"
18
#include "user-record.h"
19
#include "user-record-nss.h"
20
#include "user-util.h"
21
#include "userdb.h"
22
#include "varlink-io.systemd.UserDatabase.h"
23
#include "varlink-util.h"
24

25
#define ITERATIONS_MAX 64U
26
#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
27
#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
28
#define CONNECTION_IDLE_USEC (15 * USEC_PER_SEC)
29
#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
30

31
typedef struct LookupParameters {
32
        const char *name;
33
        union {
34
                uid_t uid;
35
                gid_t gid;
36
        };
37
        const char *service;
38
        UserDBMatch match;
39
} LookupParameters;
40

41
static void lookup_parameters_done(LookupParameters *p) {
15,344✔
42
        assert(p);
15,344✔
43

44
        userdb_match_done(&p->match);
15,344✔
45
}
15,344✔
46

47
static int add_nss_service(sd_json_variant **v) {
2,362✔
48
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
4,724✔
49
        sd_id128_t mid;
2,362✔
50
        int r;
2,362✔
51

52
        assert(v);
2,362✔
53

54
        /* Patch in service field if it's missing. The assumption here is that this field is unset only for
55
         * NSS records */
56

57
        if (sd_json_variant_by_key(*v, "service"))
2,362✔
58
                return 0;
59

60
        r = sd_id128_get_machine(&mid);
2,362✔
61
        if (r < 0)
2,362✔
62
                return r;
63

64
        status = sd_json_variant_ref(sd_json_variant_by_key(*v, "status"));
2,362✔
65
        z = sd_json_variant_ref(sd_json_variant_by_key(status, SD_ID128_TO_STRING(mid)));
2,362✔
66

67
        if (sd_json_variant_by_key(z, "service"))
2,362✔
68
                return 0;
69

70
        r = sd_json_variant_set_field_string(&z, "service", "io.systemd.NameServiceSwitch");
2,237✔
71
        if (r < 0)
2,237✔
72
                return r;
73

74
        r = sd_json_variant_set_field(&status, SD_ID128_TO_STRING(mid), z);
2,237✔
75
        if (r < 0)
2,237✔
76
                return r;
77

78
        return sd_json_variant_set_field(v, "status", status);
2,237✔
79
}
80

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

89
        assert(ur);
1,965✔
90
        assert(ret);
1,965✔
91

92
        r = sd_varlink_get_peer_uid(link, &peer_uid);
1,965✔
93
        if (r < 0) {
1,965✔
94
                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
×
95
                trusted = false;
96
        } else
97
                trusted = peer_uid == 0 || peer_uid == ur->uid;
1,965✔
98

99
        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;
100
        if (trusted)
101
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
102
        else
103
                flags |= USER_RECORD_STRIP_PRIVILEGED;
104

105
        r = user_record_clone(ur, flags, &stripped);
1,965✔
106
        if (r < 0)
1,965✔
107
                return r;
108

109
        stripped->incomplete =
3,930✔
110
                ur->incomplete ||
1,965✔
111
                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
1,965✔
112
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
1,450✔
113

114
        v = sd_json_variant_ref(stripped->json);
1,965✔
115
        r = add_nss_service(&v);
1,965✔
116
        if (r < 0)
1,965✔
117
                return r;
118

119
        return sd_json_buildo(
1,965✔
120
                        ret,
121
                        SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_VARIANT(v)),
122
                        SD_JSON_BUILD_PAIR("incomplete", SD_JSON_BUILD_BOOLEAN(stripped->incomplete)));
123
}
124

125
static int userdb_flags_from_service(sd_varlink *link, const char *service, UserDBFlags *ret) {
29,201✔
126
        assert(link);
29,201✔
127
        assert(ret);
29,201✔
128

129
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
29,201✔
130
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
13✔
131
        else if (streq_ptr(service, "io.systemd.DropIn"))
29,188✔
132
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
28,044✔
133
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
1,144✔
134
                *ret = USERDB_AVOID_MULTIPLEXER;
1,144✔
135
        else
136
                return sd_varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
×
137

138
        return 0;
139
}
140

141
static int vl_method_get_user_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
2,254✔
142

143
        static const sd_json_dispatch_field dispatch_table[] = {
2,254✔
144
                { "uid",             _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, uid),                    0             },
145
                { "userName",        SD_JSON_VARIANT_STRING,        json_dispatch_const_user_group_name, offsetof(LookupParameters, name),                   SD_JSON_RELAX },
146
                { "service",         SD_JSON_VARIANT_STRING,        sd_json_dispatch_const_string,       offsetof(LookupParameters, service),                0             },
147
                { "fuzzyNames",      SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_strv,               offsetof(LookupParameters, match.fuzzy_names),      0             },
148
                { "dispositionMask", SD_JSON_VARIANT_ARRAY,         json_dispatch_dispositions_mask,     offsetof(LookupParameters, match.disposition_mask), 0             },
149
                { "uidMin",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.uid_min),          0             },
150
                { "uidMax",          _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uid_gid,            offsetof(LookupParameters, match.uid_max),          0             },
151
                {}
152
        };
153

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

163
        assert(parameters);
2,254✔
164

165
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
2,254✔
166
        if (r != 0)
2,254✔
167
                return r;
168

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

174
        if (uid_is_valid(p.uid))
2,253✔
175
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
685✔
176
        else if (p.name)
1,568✔
177
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
1,550✔
178
        else {
179
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
180
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
18✔
181

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

194
                for (;;) {
313✔
195
                        _cleanup_(user_record_unrefp) UserRecord *z = NULL;
331✔
196

197
                        r = userdb_iterator_get(iterator, &p.match, &z);
331✔
198
                        if (r == -ESRCH)
331✔
199
                                break;
200
                        if (r < 0)
313✔
201
                                return r;
202

203
                        if (last) {
313✔
204
                                r = sd_varlink_notify(link, last);
297✔
205
                                if (r < 0)
297✔
206
                                        return r;
207

208
                                last = sd_json_variant_unref(last);
297✔
209
                        }
210

211
                        r = build_user_json(link, z, &last);
313✔
212
                        if (r < 0)
313✔
213
                                return r;
214
                }
215

216
                if (!last)
18✔
217
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
2✔
218

219
                return sd_varlink_reply(link, last);
16✔
220
        }
221
        if (r == -ESRCH)
2,235✔
222
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
583✔
223
        if (r == -ENOEXEC)
1,652✔
224
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
225
        if (r < 0) {
1,652✔
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) ||
1,652✔
231
            (p.name && !user_record_matches_user_name(hr, p.name)))
1,652✔
232
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
233

234
        r = build_user_json(link, hr, &v);
1,652✔
235
        if (r < 0)
1,652✔
236
                return r;
237

238
        return sd_varlink_reply(link, v);
1,652✔
239
}
240

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

249
        assert(gr);
397✔
250
        assert(ret);
397✔
251

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

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

265
        r = group_record_clone(gr, flags, &stripped);
397✔
266
        if (r < 0)
397✔
267
                return r;
268

269
        stripped->incomplete =
794✔
270
                gr->incomplete ||
397✔
271
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
397✔
272
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
18✔
273

274
        v = sd_json_variant_ref(gr->json);
397✔
275
        r = add_nss_service(&v);
397✔
276
        if (r < 0)
397✔
277
                return r;
278

279
        return sd_json_buildo(
397✔
280
                        ret,
281
                        SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_VARIANT(v)),
282
                        SD_JSON_BUILD_PAIR("incomplete", SD_JSON_BUILD_BOOLEAN(stripped->incomplete)));
283
}
284

285
static int vl_method_get_group_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
13,090✔
286

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

298
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
13,090✔
299
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
13,090✔
300
        _cleanup_(lookup_parameters_done) LookupParameters p = {
13,090✔
301
                .gid = GID_INVALID,
302
                .match = USERDB_MATCH_NULL,
303
        };
304
        UserDBFlags userdb_flags;
13,090✔
305
        int r;
13,090✔
306

307
        assert(parameters);
13,090✔
308

309
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
13,090✔
310
        if (r != 0)
13,090✔
311
                return r;
312

313
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
13,090✔
314
        if (r != 0)
13,090✔
315
                return r;
316

317
        if (gid_is_valid(p.gid))
13,090✔
318
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
1,146✔
319
        else if (p.name)
11,944✔
320
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
11,935✔
321
        else {
322
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
323
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
9✔
324

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

331
                for (;;) {
232✔
332
                        _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
241✔
333

334
                        r = groupdb_iterator_get(iterator, &p.match, &z);
241✔
335
                        if (r == -ESRCH)
241✔
336
                                break;
337
                        if (r < 0)
232✔
338
                                return r;
339

340
                        if (last) {
232✔
341
                                r = sd_varlink_notify(link, last);
225✔
342
                                if (r < 0)
225✔
343
                                        return r;
344

345
                                last = sd_json_variant_unref(last);
225✔
346
                        }
347

348
                        r = build_group_json(link, z, &last);
232✔
349
                        if (r < 0)
232✔
350
                                return r;
351
                }
352

353
                if (!last)
9✔
354
                        return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
2✔
355

356
                return sd_varlink_reply(link, last);
7✔
357
        }
358
        if (r == -ESRCH)
13,081✔
359
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
12,916✔
360
        if (r == -ENOEXEC)
165✔
361
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
×
362
        if (r < 0) {
165✔
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 ((uid_is_valid(p.gid) && g->gid != p.gid) ||
165✔
368
            (p.name && !group_record_matches_group_name(g, p.name)))
165✔
369
                return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
×
370

371
        r = build_group_json(link, g, &v);
165✔
372
        if (r < 0)
165✔
373
                return r;
374

375
        return sd_varlink_reply(link, v);
165✔
376
}
377

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

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

392
        _cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
13,858✔
393
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
13,858✔
394
        MembershipLookupParameters p = {};
13,858✔
395
        UserDBFlags userdb_flags;
13,858✔
396
        int r;
13,858✔
397

398
        assert(parameters);
13,858✔
399

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

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

408
        if (p.group_name)
13,858✔
409
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
12,622✔
410
        else if (p.user_name)
1,236✔
411
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
1,234✔
412
        else
413
                r = membershipdb_all(userdb_flags, &iterator);
2✔
414
        if (IN_SET(r, -ESRCH, -ENOLINK))
13,858✔
415
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
13,722✔
416
        if (r < 0)
136✔
417
                return r;
418

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

422
                r = membershipdb_iterator_get(iterator, &user_name, &group_name);
165✔
423
                if (r == -ESRCH)
165✔
424
                        break;
425
                if (r < 0)
29✔
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))
29✔
430
                        continue;
×
431

432
                if (last_user_name) {
29✔
433
                        assert(last_group_name);
10✔
434

435
                        r = sd_varlink_notifybo(
10✔
436
                                        link,
437
                                        SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)),
438
                                        SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name)));
439
                        if (r < 0)
10✔
440
                                return r;
441
                }
442

443
                free_and_replace(last_user_name, user_name);
29✔
444
                free_and_replace(last_group_name, group_name);
29✔
445
        }
446

447
        if (!last_user_name) {
136✔
448
                assert(!last_group_name);
117✔
449
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
117✔
450
        }
451

452
        assert(last_group_name);
19✔
453

454
        return sd_varlink_replybo(
19✔
455
                        link,
456
                        SD_JSON_BUILD_PAIR("userName", SD_JSON_BUILD_STRING(last_user_name)),
457
                        SD_JSON_BUILD_PAIR("groupName", SD_JSON_BUILD_STRING(last_group_name)));
458
}
459

460
static int process_connection(sd_varlink_server *server, int _fd) {
29,248✔
461
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
29,248✔
462
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
29,248✔
463
        int r;
29,248✔
464

465
        assert(server);
29,248✔
466
        assert(fd >= 0);
29,248✔
467

468
        r = sd_varlink_server_add_connection(server, fd, &vl);
29,248✔
469
        if (r < 0)
29,248✔
470
                return log_error_errno(r, "Failed to add connection: %m");
×
471

472
        TAKE_FD(fd);
29,248✔
473
        vl = sd_varlink_ref(vl);
29,248✔
474

475
        for (;;) {
272,925✔
476
                r = sd_varlink_process(vl);
272,925✔
477
                if (r == -ENOTCONN) {
272,925✔
478
                        log_debug("Connection terminated.");
29,248✔
479
                        break;
480
                }
481
                if (r < 0)
243,677✔
482
                        return log_error_errno(r, "Failed to process connection: %m");
×
483
                if (r > 0)
243,677✔
484
                        continue;
204,552✔
485

486
                r = sd_varlink_wait(vl, CONNECTION_IDLE_USEC);
39,125✔
487
                if (r < 0)
39,125✔
488
                        return log_error_errno(r, "Failed to wait for connection events: %m");
×
489
                if (r == 0)
39,125✔
490
                        break;
491
        }
492

493
        return 0;
494
}
495

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

503
        log_setup();
483✔
504

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

513
        listen_fd = SD_LISTEN_FDS_START;
483✔
514

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

519
        r = varlink_server_new(&server, 0, NULL);
483✔
520
        if (r < 0)
483✔
521
                return log_error_errno(r, "Failed to allocate varlink server: %m");
×
522

523
        r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_UserDatabase);
483✔
524
        if (r < 0)
483✔
525
                return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
×
526

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

535
        r = getenv_bool("USERDB_FIXED_WORKER");
483✔
536
        if (r < 0)
483✔
537
                return log_error_errno(r, "Failed to parse USERDB_FIXED_WORKER: %m");
×
538
        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
483✔
539

540
        r = userdb_block_nss_systemd(true);
483✔
541
        if (r < 0)
483✔
542
                return log_error_errno(r, "Failed to disable userdb NSS compatibility: %m");
×
543

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

550
        start_time = now(CLOCK_MONOTONIC);
483✔
551

552
        for (;;) {
30,064✔
553
                _cleanup_close_ int fd = -EBADF;
483✔
554
                usec_t n;
30,064✔
555

556
                /* Exit the worker in regular intervals, to flush out all memory use */
557
                if (n_iterations++ > ITERATIONS_MAX) {
30,064✔
558
                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
483✔
559
                        break;
560
                }
561

562
                n = now(CLOCK_MONOTONIC);
29,626✔
563
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
59,252✔
564
                        log_debug("Exiting worker, ran for %s, that's enough.",
24✔
565
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
566
                        break;
12✔
567
                }
568

569
                if (last_busy_usec == USEC_INFINITY)
29,614✔
570
                        last_busy_usec = n;
571
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
472✔
572
                        log_debug("Exiting worker, been idle for %s.",
66✔
573
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
574
                        break;
33✔
575
                }
576

577
                (void) rename_process("systemd-userwork: waiting...");
29,581✔
578
                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
29,581✔
579
                (void) rename_process("systemd-userwork: processing...");
29,581✔
580

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

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

594
                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
23,765✔
595
                        if (r < 0)
23,765✔
596
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
597

598
                        if (FLAGS_SET(r, POLLIN)) {
23,765✔
599
                                r = pidref_kill(&parent, SIGUSR2);
243✔
600
                                if (r == -ESRCH)
243✔
601
                                        return log_error_errno(r, "Parent already died?");
×
602
                                if (r < 0)
243✔
603
                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent: %m");
×
604
                        }
605
                }
606

607
                (void) process_connection(server, TAKE_FD(fd));
29,248✔
608
                last_busy_usec = USEC_INFINITY;
29,248✔
609
        }
610

611
        return 0;
612
}
613

614
DEFINE_MAIN_FUNCTION(run);
966✔
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