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