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