chiark / gitweb /
35e261464067abfe8c18807fcfeaaa4b4f0cc5c5
[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
31 #include "sd-bus.h"
32 #include "bus-error.h"
33
34 bool bus_error_is_dirty(sd_bus_error *e) {
35         if (!e)
36                 return 0;
37
38         return e->name || e->message || e->need_free;
39 }
40
41 void sd_bus_error_free(sd_bus_error *e) {
42         if (!e)
43                 return;
44
45         if (e->need_free) {
46                 free((void*) e->name);
47                 free((void*) e->message);
48         }
49
50         e->name = e->message = NULL;
51         e->need_free = false;
52 }
53
54 int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
55         char *n, *m = NULL;
56
57         if (!e)
58                 return 0;
59
60         assert_return(!bus_error_is_dirty(e), -EINVAL);
61         assert_return(name, -EINVAL);
62
63         n = strdup(name);
64         if (!n)
65                 return -ENOMEM;
66
67         if (message) {
68                 m = strdup(message);
69                 if (!m)
70                         return -ENOMEM;
71         }
72
73         e->name = n;
74         e->message = m;
75         e->need_free = true;
76
77         return 0;
78 }
79
80 int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
81         char *n, *m = NULL;
82         int r;
83
84         if (!e)
85                 return 0;
86
87         assert_return(!bus_error_is_dirty(e), -EINVAL);
88         assert_return(name, -EINVAL);
89
90         n = strdup(name);
91         if (!n)
92                 return -ENOMEM;
93
94         if (format) {
95                 r = vasprintf(&m, format, ap);
96                 if (r < 0) {
97                         free(n);
98                         return -ENOMEM;
99                 }
100         }
101
102         e->name = n;
103         e->message = m;
104         e->need_free = true;
105
106         return 0;
107 }
108
109 int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
110
111         if (format) {
112                 int r;
113                 va_list ap;
114
115                 va_start(ap, format);
116                 r = bus_error_setfv(e, name, format, ap);
117                 va_end(ap);
118
119                 return r;
120         }
121
122         return sd_bus_error_set(e, name, NULL);
123 }
124
125 int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
126         char *x, *y = NULL;
127
128         if (!dest)
129                 return 0;
130         if (!sd_bus_error_is_set(e))
131                 return 0;
132
133         assert_return(!bus_error_is_dirty(dest), -EINVAL);
134
135         x = strdup(e->name);
136         if (!x)
137                 return -ENOMEM;
138
139         if (e->message) {
140                 y = strdup(e->message);
141                 if (!y) {
142                         free(x);
143                         return -ENOMEM;
144                 }
145         }
146
147         dest->name = x;
148         dest->message = y;
149         dest->need_free = true;
150         return 0;
151 }
152
153 int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
154         if (!e)
155                 return 0;
156
157         assert_return(!bus_error_is_dirty(e), -EINVAL);
158         assert_return(name, -EINVAL);
159
160         *e = SD_BUS_ERROR_MAKE(name, message);
161         return 0;
162 }
163
164 int sd_bus_error_is_set(const sd_bus_error *e) {
165         if (!e)
166                 return 0;
167
168         return !!e->name;
169 }
170
171 int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
172         if (!e)
173                 return 0;
174
175         return streq_ptr(e->name, name);
176 }
177
178 int sd_bus_error_get_errno(const sd_bus_error* e) {
179
180         /* Better replce this with a gperf table */
181
182         if (!e)
183                 return EIO;
184
185         if (!e->name)
186                 return EIO;
187
188         if (streq(e->name, SD_BUS_ERROR_NO_MEMORY))
189                 return ENOMEM;
190
191         if (streq(e->name, SD_BUS_ERROR_SERVICE_UNKNOWN))
192                 return EHOSTUNREACH;
193
194         if (streq(e->name, SD_BUS_ERROR_NAME_HAS_NO_OWNER))
195                 return ENXIO;
196
197         if (streq(e->name, SD_BUS_ERROR_NO_REPLY) ||
198             streq(e->name, SD_BUS_ERROR_TIMEOUT) ||
199             streq(e->name, "org.freedesktop.DBus.Error.TimedOut"))
200                 return ETIMEDOUT;
201
202         if (streq(e->name, SD_BUS_ERROR_IO_ERROR))
203                 return EIO;
204
205         if (streq(e->name, SD_BUS_ERROR_BAD_ADDRESS))
206                 return EADDRNOTAVAIL;
207
208         if (streq(e->name, SD_BUS_ERROR_NOT_SUPPORTED))
209                 return ENOTSUP;
210
211         if (streq(e->name, SD_BUS_ERROR_LIMITS_EXCEEDED))
212                 return ENOBUFS;
213
214         if (streq(e->name, SD_BUS_ERROR_ACCESS_DENIED) ||
215             streq(e->name, SD_BUS_ERROR_AUTH_FAILED))
216                 return EACCES;
217
218         if (streq(e->name, SD_BUS_ERROR_NO_SERVER))
219                 return EHOSTDOWN;
220
221         if (streq(e->name, SD_BUS_ERROR_NO_NETWORK))
222                 return ENONET;
223
224         if (streq(e->name, SD_BUS_ERROR_ADDRESS_IN_USE))
225                 return EADDRINUSE;
226
227         if (streq(e->name, SD_BUS_ERROR_DISCONNECTED))
228                 return ECONNRESET;
229
230         if (streq(e->name, SD_BUS_ERROR_INVALID_ARGS) ||
231             streq(e->name, SD_BUS_ERROR_INVALID_SIGNATURE) ||
232             streq(e->name, "org.freedesktop.DBus.Error.MatchRuleInvalid") ||
233             streq(e->name, "org.freedesktop.DBus.Error.InvalidFileContent"))
234                 return EINVAL;
235
236         if (streq(e->name, SD_BUS_ERROR_FILE_NOT_FOUND) ||
237             streq(e->name, "org.freedesktop.DBus.Error.MatchRuleNotFound"))
238                 return ENOENT;
239
240         if (streq(e->name, SD_BUS_ERROR_FILE_EXISTS))
241                 return EEXIST;
242
243         if (streq(e->name, SD_BUS_ERROR_UNKNOWN_METHOD) ||
244             streq(e->name, SD_BUS_ERROR_UNKNOWN_OBJECT) ||
245             streq(e->name, SD_BUS_ERROR_UNKNOWN_INTERFACE) ||
246             streq(e->name, SD_BUS_ERROR_UNKNOWN_PROPERTY))
247                 return EBADR;
248
249         if (streq(e->name, SD_BUS_ERROR_PROPERTY_READ_ONLY))
250                 return EROFS;
251
252         if (streq(e->name, SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN) ||
253             streq(e->name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown"))
254                 return ESRCH;
255
256         if (streq(e->name, SD_BUS_ERROR_INCONSISTENT_MESSAGE))
257                 return EBADMSG;
258
259         if (streq(e->name, "org.freedesktop.DBus.Error.ObjectPathInUse"))
260                 return EBUSY;
261
262         return EIO;
263 }
264
265 static int bus_error_set_strerror_or_const(sd_bus_error *e, const char *name, int error, const char *fallback) {
266         size_t k = 64;
267         char *n = NULL, *m = NULL;
268
269         if (error < 0)
270                 error = -error;
271
272         if (!e)
273                 return -error;
274
275         assert_return(!bus_error_is_dirty(e), -EINVAL);
276         assert_return(name, -EINVAL);
277
278         for (;;) {
279                 char *x;
280
281                 m = new(char, k);
282                 if (!m)
283                         goto use_fallback;
284
285                 errno = 0;
286                 x = strerror_r(error, m, k);
287                 if (errno == ERANGE || strlen(x) >= k - 1) {
288                         free(m);
289                         k *= 2;
290                         continue;
291                 }
292
293                 if (!x || errno) {
294                         free(m);
295                         goto use_fallback;
296                 }
297
298
299                 if (x != m) {
300                         free(m);
301                         sd_bus_error_set_const(e, name, x);
302                         return -error;
303                 }
304
305                 break;
306         }
307
308
309         n = strdup(name);
310         if (!n) {
311                 free(m);
312                 goto use_fallback;
313         }
314
315         e->name = n;
316         e->message = m;
317         e->need_free = true;
318
319         return -error;
320
321 use_fallback:
322         sd_bus_error_set_const(e, name, fallback);
323         return -error;
324 }
325
326 static sd_bus_error map_from_errno(int error) {
327
328         if (error < 0)
329                 error = -error;
330
331         switch (error) {
332
333         case ENOMEM:
334                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NO_NETWORK, "Out of memory");
335
336         case EPERM:
337         case EACCES:
338                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
339
340         case EINVAL:
341                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
342
343         case ESRCH:
344                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
345
346         case ENOENT:
347                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
348
349         case EEXIST:
350                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FILE_EXISTS, "File exists");
351
352         case ETIMEDOUT:
353         case ETIME:
354                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_TIMEOUT, "Timed out");
355
356         case EIO:
357                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_IO_ERROR, "Input/output error");
358
359         case ENETRESET:
360         case ECONNABORTED:
361         case ECONNRESET:
362                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
363
364         case ENOTSUP:
365                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
366
367         case EADDRNOTAVAIL:
368                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
369
370         case ENOBUFS:
371                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
372
373         case EADDRINUSE:
374                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
375
376         case EBADMSG:
377                 return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
378         }
379
380         return SD_BUS_ERROR_MAKE(SD_BUS_ERROR_FAILED, "Operation failed");
381 }
382
383 int sd_bus_error_set_errno(sd_bus_error *e, int error) {
384         sd_bus_error x;
385
386         x = map_from_errno(error);
387
388         return bus_error_set_strerror_or_const(e, x.name, error, x.message);
389 }
390
391 int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
392         sd_bus_error x;
393         int r;
394
395         if (error < 0)
396                 error = -error;
397
398         if (!e)
399                 return 0;
400
401         assert_return(!bus_error_is_dirty(e), -EINVAL);
402
403         x = map_from_errno(error);
404
405         if (format) {
406                 char *n, *m;
407
408                 r = vasprintf(&m, format, ap);
409                 if (r < 0)
410                         goto fallback;
411
412                 n = strdup(x.name);
413                 if (!n) {
414                         free(m);
415                         goto fallback;
416                 }
417
418                 e->name = n;
419                 e->message = m;
420                 e->need_free = true;
421                 return -error;
422         }
423
424 fallback:
425         return bus_error_set_strerror_or_const(e, x.name, error, x.message);
426 }
427
428 int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
429         int r;
430
431         if (!e)
432                 return 0;
433
434         assert_return(!bus_error_is_dirty(e), -EINVAL);
435
436         if (format) {
437                 va_list ap;
438
439                 va_start(ap, format);
440                 r = bus_error_set_errnofv(e, error, format, ap);
441                 va_end(ap);
442
443                 return r;
444         }
445
446         return sd_bus_error_set_errno(e, error);
447 }
448
449 const char *bus_error_message(const sd_bus_error *e, int error) {
450
451         if (e) {
452                 /* Sometimes the D-Bus server is a little bit too verbose with
453                  * its error messages, so let's override them here */
454                 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
455                         return "Access denied";
456
457                 if (e->message)
458                         return e->message;
459         }
460
461         if (error < 0)
462                 error = -error;
463
464         return strerror(error);
465 }