chiark / gitweb /
404eaa3c895bbb65aa8d3071e3276b02219d0223
[elogind.git] / src / libelogind / 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         }
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 }