]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-error.c
Merge pull request #2329 from ssahani/tunnel
[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 "alloc-util.h"
32 #include "bus-error.h"
33 #include "errno-list.h"
34 #include "string-util.h"
35 #include "util.h"
36
37 BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO),
44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL),
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET),
53 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE),
54 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET),
55 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL),
56 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT),
57 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST),
58 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR),
59 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR),
60 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR),
61 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR),
62 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS),
63 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH),
64 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL),
65 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG),
66 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT),
67 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL),
68 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL),
69 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT),
70 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH),
71 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY),
72 SD_BUS_ERROR_MAP_END
73 };
74
75 /* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section.
76 * Hide them; for currently unknown reasons they get exported to the shared libries
77 * even without being listed in the sym file. */
78 extern const sd_bus_error_map __start_BUS_ERROR_MAP[] _hidden_;
79 extern const sd_bus_error_map __stop_BUS_ERROR_MAP[] _hidden_;
80
81 /* Additional maps registered with sd_bus_error_add_map() are in this
82 * NULL terminated array */
83 static const sd_bus_error_map **additional_error_maps = NULL;
84
85 static int bus_error_name_to_errno(const char *name) {
86 const sd_bus_error_map **map, *m;
87 const char *p;
88 int r;
89
90 if (!name)
91 return EINVAL;
92
93 p = startswith(name, "System.Error.");
94 if (p) {
95 r = errno_from_name(p);
96 if (r < 0)
97 return EIO;
98
99 return r;
100 }
101
102 if (additional_error_maps)
103 for (map = additional_error_maps; *map; map++)
104 for (m = *map;; m++) {
105 /* For additional error maps the end marker is actually the end marker */
106 if (m->code == BUS_ERROR_MAP_END_MARKER)
107 break;
108
109 if (streq(m->name, name))
110 return m->code;
111 }
112
113 m = __start_BUS_ERROR_MAP;
114 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 next 8 byte
119 * boundary, which is the selected alignment for the
120 * arrays. */
121 if (m->code == BUS_ERROR_MAP_END_MARKER) {
122 m = ALIGN8_PTR(m+1);
123 continue;
124 }
125
126 if (streq(m->name, name))
127 return m->code;
128
129 m++;
130 }
131
132 return EIO;
133 }
134
135 static sd_bus_error errno_to_bus_error_const(int error) {
136
137 if (error < 0)
138 error = -error;
139
140 switch (error) {
141
142 case ENOMEM:
143 return BUS_ERROR_OOM;
144
145 case EPERM:
146 case EACCES:
147 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
148
149 case EINVAL:
150 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
151
152 case ESRCH:
153 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
154
155 case ENOENT:
156 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
157
158 case EEXIST:
159 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
160
161 case ETIMEDOUT:
162 case ETIME:
163 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
164
165 case EIO:
166 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 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
172
173 case EOPNOTSUPP:
174 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
175
176 case EADDRNOTAVAIL:
177 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
178
179 case ENOBUFS:
180 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
181
182 case EADDRINUSE:
183 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
184
185 case EBADMSG:
186 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
187 }
188
189 return SD_BUS_ERROR_NULL;
190 }
191
192 static int errno_to_bus_error_name_new(int error, char **ret) {
193 const char *name;
194 char *n;
195
196 if (error < 0)
197 error = -error;
198
199 name = errno_to_name(error);
200 if (!name)
201 return 0;
202
203 n = strappend("System.Error.", name);
204 if (!n)
205 return -ENOMEM;
206
207 *ret = n;
208 return 1;
209 }
210
211 bool bus_error_is_dirty(sd_bus_error *e) {
212 if (!e)
213 return false;
214
215 return e->name || e->message || e->_need_free != 0;
216 }
217
218 _public_ void sd_bus_error_free(sd_bus_error *e) {
219 if (!e)
220 return;
221
222 if (e->_need_free > 0) {
223 free((void*) e->name);
224 free((void*) e->message);
225 }
226
227 e->name = e->message = NULL;
228 e->_need_free = 0;
229 }
230
231 _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
232
233 if (!name)
234 return 0;
235 if (!e)
236 goto finish;
237
238 assert_return(!bus_error_is_dirty(e), -EINVAL);
239
240 e->name = strdup(name);
241 if (!e->name) {
242 *e = BUS_ERROR_OOM;
243 return -ENOMEM;
244 }
245
246 if (message)
247 e->message = strdup(message);
248
249 e->_need_free = 1;
250
251 finish:
252 return -bus_error_name_to_errno(name);
253 }
254
255 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
256
257 if (!name)
258 return 0;
259
260 if (e) {
261 assert_return(!bus_error_is_dirty(e), -EINVAL);
262
263 e->name = strdup(name);
264 if (!e->name) {
265 *e = BUS_ERROR_OOM;
266 return -ENOMEM;
267 }
268
269 /* If we hit OOM on formatting the pretty message, we ignore
270 * this, since we at least managed to write the error name */
271 if (format)
272 (void) vasprintf((char**) &e->message, format, ap);
273
274 e->_need_free = 1;
275 }
276
277 return -bus_error_name_to_errno(name);
278 }
279
280 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
281
282 if (format) {
283 int r;
284 va_list ap;
285
286 va_start(ap, format);
287 r = bus_error_setfv(e, name, format, ap);
288 va_end(ap);
289
290 return r;
291 }
292
293 return sd_bus_error_set(e, name, NULL);
294 }
295
296 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
297
298 if (!sd_bus_error_is_set(e))
299 return 0;
300 if (!dest)
301 goto finish;
302
303 assert_return(!bus_error_is_dirty(dest), -EINVAL);
304
305 /*
306 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
307 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
308 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
309 */
310
311 if (e->_need_free == 0)
312 *dest = *e;
313 else {
314 dest->name = strdup(e->name);
315 if (!dest->name) {
316 *dest = BUS_ERROR_OOM;
317 return -ENOMEM;
318 }
319
320 if (e->message)
321 dest->message = strdup(e->message);
322
323 dest->_need_free = 1;
324 }
325
326 finish:
327 return -bus_error_name_to_errno(e->name);
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 = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2);
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 }