mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-08-27 19:11:16 +00:00
proxy/libvirt_proxy.c qemud/Makefile.am qemud/conf.c qemud/conf.h qemud/dispatch.c qemud/internal.h qemud/protocol.h src/driver.h src/internal.h src/libvirt.c src/libvirt_sym.version src/proxy_internal.c src/proxy_internal.h src/qemu_internal.c src/test.c src/virsh.c src/virterror.c src/xen_internal.c src/xen_internal.h src/xend_internal.c src/xm_internal.c src/xml.h src/xs_internal.c: applied patch from Richard Jones adding virConnectGetCapabilities(), plus various small little fixes * docs/*: fixed the api extractor script and regenerated Daniel
630 lines
17 KiB
C
630 lines
17 KiB
C
/*
|
|
* virterror.c: implements error handling and reporting code for libvirt
|
|
*
|
|
* Copy: Copyright (C) 2006 Red Hat, Inc.
|
|
*
|
|
* See COPYING.LIB for the License of this software
|
|
*
|
|
* Author: Daniel Veillard <veillard@redhat.com>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include "libvirt/libvirt.h"
|
|
#include "libvirt/virterror.h"
|
|
#include "internal.h"
|
|
|
|
static virError lastErr = /* the last error */
|
|
{ 0, 0, NULL, VIR_ERR_NONE, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
|
|
static virErrorFunc virErrorHandler = NULL; /* global error handlet */
|
|
static void *virUserData = NULL; /* associated data */
|
|
|
|
/*
|
|
* Macro used to format the message as a string in __virRaiseError
|
|
* and borrowed from libxml2.
|
|
*/
|
|
#define VIR_GET_VAR_STR(msg, str) { \
|
|
int size, prev_size = -1; \
|
|
int chars; \
|
|
char *larger; \
|
|
va_list ap; \
|
|
\
|
|
str = (char *) malloc(150); \
|
|
if (str != NULL) { \
|
|
\
|
|
size = 150; \
|
|
\
|
|
while (1) { \
|
|
va_start(ap, msg); \
|
|
chars = vsnprintf(str, size, msg, ap); \
|
|
va_end(ap); \
|
|
if ((chars > -1) && (chars < size)) { \
|
|
if (prev_size == chars) { \
|
|
break; \
|
|
} else { \
|
|
prev_size = chars; \
|
|
} \
|
|
} \
|
|
if (chars > -1) \
|
|
size += chars + 1; \
|
|
else \
|
|
size += 100; \
|
|
if ((larger = (char *) realloc(str, size)) == NULL) { \
|
|
break; \
|
|
} \
|
|
str = larger; \
|
|
}} \
|
|
}
|
|
|
|
/*
|
|
* virGetLastError:
|
|
*
|
|
* Provide a pointer to the last error caught at the library level
|
|
* Simpler but may not be suitable for multithreaded accesses, in which
|
|
* case use virCopyLastError()
|
|
*
|
|
* Returns a pointer to the last error or NULL if none occured.
|
|
*/
|
|
virErrorPtr
|
|
virGetLastError(void)
|
|
{
|
|
if (lastErr.code == VIR_ERR_OK)
|
|
return (NULL);
|
|
return (&lastErr);
|
|
}
|
|
|
|
/*
|
|
* virCopyLastError:
|
|
* @to: target to receive the copy
|
|
*
|
|
* Copy the content of the last error caught at the library level
|
|
* One will need to free the result with virResetError()
|
|
*
|
|
* Returns 0 if no error was found and the error code otherwise and -1 in case
|
|
* of parameter error.
|
|
*/
|
|
int
|
|
virCopyLastError(virErrorPtr to)
|
|
{
|
|
if (to == NULL)
|
|
return (-1);
|
|
if (lastErr.code == VIR_ERR_OK)
|
|
return (0);
|
|
memcpy(to, &lastErr, sizeof(virError));
|
|
return (lastErr.code);
|
|
}
|
|
|
|
/**
|
|
* virResetError:
|
|
* @err: pointer to the virError to clean up
|
|
*
|
|
* Reset the error being pointed to
|
|
*/
|
|
void
|
|
virResetError(virErrorPtr err)
|
|
{
|
|
if (err == NULL)
|
|
return;
|
|
if (err->message != NULL)
|
|
free(err->message);
|
|
if (err->str1 != NULL)
|
|
free(err->str1);
|
|
if (err->str2 != NULL)
|
|
free(err->str2);
|
|
if (err->str3 != NULL)
|
|
free(err->str3);
|
|
memset(err, 0, sizeof(virError));
|
|
}
|
|
|
|
/**
|
|
* virResetLastError:
|
|
*
|
|
* Reset the last error caught at the library level.
|
|
*/
|
|
void
|
|
virResetLastError(void)
|
|
{
|
|
virResetError(&lastErr);
|
|
}
|
|
|
|
/**
|
|
* virConnGetLastError:
|
|
* @conn: pointer to the hypervisor connection
|
|
*
|
|
* Provide a pointer to the last error caught on that connection
|
|
* Simpler but may not be suitable for multithreaded accesses, in which
|
|
* case use virConnCopyLastError()
|
|
*
|
|
* Returns a pointer to the last error or NULL if none occured.
|
|
*/
|
|
virErrorPtr
|
|
virConnGetLastError(virConnectPtr conn)
|
|
{
|
|
if (conn == NULL)
|
|
return (NULL);
|
|
return (&conn->err);
|
|
}
|
|
|
|
/**
|
|
* virConnCopyLastError:
|
|
* @conn: pointer to the hypervisor connection
|
|
* @to: target to receive the copy
|
|
*
|
|
* Copy the content of the last error caught on that connection
|
|
* One will need to free the result with virResetError()
|
|
*
|
|
* Returns 0 if no error was found and the error code otherwise and -1 in case
|
|
* of parameter error.
|
|
*/
|
|
int
|
|
virConnCopyLastError(virConnectPtr conn, virErrorPtr to)
|
|
{
|
|
if (conn == NULL)
|
|
return (-1);
|
|
if (to == NULL)
|
|
return (-1);
|
|
if (conn->err.code == VIR_ERR_OK)
|
|
return (0);
|
|
memcpy(to, &conn->err, sizeof(virError));
|
|
return (conn->err.code);
|
|
}
|
|
|
|
/**
|
|
* virConnResetLastError:
|
|
* @conn: pointer to the hypervisor connection
|
|
*
|
|
* Reset the last error caught on that connection
|
|
*/
|
|
void
|
|
virConnResetLastError(virConnectPtr conn)
|
|
{
|
|
if (conn == NULL)
|
|
return;
|
|
virResetError(&conn->err);
|
|
}
|
|
|
|
/**
|
|
* virSetErrorFunc:
|
|
* @userData: pointer to the user data provided in the handler callback
|
|
* @handler: the function to get called in case of error or NULL
|
|
*
|
|
* Set a library global error handling function, if @handler is NULL,
|
|
* it will reset to default printing on stderr. The error raised there
|
|
* are those for which no handler at the connection level could caught.
|
|
*/
|
|
void
|
|
virSetErrorFunc(void *userData, virErrorFunc handler)
|
|
{
|
|
virErrorHandler = handler;
|
|
virUserData = userData;
|
|
}
|
|
|
|
/**
|
|
* virConnSetErrorFunc:
|
|
* @conn: pointer to the hypervisor connection
|
|
* @userData: pointer to the user data provided in the handler callback
|
|
* @handler: the function to get called in case of error or NULL
|
|
*
|
|
* Set a connection error handling function, if @handler is NULL
|
|
* it will reset to default which is to pass error back to the global
|
|
* library handler.
|
|
*/
|
|
void
|
|
virConnSetErrorFunc(virConnectPtr conn, void *userData,
|
|
virErrorFunc handler)
|
|
{
|
|
if (conn == NULL)
|
|
return;
|
|
conn->handler = handler;
|
|
conn->userData = userData;
|
|
}
|
|
|
|
/**
|
|
* virDefaultErrorFunc:
|
|
* @err: pointer to the error.
|
|
*
|
|
* Default routine reporting an error to stderr.
|
|
*/
|
|
void
|
|
virDefaultErrorFunc(virErrorPtr err)
|
|
{
|
|
const char *lvl = "", *dom = "", *domain = "", *network = "";
|
|
int len;
|
|
|
|
if ((err == NULL) || (err->code == VIR_ERR_OK))
|
|
return;
|
|
switch (err->level) {
|
|
case VIR_ERR_NONE:
|
|
lvl = "";
|
|
break;
|
|
case VIR_ERR_WARNING:
|
|
lvl = _("warning");
|
|
break;
|
|
case VIR_ERR_ERROR:
|
|
lvl = _("error");
|
|
break;
|
|
}
|
|
switch (err->domain) {
|
|
case VIR_FROM_NONE:
|
|
dom = "";
|
|
break;
|
|
case VIR_FROM_XEN:
|
|
dom = "Xen ";
|
|
break;
|
|
case VIR_FROM_XML:
|
|
dom = "XML ";
|
|
break;
|
|
case VIR_FROM_XEND:
|
|
dom = "Xen Daemon ";
|
|
break;
|
|
case VIR_FROM_XENSTORE:
|
|
dom = "Xen Store ";
|
|
break;
|
|
case VIR_FROM_DOM:
|
|
dom = "Domain ";
|
|
break;
|
|
case VIR_FROM_RPC:
|
|
dom = "XML-RPC ";
|
|
break;
|
|
case VIR_FROM_QEMU:
|
|
dom = "QEMU ";
|
|
break;
|
|
case VIR_FROM_NET:
|
|
dom = "Network ";
|
|
break;
|
|
}
|
|
if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
|
|
domain = err->dom->name;
|
|
} else if ((err->net != NULL) && (err->code != VIR_ERR_INVALID_NETWORK)) {
|
|
network = err->net->name;
|
|
}
|
|
len = strlen(err->message);
|
|
if ((err->domain == VIR_FROM_XML) && (err->code == VIR_ERR_XML_DETAIL) &&
|
|
(err->int1 != 0))
|
|
fprintf(stderr, "libvir: %s%s %s%s: line %d: %s",
|
|
dom, lvl, domain, network, err->int1, err->message);
|
|
else if ((len == 0) || (err->message[len - 1] != '\n'))
|
|
fprintf(stderr, "libvir: %s%s %s%s: %s\n",
|
|
dom, lvl, domain, network, err->message);
|
|
else
|
|
fprintf(stderr, "libvir: %s%s %s%s: %s",
|
|
dom, lvl, domain, network, err->message);
|
|
}
|
|
|
|
/**
|
|
* __virRaiseError:
|
|
* @conn: the connection to the hypervisor if available
|
|
* @dom: the domain if available
|
|
* @net: the network if available
|
|
* @domain: the virErrorDomain indicating where it's coming from
|
|
* @code: the virErrorNumber code for the error
|
|
* @level: the virErrorLevel for the error
|
|
* @str1: extra string info
|
|
* @str2: extra string info
|
|
* @str3: extra string info
|
|
* @int1: extra int info
|
|
* @int2: extra int info
|
|
* @msg: the message to display/transmit
|
|
* @...: extra parameters for the message display
|
|
*
|
|
* Internal routine called when an error is detected. It will raise it
|
|
* immediately if a callback is found and store it for later handling.
|
|
*/
|
|
void
|
|
__virRaiseError(virConnectPtr conn, virDomainPtr dom, virNetworkPtr net,
|
|
int domain, int code, virErrorLevel level,
|
|
const char *str1, const char *str2, const char *str3,
|
|
int int1, int int2, const char *msg, ...)
|
|
{
|
|
virErrorPtr to = &lastErr;
|
|
void *userData = virUserData;
|
|
virErrorFunc handler = virErrorHandler;
|
|
char *str;
|
|
|
|
if (code == VIR_ERR_OK)
|
|
return;
|
|
|
|
/*
|
|
* try to find the best place to save and report the error
|
|
*/
|
|
if (conn != NULL) {
|
|
to = &conn->err;
|
|
if (conn->handler != NULL) {
|
|
handler = conn->handler;
|
|
userData = conn->userData;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* formats the message
|
|
*/
|
|
if (msg == NULL) {
|
|
str = strdup(_("No error message provided"));
|
|
} else {
|
|
VIR_GET_VAR_STR(msg, str);
|
|
}
|
|
|
|
/*
|
|
* Save the information about the error
|
|
*/
|
|
virResetError(to);
|
|
to->conn = conn;
|
|
to->dom = dom;
|
|
to->net = net;
|
|
to->domain = domain;
|
|
to->code = code;
|
|
to->message = str;
|
|
to->level = level;
|
|
if (str1 != NULL)
|
|
to->str1 = strdup(str1);
|
|
if (str2 != NULL)
|
|
to->str2 = strdup(str2);
|
|
if (str3 != NULL)
|
|
to->str3 = strdup(str3);
|
|
to->int1 = int1;
|
|
to->int2 = int2;
|
|
|
|
/*
|
|
* now, report it
|
|
*/
|
|
if (handler != NULL) {
|
|
handler(userData, to);
|
|
} else {
|
|
virDefaultErrorFunc(to);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* __virErrorMsg:
|
|
* @error: the virErrorNumber
|
|
* @info: usually the first paprameter string
|
|
*
|
|
* Internal routine to get the message associated to an error raised
|
|
* from the library
|
|
*
|
|
* Returns the constant string associated to @error
|
|
*/
|
|
const char *
|
|
__virErrorMsg(virErrorNumber error, const char *info)
|
|
{
|
|
const char *errmsg = NULL;
|
|
|
|
switch (error) {
|
|
case VIR_ERR_OK:
|
|
return (NULL);
|
|
case VIR_ERR_INTERNAL_ERROR:
|
|
if (info != NULL)
|
|
errmsg = _("internal error %s");
|
|
else
|
|
errmsg = _("internal error");
|
|
break;
|
|
case VIR_ERR_NO_MEMORY:
|
|
errmsg = _("out of memory");
|
|
break;
|
|
case VIR_ERR_NO_SUPPORT:
|
|
if (info != NULL)
|
|
errmsg = _("no support for hypervisor");
|
|
else
|
|
errmsg = _("no support for hypervisor %s");
|
|
break;
|
|
case VIR_ERR_NO_CONNECT:
|
|
if (info == NULL)
|
|
errmsg = _("could not connect to hypervisor");
|
|
else
|
|
errmsg = _("could not connect to %s");
|
|
break;
|
|
case VIR_ERR_INVALID_CONN:
|
|
if (info == NULL)
|
|
errmsg = _("invalid connection pointer in");
|
|
else
|
|
errmsg = _("invalid connection pointer in %s");
|
|
break;
|
|
case VIR_ERR_INVALID_DOMAIN:
|
|
if (info == NULL)
|
|
errmsg = _("invalid domain pointer in");
|
|
else
|
|
errmsg = _("invalid domain pointer in %s");
|
|
break;
|
|
case VIR_ERR_INVALID_ARG:
|
|
if (info == NULL)
|
|
errmsg = _("invalid argument in");
|
|
else
|
|
errmsg = _("invalid argument in %s");
|
|
break;
|
|
case VIR_ERR_OPERATION_FAILED:
|
|
if (info != NULL)
|
|
errmsg = _("operation failed: %s");
|
|
else
|
|
errmsg = _("operation failed");
|
|
break;
|
|
case VIR_ERR_GET_FAILED:
|
|
if (info != NULL)
|
|
errmsg = _("GET operation failed: %s");
|
|
else
|
|
errmsg = _("GET operation failed");
|
|
break;
|
|
case VIR_ERR_POST_FAILED:
|
|
if (info != NULL)
|
|
errmsg = _("POST operation failed: %s");
|
|
else
|
|
errmsg = _("POST operation failed");
|
|
break;
|
|
case VIR_ERR_HTTP_ERROR:
|
|
errmsg = _("got unknown HTTP error code %d");
|
|
break;
|
|
case VIR_ERR_UNKNOWN_HOST:
|
|
if (info != NULL)
|
|
errmsg = _("unknown host %s");
|
|
else
|
|
errmsg = _("unknown host");
|
|
break;
|
|
case VIR_ERR_SEXPR_SERIAL:
|
|
if (info != NULL)
|
|
errmsg = _("failed to serialize S-Expr: %s");
|
|
else
|
|
errmsg = _("failed to serialize S-Expr");
|
|
break;
|
|
case VIR_ERR_NO_XEN:
|
|
if (info == NULL)
|
|
errmsg = _("could not use Xen hypervisor entry");
|
|
else
|
|
errmsg = _("could not use Xen hypervisor entry %s");
|
|
break;
|
|
case VIR_ERR_NO_XENSTORE:
|
|
if (info == NULL)
|
|
errmsg = _("could not connect to Xen Store");
|
|
else
|
|
errmsg = _("could not connect to Xen Store %s");
|
|
break;
|
|
case VIR_ERR_XEN_CALL:
|
|
errmsg = _("failed Xen syscall %s %d");
|
|
break;
|
|
case VIR_ERR_OS_TYPE:
|
|
if (info == NULL)
|
|
errmsg = _("unknown OS type");
|
|
else
|
|
errmsg = _("unknown OS type %s");
|
|
break;
|
|
case VIR_ERR_NO_KERNEL:
|
|
errmsg = _("missing kernel information");
|
|
break;
|
|
case VIR_ERR_NO_ROOT:
|
|
if (info == NULL)
|
|
errmsg = _("missing root device information");
|
|
else
|
|
errmsg = _("missing root device information in %s");
|
|
break;
|
|
case VIR_ERR_NO_SOURCE:
|
|
if (info == NULL)
|
|
errmsg = _("missing source information for device");
|
|
else
|
|
errmsg = _("missing source information for device %s");
|
|
break;
|
|
case VIR_ERR_NO_TARGET:
|
|
if (info == NULL)
|
|
errmsg = _("missing target information for device");
|
|
else
|
|
errmsg = _("missing target information for device %s");
|
|
break;
|
|
case VIR_ERR_NO_NAME:
|
|
if (info == NULL)
|
|
errmsg = _("missing domain name information");
|
|
else
|
|
errmsg = _("missing domain name information in %s");
|
|
break;
|
|
case VIR_ERR_NO_OS:
|
|
if (info == NULL)
|
|
errmsg = _("missing operating system information");
|
|
else
|
|
errmsg = _("missing operating system information for %s");
|
|
break;
|
|
case VIR_ERR_NO_DEVICE:
|
|
if (info == NULL)
|
|
errmsg = _("missing devices information");
|
|
else
|
|
errmsg = _("missing devices information for %s");
|
|
break;
|
|
case VIR_ERR_DRIVER_FULL:
|
|
if (info == NULL)
|
|
errmsg = _("too many drivers registered");
|
|
else
|
|
errmsg = _("too many drivers registered in %s");
|
|
break;
|
|
case VIR_ERR_CALL_FAILED:
|
|
if (info == NULL)
|
|
errmsg = _("library call failed, possibly not supported");
|
|
else
|
|
errmsg = _("library call %s failed, possibly not supported");
|
|
break;
|
|
case VIR_ERR_XML_ERROR:
|
|
if (info == NULL)
|
|
errmsg = _("XML description not well formed or invalid");
|
|
else
|
|
errmsg = _("XML description for %s is not well formed or invalid");
|
|
break;
|
|
case VIR_ERR_DOM_EXIST:
|
|
if (info == NULL)
|
|
errmsg = _("this domain exists already");
|
|
else
|
|
errmsg = _("domain %s exists already");
|
|
break;
|
|
case VIR_ERR_OPERATION_DENIED:
|
|
if (info == NULL)
|
|
errmsg = _("operation forbidden for read only access");
|
|
else
|
|
errmsg = _("operation %s forbidden for read only access");
|
|
break;
|
|
case VIR_ERR_OPEN_FAILED:
|
|
if (info == NULL)
|
|
errmsg = _("failed to open configuration file for reading");
|
|
else
|
|
errmsg = _("failed to open %s for reading");
|
|
break;
|
|
case VIR_ERR_READ_FAILED:
|
|
if (info == NULL)
|
|
errmsg = _("failed to read configuration file");
|
|
else
|
|
errmsg = _("failed to read configuration file %s");
|
|
break;
|
|
case VIR_ERR_PARSE_FAILED:
|
|
if (info == NULL)
|
|
errmsg = _("failed to parse configuration file");
|
|
else
|
|
errmsg = _("failed to parse configuration file %s");
|
|
break;
|
|
case VIR_ERR_CONF_SYNTAX:
|
|
if (info == NULL)
|
|
errmsg = _("configuration file syntax error");
|
|
else
|
|
errmsg = _("configuration file syntax error: %s");
|
|
break;
|
|
case VIR_ERR_WRITE_FAILED:
|
|
if (info == NULL)
|
|
errmsg = _("failed to write configuration file");
|
|
else
|
|
errmsg = _("failed to write configuration file: %s");
|
|
break;
|
|
case VIR_ERR_XML_DETAIL:
|
|
if (info == NULL)
|
|
errmsg = _("parser error");
|
|
else
|
|
errmsg = "%s";
|
|
break;
|
|
case VIR_ERR_INVALID_NETWORK:
|
|
if (info == NULL)
|
|
errmsg = _("invalid network pointer in");
|
|
else
|
|
errmsg = _("invalid network pointer in %s");
|
|
break;
|
|
case VIR_ERR_NETWORK_EXIST:
|
|
if (info == NULL)
|
|
errmsg = _("this network exists already");
|
|
else
|
|
errmsg = _("network %s exists already");
|
|
break;
|
|
case VIR_ERR_SYSTEM_ERROR:
|
|
if (info == NULL)
|
|
errmsg = _("system call error");
|
|
else
|
|
errmsg = "%s";
|
|
break;
|
|
}
|
|
return (errmsg);
|
|
}
|
|
|
|
/*
|
|
* vim: set tabstop=4:
|
|
* vim: set shiftwidth=4:
|
|
* vim: set expandtab:
|
|
*/
|
|
/*
|
|
* Local variables:
|
|
* indent-tabs-mode: nil
|
|
* c-indent-level: 4
|
|
* c-basic-offset: 4
|
|
* tab-width: 4
|
|
* End:
|
|
*/
|