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

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

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

86.31
/src/shared/group-record.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "bitfield.h"
5
#include "group-record.h"
6
#include "json-util.h"
7
#include "log.h"
8
#include "strv.h"
9
#include "uid-classification.h"
10
#include "user-util.h"
11

12
GroupRecord* group_record_new(void) {
4,021✔
13
        GroupRecord *h;
4,021✔
14

15
        h = new(GroupRecord, 1);
4,021✔
16
        if (!h)
4,021✔
17
                return NULL;
18

19
        *h = (GroupRecord) {
4,021✔
20
                .n_ref = 1,
21
                .disposition = _USER_DISPOSITION_INVALID,
22
                .last_change_usec = UINT64_MAX,
23
                .gid = GID_INVALID,
24
        };
25

26
        return h;
4,021✔
27
}
28

29
static GroupRecord *group_record_free(GroupRecord *g) {
4,011✔
30
        if (!g)
4,011✔
31
                return NULL;
32

33
        free(g->group_name);
4,011✔
34
        free(g->realm);
4,011✔
35
        free(g->group_name_and_realm_auto);
4,011✔
36
        free(g->description);
4,011✔
37

38
        strv_free(g->members);
4,011✔
39
        free(g->service);
4,011✔
40
        strv_free(g->administrators);
4,011✔
41
        strv_free_erase(g->hashed_password);
4,011✔
42

43
        sd_json_variant_unref(g->json);
4,011✔
44

45
        return mfree(g);
4,011✔
46
}
47

48
DEFINE_TRIVIAL_REF_UNREF_FUNC(GroupRecord, group_record, group_record_free);
34,028✔
49

50
static int dispatch_privileged(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
50✔
51

52
        static const sd_json_dispatch_field privileged_dispatch_table[] = {
50✔
53
                { "hashedPassword", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_strv, offsetof(GroupRecord, hashed_password), SD_JSON_STRICT },
54
                {},
55
        };
56

57
        return sd_json_dispatch(variant, privileged_dispatch_table, flags, userdata);
50✔
58
}
59

60
static int dispatch_binding(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,391✔
61

62
        static const sd_json_dispatch_field binding_dispatch_table[] = {
1,391✔
63
                { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
64
                {},
65
        };
66

67
        sd_json_variant *m;
1,391✔
68
        sd_id128_t mid;
1,391✔
69
        int r;
1,391✔
70

71
        if (!variant)
1,391✔
72
                return 0;
1,391✔
73

74
        if (!sd_json_variant_is_object(variant))
243✔
UNCOV
75
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
76

77
        r = sd_id128_get_machine(&mid);
243✔
78
        if (r < 0)
243✔
UNCOV
79
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
80

81
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
243✔
82
        if (!m)
243✔
83
                return 0;
84

85
        return sd_json_dispatch(m, binding_dispatch_table, flags, userdata);
243✔
86
}
87

88
static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,391✔
89

90
        static const sd_json_dispatch_field per_machine_dispatch_table[] = {
1,391✔
91
                { "matchMachineId", _SD_JSON_VARIANT_TYPE_INVALID, NULL,                           0,                                     0             },
92
                { "matchHostname",  _SD_JSON_VARIANT_TYPE_INVALID, NULL,                           0,                                     0             },
93
                { "gid",            SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,       offsetof(GroupRecord, gid),            0             },
94
                { "members",        SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, members),        SD_JSON_RELAX },
95
                { "administrators", SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, administrators), SD_JSON_RELAX },
96
                {},
97
        };
98

99
        sd_json_variant *e;
1,391✔
100
        int r;
1,391✔
101

102
        if (!variant)
1,391✔
103
                return 0;
104

105
        if (!sd_json_variant_is_array(variant))
×
UNCOV
106
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
×
107

108
        JSON_VARIANT_ARRAY_FOREACH(e, variant) {
×
109
                if (!sd_json_variant_is_object(e))
×
UNCOV
110
                        return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of objects.", strna(name));
×
111

112
                r = per_machine_match(e, flags);
×
UNCOV
113
                if (r < 0)
×
114
                        return r;
115
                if (r == 0)
×
UNCOV
116
                        continue;
×
117

118
                r = sd_json_dispatch(e, per_machine_dispatch_table, flags, userdata);
×
UNCOV
119
                if (r < 0)
×
120
                        return r;
121
        }
122

UNCOV
123
        return 0;
×
124
}
125

126
static int dispatch_status(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
1,391✔
127

128
        static const sd_json_dispatch_field status_dispatch_table[] = {
1,391✔
129
                { "service", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(GroupRecord, service), SD_JSON_STRICT },
130
                {},
131
        };
132

133
        sd_json_variant *m;
1,391✔
134
        sd_id128_t mid;
1,391✔
135
        int r;
1,391✔
136

137
        if (!variant)
1,391✔
138
                return 0;
1,391✔
139

140
        if (!sd_json_variant_is_object(variant))
637✔
UNCOV
141
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an object.", strna(name));
×
142

143
        r = sd_id128_get_machine(&mid);
637✔
144
        if (r < 0)
637✔
UNCOV
145
                return json_log(variant, flags, r, "Failed to determine machine ID: %m");
×
146

147
        m = sd_json_variant_by_key(variant, SD_ID128_TO_STRING(mid));
637✔
148
        if (!m)
637✔
149
                return 0;
150

151
        return sd_json_dispatch(m, status_dispatch_table, flags, userdata);
637✔
152
}
153

154
static int group_record_augment(GroupRecord *h, sd_json_dispatch_flags_t json_flags) {
1,391✔
155
        assert(h);
1,391✔
156

157
        if (!FLAGS_SET(h->mask, USER_RECORD_REGULAR))
1,391✔
158
                return 0;
159

160
        assert(h->group_name);
1,281✔
161

162
        if (!h->group_name_and_realm_auto && h->realm) {
1,281✔
163
                h->group_name_and_realm_auto = strjoin(h->group_name, "@", h->realm);
36✔
164
                if (!h->group_name_and_realm_auto)
36✔
UNCOV
165
                        return json_log_oom(h->json, json_flags);
×
166
        }
167

168
        return 0;
169
}
170

171
int group_record_load(
1,391✔
172
                GroupRecord *h,
173
                sd_json_variant *v,
174
                UserRecordLoadFlags load_flags) {
175

176
        static const sd_json_dispatch_field group_dispatch_table[] = {
1,391✔
177
                { "groupName",      SD_JSON_VARIANT_STRING,        json_dispatch_user_group_name,  offsetof(GroupRecord, group_name),       SD_JSON_RELAX  },
178
                { "realm",          SD_JSON_VARIANT_STRING,        json_dispatch_realm,            offsetof(GroupRecord, realm),            0              },
179
                { "uuid",           SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,         offsetof(GroupRecord, uuid),             0              },
180
                { "description",    SD_JSON_VARIANT_STRING,        json_dispatch_gecos,            offsetof(GroupRecord, description),      0              },
181
                { "disposition",    SD_JSON_VARIANT_STRING,        json_dispatch_user_disposition, offsetof(GroupRecord, disposition),      0              },
182
                { "service",        SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,        offsetof(GroupRecord, service),          SD_JSON_STRICT },
183
                { "lastChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,        offsetof(GroupRecord, last_change_usec), 0              },
184
                { "gid",            SD_JSON_VARIANT_UNSIGNED,      sd_json_dispatch_uid_gid,       offsetof(GroupRecord, gid),              0              },
185
                { "members",        SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, members),          SD_JSON_RELAX  },
186
                { "administrators", SD_JSON_VARIANT_ARRAY,         json_dispatch_user_group_list,  offsetof(GroupRecord, administrators),   SD_JSON_RELAX  },
187

188
                { "privileged",     SD_JSON_VARIANT_OBJECT,        dispatch_privileged,            0,                                       0              },
189

190
                /* Not defined for now, for groups, but let's at least generate sensible errors about it */
191
                { "secret",         SD_JSON_VARIANT_OBJECT,        sd_json_dispatch_unsupported,   0,                                       0              },
192

193
                /* Ignore the perMachine, binding and status stuff here, and process it later, so that it overrides whatever is set above */
194
                { "perMachine",     SD_JSON_VARIANT_ARRAY,         NULL,                           0,                                       0              },
195
                { "binding",        SD_JSON_VARIANT_OBJECT,        NULL,                           0,                                       0              },
196
                { "status",         SD_JSON_VARIANT_OBJECT,        NULL,                           0,                                       0              },
197

198
                /* Ignore 'signature', we check it with explicit accessors instead */
199
                { "signature",      SD_JSON_VARIANT_ARRAY,         NULL,                           0,                                       0              },
200
                {},
201
        };
202

203
        sd_json_dispatch_flags_t json_flags = USER_RECORD_LOAD_FLAGS_TO_JSON_DISPATCH_FLAGS(load_flags);
1,391✔
204
        int r;
1,391✔
205

206
        assert(h);
1,391✔
207
        assert(!h->json);
1,391✔
208

209
        /* Note that this call will leave a half-initialized record around on failure! */
210

211
        if ((USER_RECORD_REQUIRE_MASK(load_flags) & (USER_RECORD_SECRET|USER_RECORD_PRIVILEGED)))
1,391✔
UNCOV
212
                return json_log(v, json_flags, SYNTHETIC_ERRNO(EINVAL), "Secret and privileged section currently not available for groups, refusing.");
×
213

214
        r = user_group_record_mangle(v, load_flags, &h->json, &h->mask);
1,391✔
215
        if (r < 0)
1,391✔
216
                return r;
217

218
        r = sd_json_dispatch(h->json, group_dispatch_table, json_flags | SD_JSON_ALLOW_EXTENSIONS, h);
1,391✔
219
        if (r < 0)
1,391✔
220
                return r;
221

222
        /* During the parsing operation above we ignored the 'perMachine', 'binding' and 'status' fields, since we want
223
         * them to override the global options. Let's process them now. */
224

225
        r = dispatch_per_machine("perMachine", sd_json_variant_by_key(h->json, "perMachine"), json_flags, h);
1,391✔
226
        if (r < 0)
1,391✔
227
                return r;
228

229
        r = dispatch_binding("binding", sd_json_variant_by_key(h->json, "binding"), json_flags, h);
1,391✔
230
        if (r < 0)
1,391✔
231
                return r;
232

233
        r = dispatch_status("status", sd_json_variant_by_key(h->json, "status"), json_flags, h);
1,391✔
234
        if (r < 0)
1,391✔
235
                return r;
236

237
        if (FLAGS_SET(h->mask, USER_RECORD_REGULAR) && !h->group_name)
1,391✔
UNCOV
238
                return json_log(h->json, json_flags, SYNTHETIC_ERRNO(EINVAL), "Group name field missing, refusing.");
×
239

240
        r = group_record_augment(h, json_flags);
1,391✔
241
        if (r < 0)
1,391✔
UNCOV
242
                return r;
×
243

244
        return 0;
245
}
246

247
int group_record_build(GroupRecord **ret, ...) {
27✔
248
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
27✔
249
        _cleanup_(group_record_unrefp) GroupRecord *g = NULL;
27✔
250
        va_list ap;
27✔
251
        int r;
27✔
252

253
        assert(ret);
27✔
254

255
        va_start(ap, ret);
27✔
256
        r = sd_json_buildv(&v, ap);
27✔
257
        va_end(ap);
27✔
258

259
        if (r < 0)
27✔
260
                return r;
261

262
        g = group_record_new();
27✔
263
        if (!g)
27✔
264
                return -ENOMEM;
265

266
        r = group_record_load(g, v, USER_RECORD_LOAD_FULL);
27✔
267
        if (r < 0)
27✔
268
                return r;
269

270
        *ret = TAKE_PTR(g);
27✔
271
        return 0;
27✔
272
}
273

274
const char* group_record_group_name_and_realm(GroupRecord *h) {
79✔
275
        assert(h);
79✔
276

277
        /* Return the pre-initialized joined string if it is defined */
278
        if (h->group_name_and_realm_auto)
79✔
279
                return h->group_name_and_realm_auto;
280

281
        /* If it's not defined then we cannot have a realm */
282
        assert(!h->realm);
79✔
283
        return h->group_name;
79✔
284
}
285

286
UserDisposition group_record_disposition(GroupRecord *h) {
1,080✔
287
        assert(h);
1,080✔
288

289
        if (h->disposition >= 0)
1,080✔
290
                return h->disposition;
291

292
        /* If not declared, derive from GID */
293

294
        if (!gid_is_valid(h->gid))
923✔
295
                return _USER_DISPOSITION_INVALID;
296

297
        if (h->gid == 0 || h->gid == GID_NOBODY)
923✔
298
                return USER_INTRINSIC;
299

300
        if (gid_is_system(h->gid))
866✔
301
                return USER_SYSTEM;
302

303
        if (gid_is_dynamic(h->gid))
30✔
304
                return USER_DYNAMIC;
305

306
        if (gid_is_container(h->gid))
30✔
307
                return USER_CONTAINER;
308

309
        if (gid_is_foreign(h->gid))
5✔
310
                return USER_FOREIGN;
311

312
        if (h->gid > INT32_MAX)
5✔
UNCOV
313
                return USER_RESERVED;
×
314

315
        return USER_REGULAR;
316
}
317

318
int group_record_clone(GroupRecord *h, UserRecordLoadFlags flags, GroupRecord **ret) {
610✔
319
        _cleanup_(group_record_unrefp) GroupRecord *c = NULL;
610✔
320
        int r;
610✔
321

322
        assert(h);
610✔
323
        assert(ret);
610✔
324

325
        c = group_record_new();
610✔
326
        if (!c)
610✔
327
                return -ENOMEM;
328

329
        r = group_record_load(c, h->json, flags);
610✔
330
        if (r < 0)
610✔
331
                return r;
332

333
        *ret = TAKE_PTR(c);
610✔
334
        return 0;
610✔
335
}
336

337
bool group_record_matches_group_name(const GroupRecord *g, const char *group_name) {
45✔
338
        assert(g);
45✔
339
        assert(group_name);
45✔
340

341
        if (streq_ptr(g->group_name, group_name))
45✔
342
                return true;
343

344
        if (streq_ptr(g->group_name_and_realm_auto, group_name))
×
UNCOV
345
                return true;
×
346

347
        return false;
348
}
349

350
bool group_record_match(GroupRecord *h, const UserDBMatch *match) {
1,139✔
351
        assert(h);
1,139✔
352

353
        if (!match)
1,139✔
354
                return true;
355

356
        if (!gid_is_valid(h->gid))
887✔
357
                return false;
358

359
        if (h->gid < match->gid_min || h->gid > match->gid_max)
887✔
360
                return false;
361

362
        if (!BIT_SET(match->disposition_mask, group_record_disposition(h)))
827✔
363
                return false;
364

365
        if (!strv_isempty(match->fuzzy_names)) {
705✔
366
                const char* names[] = {
120✔
367
                        h->group_name,
60✔
368
                        group_record_group_name_and_realm(h),
60✔
369
                        h->description,
60✔
370
                };
371

372
                if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
60✔
373
                        return false;
60✔
374
        }
375

376
        return true;
377
}
378

379
bool group_record_is_root(const GroupRecord *g) {
114✔
380
        assert(g);
114✔
381

382
        return g->gid == 0 || streq_ptr(g->group_name, "root");
114✔
383
}
384

385
bool group_record_is_nobody(const GroupRecord *g) {
114✔
386
        assert(g);
114✔
387

388
        return g->gid == GID_NOBODY || STRPTR_IN_SET(g->group_name, NOBODY_GROUP_NAME, "nobody");
114✔
389
}
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