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