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