bus-error.c revision d469dd44c4f49bafe839c6e0bceb6ce4bfe7b3fe
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2013 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sd-bus.h"
#include "alloc-util.h"
#include "bus-error.h"
#include "errno-list.h"
#include "string-util.h"
#include "util.h"
};
/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section.
* Hide them; for currently unknown reasons they get exported to the shared libries
* even without being listed in the sym file. */
/* Additional maps registered with sd_bus_error_add_map() are in this
* NULL terminated array */
static int bus_error_name_to_errno(const char *name) {
const sd_bus_error_map **map, *m;
const char *p;
int r;
if (!name)
return EINVAL;
if (p) {
r = errno_from_name(p);
if (r < 0)
return EIO;
return r;
}
for (m = *map;; m++) {
/* For additional error maps the end marker is actually the end marker */
if (m->code == BUS_ERROR_MAP_END_MARKER)
break;
return m->code;
}
while (m < __stop_BUS_ERROR_MAP) {
/* For magic ELF error maps, the end marker might
* appear in the middle of things, since multiple maps
* might appear in the same section. Hence, let's skip
* over it, but realign the pointer to the next 8 byte
* boundary, which is the selected alignment for the
* arrays. */
if (m->code == BUS_ERROR_MAP_END_MARKER) {
m = ALIGN8_PTR(m+1);
continue;
}
return m->code;
m++;
}
return EIO;
}
if (error < 0)
switch (error) {
case ENOMEM:
return BUS_ERROR_OOM;
case EPERM:
case EACCES:
case EINVAL:
case ESRCH:
case ENOENT:
case EEXIST:
case ETIMEDOUT:
case ETIME:
case EIO:
case ENETRESET:
case ECONNABORTED:
case ECONNRESET:
case EOPNOTSUPP:
case EADDRNOTAVAIL:
case ENOBUFS:
case EADDRINUSE:
case EBADMSG:
}
return SD_BUS_ERROR_NULL;
}
const char *name;
char *n;
if (error < 0)
if (!name)
return 0;
if (!n)
return -ENOMEM;
*ret = n;
return 1;
}
bool bus_error_is_dirty(sd_bus_error *e) {
if (!e)
return false;
}
if (!e)
return;
if (e->_need_free > 0) {
}
e->_need_free = 0;
}
if (!name)
return 0;
if (!e)
goto finish;
if (!e->name) {
*e = BUS_ERROR_OOM;
return -ENOMEM;
}
if (message)
e->_need_free = 1;
return -bus_error_name_to_errno(name);
}
if (!name)
return 0;
if (e) {
if (!e->name) {
*e = BUS_ERROR_OOM;
return -ENOMEM;
}
/* If we hit OOM on formatting the pretty message, we ignore
* this, since we at least managed to write the error name */
if (format)
e->_need_free = 1;
}
return -bus_error_name_to_errno(name);
}
if (format) {
int r;
return r;
}
}
if (!sd_bus_error_is_set(e))
return 0;
if (!dest)
goto finish;
/*
* _need_free < 0 indicates that the error is temporarily const, needs deep copying
* _need_free == 0 indicates that the error is perpetually const, needs no deep copying
* _need_free > 0 indicates that the error is fully dynamic, needs deep copying
*/
if (e->_need_free == 0)
*dest = *e;
else {
*dest = BUS_ERROR_OOM;
return -ENOMEM;
}
if (e->message)
}
return -bus_error_name_to_errno(e->name);
}
if (!name)
return 0;
if (!e)
goto finish;
return -bus_error_name_to_errno(name);
}
if (!e)
return 0;
return !!e->name;
}
if (!e)
return 0;
}
if (!e)
return 0;
if (!e->name)
return 0;
return bus_error_name_to_errno(e->name);
}
size_t k = 64;
char *m;
assert(e);
for (;;) {
char *x;
m = new(char, k);
if (!m)
return;
errno = 0;
x = strerror_r(error, m, k);
free(m);
k *= 2;
continue;
}
if (errno) {
free(m);
return;
}
if (x == m) {
if (e->_need_free > 0) {
/* Error is already dynamic, let's just update the message */
e->message = x;
} else {
char *t;
/* Error was const so far, let's make it dynamic, if we can */
if (!t) {
free(m);
return;
}
e->_need_free = 1;
e->name = t;
e->message = x;
}
} else {
free(m);
if (e->_need_free > 0) {
char *t;
/* Error is dynamic, let's hence make the message also dynamic */
t = strdup(x);
if (!t)
return;
e->message = t;
} else {
/* Error is const, hence we can just override */
e->message = x;
}
}
return;
}
}
if (error < 0)
if (!e)
return -error;
if (error == 0)
return -error;
/* First, try a const translation */
*e = errno_to_bus_error_const(error);
if (!sd_bus_error_is_set(e)) {
int k;
/* If that didn't work, try a dynamic one. */
if (k > 0)
e->_need_free = 1;
else if (k < 0) {
*e = BUS_ERROR_OOM;
return -error;
} else
*e = BUS_ERROR_FAILED;
}
/* Now, fill in the message from strerror() if we can */
bus_error_strerror(e, error);
return -error;
}
int r;
if (error < 0)
if (!e)
return -error;
if (error == 0)
return 0;
/* First, try a const translation */
*e = errno_to_bus_error_const(error);
if (!sd_bus_error_is_set(e)) {
int k;
/* If that didn't work, try a dynamic one */
if (k > 0)
e->_need_free = 1;
else if (k < 0) {
*e = BUS_ERROR_OOM;
return -ENOMEM;
} else
*e = BUS_ERROR_FAILED;
}
if (format) {
char *m;
/* Then, let's try to fill in the supplied message */
if (r >= 0) {
if (e->_need_free <= 0) {
char *t;
if (t) {
e->_need_free = 1;
e->name = t;
e->message = m;
return -error;
}
free(m);
} else {
e->message = m;
return -error;
}
}
}
/* If that didn't work, use strerror() for the message */
bus_error_strerror(e, error);
return -error;
}
int r;
if (error < 0)
if (!e)
return -error;
if (error == 0)
return 0;
if (format) {
return r;
}
return sd_bus_error_set_errno(e, error);
}
if (e) {
/* Sometimes, the D-Bus server is a little bit too verbose with
* its error messages, so let's override them here */
return "Access denied";
if (e->message)
return e->message;
}
if (error < 0)
}
unsigned n = 0;
if (additional_error_maps) {
for (;; n++) {
if (additional_error_maps[n] == NULL)
break;
if (additional_error_maps[n] == map)
return 0;
}
}
if (!maps)
return -ENOMEM;
return 1;
}