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

systemd / systemd / 16791678039

06 Aug 2025 11:10PM UTC coverage: 72.181% (-0.04%) from 72.223%
16791678039

push

github

yuwata
logging: Improve logging messages related to NFTSet.

The 'NFTSet' directive in various units adds and removes entries in nftables
sets, it does not add or remove entire sets. The logging messages should
indicate that an entry was added or removed, not that a set was added or
removed.

2 of 6 new or added lines in 3 files covered. (33.33%)

496 existing lines in 52 files now uncovered.

302228 of 418708 relevant lines covered (72.18%)

647735.83 hits per line

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

88.14
/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) {
22,674✔
43
        assert(p);
22,674✔
44

45
        userdb_match_done(&p->match);
22,674✔
46
}
22,674✔
47

48
static int add_nss_service(sd_json_variant **v) {
4,984✔
49
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *status = NULL, *z = NULL;
9,968✔
50
        sd_id128_t mid;
4,984✔
51
        int r;
4,984✔
52

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

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

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

68
        if (sd_json_variant_by_key(z, "service"))
4,984✔
69
                return 0;
70

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

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

79
        return sd_json_variant_set_field(v, "status", status);
4,834✔
80
}
81

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

90
        assert(ur);
2,588✔
91
        assert(ret);
2,588✔
92

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

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

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

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

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

130
        if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
35,068✔
131
                *ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
58✔
132
        else if (streq_ptr(service, "io.systemd.DropIn"))
35,010✔
133
                *ret = USERDB_DROPIN_ONLY|USERDB_AVOID_MULTIPLEXER;
25,885✔
134
        else if (streq_ptr(service, "io.systemd.Multiplexer"))
9,125✔
135
                *ret = USERDB_AVOID_MULTIPLEXER;
9,125✔
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) {
6,139✔
143

144
        static const sd_json_dispatch_field dispatch_table[] = {
6,139✔
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
                {}
153
        };
154

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

164
        assert(parameters);
6,139✔
165

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

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

175
        if (uid_is_valid(p.uid))
6,138✔
176
                r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
3,909✔
177
        else if (p.name)
2,229✔
178
                r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
2,211✔
179
        else {
180
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
181
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
18✔
182

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

195
                for (;;) {
334✔
196
                        _cleanup_(user_record_unrefp) UserRecord *z = NULL;
352✔
197

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

204
                        if (last) {
334✔
205
                                r = sd_varlink_notify(link, last);
318✔
206
                                if (r < 0)
318✔
207
                                        return r;
208

209
                                last = sd_json_variant_unref(last);
318✔
210
                        }
211

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

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

220
                return sd_varlink_reply(link, last);
16✔
221
        }
222
        if (r == -ESRCH)
6,120✔
223
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
3,860✔
224
        if (r == -ENOEXEC)
2,260✔
225
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
6✔
226
        if (r < 0) {
2,254✔
227
                log_debug_errno(r, "User lookup failed abnormally: %m");
×
228
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
229
        }
230

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

235
        r = build_user_json(link, hr, &v);
2,254✔
236
        if (r < 0)
2,254✔
237
                return r;
238

239
        return sd_varlink_reply(link, v);
2,254✔
240
}
241

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

250
        assert(gr);
2,396✔
251
        assert(ret);
2,396✔
252

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

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

270
        stripped->incomplete =
4,792✔
271
                gr->incomplete ||
2,396✔
272
                (FLAGS_SET(gr->mask, USER_RECORD_PRIVILEGED) &&
2,396✔
273
                 !FLAGS_SET(stripped->mask, USER_RECORD_PRIVILEGED));
44✔
274

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

280
        return sd_json_buildo(
2,396✔
281
                        ret,
282
                        SD_JSON_BUILD_PAIR("record", SD_JSON_BUILD_VARIANT(v)),
283
                        SD_JSON_BUILD_PAIR("incomplete", SD_JSON_BUILD_BOOLEAN(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) {
16,535✔
287

288
        static const sd_json_dispatch_field dispatch_table[] = {
16,535✔
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
                {}
297
        };
298

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

308
        assert(parameters);
16,535✔
309

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

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

318
        if (gid_is_valid(p.gid))
16,535✔
319
                r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
4,286✔
320
        else if (p.name)
12,249✔
321
                r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
12,239✔
322
        else {
323
                _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
×
324
                _cleanup_(sd_json_variant_unrefp) sd_json_variant *last = NULL;
10✔
325

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

332
                for (;;) {
286✔
333
                        _cleanup_(group_record_unrefp) GroupRecord *z = NULL;
296✔
334

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

341
                        if (last) {
286✔
342
                                r = sd_varlink_notify(link, last);
278✔
343
                                if (r < 0)
278✔
344
                                        return r;
345

346
                                last = sd_json_variant_unref(last);
278✔
347
                        }
348

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

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

357
                return sd_varlink_reply(link, last);
8✔
358
        }
359
        if (r == -ESRCH)
16,525✔
360
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
14,411✔
361
        if (r == -ENOEXEC)
2,114✔
362
                return sd_varlink_error(link, "io.systemd.UserDatabase.NonMatchingRecordFound", NULL);
4✔
363
        if (r < 0) {
2,110✔
364
                log_debug_errno(r, "Group lookup failed abnormally: %m");
×
365
                return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
×
366
        }
367

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

372
        r = build_group_json(link, g, &v);
2,110✔
373
        if (r < 0)
2,110✔
374
                return r;
375

376
        return sd_varlink_reply(link, v);
2,110✔
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) {
12,395✔
386
        static const sd_json_dispatch_field dispatch_table[] = {
12,395✔
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_free_ char *last_user_name = NULL, *last_group_name = NULL;
12,395✔
394
        _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
12,395✔
395
        MembershipLookupParameters p = {};
12,395✔
396
        UserDBFlags userdb_flags;
12,395✔
397
        int r;
12,395✔
398

399
        assert(parameters);
12,395✔
400

401
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
12,395✔
402
        if (r != 0)
12,395✔
403
                return r;
404

405
        r = userdb_flags_from_service(link, p.service, &userdb_flags);
12,395✔
406
        if (r != 0)
12,395✔
407
                return r;
408

409
        if (p.group_name)
12,395✔
410
                r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
11,176✔
411
        else if (p.user_name)
1,219✔
412
                r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
1,217✔
413
        else
414
                r = membershipdb_all(userdb_flags, &iterator);
2✔
415
        if (IN_SET(r, -ESRCH, -ENOLINK))
12,395✔
416
                return sd_varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
12,253✔
417
        if (r < 0)
142✔
418
                return r;
419

420
        for (;;) {
173✔
UNCOV
421
                _cleanup_free_ char *user_name = NULL, *group_name = NULL;
×
422

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

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

433
                if (last_user_name) {
31✔
434
                        assert(last_group_name);
10✔
435

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

444
                free_and_replace(last_user_name, user_name);
31✔
445
                free_and_replace(last_group_name, group_name);
31✔
446
        }
447

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

453
        assert(last_group_name);
21✔
454

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

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

466
        assert(server);
35,094✔
467
        assert(fd >= 0);
35,094✔
468

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

473
        TAKE_FD(fd);
35,094✔
474
        vl = sd_varlink_ref(vl);
35,094✔
475

476
        for (;;) {
327,539✔
477
                r = sd_varlink_process(vl);
327,539✔
478
                if (r == -ENOTCONN) {
327,539✔
479
                        log_debug("Connection terminated.");
35,094✔
480
                        break;
481
                }
482
                if (r < 0)
292,445✔
483
                        return log_error_errno(r, "Failed to process connection: %m");
×
484
                if (r > 0)
292,445✔
485
                        continue;
245,558✔
486

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

494
        return 0;
495
}
496

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

504
        log_setup();
559✔
505

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

514
        listen_fd = SD_LISTEN_FDS_START;
559✔
515

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

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

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

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

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

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

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

551
        start_time = now(CLOCK_MONOTONIC);
559✔
552

553
        for (;;) {
35,916✔
554
                _cleanup_close_ int fd = -EBADF;
559✔
555
                usec_t n;
35,916✔
556

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

563
                n = now(CLOCK_MONOTONIC);
35,388✔
564
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
70,776✔
565
                        log_debug("Exiting worker, ran for %s, that's enough.",
22✔
566
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
567
                        break;
11✔
568
                }
569

570
                if (last_busy_usec == USEC_INFINITY)
35,377✔
571
                        last_busy_usec = n;
572
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
344✔
573
                        log_debug("Exiting worker, been idle for %s.",
40✔
574
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
575
                        break;
20✔
576
                }
577

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

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

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

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

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

608
                (void) process_connection(server, TAKE_FD(fd));
35,094✔
609
                last_busy_usec = USEC_INFINITY;
35,094✔
610
        }
611

612
        return 0;
613
}
614

615
DEFINE_MAIN_FUNCTION(run);
559✔
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