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