chiark / gitweb /
bus: remove unused check
[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                 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 (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 }