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