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