]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-error.c
66a09a35f3499c0aafb8e7bf94d63ec3c4f2a1aa
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-error.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <stdarg.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "sd-bus.h"
29
30 #include "alloc-util.h"
31 #include "bus-error.h"
32 #include "errno-list.h"
33 #include "string-util.h"
34 #include "util.h"
35
36 BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
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),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
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
72 };
73
74 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
75 extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
76 extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
77
78 /* Additional maps registered with sd_bus_error_add_map() are in this
79 * NULL terminated array */
80 static const sd_bus_error_map **additional_error_maps = NULL;
81
82 static int bus_error_name_to_errno(const char *name) {
83 const sd_bus_error_map **map, *m;
84 const char *p;
85 int r;
86
87 if (!name)
88 return EINVAL;
89
90 p = startswith(name, "System.Error.");
91 if (p) {
92 r = errno_from_name(p);
93 if (r < 0)
94 return EIO;
95
96 return r;
97 }
98
99 if (additional_error_maps)
100 for (map = additional_error_maps; *map; map++)
101 for (m = *map;; m++) {
102 /* For additional error maps the end marker is actually the end marker */
103 if (m->code == BUS_ERROR_MAP_END_MARKER)
104 break;
105
106 if (streq(m->name, name))
107 return m->code;
108 }
109
110 m = __start_BUS_ERROR_MAP;
111 #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
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 next 8 byte
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 #endif
130
131 return EIO;
132 }
133
134 static sd_bus_error errno_to_bus_error_const(int error) {
135
136 if (error < 0)
137 error = -error;
138
139 switch (error) {
140
141 case ENOMEM:
142 return BUS_ERROR_OOM;
143
144 case EPERM:
145 case EACCES:
146 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
147
148 case EINVAL:
149 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
150
151 case ESRCH:
152 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
153
154 case ENOENT:
155 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
156
157 case EEXIST:
158 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
159
160 case ETIMEDOUT:
161 case ETIME:
162 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
163
164 case EIO:
165 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
166
167 case ENETRESET:
168 case ECONNABORTED:
169 case ECONNRESET:
170 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
171
172 case EOPNOTSUPP:
173 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
174
175 case EADDRNOTAVAIL:
176 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
177
178 case ENOBUFS:
179 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
180
181 case EADDRINUSE:
182 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
183
184 case EBADMSG:
185 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
186 }
187
188 return SD_BUS_ERROR_NULL;
189 }
190
191 static int errno_to_bus_error_name_new(int error, char **ret) {
192 const char *name;
193 char *n;
194
195 if (error < 0)
196 error = -error;
197
198 name = errno_to_name(error);
199 if (!name)
200 return 0;
201
202 n = strappend("System.Error.", name);
203 if (!n)
204 return -ENOMEM;
205
206 *ret = n;
207 return 1;
208 }
209
210 bool bus_error_is_dirty(sd_bus_error *e) {
211 if (!e)
212 return false;
213
214 return e->name || e->message || e->_need_free != 0;
215 }
216
217 _public_ void sd_bus_error_free(sd_bus_error *e) {
218 if (!e)
219 return;
220
221 if (e->_need_free > 0) {
222 free((void*) e->name);
223 free((void*) e->message);
224 }
225
226 e->name = e->message = NULL;
227 e->_need_free = 0;
228 }
229
230 _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
231
232 if (!name)
233 return 0;
234 if (!e)
235 goto finish;
236
237 assert_return(!bus_error_is_dirty(e), -EINVAL);
238
239 e->name = strdup(name);
240 if (!e->name) {
241 *e = BUS_ERROR_OOM;
242 return -ENOMEM;
243 }
244
245 if (message)
246 e->message = strdup(message);
247
248 e->_need_free = 1;
249
250 finish:
251 return -bus_error_name_to_errno(name);
252 }
253
254 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
255
256 if (!name)
257 return 0;
258
259 if (e) {
260 assert_return(!bus_error_is_dirty(e), -EINVAL);
261
262 e->name = strdup(name);
263 if (!e->name) {
264 *e = BUS_ERROR_OOM;
265 return -ENOMEM;
266 }
267
268 /* If we hit OOM on formatting the pretty message, we ignore
269 * this, since we at least managed to write the error name */
270 if (format)
271 (void) vasprintf((char**) &e->message, format, ap);
272
273 e->_need_free = 1;
274 }
275
276 return -bus_error_name_to_errno(name);
277 }
278
279 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
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
295 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
296
297 if (!sd_bus_error_is_set(e))
298 return 0;
299 if (!dest)
300 goto finish;
301
302 assert_return(!bus_error_is_dirty(dest), -EINVAL);
303
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)
311 *dest = *e;
312 else {
313 dest->name = strdup(e->name);
314 if (!dest->name) {
315 *dest = BUS_ERROR_OOM;
316 return -ENOMEM;
317 }
318
319 if (e->message)
320 dest->message = strdup(e->message);
321
322 dest->_need_free = 1;
323 }
324
325 finish:
326 return -bus_error_name_to_errno(e->name);
327 }
328
329 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
330 if (!name)
331 return 0;
332 if (!e)
333 goto finish;
334
335 assert_return(!bus_error_is_dirty(e), -EINVAL);
336
337 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
338
339 finish:
340 return -bus_error_name_to_errno(name);
341 }
342
343 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
344 if (!e)
345 return 0;
346
347 return !!e->name;
348 }
349
350 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
351 if (!e)
352 return 0;
353
354 return streq_ptr(e->name, name);
355 }
356
357 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
358 if (!e)
359 return 0;
360
361 if (!e->name)
362 return 0;
363
364 return bus_error_name_to_errno(e->name);
365 }
366
367 static void bus_error_strerror(sd_bus_error *e, int error) {
368 size_t k = 64;
369 char *m;
370
371 assert(e);
372
373 for (;;) {
374 char *x;
375
376 m = new(char, k);
377 if (!m)
378 return;
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 }
387
388 if (errno) {
389 free(m);
390 return;
391 }
392
393 if (x == m) {
394 if (e->_need_free > 0) {
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
409 e->_need_free = 1;
410 e->name = t;
411 e->message = x;
412 }
413 } else {
414 free(m);
415
416 if (e->_need_free > 0) {
417 char *t;
418
419 /* Error is dynamic, let's hence make the message also dynamic */
420 t = strdup(x);
421 if (!t)
422 return;
423
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 }
431
432 return;
433 }
434 }
435
436 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
437
438 if (error < 0)
439 error = -error;
440
441 if (!e)
442 return -error;
443 if (error == 0)
444 return -error;
445
446 assert_return(!bus_error_is_dirty(e), -EINVAL);
447
448 /* First, try a const translation */
449 *e = errno_to_bus_error_const(error);
450
451 if (!sd_bus_error_is_set(e)) {
452 int k;
453
454 /* If that didn't work, try a dynamic one. */
455
456 k = errno_to_bus_error_name_new(error, (char**) &e->name);
457 if (k > 0)
458 e->_need_free = 1;
459 else if (k < 0) {
460 *e = BUS_ERROR_OOM;
461 return -error;
462 } else
463 *e = BUS_ERROR_FAILED;
464 }
465
466 /* Now, fill in the message from strerror() if we can */
467 bus_error_strerror(e, error);
468 return -error;
469 }
470
471 _public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
472 PROTECT_ERRNO;
473 int r;
474
475 if (error < 0)
476 error = -error;
477
478 if (!e)
479 return -error;
480 if (error == 0)
481 return 0;
482
483 assert_return(!bus_error_is_dirty(e), -EINVAL);
484
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)
495 e->_need_free = 1;
496 else if (k < 0) {
497 *e = BUS_ERROR_OOM;
498 return -ENOMEM;
499 } else
500 *e = BUS_ERROR_FAILED;
501 }
502
503 if (format) {
504 char *m;
505
506 /* Then, let's try to fill in the supplied message */
507
508 errno = error; /* Make sure that %m resolves to the specified error */
509 r = vasprintf(&m, format, ap);
510 if (r >= 0) {
511
512 if (e->_need_free <= 0) {
513 char *t;
514
515 t = strdup(e->name);
516 if (t) {
517 e->_need_free = 1;
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 }
529 }
530 }
531
532 /* If that didn't work, use strerror() for the message */
533 bus_error_strerror(e, error);
534 return -error;
535 }
536
537 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
538 int r;
539
540 if (error < 0)
541 error = -error;
542
543 if (!e)
544 return -error;
545 if (error == 0)
546 return 0;
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 = sd_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);
561 }
562
563 const char *bus_error_message(const sd_bus_error *e, int error) {
564
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
575 if (error < 0)
576 error = -error;
577
578 return strerror(error);
579 }
580
581 static bool map_ok(const sd_bus_error_map *map) {
582 for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
583 if (!map->name || map->code <=0)
584 return false;
585 return true;
586 }
587
588 _public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
589 const sd_bus_error_map **maps = NULL;
590 unsigned n = 0;
591
592 assert_return(map, -EINVAL);
593 assert_return(map_ok(map), -EINVAL);
594
595 if (additional_error_maps)
596 for (; additional_error_maps[n] != NULL; n++)
597 if (additional_error_maps[n] == map)
598 return 0;
599
600 maps = reallocarray(additional_error_maps, n + 2, sizeof(struct sd_bus_error_map*));
601 if (!maps)
602 return -ENOMEM;
603
604 maps[n] = map;
605 maps[n+1] = NULL;
606
607 additional_error_maps = maps;
608 return 1;
609 }