chiark / gitweb /
3bf0c5d3e40dbeeea43f20fa52a419be4e12574c
[elogind.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 _public_ int sd_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 = sd_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 }