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