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