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