]>
Commit | Line | Data |
---|---|---|
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" | |
30 | ||
31 | #include "sd-bus.h" | |
32 | #include "bus-error.h" | |
33 | ||
fbfa72b0 LP |
34 | bool bus_error_is_dirty(sd_bus_error *e) { |
35 | if (!e) | |
36 | return 0; | |
37 | ||
38 | return e->name || e->message || e->need_free; | |
39 | } | |
40 | ||
d9f644e2 | 41 | _public_ void sd_bus_error_free(sd_bus_error *e) { |
de1c301e LP |
42 | if (!e) |
43 | return; | |
44 | ||
45 | if (e->need_free) { | |
46 | free((void*) e->name); | |
47 | free((void*) e->message); | |
48 | } | |
49 | ||
50 | e->name = e->message = NULL; | |
51 | e->need_free = false; | |
52 | } | |
53 | ||
d9f644e2 | 54 | _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) { |
29ddb38f LP |
55 | char *n, *m = NULL; |
56 | ||
57 | if (!e) | |
58 | return 0; | |
40ca29a1 LP |
59 | |
60 | assert_return(!bus_error_is_dirty(e), -EINVAL); | |
61 | assert_return(name, -EINVAL); | |
29ddb38f LP |
62 | |
63 | n = strdup(name); | |
64 | if (!n) | |
65 | return -ENOMEM; | |
66 | ||
67 | if (message) { | |
68 | m = strdup(message); | |
69 | if (!m) | |
70 | return -ENOMEM; | |
71 | } | |
72 | ||
73 | e->name = n; | |
74 | e->message = m; | |
75 | e->need_free = true; | |
76 | ||
80c19740 | 77 | return sd_bus_error_get_errno(e); |
29ddb38f LP |
78 | } |
79 | ||
40ca29a1 | 80 | int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) { |
de1c301e | 81 | char *n, *m = NULL; |
de1c301e LP |
82 | int r; |
83 | ||
84 | if (!e) | |
85 | return 0; | |
40ca29a1 LP |
86 | |
87 | assert_return(!bus_error_is_dirty(e), -EINVAL); | |
88 | assert_return(name, -EINVAL); | |
de1c301e LP |
89 | |
90 | n = strdup(name); | |
91 | if (!n) | |
92 | return -ENOMEM; | |
93 | ||
94 | if (format) { | |
de1c301e | 95 | r = vasprintf(&m, format, ap); |
de1c301e LP |
96 | if (r < 0) { |
97 | free(n); | |
98 | return -ENOMEM; | |
99 | } | |
100 | } | |
101 | ||
102 | e->name = n; | |
103 | e->message = m; | |
104 | e->need_free = true; | |
105 | ||
80c19740 | 106 | return sd_bus_error_get_errno(e); |
de1c301e LP |
107 | } |
108 | ||
d9f644e2 | 109 | _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) { |
40ca29a1 LP |
110 | |
111 | if (format) { | |
112 | int r; | |
113 | va_list ap; | |
114 | ||
115 | va_start(ap, format); | |
116 | r = bus_error_setfv(e, name, format, ap); | |
117 | va_end(ap); | |
118 | ||
119 | return r; | |
120 | } | |
121 | ||
122 | return sd_bus_error_set(e, name, NULL); | |
123 | } | |
124 | ||
d9f644e2 | 125 | _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) { |
89ffcd2a LP |
126 | char *x, *y = NULL; |
127 | ||
de1c301e LP |
128 | if (!dest) |
129 | return 0; | |
de1c301e LP |
130 | if (!sd_bus_error_is_set(e)) |
131 | return 0; | |
132 | ||
40ca29a1 LP |
133 | assert_return(!bus_error_is_dirty(dest), -EINVAL); |
134 | ||
89ffcd2a LP |
135 | x = strdup(e->name); |
136 | if (!x) | |
137 | return -ENOMEM; | |
de1c301e | 138 | |
89ffcd2a LP |
139 | if (e->message) { |
140 | y = strdup(e->message); | |
141 | if (!y) { | |
142 | free(x); | |
de1c301e | 143 | return -ENOMEM; |
de1c301e | 144 | } |
89ffcd2a | 145 | } |
de1c301e | 146 | |
89ffcd2a LP |
147 | dest->name = x; |
148 | dest->message = y; | |
149 | dest->need_free = true; | |
80c19740 | 150 | return sd_bus_error_get_errno(e); |
de1c301e LP |
151 | } |
152 | ||
d9f644e2 | 153 | _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) { |
de1c301e | 154 | if (!e) |
40ca29a1 LP |
155 | return 0; |
156 | ||
157 | assert_return(!bus_error_is_dirty(e), -EINVAL); | |
158 | assert_return(name, -EINVAL); | |
de1c301e | 159 | |
29ddb38f | 160 | *e = SD_BUS_ERROR_MAKE(name, message); |
80c19740 | 161 | return sd_bus_error_get_errno(e); |
de1c301e LP |
162 | } |
163 | ||
d9f644e2 | 164 | _public_ int sd_bus_error_is_set(const sd_bus_error *e) { |
de1c301e LP |
165 | if (!e) |
166 | return 0; | |
167 | ||
fbfa72b0 | 168 | return !!e->name; |
de1c301e LP |
169 | } |
170 | ||
d9f644e2 | 171 | _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) { |
de1c301e LP |
172 | if (!e) |
173 | return 0; | |
174 | ||
175 | return streq_ptr(e->name, name); | |
176 | } | |
177 | ||
d9f644e2 | 178 | _public_ int sd_bus_error_get_errno(const sd_bus_error* e) { |
de1c301e LP |
179 | |
180 | /* Better replce this with a gperf table */ | |
181 | ||
eb01ba5d | 182 | if (!e) |
40ca29a1 | 183 | return EIO; |
eb01ba5d | 184 | |
de1c301e | 185 | if (!e->name) |
40ca29a1 | 186 | return EIO; |
de1c301e | 187 | |
40ca29a1 LP |
188 | if (streq(e->name, SD_BUS_ERROR_NO_MEMORY)) |
189 | return ENOMEM; | |
190 | ||
191 | if (streq(e->name, SD_BUS_ERROR_SERVICE_UNKNOWN)) | |
192 | return EHOSTUNREACH; | |
193 | ||
194 | if (streq(e->name, SD_BUS_ERROR_NAME_HAS_NO_OWNER)) | |
195 | return ENXIO; | |
196 | ||
197 | if (streq(e->name, SD_BUS_ERROR_NO_REPLY) || | |
198 | streq(e->name, SD_BUS_ERROR_TIMEOUT) || | |
199 | streq(e->name, "org.freedesktop.DBus.Error.TimedOut")) | |
200 | return ETIMEDOUT; | |
201 | ||
202 | if (streq(e->name, SD_BUS_ERROR_IO_ERROR)) | |
203 | return EIO; | |
204 | ||
205 | if (streq(e->name, SD_BUS_ERROR_BAD_ADDRESS)) | |
206 | return EADDRNOTAVAIL; | |
de1c301e | 207 | |
40ca29a1 LP |
208 | if (streq(e->name, SD_BUS_ERROR_NOT_SUPPORTED)) |
209 | return ENOTSUP; | |
de1c301e | 210 | |
40ca29a1 LP |
211 | if (streq(e->name, SD_BUS_ERROR_LIMITS_EXCEEDED)) |
212 | return ENOBUFS; | |
eb01ba5d | 213 | |
40ca29a1 LP |
214 | if (streq(e->name, SD_BUS_ERROR_ACCESS_DENIED) || |
215 | streq(e->name, SD_BUS_ERROR_AUTH_FAILED)) | |
216 | return EACCES; | |
eb01ba5d | 217 | |
40ca29a1 LP |
218 | if (streq(e->name, SD_BUS_ERROR_NO_SERVER)) |
219 | return EHOSTDOWN; | |
eb01ba5d | 220 | |
40ca29a1 LP |
221 | if (streq(e->name, SD_BUS_ERROR_NO_NETWORK)) |
222 | return ENONET; | |
eb01ba5d | 223 | |
40ca29a1 LP |
224 | if (streq(e->name, SD_BUS_ERROR_ADDRESS_IN_USE)) |
225 | return EADDRINUSE; | |
eb01ba5d | 226 | |
40ca29a1 LP |
227 | if (streq(e->name, SD_BUS_ERROR_DISCONNECTED)) |
228 | return ECONNRESET; | |
eb01ba5d | 229 | |
40ca29a1 LP |
230 | if (streq(e->name, SD_BUS_ERROR_INVALID_ARGS) || |
231 | streq(e->name, SD_BUS_ERROR_INVALID_SIGNATURE) || | |
232 | streq(e->name, "org.freedesktop.DBus.Error.MatchRuleInvalid") || | |
233 | streq(e->name, "org.freedesktop.DBus.Error.InvalidFileContent")) | |
234 | return EINVAL; | |
eb01ba5d | 235 | |
40ca29a1 LP |
236 | if (streq(e->name, SD_BUS_ERROR_FILE_NOT_FOUND) || |
237 | streq(e->name, "org.freedesktop.DBus.Error.MatchRuleNotFound")) | |
238 | return ENOENT; | |
eb01ba5d | 239 | |
40ca29a1 LP |
240 | if (streq(e->name, SD_BUS_ERROR_FILE_EXISTS)) |
241 | return EEXIST; | |
242 | ||
243 | if (streq(e->name, SD_BUS_ERROR_UNKNOWN_METHOD) || | |
244 | streq(e->name, SD_BUS_ERROR_UNKNOWN_OBJECT) || | |
245 | streq(e->name, SD_BUS_ERROR_UNKNOWN_INTERFACE) || | |
246 | streq(e->name, SD_BUS_ERROR_UNKNOWN_PROPERTY)) | |
247 | return EBADR; | |
248 | ||
249 | if (streq(e->name, SD_BUS_ERROR_PROPERTY_READ_ONLY)) | |
250 | return EROFS; | |
251 | ||
252 | if (streq(e->name, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN) || | |
253 | streq(e->name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) | |
254 | return ESRCH; | |
255 | ||
256 | if (streq(e->name, SD_BUS_ERROR_INCONSISTENT_MESSAGE)) | |
257 | return EBADMSG; | |
258 | ||
259 | if (streq(e->name, "org.freedesktop.DBus.Error.ObjectPathInUse")) | |
260 | return EBUSY; | |
261 | ||
262 | return EIO; | |
de1c301e LP |
263 | } |
264 | ||
40ca29a1 LP |
265 | static int bus_error_set_strerror_or_const(sd_bus_error *e, const char *name, int error, const char *fallback) { |
266 | size_t k = 64; | |
267 | char *n = NULL, *m = NULL; | |
268 | ||
269 | if (error < 0) | |
270 | error = -error; | |
271 | ||
de1c301e | 272 | if (!e) |
40ca29a1 | 273 | return -error; |
de1c301e | 274 | |
40ca29a1 LP |
275 | assert_return(!bus_error_is_dirty(e), -EINVAL); |
276 | assert_return(name, -EINVAL); | |
eb01ba5d | 277 | |
40ca29a1 LP |
278 | for (;;) { |
279 | char *x; | |
eb01ba5d | 280 | |
40ca29a1 LP |
281 | m = new(char, k); |
282 | if (!m) | |
283 | goto use_fallback; | |
284 | ||
285 | errno = 0; | |
286 | x = strerror_r(error, m, k); | |
287 | if (errno == ERANGE || strlen(x) >= k - 1) { | |
288 | free(m); | |
289 | k *= 2; | |
290 | continue; | |
291 | } | |
eb01ba5d | 292 | |
40ca29a1 LP |
293 | if (!x || errno) { |
294 | free(m); | |
295 | goto use_fallback; | |
296 | } | |
eb01ba5d | 297 | |
eb01ba5d | 298 | |
40ca29a1 LP |
299 | if (x != m) { |
300 | free(m); | |
301 | sd_bus_error_set_const(e, name, x); | |
302 | return -error; | |
303 | } | |
eb01ba5d | 304 | |
eb01ba5d | 305 | break; |
40ca29a1 | 306 | } |
eb01ba5d | 307 | |
eb01ba5d | 308 | |
40ca29a1 LP |
309 | n = strdup(name); |
310 | if (!n) { | |
311 | free(m); | |
312 | goto use_fallback; | |
313 | } | |
eb01ba5d | 314 | |
40ca29a1 LP |
315 | e->name = n; |
316 | e->message = m; | |
317 | e->need_free = true; | |
eb01ba5d | 318 | |
40ca29a1 LP |
319 | return -error; |
320 | ||
321 | use_fallback: | |
322 | sd_bus_error_set_const(e, name, fallback); | |
323 | return -error; | |
324 | } | |
325 | ||
326 | static sd_bus_error map_from_errno(int error) { | |
327 | ||
328 | if (error < 0) | |
329 | error = -error; | |
330 | ||
331 | switch (error) { | |
332 | ||
333 | case ENOMEM: | |
334 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NO_NETWORK, "Out of memory"); | |
335 | ||
336 | case EPERM: | |
337 | case EACCES: | |
338 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ACCESS_DENIED, "Access denied"); | |
339 | ||
340 | case EINVAL: | |
341 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument"); | |
342 | ||
343 | case ESRCH: | |
344 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process"); | |
345 | ||
346 | case ENOENT: | |
347 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found"); | |
348 | ||
349 | case EEXIST: | |
350 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_EXISTS, "File exists"); | |
351 | ||
352 | case ETIMEDOUT: | |
353 | case ETIME: | |
354 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_TIMEOUT, "Timed out"); | |
355 | ||
356 | case EIO: | |
357 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_IO_ERROR, "Input/output error"); | |
358 | ||
359 | case ENETRESET: | |
360 | case ECONNABORTED: | |
361 | case ECONNRESET: | |
362 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_DISCONNECTED, "Disconnected"); | |
363 | ||
364 | case ENOTSUP: | |
365 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported"); | |
366 | ||
367 | case EADDRNOTAVAIL: | |
368 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_BAD_ADDRESS, "Address not available"); | |
369 | ||
370 | case ENOBUFS: | |
371 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded"); | |
372 | ||
373 | case EADDRINUSE: | |
374 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use"); | |
375 | ||
376 | case EBADMSG: | |
377 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message"); | |
378 | } | |
379 | ||
380 | return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FAILED, "Operation failed"); | |
381 | } | |
382 | ||
d9f644e2 | 383 | _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { |
40ca29a1 LP |
384 | sd_bus_error x; |
385 | ||
386 | x = map_from_errno(error); | |
387 | ||
388 | return bus_error_set_strerror_or_const(e, x.name, error, x.message); | |
389 | } | |
390 | ||
391 | int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { | |
392 | sd_bus_error x; | |
393 | int r; | |
394 | ||
395 | if (error < 0) | |
396 | error = -error; | |
397 | ||
398 | if (!e) | |
80c19740 | 399 | return -error; |
40ca29a1 LP |
400 | |
401 | assert_return(!bus_error_is_dirty(e), -EINVAL); | |
402 | ||
403 | x = map_from_errno(error); | |
404 | ||
405 | if (format) { | |
406 | char *n, *m; | |
407 | ||
408 | r = vasprintf(&m, format, ap); | |
409 | if (r < 0) | |
410 | goto fallback; | |
411 | ||
412 | n = strdup(x.name); | |
413 | if (!n) { | |
414 | free(m); | |
415 | goto fallback; | |
416 | } | |
417 | ||
418 | e->name = n; | |
419 | e->message = m; | |
420 | e->need_free = true; | |
421 | return -error; | |
eb01ba5d | 422 | } |
de1c301e | 423 | |
40ca29a1 LP |
424 | fallback: |
425 | return bus_error_set_strerror_or_const(e, x.name, error, x.message); | |
426 | } | |
427 | ||
d9f644e2 | 428 | _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) { |
40ca29a1 LP |
429 | int r; |
430 | ||
80c19740 LP |
431 | if (error < 0) |
432 | error = -error; | |
433 | ||
40ca29a1 | 434 | if (!e) |
80c19740 | 435 | return -error; |
40ca29a1 LP |
436 | |
437 | assert_return(!bus_error_is_dirty(e), -EINVAL); | |
438 | ||
439 | if (format) { | |
440 | va_list ap; | |
441 | ||
442 | va_start(ap, format); | |
443 | r = bus_error_set_errnofv(e, error, format, ap); | |
444 | va_end(ap); | |
445 | ||
446 | return r; | |
447 | } | |
448 | ||
449 | return sd_bus_error_set_errno(e, error); | |
de1c301e | 450 | } |
e3017af9 LP |
451 | |
452 | const char *bus_error_message(const sd_bus_error *e, int error) { | |
40ca29a1 | 453 | |
c9b6cb28 LP |
454 | if (e) { |
455 | /* Sometimes the D-Bus server is a little bit too verbose with | |
456 | * its error messages, so let's override them here */ | |
457 | if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED)) | |
458 | return "Access denied"; | |
459 | ||
460 | if (e->message) | |
461 | return e->message; | |
462 | } | |
463 | ||
40ca29a1 LP |
464 | if (error < 0) |
465 | error = -error; | |
466 | ||
e3017af9 LP |
467 | return strerror(error); |
468 | } |