Coverage Report

Created: 2026-07-30 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/libsystemd/sd-bus/bus-error.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "sd-bus.h"
4
5
#include "alloc-util.h"
6
#include "bus-error.h"
7
#include "errno-list.h"
8
#include "errno-util.h"
9
#include "string-util.h"
10
#include "utf8.h"
11
12
BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
13
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_FAILED,                             EACCES),
14
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NO_MEMORY,                          ENOMEM),
15
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_SERVICE_UNKNOWN,                    EHOSTUNREACH),
16
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NAME_HAS_NO_OWNER,                  ENXIO),
17
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NO_REPLY,                           ETIMEDOUT),
18
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_IO_ERROR,                           EIO),
19
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_BAD_ADDRESS,                        EADDRNOTAVAIL),
20
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NOT_SUPPORTED,                      EOPNOTSUPP),
21
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_LIMITS_EXCEEDED,                    ENOBUFS),
22
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_ACCESS_DENIED,                      EACCES),
23
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_AUTH_FAILED,                        EACCES),
24
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NO_SERVER,                          EHOSTDOWN),
25
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_TIMEOUT,                            ETIMEDOUT),
26
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_NO_NETWORK,                         ENONET),
27
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_ADDRESS_IN_USE,                     EADDRINUSE),
28
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_DISCONNECTED,                       ECONNRESET),
29
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_INVALID_ARGS,                       EINVAL),
30
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_FILE_NOT_FOUND,                     ENOENT),
31
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_FILE_EXISTS,                        EEXIST),
32
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_UNKNOWN_METHOD,                     EBADR),
33
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_UNKNOWN_OBJECT,                     EBADR),
34
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_UNKNOWN_INTERFACE,                  EBADR),
35
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_UNKNOWN_PROPERTY,                   EBADR),
36
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_PROPERTY_READ_ONLY,                 EROFS),
37
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN,            ESRCH),
38
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_INVALID_SIGNATURE,                  EINVAL),
39
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_INCONSISTENT_MESSAGE,               EBADMSG),
40
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_TIMED_OUT,                          ETIMEDOUT),
41
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_MATCH_RULE_NOT_FOUND,               ENOENT),
42
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_MATCH_RULE_INVALID,                 EINVAL),
43
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED, EACCES),
44
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_INVALID_FILE_CONTENT,               EINVAL),
45
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN,   ESRCH),
46
        SD_BUS_ERROR_MAP(SD_BUS_ERROR_OBJECT_PATH_IN_USE,                 EBUSY),
47
        SD_BUS_ERROR_MAP_END
48
};
49
50
/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
51
extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[];
52
extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[];
53
54
/* Additional maps registered with sd_bus_error_add_map() are in this
55
 * NULL terminated array */
56
static const sd_bus_error_map **additional_error_maps = NULL;
57
58
62.0k
static int bus_error_name_to_errno(const char *name) {
59
62.0k
        const char *p;
60
62.0k
        int r;
61
62
62.0k
        assert_return(name, EINVAL);
63
64
62.0k
        p = startswith(name, "System.Error.");
65
62.0k
        if (p) {
66
0
                r = errno_from_name(p);
67
0
                if (r < 0)
68
0
                        return EIO;
69
70
0
                return r;
71
0
        }
72
73
62.0k
        if (additional_error_maps)
74
0
                for (const sd_bus_error_map **map = additional_error_maps; *map; map++)
75
0
                        for (const sd_bus_error_map *m = *map;; m++) {
76
                                /* For additional error maps the end marker is actually the end marker */
77
0
                                if (m->code == BUS_ERROR_MAP_END_MARKER)
78
0
                                        break;
79
80
0
                                if (streq(m->name, name)) {
81
0
                                        assert(m->code > 0);
82
0
                                        return m->code;
83
0
                                }
84
0
                        }
85
86
62.0k
        assert_cc(sizeof(sd_bus_error_map) % sizeof(void*) == 0);
87
88
9.92M
        for (const sd_bus_error_map *m = __start_SYSTEMD_BUS_ERROR_MAP; m < __stop_SYSTEMD_BUS_ERROR_MAP; m++) {
89
                /* For magic ELF error maps, the end marker might appear in the middle of things, since
90
                 * multiple maps might appear in the same section. Skip over it. */
91
92
9.92M
                if (m->code != BUS_ERROR_MAP_END_MARKER &&
93
9.86M
                    streq(m->name, name)) {
94
62.0k
                        assert(m->code > 0);
95
62.0k
                        return m->code;
96
62.0k
                }
97
9.92M
        }
98
99
0
        return EIO;
100
62.0k
}
101
102
284
static sd_bus_error errno_to_bus_error_const(int error) {
103
104
284
        if (error < 0)
105
0
                error = -error;
106
107
284
        switch (error) {
108
109
0
        case ENOMEM:
110
0
                return BUS_ERROR_OOM;
111
112
0
        case EPERM:
113
0
        case EACCES:
114
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
115
116
0
        case EINVAL:
117
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
118
119
0
        case ESRCH:
120
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
121
122
0
        case ENOENT:
123
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
124
125
0
        case EEXIST:
126
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
127
128
0
        case ETIMEDOUT:
129
0
        case ETIME:
130
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
131
132
0
        case EIO:
133
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
134
135
0
        case ENETRESET:
136
0
        case ECONNABORTED:
137
0
        case ECONNRESET:
138
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
139
140
0
        case EOPNOTSUPP:
141
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
142
143
0
        case EADDRNOTAVAIL:
144
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
145
146
0
        case ENOBUFS:
147
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
148
149
0
        case EADDRINUSE:
150
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
151
152
0
        case EBADMSG:
153
0
                return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
154
284
        }
155
156
284
        return SD_BUS_ERROR_NULL;
157
284
}
158
159
284
static int errno_to_bus_error_name_new(int error, char **ret) {
160
284
        const char *name;
161
284
        char *n;
162
163
284
        assert(ret);
164
165
        /* D-Bus names must not start with a digit. Thus, an name like System.Error.500 would not be legal.
166
         * Let's just return 0 if an unknown errno is encountered, which will cause the caller to fall back
167
         * to BUS_ERROR_FAILED.
168
         */
169
284
        name = errno_name_no_fallback(error);
170
284
        if (!name)
171
0
                return 0;
172
173
284
        n = strjoin("System.Error.", name);
174
284
        if (!n)
175
0
                return -ENOMEM;
176
177
284
        *ret = n;
178
284
        return 1;
179
284
}
180
181
2.20k
bool bus_error_is_dirty(sd_bus_error *e) {
182
2.20k
        if (!e)
183
0
                return false;
184
185
2.20k
        return e->name || e->message || e->_need_free != 0;
186
2.20k
}
187
188
8.95k
_public_ void sd_bus_error_free(sd_bus_error *e) {
189
8.95k
        if (!e)
190
0
                return;
191
192
8.95k
        if (e->_need_free > 0) {
193
1.92k
                free((void*) e->name);
194
1.92k
                free((void*) e->message);
195
1.92k
        }
196
197
8.95k
        *e = SD_BUS_ERROR_NULL;
198
8.95k
}
199
200
0
_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
201
0
        int r;
202
203
0
        if (!name)
204
0
                return 0;
205
206
0
        if (e) {
207
0
                assert_return(!bus_error_is_dirty(e), -EINVAL);
208
209
0
                e->name = strdup(name);
210
0
                if (!e->name) {
211
0
                        *e = BUS_ERROR_OOM;
212
0
                        return -ENOMEM;
213
0
                }
214
215
0
                if (message)
216
0
                        e->message = strdup(message);
217
218
0
                e->_need_free = 1;
219
0
        }
220
221
0
        r = bus_error_name_to_errno(name);
222
0
        assert(r > 0);
223
0
        return -r;
224
0
}
225
226
62.0k
_public_ int sd_bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
227
62.0k
        int r;
228
229
62.0k
        if (!name)
230
0
                return 0;
231
232
62.0k
        if (e) {
233
1.64k
                assert_return(!bus_error_is_dirty(e), -EINVAL);
234
235
1.64k
                e->name = strdup(name);
236
1.64k
                if (!e->name) {
237
0
                        *e = BUS_ERROR_OOM;
238
0
                        return -ENOMEM;
239
0
                }
240
241
1.64k
                if (format) {
242
1.64k
                        _cleanup_free_ char *mesg = NULL;
243
244
                        /* If we hit OOM on formatting the pretty message, we ignore
245
                         * this, since we at least managed to write the error name */
246
247
1.64k
                        if (vasprintf(&mesg, format, ap) >= 0)
248
1.64k
                                e->message = utf8_escape_non_printable(mesg);
249
1.64k
                }
250
251
1.64k
                e->_need_free = 1;
252
1.64k
        }
253
254
62.0k
        r = bus_error_name_to_errno(name);
255
62.0k
        assert(r > 0);
256
62.0k
        return -r;
257
62.0k
}
258
259
62.0k
_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
260
62.0k
        int r;
261
262
62.0k
        if (format) {
263
62.0k
                va_list ap;
264
265
62.0k
                va_start(ap, format);
266
62.0k
                r = sd_bus_error_setfv(e, name, format, ap);
267
62.0k
                if (name)
268
62.0k
                        assert(r < 0);
269
62.0k
                va_end(ap);
270
271
62.0k
                return r;
272
62.0k
        }
273
274
0
        r = sd_bus_error_set(e, name, NULL);
275
0
        if (name)
276
0
                assert(r < 0);
277
0
        return r;
278
62.0k
}
279
280
0
_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
281
282
0
        if (!sd_bus_error_is_set(e))
283
0
                return 0;
284
0
        if (!dest)
285
0
                goto finish;
286
287
0
        assert_return(!bus_error_is_dirty(dest), -EINVAL);
288
289
        /*
290
         * _need_free  < 0 indicates that the error is temporarily const, needs deep copying
291
         * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
292
         * _need_free  > 0 indicates that the error is fully dynamic, needs deep copying
293
         */
294
295
0
        if (e->_need_free == 0)
296
0
                *dest = *e;
297
0
        else {
298
0
                dest->name = strdup(e->name);
299
0
                if (!dest->name) {
300
0
                        *dest = BUS_ERROR_OOM;
301
0
                        return -ENOMEM;
302
0
                }
303
304
0
                if (e->message)
305
0
                        dest->message = strdup(e->message);
306
307
0
                dest->_need_free = 1;
308
0
        }
309
310
0
finish:
311
0
        return -bus_error_name_to_errno(e->name);
312
0
}
313
314
0
_public_ int sd_bus_error_move(sd_bus_error *dest, sd_bus_error *e) {
315
0
        int r;
316
317
0
        if (!sd_bus_error_is_set(e)) {
318
319
0
                if (dest)
320
0
                        *dest = SD_BUS_ERROR_NULL;
321
322
0
                return 0;
323
0
        }
324
325
0
        r = -bus_error_name_to_errno(e->name);
326
327
0
        if (dest) {
328
0
                *dest = *e;
329
0
                *e = SD_BUS_ERROR_NULL;
330
0
        } else
331
0
                sd_bus_error_free(e);
332
333
0
        return r;
334
0
}
335
336
0
_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
337
0
        if (!name)
338
0
                return 0;
339
0
        if (!e)
340
0
                goto finish;
341
342
0
        assert_return(!bus_error_is_dirty(e), -EINVAL);
343
344
0
        *e = SD_BUS_ERROR_MAKE_CONST(name, message);
345
346
0
finish:
347
0
        return -bus_error_name_to_errno(name);
348
0
}
349
350
2.79k
_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
351
2.79k
        if (!e)
352
0
                return 0;
353
354
2.79k
        return !!e->name;
355
2.79k
}
356
357
0
_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
358
0
        if (!e)
359
0
                return 0;
360
361
0
        return streq_ptr(e->name, name);
362
0
}
363
364
0
_public_ int sd_bus_error_has_names_sentinel(const sd_bus_error *e, ...) {
365
0
        if (!e || !e->name)
366
0
                return 0;
367
368
0
        va_list ap;
369
0
        const char *p;
370
371
0
        va_start(ap, e);
372
0
        while ((p = va_arg(ap, const char *)))
373
0
                if (streq(p, e->name))
374
0
                        break;
375
0
        va_end(ap);
376
0
        return !!p;
377
0
}
378
379
0
_public_ int sd_bus_error_get_errno(const sd_bus_error *e) {
380
0
        if (!e || !e->name)
381
0
                return 0;
382
383
0
        return bus_error_name_to_errno(e->name);
384
0
}
385
386
284
static void bus_error_strerror(sd_bus_error *e, int error) {
387
284
        size_t k = 64;
388
284
        char *m;
389
390
284
        assert(e);
391
392
284
        for (;;) {
393
284
                char *x;
394
395
284
                m = new(char, k);
396
284
                if (!m)
397
0
                        return;
398
399
284
                errno = 0;
400
284
                x = strerror_r(error, m, k);
401
284
                if (errno == ERANGE || strlen(x) >= k - 1) {
402
0
                        free(m);
403
0
                        k *= 2;
404
0
                        continue;
405
0
                }
406
407
284
                if (errno) {
408
0
                        free(m);
409
0
                        return;
410
0
                }
411
412
284
                if (x == m) {
413
0
                        if (e->_need_free > 0) {
414
                                /* Error is already dynamic, let's just update the message */
415
0
                                free((char*) e->message);
416
0
                                e->message = x;
417
418
0
                        } else {
419
0
                                char *t;
420
                                /* Error was const so far, let's make it dynamic, if we can */
421
422
0
                                t = strdup(e->name);
423
0
                                if (!t) {
424
0
                                        free(m);
425
0
                                        return;
426
0
                                }
427
428
0
                                e->_need_free = 1;
429
0
                                e->name = t;
430
0
                                e->message = x;
431
0
                        }
432
284
                } else {
433
284
                        free(m);
434
435
284
                        if (e->_need_free > 0) {
436
284
                                char *t;
437
438
                                /* Error is dynamic, let's hence make the message also dynamic */
439
284
                                t = strdup(x);
440
284
                                if (!t)
441
0
                                        return;
442
443
284
                                free((char*) e->message);
444
284
                                e->message = t;
445
284
                        } else {
446
                                /* Error is const, hence we can just override */
447
0
                                e->message = x;
448
0
                        }
449
284
                }
450
451
284
                return;
452
284
        }
453
284
}
454
455
284
_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
456
457
284
        if (error < 0)
458
284
                error = -error;
459
460
284
        if (!e)
461
0
                return -error;
462
284
        if (error == 0)
463
0
                return 0;
464
465
284
        assert_return(!bus_error_is_dirty(e), -EINVAL);
466
467
        /* First, try a const translation */
468
284
        *e = errno_to_bus_error_const(error);
469
470
284
        if (!sd_bus_error_is_set(e)) {
471
284
                int k;
472
473
                /* If that didn't work, try a dynamic one. */
474
475
284
                k = errno_to_bus_error_name_new(error, (char**) &e->name);
476
284
                if (k > 0)
477
284
                        e->_need_free = 1;
478
0
                else if (k < 0) {
479
0
                        *e = BUS_ERROR_OOM;
480
0
                        return -error;
481
0
                } else
482
0
                        *e = BUS_ERROR_FAILED;
483
284
        }
484
485
        /* Now, fill in the message from strerror_r() if we can */
486
284
        bus_error_strerror(e, error);
487
284
        return -error;
488
284
}
489
490
0
_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
491
0
        PROTECT_ERRNO;
492
493
0
        if (error < 0)
494
0
                error = -error;
495
496
0
        if (!e)
497
0
                return -error;
498
0
        if (error == 0)
499
0
                return 0;
500
501
0
        assert_return(!bus_error_is_dirty(e), -EINVAL);
502
503
        /* First, try a const translation */
504
0
        *e = errno_to_bus_error_const(error);
505
506
0
        if (!sd_bus_error_is_set(e)) {
507
0
                int k;
508
509
                /* If that didn't work, try a dynamic one */
510
511
0
                k = errno_to_bus_error_name_new(error, (char**) &e->name);
512
0
                if (k > 0)
513
0
                        e->_need_free = 1;
514
0
                else if (k < 0) {
515
0
                        *e = BUS_ERROR_OOM;
516
0
                        return -ENOMEM;
517
0
                } else
518
0
                        *e = BUS_ERROR_FAILED;
519
0
        }
520
521
0
        if (format) {
522
0
                _cleanup_free_ char *m = NULL;
523
524
                /* Then, let's try to fill in the supplied message */
525
526
0
                errno = error; /* Make sure that %m resolves to the specified error */
527
0
                if (vasprintf(&m, format, ap) < 0)
528
0
                        goto fail;
529
530
0
                if (e->_need_free <= 0) {
531
0
                        char *t;
532
533
0
                        t = strdup(e->name);
534
0
                        if (!t)
535
0
                                goto fail;
536
537
0
                        e->_need_free = 1;
538
0
                        e->name = t;
539
0
                }
540
541
0
                e->message = TAKE_PTR(m);
542
0
                return -error;
543
0
        }
544
545
0
fail:
546
        /* If that didn't work, use strerror_r() for the message */
547
0
        bus_error_strerror(e, error);
548
0
        return -error;
549
0
}
550
551
0
_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
552
0
        int r;
553
554
0
        if (error < 0)
555
0
                error = -error;
556
557
0
        if (!e)
558
0
                return -error;
559
0
        if (error == 0)
560
0
                return 0;
561
562
0
        assert_return(!bus_error_is_dirty(e), -EINVAL);
563
564
0
        if (format) {
565
0
                va_list ap;
566
567
0
                va_start(ap, format);
568
0
                r = sd_bus_error_set_errnofv(e, error, format, ap);
569
0
                va_end(ap);
570
571
0
                return r;
572
0
        }
573
574
0
        return sd_bus_error_set_errno(e, error);
575
0
}
576
577
0
const char* _bus_error_message(const sd_bus_error *e, int error, char buf[static ERRNO_BUF_LEN]) {
578
        /* Sometimes, the D-Bus server is a little bit too verbose with
579
         * its error messages, so let's override them here */
580
0
        if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
581
0
                return "Access denied";
582
583
0
        if (e && e->message)
584
0
                return e->message;
585
586
0
        return strerror_r(ABS(error), buf, ERRNO_BUF_LEN);
587
0
}
588
589
0
static bool map_ok(const sd_bus_error_map *map) {
590
0
        for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
591
0
                if (!map->name || map->code <= 0)
592
0
                        return false;
593
0
        return true;
594
0
}
595
596
0
_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
597
0
        unsigned n = 0;
598
599
0
        assert_return(map, -EINVAL);
600
0
        assert_return(map_ok(map), -EINVAL);
601
602
0
        if (additional_error_maps)
603
0
                for (; additional_error_maps[n] != NULL; n++)
604
0
                        if (additional_error_maps[n] == map)
605
0
                                return 0;
606
607
0
        if (!GREEDY_REALLOC(additional_error_maps, n + 2))
608
0
                return -ENOMEM;
609
610
0
        additional_error_maps[n] = map;
611
0
        additional_error_maps[n+1] = NULL;
612
613
0
        return 1;
614
0
}