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