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