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