chiark / gitweb /
Add more _printf_'s for format-nonliterals
[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 _printf_(3,0)
254 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
255
256         if (!name)
257                 return 0;
258         if (!e)
259                 goto finish;
260
261         assert_return(!bus_error_is_dirty(e), -EINVAL);
262
263         e->name = strdup(name);
264         if (!e->name) {
265                 *e = BUS_ERROR_OOM;
266                 return -ENOMEM;
267         }
268
269         if (format)
270                 vasprintf((char**) &e->message, format, ap);
271
272         e->_need_free = 1;
273
274 finish:
275         return -bus_error_name_to_errno(name);
276 }
277
278 _public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
279
280         if (format) {
281                 int r;
282                 va_list ap;
283
284                 va_start(ap, format);
285                 r = bus_error_setfv(e, name, format, ap);
286                 va_end(ap);
287
288                 return r;
289         }
290
291         return sd_bus_error_set(e, name, NULL);
292 }
293
294 _public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
295
296         if (!sd_bus_error_is_set(e))
297                 return 0;
298         if (!dest)
299                 goto finish;
300
301         assert_return(!bus_error_is_dirty(dest), -EINVAL);
302
303         /*
304          * _need_free  < 0 indicates that the error is temporarily const, needs deep copying
305          * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
306          * _need_free  > 0 indicates that the error is fully dynamic, needs deep copying
307          */
308
309         if (e->_need_free == 0)
310                 *dest = *e;
311         else {
312                 dest->name = strdup(e->name);
313                 if (!dest->name) {
314                         *dest = BUS_ERROR_OOM;
315                         return -ENOMEM;
316                 }
317
318                 if (e->message)
319                         dest->message = strdup(e->message);
320
321                 dest->_need_free = 1;
322         }
323
324 finish:
325         return -bus_error_name_to_errno(e->name);
326 }
327
328 _public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
329         if (!name)
330                 return 0;
331         if (!e)
332                 goto finish;
333
334         assert_return(!bus_error_is_dirty(e), -EINVAL);
335
336         *e = SD_BUS_ERROR_MAKE_CONST(name, message);
337
338 finish:
339         return -bus_error_name_to_errno(name);
340 }
341
342 _public_ int sd_bus_error_is_set(const sd_bus_error *e) {
343         if (!e)
344                 return 0;
345
346         return !!e->name;
347 }
348
349 _public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
350         if (!e)
351                 return 0;
352
353         return streq_ptr(e->name, name);
354 }
355
356 _public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
357         if (!e)
358                 return 0;
359
360         return bus_error_name_to_errno(e->name);
361 }
362
363 static void bus_error_strerror(sd_bus_error *e, int error) {
364         size_t k = 64;
365         char *m;
366
367         assert(e);
368
369         for (;;) {
370                 char *x;
371
372                 m = new(char, k);
373                 if (!m)
374                         return;
375
376                 errno = 0;
377                 x = strerror_r(error, m, k);
378                 if (errno == ERANGE || strlen(x) >= k - 1) {
379                         free(m);
380                         k *= 2;
381                         continue;
382                 }
383
384                 if (!x || errno) {
385                         free(m);
386                         return;
387                 }
388
389                 if (x == m) {
390                         if (e->_need_free > 0) {
391                                 /* Error is already dynamic, let's just update the message */
392                                 free((char*) e->message);
393                                 e->message = x;
394
395                         } else {
396                                 char *t;
397                                 /* Error was const so far, let's make it dynamic, if we can */
398
399                                 t = strdup(e->name);
400                                 if (!t) {
401                                         free(m);
402                                         return;
403                                 }
404
405                                 e->_need_free = 1;
406                                 e->name = t;
407                                 e->message = x;
408                         }
409                 } else {
410                         free(m);
411
412                         if (e->_need_free > 0) {
413                                 char *t;
414
415                                 /* Error is dynamic, let's hence make the message also dynamic */
416                                 t = strdup(x);
417                                 if (!t)
418                                         return;
419
420                                 free((char*) e->message);
421                                 e->message = t;
422                         } else {
423                                 /* Error is const, hence we can just override */
424                                 e->message = x;
425                         }
426                 }
427
428                 return;
429         }
430 }
431
432 _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
433
434         if (error < 0)
435                 error = -error;
436
437         if (!e)
438                 return -error;
439         if (error == 0)
440                 return -error;
441
442         assert_return(!bus_error_is_dirty(e), -EINVAL);
443
444         /* First, try a const translation */
445         *e = errno_to_bus_error_const(error);
446
447         if (!sd_bus_error_is_set(e)) {
448                 int k;
449
450                 /* If that didn't work, try a dynamic one. */
451
452                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
453                 if (k > 0)
454                         e->_need_free = 1;
455                 else if (k < 0) {
456                         *e = BUS_ERROR_OOM;
457                         return -error;
458                 } else
459                         *e = BUS_ERROR_FAILED;
460         }
461
462         /* Now, fill in the message from strerror() if we can */
463         bus_error_strerror(e, error);
464         return -error;
465 }
466
467 _printf_(3,0)
468 int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
469         int r;
470
471         if (error < 0)
472                 error = -error;
473
474         if (!e)
475                 return -error;
476         if (error == 0)
477                 return 0;
478
479         assert_return(!bus_error_is_dirty(e), -EINVAL);
480
481         /* First, try a const translation */
482         *e = errno_to_bus_error_const(error);
483
484         if (!sd_bus_error_is_set(e)) {
485                 int k;
486
487                 /* If that didn't work, try a dynamic one */
488
489                 k = errno_to_bus_error_name_new(error, (char**) &e->name);
490                 if (k > 0)
491                         e->_need_free = 1;
492                 else if (k < 0) {
493                         *e = BUS_ERROR_OOM;
494                         return -ENOMEM;
495                 } else
496                         *e = BUS_ERROR_FAILED;
497         }
498
499         if (format) {
500                 char *m;
501
502                 /* First, let's try to fill in the supplied message */
503
504                 r = vasprintf(&m, format, ap);
505                 if (r >= 0) {
506
507                         if (e->_need_free <= 0) {
508                                 char *t;
509
510                                 t = strdup(e->name);
511                                 if (t) {
512                                         e->_need_free = 1;
513                                         e->name = t;
514                                         e->message = m;
515                                         return -error;
516                                 }
517
518                                 free(m);
519                         } else {
520                                 free((char*) e->message);
521                                 e->message = m;
522                                 return -error;
523                         }
524                 }
525         }
526
527         /* If that didn't work, use strerror() for the message */
528         bus_error_strerror(e, error);
529         return -error;
530 }
531
532 _public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
533         int r;
534
535         if (error < 0)
536                 error = -error;
537
538         if (!e)
539                 return -error;
540         if (error == 0)
541                 return 0;
542
543         assert_return(!bus_error_is_dirty(e), -EINVAL);
544
545         if (format) {
546                 va_list ap;
547
548                 va_start(ap, format);
549                 r = bus_error_set_errnofv(e, error, format, ap);
550                 va_end(ap);
551
552                 return r;
553         }
554
555         return sd_bus_error_set_errno(e, error);
556 }
557
558 const char *bus_error_message(const sd_bus_error *e, int error) {
559
560         if (e) {
561                 /* Sometimes the D-Bus server is a little bit too verbose with
562                  * its error messages, so let's override them here */
563                 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
564                         return "Access denied";
565
566                 if (e->message)
567                         return e->message;
568         }
569
570         if (error < 0)
571                 error = -error;
572
573         return strerror(error);
574 }