chiark / gitweb /
af83c12d53971cbcca81465ced712da0abf865b0
[elogind.git] / src / libsystemd / sd-bus / 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         /* Of we hit OOM on formatting the pretty message, we ignore
198          * this, since we at least managed to write the error name */
199         if (format)
200                 (void) vasprintf((char**) &e->message, format, ap);
201
202         e->_need_free = 1;
203
204 finish:
205         return -bus_error_name_to_errno(name);
206 }
207
208 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
209
210         if (format) {
211                 int r;
212                 va_list ap;
213
214                 va_start(ap, format);
215                 r = bus_error_setfv(e, name, format, ap);
216                 va_end(ap);
217
218                 return r;
219         }
220
221         return sd_bus_error_set(e, name, NULL);
222 }
223
224 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
225
226         if (!sd_bus_error_is_set(e))
227                 return 0;
228         if (!dest)
229                 goto finish;
230
231         assert_return(!bus_error_is_dirty(dest), -EINVAL);
232
233         /*
234          * _need_free  < 0 indicates that the error is temporarily const, needs deep copying
235          * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
236          * _need_free  > 0 indicates that the error is fully dynamic, needs deep copying
237          */
238
239         if (e->_need_free == 0)
240                 *dest = *e;
241         else {
242                 dest->name = strdup(e->name);
243                 if (!dest->name) {
244                         *dest = BUS_ERROR_OOM;
245                         return -ENOMEM;
246                 }
247
248                 if (e->message)
249                         dest->message = strdup(e->message);
250
251                 dest->_need_free = 1;
252         }
253
254 finish:
255         return -bus_error_name_to_errno(e->name);
256 }
257
258 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
259         if (!name)
260                 return 0;
261         if (!e)
262                 goto finish;
263
264         assert_return(!bus_error_is_dirty(e), -EINVAL);
265
266         *e = SD_BUS_ERROR_MAKE_CONST(name, message);
267
268 finish:
269         return -bus_error_name_to_errno(name);
270 }
271
272 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
273         if (!e)
274                 return 0;
275
276         return !!e->name;
277 }
278
279 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
280         if (!e)
281                 return 0;
282
283         return streq_ptr(e->name, name);
284 }
285
286 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
287         if (!e)
288                 return 0;
289
290         if (!e->name)
291                 return 0;
292
293         return bus_error_name_to_errno(e->name);
294 }
295
296 static void bus_error_strerror(sd_bus_error *e, int error) {
297         size_t k = 64;
298         char *m;
299
300         assert(e);
301
302         for (;;) {
303                 char *x;
304
305                 m = new(char, k);
306                 if (!m)
307                         return;
308
309                 errno = 0;
310                 x = strerror_r(error, m, k);
311                 if (errno == ERANGE || strlen(x) >= k - 1) {
312                         free(m);
313                         k *= 2;
314                         continue;
315                 }
316
317                 if (errno) {
318                         free(m);
319                         return;
320                 }
321
322                 if (x == m) {
323                         if (e->_need_free > 0) {
324                                 /* Error is already dynamic, let's just update the message */
325                                 free((char*) e->message);
326                                 e->message = x;
327
328                         } else {
329                                 char *t;
330                                 /* Error was const so far, let's make it dynamic, if we can */
331
332                                 t = strdup(e->name);
333                                 if (!t) {
334                                         free(m);
335                                         return;
336                                 }
337
338                                 e->_need_free = 1;
339                                 e->name = t;
340                                 e->message = x;
341                         }
342                 } else {
343                         free(m);
344
345                         if (e->_need_free > 0) {
346                                 char *t;
347
348                                 /* Error is dynamic, let's hence make the message also dynamic */
349                                 t = strdup(x);
350                                 if (!t)
351                                         return;
352
353                                 free((char*) e->message);
354                                 e->message = t;
355                         } else {
356                                 /* Error is const, hence we can just override */
357                                 e->message = x;
358                         }
359                 }
360
361                 return;
362         }
363 }
364
365 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
366
367         if (error < 0)
368                 error = -error;
369
370         if (!e)
371                 return -error;
372         if (error == 0)
373                 return -error;
374
375         assert_return(!bus_error_is_dirty(e), -EINVAL);
376
377         /* First, try a const translation */
378         *e = errno_to_bus_error_const(error);
379
380         if (!sd_bus_error_is_set(e)) {
381                 int k;
382
383                 /* If that didn't work, try a dynamic one. */
384
385                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
386                 if (k > 0)
387                         e->_need_free = 1;
388                 else if (k < 0) {
389                         *e = BUS_ERROR_OOM;
390                         return -error;
391                 } else
392                         *e = BUS_ERROR_FAILED;
393         }
394
395         /* Now, fill in the message from strerror() if we can */
396         bus_error_strerror(e, error);
397         return -error;
398 }
399
400 int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
401         int r;
402
403         if (error < 0)
404                 error = -error;
405
406         if (!e)
407                 return -error;
408         if (error == 0)
409                 return 0;
410
411         assert_return(!bus_error_is_dirty(e), -EINVAL);
412
413         /* First, try a const translation */
414         *e = errno_to_bus_error_const(error);
415
416         if (!sd_bus_error_is_set(e)) {
417                 int k;
418
419                 /* If that didn't work, try a dynamic one */
420
421                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
422                 if (k > 0)
423                         e->_need_free = 1;
424                 else if (k < 0) {
425                         *e = BUS_ERROR_OOM;
426                         return -ENOMEM;
427                 } else
428                         *e = BUS_ERROR_FAILED;
429         }
430
431         if (format) {
432                 char *m;
433
434                 /* First, let's try to fill in the supplied message */
435
436                 r = vasprintf(&m, format, ap);
437                 if (r >= 0) {
438
439                         if (e->_need_free <= 0) {
440                                 char *t;
441
442                                 t = strdup(e->name);
443                                 if (t) {
444                                         e->_need_free = 1;
445                                         e->name = t;
446                                         e->message = m;
447                                         return -error;
448                                 }
449
450                                 free(m);
451                         } else {
452                                 free((char*) e->message);
453                                 e->message = m;
454                                 return -error;
455                         }
456                 }
457         }
458
459         /* If that didn't work, use strerror() for the message */
460         bus_error_strerror(e, error);
461         return -error;
462 }
463
464 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
465         int r;
466
467         if (error < 0)
468                 error = -error;
469
470         if (!e)
471                 return -error;
472         if (error == 0)
473                 return 0;
474
475         assert_return(!bus_error_is_dirty(e), -EINVAL);
476
477         if (format) {
478                 va_list ap;
479
480                 va_start(ap, format);
481                 r = bus_error_set_errnofv(e, error, format, ap);
482                 va_end(ap);
483
484                 return r;
485         }
486
487         return sd_bus_error_set_errno(e, error);
488 }
489
490 const char *bus_error_message(const sd_bus_error *e, int error) {
491
492         if (e) {
493                 /* Sometimes the D-Bus server is a little bit too verbose with
494                  * its error messages, so let's override them here */
495                 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
496                         return "Access denied";
497
498                 if (e->message)
499                         return e->message;
500         }
501
502         if (error < 0)
503                 error = -error;
504
505         return strerror(error);
506 }