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