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