]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-error.c
terminal: provide display dimensions to API users
[thirdparty/systemd.git] / src / libsystemd / sd-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"
780896a4 30#include "errno-list.h"
de1c301e
LP
31
32#include "sd-bus.h"
33#include "bus-error.h"
34
780896a4
LP
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
38static int bus_error_name_to_errno(const char *name) {
39 const char *p;
40 int r;
0b6c04bb 41 const name_error_mapping *m;
780896a4 42
35460afc
LP
43 if (!name)
44 return EINVAL;
780896a4 45
763e20e6 46 p = startswith(name, "System.Error.");
780896a4
LP
47 if (p) {
48 r = errno_from_name(p);
49 if (r <= 0)
50 return EIO;
51
52 return r;
53 }
54
0b6c04bb
ZJS
55 m = bus_error_mapping_lookup(name, strlen(name));
56 if (m)
57 return m->code;
780896a4
LP
58
59 return EIO;
60}
61
62static sd_bus_error errno_to_bus_error_const(int error) {
63
64 if (error < 0)
65 error = -error;
66
67 switch (error) {
68
69 case ENOMEM:
70 return BUS_ERROR_OOM;
71
72 case EPERM:
73 case EACCES:
74 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
75
76 case EINVAL:
77 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
78
79 case ESRCH:
80 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
81
82 case ENOENT:
83 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
84
85 case EEXIST:
86 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
87
88 case ETIMEDOUT:
89 case ETIME:
90 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
91
92 case EIO:
93 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
94
95 case ENETRESET:
96 case ECONNABORTED:
97 case ECONNRESET:
98 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
99
100 case ENOTSUP:
101 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
102
103 case EADDRNOTAVAIL:
104 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
105
106 case ENOBUFS:
107 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
108
109 case EADDRINUSE:
110 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
111
112 case EBADMSG:
113 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
114 }
115
116 return SD_BUS_ERROR_NULL;
117}
118
119static int errno_to_bus_error_name_new(int error, char **ret) {
120 const char *name;
121 char *n;
122
123 if (error < 0)
124 error = -error;
125
126 name = errno_to_name(error);
127 if (!name)
128 return 0;
129
763e20e6 130 n = strappend("System.Error.", name);
780896a4
LP
131 if (!n)
132 return -ENOMEM;
133
134 *ret = n;
135 return 1;
136}
137
fbfa72b0
LP
138bool bus_error_is_dirty(sd_bus_error *e) {
139 if (!e)
780896a4 140 return false;
fbfa72b0 141
79f8d3d2 142 return e->name || e->message || e->_need_free != 0;
fbfa72b0
LP
143}
144
d9f644e2 145_public_ void sd_bus_error_free(sd_bus_error *e) {
de1c301e
LP
146 if (!e)
147 return;
148
79f8d3d2 149 if (e->_need_free > 0) {
de1c301e
LP
150 free((void*) e->name);
151 free((void*) e->message);
152 }
153
154 e->name = e->message = NULL;
79f8d3d2 155 e->_need_free = 0;
de1c301e
LP
156}
157
d9f644e2 158_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
29ddb38f 159
780896a4 160 if (!name)
29ddb38f 161 return 0;
780896a4
LP
162 if (!e)
163 goto finish;
40ca29a1
LP
164
165 assert_return(!bus_error_is_dirty(e), -EINVAL);
29ddb38f 166
780896a4
LP
167 e->name = strdup(name);
168 if (!e->name) {
169 *e = BUS_ERROR_OOM;
29ddb38f 170 return -ENOMEM;
29ddb38f
LP
171 }
172
780896a4
LP
173 if (message)
174 e->message = strdup(message);
175
79f8d3d2 176 e->_need_free = 1;
29ddb38f 177
780896a4
LP
178finish:
179 return -bus_error_name_to_errno(name);
29ddb38f
LP
180}
181
40ca29a1 182int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
de1c301e 183
780896a4 184 if (!name)
de1c301e 185 return 0;
780896a4
LP
186 if (!e)
187 goto finish;
40ca29a1
LP
188
189 assert_return(!bus_error_is_dirty(e), -EINVAL);
de1c301e 190
780896a4
LP
191 e->name = strdup(name);
192 if (!e->name) {
193 *e = BUS_ERROR_OOM;
de1c301e 194 return -ENOMEM;
de1c301e
LP
195 }
196
780896a4
LP
197 if (format)
198 vasprintf((char**) &e->message, format, ap);
199
79f8d3d2 200 e->_need_free = 1;
de1c301e 201
780896a4
LP
202finish:
203 return -bus_error_name_to_errno(name);
de1c301e
LP
204}
205
d9f644e2 206_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
40ca29a1
LP
207
208 if (format) {
209 int r;
210 va_list ap;
211
212 va_start(ap, format);
213 r = bus_error_setfv(e, name, format, ap);
214 va_end(ap);
215
216 return r;
217 }
218
219 return sd_bus_error_set(e, name, NULL);
220}
221
d9f644e2 222_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
89ffcd2a 223
de1c301e
LP
224 if (!sd_bus_error_is_set(e))
225 return 0;
780896a4
LP
226 if (!dest)
227 goto finish;
de1c301e 228
40ca29a1
LP
229 assert_return(!bus_error_is_dirty(dest), -EINVAL);
230
79f8d3d2
LP
231 /*
232 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
233 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
234 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
235 */
236
237 if (e->_need_free == 0)
780896a4
LP
238 *dest = *e;
239 else {
240 dest->name = strdup(e->name);
241 if (!dest->name) {
242 *dest = BUS_ERROR_OOM;
de1c301e 243 return -ENOMEM;
de1c301e 244 }
780896a4
LP
245
246 if (e->message)
247 dest->message = strdup(e->message);
248
79f8d3d2 249 dest->_need_free = 1;
89ffcd2a 250 }
de1c301e 251
780896a4
LP
252finish:
253 return -bus_error_name_to_errno(e->name);
de1c301e
LP
254}
255
d9f644e2 256_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
780896a4 257 if (!name)
40ca29a1 258 return 0;
780896a4
LP
259 if (!e)
260 goto finish;
40ca29a1
LP
261
262 assert_return(!bus_error_is_dirty(e), -EINVAL);
de1c301e 263
14c24659 264 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
780896a4
LP
265
266finish:
267 return -bus_error_name_to_errno(name);
de1c301e
LP
268}
269
d9f644e2 270_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
de1c301e
LP
271 if (!e)
272 return 0;
273
fbfa72b0 274 return !!e->name;
de1c301e
LP
275}
276
d9f644e2 277_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
de1c301e
LP
278 if (!e)
279 return 0;
280
281 return streq_ptr(e->name, name);
282}
283
d9f644e2 284_public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
eb01ba5d 285 if (!e)
780896a4 286 return 0;
40ca29a1 287
bc6a6232
LP
288 if (!e->name)
289 return 0;
290
780896a4 291 return bus_error_name_to_errno(e->name);
de1c301e
LP
292}
293
780896a4 294static void bus_error_strerror(sd_bus_error *e, int error) {
40ca29a1 295 size_t k = 64;
780896a4 296 char *m;
40ca29a1 297
780896a4 298 assert(e);
eb01ba5d 299
40ca29a1
LP
300 for (;;) {
301 char *x;
eb01ba5d 302
40ca29a1
LP
303 m = new(char, k);
304 if (!m)
780896a4 305 return;
40ca29a1
LP
306
307 errno = 0;
308 x = strerror_r(error, m, k);
309 if (errno == ERANGE || strlen(x) >= k - 1) {
310 free(m);
311 k *= 2;
312 continue;
313 }
eb01ba5d 314
40ca29a1
LP
315 if (!x || errno) {
316 free(m);
780896a4 317 return;
40ca29a1 318 }
eb01ba5d 319
780896a4 320 if (x == m) {
79f8d3d2 321 if (e->_need_free > 0) {
780896a4
LP
322 /* Error is already dynamic, let's just update the message */
323 free((char*) e->message);
324 e->message = x;
325
326 } else {
327 char *t;
328 /* Error was const so far, let's make it dynamic, if we can */
329
330 t = strdup(e->name);
331 if (!t) {
332 free(m);
333 return;
334 }
335
79f8d3d2 336 e->_need_free = 1;
780896a4
LP
337 e->name = t;
338 e->message = x;
339 }
340 } else {
40ca29a1 341 free(m);
eb01ba5d 342
79f8d3d2 343 if (e->_need_free > 0) {
780896a4 344 char *t;
eb01ba5d 345
780896a4
LP
346 /* Error is dynamic, let's hence make the message also dynamic */
347 t = strdup(x);
348 if (!t)
349 return;
eb01ba5d 350
780896a4
LP
351 free((char*) e->message);
352 e->message = t;
353 } else {
354 /* Error is const, hence we can just override */
355 e->message = x;
356 }
357 }
40ca29a1 358
780896a4
LP
359 return;
360 }
40ca29a1
LP
361}
362
780896a4 363_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
40ca29a1
LP
364
365 if (error < 0)
366 error = -error;
367
780896a4
LP
368 if (!e)
369 return -error;
370 if (error == 0)
371 return -error;
40ca29a1 372
780896a4 373 assert_return(!bus_error_is_dirty(e), -EINVAL);
40ca29a1 374
780896a4
LP
375 /* First, try a const translation */
376 *e = errno_to_bus_error_const(error);
40ca29a1 377
780896a4
LP
378 if (!sd_bus_error_is_set(e)) {
379 int k;
40ca29a1 380
780896a4 381 /* If that didn't work, try a dynamic one. */
40ca29a1 382
780896a4
LP
383 k = errno_to_bus_error_name_new(error, (char**) &e->name);
384 if (k > 0)
79f8d3d2 385 e->_need_free = 1;
780896a4
LP
386 else if (k < 0) {
387 *e = BUS_ERROR_OOM;
388 return -error;
389 } else
390 *e = BUS_ERROR_FAILED;
40ca29a1
LP
391 }
392
780896a4
LP
393 /* Now, fill in the message from strerror() if we can */
394 bus_error_strerror(e, error);
395 return -error;
40ca29a1
LP
396}
397
398int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
40ca29a1
LP
399 int r;
400
401 if (error < 0)
402 error = -error;
403
404 if (!e)
80c19740 405 return -error;
780896a4
LP
406 if (error == 0)
407 return 0;
40ca29a1
LP
408
409 assert_return(!bus_error_is_dirty(e), -EINVAL);
410
780896a4
LP
411 /* First, try a const translation */
412 *e = errno_to_bus_error_const(error);
413
414 if (!sd_bus_error_is_set(e)) {
415 int k;
416
417 /* If that didn't work, try a dynamic one */
418
419 k = errno_to_bus_error_name_new(error, (char**) &e->name);
420 if (k > 0)
79f8d3d2 421 e->_need_free = 1;
780896a4
LP
422 else if (k < 0) {
423 *e = BUS_ERROR_OOM;
424 return -ENOMEM;
425 } else
426 *e = BUS_ERROR_FAILED;
427 }
40ca29a1
LP
428
429 if (format) {
780896a4 430 char *m;
40ca29a1 431
780896a4 432 /* First, let's try to fill in the supplied message */
40ca29a1 433
780896a4
LP
434 r = vasprintf(&m, format, ap);
435 if (r >= 0) {
436
79f8d3d2 437 if (e->_need_free <= 0) {
780896a4
LP
438 char *t;
439
440 t = strdup(e->name);
441 if (t) {
79f8d3d2 442 e->_need_free = 1;
780896a4
LP
443 e->name = t;
444 e->message = m;
445 return -error;
446 }
447
448 free(m);
449 } else {
450 free((char*) e->message);
451 e->message = m;
452 return -error;
453 }
40ca29a1 454 }
eb01ba5d 455 }
de1c301e 456
780896a4
LP
457 /* If that didn't work, use strerror() for the message */
458 bus_error_strerror(e, error);
459 return -error;
40ca29a1
LP
460}
461
d9f644e2 462_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
40ca29a1
LP
463 int r;
464
80c19740
LP
465 if (error < 0)
466 error = -error;
467
40ca29a1 468 if (!e)
80c19740 469 return -error;
780896a4
LP
470 if (error == 0)
471 return 0;
40ca29a1
LP
472
473 assert_return(!bus_error_is_dirty(e), -EINVAL);
474
475 if (format) {
476 va_list ap;
477
478 va_start(ap, format);
479 r = bus_error_set_errnofv(e, error, format, ap);
480 va_end(ap);
481
482 return r;
483 }
484
485 return sd_bus_error_set_errno(e, error);
de1c301e 486}
e3017af9
LP
487
488const char *bus_error_message(const sd_bus_error *e, int error) {
40ca29a1 489
c9b6cb28
LP
490 if (e) {
491 /* Sometimes the D-Bus server is a little bit too verbose with
492 * its error messages, so let's override them here */
493 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
494 return "Access denied";
495
496 if (e->message)
497 return e->message;
498 }
499
40ca29a1
LP
500 if (error < 0)
501 error = -error;
502
e3017af9
LP
503 return strerror(error);
504}