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