chiark / gitweb /
bus: update PORTING-DBUS1
[elogind.git] / src / libsystemd-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                 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         return bus_error_name_to_errno(e->name);
289 }
290
291 static void bus_error_strerror(sd_bus_error *e, int error) {
292         size_t k = 64;
293         char *m;
294
295         assert(e);
296
297         for (;;) {
298                 char *x;
299
300                 m = new(char, k);
301                 if (!m)
302                         return;
303
304                 errno = 0;
305                 x = strerror_r(error, m, k);
306                 if (errno == ERANGE || strlen(x) >= k - 1) {
307                         free(m);
308                         k *= 2;
309                         continue;
310                 }
311
312                 if (!x || errno) {
313                         free(m);
314                         return;
315                 }
316
317                 if (x == m) {
318                         if (e->_need_free > 0) {
319                                 /* Error is already dynamic, let's just update the message */
320                                 free((char*) e->message);
321                                 e->message = x;
322
323                         } else {
324                                 char *t;
325                                 /* Error was const so far, let's make it dynamic, if we can */
326
327                                 t = strdup(e->name);
328                                 if (!t) {
329                                         free(m);
330                                         return;
331                                 }
332
333                                 e->_need_free = 1;
334                                 e->name = t;
335                                 e->message = x;
336                         }
337                 } else {
338                         free(m);
339
340                         if (e->_need_free > 0) {
341                                 char *t;
342
343                                 /* Error is dynamic, let's hence make the message also dynamic */
344                                 t = strdup(x);
345                                 if (!t)
346                                         return;
347
348                                 free((char*) e->message);
349                                 e->message = t;
350                         } else {
351                                 /* Error is const, hence we can just override */
352                                 e->message = x;
353                         }
354                 }
355
356                 return;
357         }
358 }
359
360 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
361
362         if (error < 0)
363                 error = -error;
364
365         if (!e)
366                 return -error;
367         if (error == 0)
368                 return -error;
369
370         assert_return(!bus_error_is_dirty(e), -EINVAL);
371
372         /* First, try a const translation */
373         *e = errno_to_bus_error_const(error);
374
375         if (!sd_bus_error_is_set(e)) {
376                 int k;
377
378                 /* If that didn't work, try a dynamic one. */
379
380                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
381                 if (k > 0)
382                         e->_need_free = 1;
383                 else if (k < 0) {
384                         *e = BUS_ERROR_OOM;
385                         return -error;
386                 } else
387                         *e = BUS_ERROR_FAILED;
388         }
389
390         /* Now, fill in the message from strerror() if we can */
391         bus_error_strerror(e, error);
392         return -error;
393 }
394
395 int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
396         int r;
397
398         if (error < 0)
399                 error = -error;
400
401         if (!e)
402                 return -error;
403         if (error == 0)
404                 return 0;
405
406         assert_return(!bus_error_is_dirty(e), -EINVAL);
407
408         /* First, try a const translation */
409         *e = errno_to_bus_error_const(error);
410
411         if (!sd_bus_error_is_set(e)) {
412                 int k;
413
414                 /* If that didn't work, try a dynamic one */
415
416                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
417                 if (k > 0)
418                         e->_need_free = 1;
419                 else if (k < 0) {
420                         *e = BUS_ERROR_OOM;
421                         return -ENOMEM;
422                 } else
423                         *e = BUS_ERROR_FAILED;
424         }
425
426         if (format) {
427                 char *m;
428
429                 /* First, let's try to fill in the supplied message */
430
431                 r = vasprintf(&m, format, ap);
432                 if (r >= 0) {
433
434                         if (e->_need_free <= 0) {
435                                 char *t;
436
437                                 t = strdup(e->name);
438                                 if (t) {
439                                         e->_need_free = 1;
440                                         e->name = t;
441                                         e->message = m;
442                                         return -error;
443                                 }
444
445                                 free(m);
446                         } else {
447                                 free((char*) e->message);
448                                 e->message = m;
449                                 return -error;
450                         }
451                 }
452         }
453
454         /* If that didn't work, use strerror() for the message */
455         bus_error_strerror(e, error);
456         return -error;
457 }
458
459 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
460         int r;
461
462         if (error < 0)
463                 error = -error;
464
465         if (!e)
466                 return -error;
467         if (error == 0)
468                 return 0;
469
470         assert_return(!bus_error_is_dirty(e), -EINVAL);
471
472         if (format) {
473                 va_list ap;
474
475                 va_start(ap, format);
476                 r = bus_error_set_errnofv(e, error, format, ap);
477                 va_end(ap);
478
479                 return r;
480         }
481
482         return sd_bus_error_set_errno(e, error);
483 }
484
485 const char *bus_error_message(const sd_bus_error *e, int error) {
486
487         if (e) {
488                 /* Sometimes the D-Bus server is a little bit too verbose with
489                  * its error messages, so let's override them here */
490                 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
491                         return "Access denied";
492
493                 if (e->message)
494                         return e->message;
495         }
496
497         if (error < 0)
498                 error = -error;
499
500         return strerror(error);
501 }