]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-bus/bus-error.c
machinectl: use initialization instead of zeroing
[thirdparty/systemd.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 }