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