]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/bus-error.c
libsystemd-bus: rename to libsystemd
[thirdparty/systemd.git] / src / libsystemd / bus-error.c
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 #include "errno-list.h"
31
32 #include "sd-bus.h"
33 #include "bus-error.h"
34
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
38 static int bus_error_name_to_errno(const char *name) {
39 const char *p;
40 int r;
41 const name_error_mapping *m;
42
43 if (!name)
44 return EINVAL;
45
46 p = startswith(name, "System.Error.");
47 if (p) {
48 r = errno_from_name(p);
49 if (r <= 0)
50 return EIO;
51
52 return r;
53 }
54
55 m = bus_error_mapping_lookup(name, strlen(name));
56 if (m)
57 return m->code;
58
59 return EIO;
60 }
61
62 static sd_bus_error errno_to_bus_error_const(int error) {
63
64 if (error < 0)
65 error = -error;
66
67 switch (error) {
68
69 case ENOMEM:
70 return BUS_ERROR_OOM;
71
72 case EPERM:
73 case EACCES:
74 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
75
76 case EINVAL:
77 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
78
79 case ESRCH:
80 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
81
82 case ENOENT:
83 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
84
85 case EEXIST:
86 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
87
88 case ETIMEDOUT:
89 case ETIME:
90 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
91
92 case EIO:
93 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
94
95 case ENETRESET:
96 case ECONNABORTED:
97 case ECONNRESET:
98 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
99
100 case ENOTSUP:
101 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
102
103 case EADDRNOTAVAIL:
104 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
105
106 case ENOBUFS:
107 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
108
109 case EADDRINUSE:
110 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
111
112 case EBADMSG:
113 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
114 }
115
116 return SD_BUS_ERROR_NULL;
117 }
118
119 static int errno_to_bus_error_name_new(int error, char **ret) {
120 const char *name;
121 char *n;
122
123 if (error < 0)
124 error = -error;
125
126 name = errno_to_name(error);
127 if (!name)
128 return 0;
129
130 n = strappend("System.Error.", name);
131 if (!n)
132 return -ENOMEM;
133
134 *ret = n;
135 return 1;
136 }
137
138 bool bus_error_is_dirty(sd_bus_error *e) {
139 if (!e)
140 return false;
141
142 return e->name || e->message || e->_need_free != 0;
143 }
144
145 _public_ void sd_bus_error_free(sd_bus_error *e) {
146 if (!e)
147 return;
148
149 if (e->_need_free > 0) {
150 free((void*) e->name);
151 free((void*) e->message);
152 }
153
154 e->name = e->message = NULL;
155 e->_need_free = 0;
156 }
157
158 _public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
159
160 if (!name)
161 return 0;
162 if (!e)
163 goto finish;
164
165 assert_return(!bus_error_is_dirty(e), -EINVAL);
166
167 e->name = strdup(name);
168 if (!e->name) {
169 *e = BUS_ERROR_OOM;
170 return -ENOMEM;
171 }
172
173 if (message)
174 e->message = strdup(message);
175
176 e->_need_free = 1;
177
178 finish:
179 return -bus_error_name_to_errno(name);
180 }
181
182 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
183
184 if (!name)
185 return 0;
186 if (!e)
187 goto finish;
188
189 assert_return(!bus_error_is_dirty(e), -EINVAL);
190
191 e->name = strdup(name);
192 if (!e->name) {
193 *e = BUS_ERROR_OOM;
194 return -ENOMEM;
195 }
196
197 if (format)
198 vasprintf((char**) &e->message, format, ap);
199
200 e->_need_free = 1;
201
202 finish:
203 return -bus_error_name_to_errno(name);
204 }
205
206 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
207
208 if (format) {
209 int r;
210 va_list ap;
211
212 va_start(ap, format);
213 r = bus_error_setfv(e, name, format, ap);
214 va_end(ap);
215
216 return r;
217 }
218
219 return sd_bus_error_set(e, name, NULL);
220 }
221
222 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
223
224 if (!sd_bus_error_is_set(e))
225 return 0;
226 if (!dest)
227 goto finish;
228
229 assert_return(!bus_error_is_dirty(dest), -EINVAL);
230
231 /*
232 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
233 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
234 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
235 */
236
237 if (e->_need_free == 0)
238 *dest = *e;
239 else {
240 dest->name = strdup(e->name);
241 if (!dest->name) {
242 *dest = BUS_ERROR_OOM;
243 return -ENOMEM;
244 }
245
246 if (e->message)
247 dest->message = strdup(e->message);
248
249 dest->_need_free = 1;
250 }
251
252 finish:
253 return -bus_error_name_to_errno(e->name);
254 }
255
256 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
257 if (!name)
258 return 0;
259 if (!e)
260 goto finish;
261
262 assert_return(!bus_error_is_dirty(e), -EINVAL);
263
264 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
265
266 finish:
267 return -bus_error_name_to_errno(name);
268 }
269
270 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
271 if (!e)
272 return 0;
273
274 return !!e->name;
275 }
276
277 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
278 if (!e)
279 return 0;
280
281 return streq_ptr(e->name, name);
282 }
283
284 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
285 if (!e)
286 return 0;
287
288 if (!e->name)
289 return 0;
290
291 return bus_error_name_to_errno(e->name);
292 }
293
294 static void bus_error_strerror(sd_bus_error *e, int error) {
295 size_t k = 64;
296 char *m;
297
298 assert(e);
299
300 for (;;) {
301 char *x;
302
303 m = new(char, k);
304 if (!m)
305 return;
306
307 errno = 0;
308 x = strerror_r(error, m, k);
309 if (errno == ERANGE || strlen(x) >= k - 1) {
310 free(m);
311 k *= 2;
312 continue;
313 }
314
315 if (!x || errno) {
316 free(m);
317 return;
318 }
319
320 if (x == m) {
321 if (e->_need_free > 0) {
322 /* Error is already dynamic, let's just update the message */
323 free((char*) e->message);
324 e->message = x;
325
326 } else {
327 char *t;
328 /* Error was const so far, let's make it dynamic, if we can */
329
330 t = strdup(e->name);
331 if (!t) {
332 free(m);
333 return;
334 }
335
336 e->_need_free = 1;
337 e->name = t;
338 e->message = x;
339 }
340 } else {
341 free(m);
342
343 if (e->_need_free > 0) {
344 char *t;
345
346 /* Error is dynamic, let's hence make the message also dynamic */
347 t = strdup(x);
348 if (!t)
349 return;
350
351 free((char*) e->message);
352 e->message = t;
353 } else {
354 /* Error is const, hence we can just override */
355 e->message = x;
356 }
357 }
358
359 return;
360 }
361 }
362
363 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
364
365 if (error < 0)
366 error = -error;
367
368 if (!e)
369 return -error;
370 if (error == 0)
371 return -error;
372
373 assert_return(!bus_error_is_dirty(e), -EINVAL);
374
375 /* First, try a const translation */
376 *e = errno_to_bus_error_const(error);
377
378 if (!sd_bus_error_is_set(e)) {
379 int k;
380
381 /* If that didn't work, try a dynamic one. */
382
383 k = errno_to_bus_error_name_new(error, (char**) &e->name);
384 if (k > 0)
385 e->_need_free = 1;
386 else if (k < 0) {
387 *e = BUS_ERROR_OOM;
388 return -error;
389 } else
390 *e = BUS_ERROR_FAILED;
391 }
392
393 /* Now, fill in the message from strerror() if we can */
394 bus_error_strerror(e, error);
395 return -error;
396 }
397
398 int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
399 int r;
400
401 if (error < 0)
402 error = -error;
403
404 if (!e)
405 return -error;
406 if (error == 0)
407 return 0;
408
409 assert_return(!bus_error_is_dirty(e), -EINVAL);
410
411 /* First, try a const translation */
412 *e = errno_to_bus_error_const(error);
413
414 if (!sd_bus_error_is_set(e)) {
415 int k;
416
417 /* If that didn't work, try a dynamic one */
418
419 k = errno_to_bus_error_name_new(error, (char**) &e->name);
420 if (k > 0)
421 e->_need_free = 1;
422 else if (k < 0) {
423 *e = BUS_ERROR_OOM;
424 return -ENOMEM;
425 } else
426 *e = BUS_ERROR_FAILED;
427 }
428
429 if (format) {
430 char *m;
431
432 /* First, let's try to fill in the supplied message */
433
434 r = vasprintf(&m, format, ap);
435 if (r >= 0) {
436
437 if (e->_need_free <= 0) {
438 char *t;
439
440 t = strdup(e->name);
441 if (t) {
442 e->_need_free = 1;
443 e->name = t;
444 e->message = m;
445 return -error;
446 }
447
448 free(m);
449 } else {
450 free((char*) e->message);
451 e->message = m;
452 return -error;
453 }
454 }
455 }
456
457 /* If that didn't work, use strerror() for the message */
458 bus_error_strerror(e, error);
459 return -error;
460 }
461
462 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
463 int r;
464
465 if (error < 0)
466 error = -error;
467
468 if (!e)
469 return -error;
470 if (error == 0)
471 return 0;
472
473 assert_return(!bus_error_is_dirty(e), -EINVAL);
474
475 if (format) {
476 va_list ap;
477
478 va_start(ap, format);
479 r = bus_error_set_errnofv(e, error, format, ap);
480 va_end(ap);
481
482 return r;
483 }
484
485 return sd_bus_error_set_errno(e, error);
486 }
487
488 const char *bus_error_message(const sd_bus_error *e, int error) {
489
490 if (e) {
491 /* Sometimes the D-Bus server is a little bit too verbose with
492 * its error messages, so let's override them here */
493 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
494 return "Access denied";
495
496 if (e->message)
497 return e->message;
498 }
499
500 if (error < 0)
501 error = -error;
502
503 return strerror(error);
504 }