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

systemd / systemd / 13042844938

29 Jan 2025 04:17PM UTC coverage: 71.718% (+0.2%) from 71.53%
13042844938

push

github

bluca
tree-wide: remove some spurious newlines

Specifically, remove double newlines, and newlines before '}'.

292558 of 407928 relevant lines covered (71.72%)

710701.19 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,339✔
42
        assert(p);
15,339✔
43

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

47
static int add_nss_service(sd_json_variant **v) {
915✔
48
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
1,830✔
49
        sd_id128_t mid;
915✔
50
        int r;
915✔
51

52
        assert(v);
915✔
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"))
915✔
58
                return 0;
59

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

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

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

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

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

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

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

89
        assert(ur);
588✔
90
        assert(ret);
588✔
91

92
        r = sd_varlink_get_peer_uid(link, &peer_uid);
588✔
93
        if (r < 0) {
588✔
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;
588✔
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);
588✔
106
        if (r < 0)
588✔
107
                return r;
108

109
        stripped->incomplete =
1,176✔
110
                ur->incomplete ||
588✔
111
                (FLAGS_SET(ur->mask, USER_RECORD_PRIVILEGED) &&
588✔
112
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
192✔
113

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

119
        return sd_json_buildo(
588✔
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) {
31,067✔
126
        assert(link);
31,067✔
127
        assert(ret);
31,067✔
128

129
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
31,067✔
130
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
14✔
131
        else if (streq_ptr(service, "io.systemd.DropIn"))
31,053✔
132
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
30,561✔
133
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
492✔
134
                *ret = USERDB_AVOID_MULTIPLEXER;
492✔
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) {
519✔
142

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

163
        assert(parameters);
519✔
164

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

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

174
        if (uid_is_valid(p.uid))
519✔
175
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
252✔
176
        else if (p.name)
267✔
177
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
250✔
178
        else {
179
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
180
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
17✔
181

182
                r = userdb_all(&p.match, userdb_flags, &iterator);
17✔
183
                if (IN_SET(r, -ESRCH, -ENOLINK))
17✔
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)
17✔
192
                        return r;
193

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

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

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

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

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

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

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

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

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

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

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

252
        r = sd_varlink_get_peer_uid(link, &peer_uid);
327✔
253
        if (r < 0) {
327✔
254
                log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
×
255
                trusted = false;
256
        } else
257
                trusted = peer_uid == 0;
327✔
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;
327✔
260
        if (trusted)
327✔
261
                flags |= USER_RECORD_ALLOW_PRIVILEGED;
262
        else
263
                flags |= USER_RECORD_STRIP_PRIVILEGED;
264

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

269
        stripped->incomplete =
654✔
270
                gr->incomplete ||
327✔
271
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
327✔
272
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
27✔
273

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

279
        return sd_json_buildo(
327✔
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) {
14,820✔
286

287
        static const sd_json_dispatch_field dispatch_table[] = {
14,820✔
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;
14,820✔
299
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
14,820✔
300
        _cleanup_(lookup_parameters_done) LookupParameters p = {
14,820✔
301
                .gid = GID_INVALID,
302
                .match = USERDB_MATCH_NULL,
303
        };
304
        UserDBFlags userdb_flags;
14,820✔
305
        int r;
14,820✔
306

307
        assert(parameters);
14,820✔
308

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

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

317
        if (gid_is_valid(p.gid))
14,820✔
318
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
955✔
319
        else if (p.name)
13,865✔
320
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
13,857✔
321
        else {
322
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
323
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
8✔
324

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

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

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

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

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

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

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

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

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

375
        return sd_varlink_reply(link, v);
96✔
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) {
15,728✔
385
        static const sd_json_dispatch_field dispatch_table[] = {
15,728✔
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;
15,728✔
393
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
15,728✔
394
        MembershipLookupParameters p = {};
15,728✔
395
        UserDBFlags userdb_flags;
15,728✔
396
        int r;
15,728✔
397

398
        assert(parameters);
15,728✔
399

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

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

408
        if (p.group_name)
15,728✔
409
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
14,552✔
410
        else if (p.user_name)
1,176✔
411
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
1,175✔
412
        else
413
                r = membershipdb_all(userdb_flags, &iterator);
1✔
414
        if (IN_SET(r, -ESRCH, -ENOLINK))
15,728✔
415
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
15,627✔
416
        if (r < 0)
101✔
417
                return r;
418

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

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

432
                if (last_user_name) {
17✔
433
                        assert(last_group_name);
6✔
434

435
                        r = sd_varlink_notifybo(
6✔
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)
6✔
440
                                return r;
441
                }
442

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

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

452
        assert(last_group_name);
11✔
453

454
        return sd_varlink_replybo(
11✔
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) {
31,076✔
461
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
31,076✔
462
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
31,076✔
463
        int r;
31,076✔
464

465
        assert(server);
31,076✔
466
        assert(fd >= 0);
31,076✔
467

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

472
        TAKE_FD(fd);
31,076✔
473
        vl = sd_varlink_ref(vl);
31,076✔
474

475
        for (;;) {
287,395✔
476
                r = sd_varlink_process(vl);
287,395✔
477
                if (r == -ENOTCONN) {
287,395✔
478
                        log_debug("Connection terminated.");
31,076✔
479
                        break;
480
                }
481
                if (r < 0)
256,319✔
482
                        return log_error_errno(r, "Failed to process connection: %m");
×
483
                if (r > 0)
256,319✔
484
                        continue;
217,496✔
485

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

493
        return 0;
494
}
495

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

503
        log_setup();
506✔
504

505
        m = sd_listen_fds(false);
506✔
506
        if (m < 0)
506✔
507
                return log_error_errno(m, "Failed to determine number of listening fds: %m");
×
508
        if (m == 0)
506✔
509
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
×
510
        if (m > 1)
506✔
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;
506✔
514

515
        r = fd_nonblock(listen_fd, false);
506✔
516
        if (r < 0)
506✔
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);
506✔
520
        if (r < 0)
506✔
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);
506✔
524
        if (r < 0)
506✔
525
                return log_error_errno(r, "Failed to add UserDatabase interface to varlink server: %m");
×
526

527
        r = sd_varlink_server_bind_method_many(
506✔
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)
506✔
533
                return log_error_errno(r, "Failed to bind methods: %m");
×
534

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

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

544
        r = pidref_set_parent(&parent);
506✔
545
        if (r < 0)
506✔
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? */
506✔
548
                return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent already died, exiting.");
×
549

550
        start_time = now(CLOCK_MONOTONIC);
506✔
551

552
        for (;;) {
31,898✔
553
                _cleanup_close_ int fd = -EBADF;
506✔
554
                usec_t n;
31,898✔
555

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

562
                n = now(CLOCK_MONOTONIC);
31,432✔
563
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
62,864✔
564
                        log_debug("Exiting worker, ran for %s, that's enough.",
38✔
565
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
566
                        break;
19✔
567
                }
568

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

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

581
                if (fd == -EAGAIN)
31,392✔
582
                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
316✔
583
                                   * after a while, let's check if it's time to exit though. */
584
                if (fd == -EINTR)
31,076✔
585
                        continue; /* Might be that somebody attached via strace, let's just continue in that
×
586
                                   * case */
587
                if (fd < 0)
31,076✔
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)) {
31,076✔
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);
26,433✔
595
                        if (r < 0)
26,433✔
596
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
597

598
                        if (FLAGS_SET(r, POLLIN)) {
26,433✔
599
                                r = pidref_kill(&parent, SIGUSR2);
241✔
600
                                if (r == -ESRCH)
241✔
601
                                        return log_error_errno(r, "Parent already died?");
×
602
                                if (r < 0)
241✔
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));
31,076✔
608
                last_busy_usec = USEC_INFINITY;
31,076✔
609
        }
610

611
        return 0;
612
}
613

614
DEFINE_MAIN_FUNCTION(run);
1,012✔
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