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