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