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