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