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