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