]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-error.c
libudev: always return NULL in _unref() APIs
[thirdparty/systemd.git] / src / libsystemd-bus / bus-error.c
CommitLineData
de1c301e
LP
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
fbfa72b0
LP
34bool 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
d9f644e2 41_public_ void sd_bus_error_free(sd_bus_error *e) {
de1c301e
LP
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
d9f644e2 54_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
29ddb38f
LP
55 char *n, *m = NULL;
56
57 if (!e)
58 return 0;
40ca29a1
LP
59
60 assert_return(!bus_error_is_dirty(e), -EINVAL);
61 assert_return(name, -EINVAL);
29ddb38f
LP
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
40ca29a1 80int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
de1c301e 81 char *n, *m = NULL;
de1c301e
LP
82 int r;
83
84 if (!e)
85 return 0;
40ca29a1
LP
86
87 assert_return(!bus_error_is_dirty(e), -EINVAL);
88 assert_return(name, -EINVAL);
de1c301e
LP
89
90 n = strdup(name);
91 if (!n)
92 return -ENOMEM;
93
94 if (format) {
de1c301e 95 r = vasprintf(&m, format, ap);
de1c301e
LP
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
d9f644e2 109_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
40ca29a1
LP
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
d9f644e2 125_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
89ffcd2a
LP
126 char *x, *y = NULL;
127
de1c301e
LP
128 if (!dest)
129 return 0;
de1c301e
LP
130 if (!sd_bus_error_is_set(e))
131 return 0;
132
40ca29a1
LP
133 assert_return(!bus_error_is_dirty(dest), -EINVAL);
134
89ffcd2a
LP
135 x = strdup(e->name);
136 if (!x)
137 return -ENOMEM;
de1c301e 138
89ffcd2a
LP
139 if (e->message) {
140 y = strdup(e->message);
141 if (!y) {
142 free(x);
de1c301e 143 return -ENOMEM;
de1c301e 144 }
89ffcd2a 145 }
de1c301e 146
89ffcd2a
LP
147 dest->name = x;
148 dest->message = y;
149 dest->need_free = true;
de1c301e
LP
150 return 0;
151}
152
d9f644e2 153_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
de1c301e 154 if (!e)
40ca29a1
LP
155 return 0;
156
157 assert_return(!bus_error_is_dirty(e), -EINVAL);
158 assert_return(name, -EINVAL);
de1c301e 159
29ddb38f 160 *e = SD_BUS_ERROR_MAKE(name, message);
40ca29a1 161 return 0;
de1c301e
LP
162}
163
d9f644e2 164_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
de1c301e
LP
165 if (!e)
166 return 0;
167
fbfa72b0 168 return !!e->name;
de1c301e
LP
169}
170
d9f644e2 171_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
de1c301e
LP
172 if (!e)
173 return 0;
174
175 return streq_ptr(e->name, name);
176}
177
d9f644e2 178_public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
de1c301e
LP
179
180 /* Better replce this with a gperf table */
181
eb01ba5d 182 if (!e)
40ca29a1 183 return EIO;
eb01ba5d 184
de1c301e 185 if (!e->name)
40ca29a1 186 return EIO;
de1c301e 187
40ca29a1
LP
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;
de1c301e 207
40ca29a1
LP
208 if (streq(e->name, SD_BUS_ERROR_NOT_SUPPORTED))
209 return ENOTSUP;
de1c301e 210
40ca29a1
LP
211 if (streq(e->name, SD_BUS_ERROR_LIMITS_EXCEEDED))
212 return ENOBUFS;
eb01ba5d 213
40ca29a1
LP
214 if (streq(e->name, SD_BUS_ERROR_ACCESS_DENIED) ||
215 streq(e->name, SD_BUS_ERROR_AUTH_FAILED))
216 return EACCES;
eb01ba5d 217
40ca29a1
LP
218 if (streq(e->name, SD_BUS_ERROR_NO_SERVER))
219 return EHOSTDOWN;
eb01ba5d 220
40ca29a1
LP
221 if (streq(e->name, SD_BUS_ERROR_NO_NETWORK))
222 return ENONET;
eb01ba5d 223
40ca29a1
LP
224 if (streq(e->name, SD_BUS_ERROR_ADDRESS_IN_USE))
225 return EADDRINUSE;
eb01ba5d 226
40ca29a1
LP
227 if (streq(e->name, SD_BUS_ERROR_DISCONNECTED))
228 return ECONNRESET;
eb01ba5d 229
40ca29a1
LP
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;
eb01ba5d 235
40ca29a1
LP
236 if (streq(e->name, SD_BUS_ERROR_FILE_NOT_FOUND) ||
237 streq(e->name, "org.freedesktop.DBus.Error.MatchRuleNotFound"))
238 return ENOENT;
eb01ba5d 239
40ca29a1
LP
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;
de1c301e
LP
263}
264
40ca29a1
LP
265static 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
de1c301e 272 if (!e)
40ca29a1 273 return -error;
de1c301e 274
40ca29a1
LP
275 assert_return(!bus_error_is_dirty(e), -EINVAL);
276 assert_return(name, -EINVAL);
eb01ba5d 277
40ca29a1
LP
278 for (;;) {
279 char *x;
eb01ba5d 280
40ca29a1
LP
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 }
eb01ba5d 292
40ca29a1
LP
293 if (!x || errno) {
294 free(m);
295 goto use_fallback;
296 }
eb01ba5d 297
eb01ba5d 298
40ca29a1
LP
299 if (x != m) {
300 free(m);
301 sd_bus_error_set_const(e, name, x);
302 return -error;
303 }
eb01ba5d 304
eb01ba5d 305 break;
40ca29a1 306 }
eb01ba5d 307
eb01ba5d 308
40ca29a1
LP
309 n = strdup(name);
310 if (!n) {
311 free(m);
312 goto use_fallback;
313 }
eb01ba5d 314
40ca29a1
LP
315 e->name = n;
316 e->message = m;
317 e->need_free = true;
eb01ba5d 318
40ca29a1
LP
319 return -error;
320
321use_fallback:
322 sd_bus_error_set_const(e, name, fallback);
323 return -error;
324}
325
326static 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
d9f644e2 383_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
40ca29a1
LP
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
391int 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;
eb01ba5d 422 }
de1c301e 423
40ca29a1
LP
424fallback:
425 return bus_error_set_strerror_or_const(e, x.name, error, x.message);
426}
427
d9f644e2 428_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
40ca29a1
LP
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);
de1c301e 447}
e3017af9
LP
448
449const char *bus_error_message(const sd_bus_error *e, int error) {
40ca29a1 450
c9b6cb28
LP
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
40ca29a1
LP
461 if (error < 0)
462 error = -error;
463
e3017af9
LP
464 return strerror(error);
465}