chiark / gitweb /
64a5a972aef719637fdf9e527eb094ae3797bdea
[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 <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 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         if (!name)
89                 return EINVAL;
90
91         p = startswith(name, "System.Error.");
92         if (p) {
93                 r = errno_from_name(p);
94                 if (r <= 0)
95                         return EIO;
96
97                 return r;
98         }
99
100         if (additional_error_maps) {
101                 for (map = additional_error_maps; *map; map++) {
102                         for (m = *map;; m++) {
103                                 /* For additional error maps the end marker is actually the end marker */
104                                 if (m->code == BUS_ERROR_MAP_END_MARKER)
105                                         break;
106
107                                 if (streq(m->name, name))
108                                         return m->code;
109                         }
110                 }
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 netx 8byte
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         if (!e)
260                 goto finish;
261
262         assert_return(!bus_error_is_dirty(e), -EINVAL);
263
264         e->name = strdup(name);
265         if (!e->name) {
266                 *e = BUS_ERROR_OOM;
267                 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         if (format)
273                 (void) vasprintf((char**) &e->message, format, ap);
274
275         e->_need_free = 1;
276
277 finish:
278         return -bus_error_name_to_errno(name);
279 }
280
281 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
282
283         if (format) {
284                 int r;
285                 va_list ap;
286
287                 va_start(ap, format);
288                 r = bus_error_setfv(e, name, format, ap);
289                 va_end(ap);
290
291                 return r;
292         }
293
294         return sd_bus_error_set(e, name, NULL);
295 }
296
297 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
298
299         if (!sd_bus_error_is_set(e))
300                 return 0;
301         if (!dest)
302                 goto finish;
303
304         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         if (e->_need_free == 0)
313                 *dest = *e;
314         else {
315                 dest->name = strdup(e->name);
316                 if (!dest->name) {
317                         *dest = BUS_ERROR_OOM;
318                         return -ENOMEM;
319                 }
320
321                 if (e->message)
322                         dest->message = strdup(e->message);
323
324                 dest->_need_free = 1;
325         }
326
327 finish:
328         return -bus_error_name_to_errno(e->name);
329 }
330
331 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
332         if (!name)
333                 return 0;
334         if (!e)
335                 goto finish;
336
337         assert_return(!bus_error_is_dirty(e), -EINVAL);
338
339         *e = SD_BUS_ERROR_MAKE_CONST(name, message);
340
341 finish:
342         return -bus_error_name_to_errno(name);
343 }
344
345 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
346         if (!e)
347                 return 0;
348
349         return !!e->name;
350 }
351
352 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
353         if (!e)
354                 return 0;
355
356         return streq_ptr(e->name, name);
357 }
358
359 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
360         if (!e)
361                 return 0;
362
363         if (!e->name)
364                 return 0;
365
366         return bus_error_name_to_errno(e->name);
367 }
368
369 static void bus_error_strerror(sd_bus_error *e, int error) {
370         size_t k = 64;
371         char *m;
372
373         assert(e);
374
375         for (;;) {
376                 char *x;
377
378                 m = new(char, k);
379                 if (!m)
380                         return;
381
382                 errno = 0;
383                 x = strerror_r(error, m, k);
384                 if (errno == ERANGE || strlen(x) >= k - 1) {
385                         free(m);
386                         k *= 2;
387                         continue;
388                 }
389
390                 if (errno) {
391                         free(m);
392                         return;
393                 }
394
395                 if (x == m) {
396                         if (e->_need_free > 0) {
397                                 /* Error is already dynamic, let's just update the message */
398                                 free((char*) e->message);
399                                 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                                 t = strdup(e->name);
406                                 if (!t) {
407                                         free(m);
408                                         return;
409                                 }
410
411                                 e->_need_free = 1;
412                                 e->name = t;
413                                 e->message = x;
414                         }
415                 } else {
416                         free(m);
417
418                         if (e->_need_free > 0) {
419                                 char *t;
420
421                                 /* Error is dynamic, let's hence make the message also dynamic */
422                                 t = strdup(x);
423                                 if (!t)
424                                         return;
425
426                                 free((char*) e->message);
427                                 e->message = t;
428                         } else {
429                                 /* Error is const, hence we can just override */
430                                 e->message = x;
431                         }
432                 }
433
434                 return;
435         }
436 }
437
438 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
439
440         if (error < 0)
441                 error = -error;
442
443         if (!e)
444                 return -error;
445         if (error == 0)
446                 return -error;
447
448         assert_return(!bus_error_is_dirty(e), -EINVAL);
449
450         /* First, try a const translation */
451         *e = errno_to_bus_error_const(error);
452
453         if (!sd_bus_error_is_set(e)) {
454                 int k;
455
456                 /* If that didn't work, try a dynamic one. */
457
458                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
459                 if (k > 0)
460                         e->_need_free = 1;
461                 else if (k < 0) {
462                         *e = BUS_ERROR_OOM;
463                         return -error;
464                 } else
465                         *e = BUS_ERROR_FAILED;
466         }
467
468         /* Now, fill in the message from strerror() if we can */
469         bus_error_strerror(e, error);
470         return -error;
471 }
472
473 _public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
474         PROTECT_ERRNO;
475         int r;
476
477         if (error < 0)
478                 error = -error;
479
480         if (!e)
481                 return -error;
482         if (error == 0)
483                 return 0;
484
485         assert_return(!bus_error_is_dirty(e), -EINVAL);
486
487         /* First, try a const translation */
488         *e = errno_to_bus_error_const(error);
489
490         if (!sd_bus_error_is_set(e)) {
491                 int k;
492
493                 /* If that didn't work, try a dynamic one */
494
495                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
496                 if (k > 0)
497                         e->_need_free = 1;
498                 else if (k < 0) {
499                         *e = BUS_ERROR_OOM;
500                         return -ENOMEM;
501                 } else
502                         *e = BUS_ERROR_FAILED;
503         }
504
505         if (format) {
506                 char *m;
507
508                 /* Then, let's try to fill in the supplied message */
509
510                 errno = error; /* Make sure that %m resolves to the specified error */
511                 r = vasprintf(&m, format, ap);
512                 if (r >= 0) {
513
514                         if (e->_need_free <= 0) {
515                                 char *t;
516
517                                 t = strdup(e->name);
518                                 if (t) {
519                                         e->_need_free = 1;
520                                         e->name = t;
521                                         e->message = m;
522                                         return -error;
523                                 }
524
525                                 free(m);
526                         } else {
527                                 free((char*) e->message);
528                                 e->message = m;
529                                 return -error;
530                         }
531                 }
532         }
533
534         /* If that didn't work, use strerror() for the message */
535         bus_error_strerror(e, error);
536         return -error;
537 }
538
539 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
540         int r;
541
542         if (error < 0)
543                 error = -error;
544
545         if (!e)
546                 return -error;
547         if (error == 0)
548                 return 0;
549
550         assert_return(!bus_error_is_dirty(e), -EINVAL);
551
552         if (format) {
553                 va_list ap;
554
555                 va_start(ap, format);
556                 r = sd_bus_error_set_errnofv(e, error, format, ap);
557                 va_end(ap);
558
559                 return r;
560         }
561
562         return sd_bus_error_set_errno(e, error);
563 }
564
565 const char *bus_error_message(const sd_bus_error *e, int error) {
566
567         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                 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
571                         return "Access denied";
572
573                 if (e->message)
574                         return e->message;
575         }
576
577         if (error < 0)
578                 error = -error;
579
580         return strerror(error);
581 }
582
583 _public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
584         const sd_bus_error_map **maps = NULL;
585         unsigned n = 0;
586
587         assert_return(map, -EINVAL);
588
589         if (additional_error_maps) {
590                 for (;; n++) {
591                         if (additional_error_maps[n] == NULL)
592                                 break;
593
594                         if (additional_error_maps[n] == map)
595                                 return 0;
596                 }
597         }
598
599         maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2);
600         if (!maps)
601                 return -ENOMEM;
602
603
604         maps[n] = map;
605         maps[n+1] = NULL;
606
607         additional_error_maps = maps;
608         return 1;
609 }