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