]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-error.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-error.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <errno.h>
4 #include <stdarg.h>
5 #include <stdbool.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "sd-bus.h"
11
12 #include "alloc-util.h"
13 #include "bus-error.h"
14 #include "errno-list.h"
15 #include "string-util.h"
16 #include "util.h"
17
18 BUS_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),
26 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
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
56 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
57 extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[];
58 extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[];
59
60 /* Additional maps registered with sd_bus_error_add_map() are in this
61 * NULL terminated array */
62 static const sd_bus_error_map **additional_error_maps = NULL;
63
64 static int bus_error_name_to_errno(const char *name) {
65 const sd_bus_error_map **map, *m;
66 const char *p;
67 int r;
68
69 if (!name)
70 return EINVAL;
71
72 p = startswith(name, "System.Error.");
73 if (p) {
74 r = errno_from_name(p);
75 if (r < 0)
76 return EIO;
77
78 return r;
79 }
80
81 if (additional_error_maps)
82 for (map = additional_error_maps; *map; map++)
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 }
91
92 m = ALIGN_TO_PTR(__start_SYSTEMD_BUS_ERROR_MAP, sizeof(void*));
93 while (m < __stop_SYSTEMD_BUS_ERROR_MAP) {
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
97 * over it, but realign the pointer to the next 8 byte
98 * boundary, which is the selected alignment for the
99 * arrays. */
100 if (m->code == BUS_ERROR_MAP_END_MARKER) {
101 m = ALIGN_TO_PTR(m + 1, sizeof(void*));
102 continue;
103 }
104
105 if (streq(m->name, name))
106 return m->code;
107
108 m++;
109 }
110
111 return EIO;
112 }
113
114 static 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
152 case EOPNOTSUPP:
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
171 static 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
190 bool 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
206 *e = SD_BUS_ERROR_NULL;
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
229 finish:
230 return -bus_error_name_to_errno(name);
231 }
232
233 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
234
235 if (!name)
236 return 0;
237
238 if (e) {
239 assert_return(!bus_error_is_dirty(e), -EINVAL);
240
241 e->name = strdup(name);
242 if (!e->name) {
243 *e = BUS_ERROR_OOM;
244 return -ENOMEM;
245 }
246
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);
251
252 e->_need_free = 1;
253 }
254
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
304 finish:
305 return -bus_error_name_to_errno(e->name);
306 }
307
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
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
340 finish:
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
368 static 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
389 if (errno) {
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
472 _public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
473 PROTECT_ERRNO;
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
507 /* Then, let's try to fill in the supplied message */
508
509 errno = error; /* Make sure that %m resolves to the specified error */
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);
555 r = sd_bus_error_set_errnofv(e, error, format, ap);
556 va_end(ap);
557
558 return r;
559 }
560
561 return sd_bus_error_set_errno(e, error);
562 }
563
564 const char *bus_error_message(const sd_bus_error *e, int error) {
565
566 if (e) {
567 /* Sometimes, the D-Bus server is a little bit too verbose with
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 }
581
582 static 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
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);
594 assert_return(map_ok(map), -EINVAL);
595
596 if (additional_error_maps)
597 for (; additional_error_maps[n] != NULL; n++)
598 if (additional_error_maps[n] == map)
599 return 0;
600
601 maps = reallocarray(additional_error_maps, n + 2, sizeof(struct sd_bus_error_map*));
602 if (!maps)
603 return -ENOMEM;
604
605 maps[n] = map;
606 maps[n+1] = NULL;
607
608 additional_error_maps = maps;
609 return 1;
610 }