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