/*
 * virterror.c: implements error handling and reporting code for libvirt
 *
 * Copy:  Copyright (C) 2006, 2008, 2009 Red Hat, Inc.
 *
 * See COPYING.LIB for the License of this software
 *
 * Author: Daniel Veillard <veillard@redhat.com>
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "virterror_internal.h"
#include "datatypes.h"
#include "logging.h"
#include "memory.h"
#include "threads.h"
#include "util.h"

virThreadLocal virLastErr;

virErrorFunc virErrorHandler = NULL;     /* global error handler */
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;						\
    }}								\
}

static virLogPriority virErrorLevelPriority(virErrorLevel level) {
    switch (level) {
        case VIR_ERR_NONE:
            return(VIR_LOG_INFO);
        case VIR_ERR_WARNING:
            return(VIR_LOG_WARN);
        case VIR_ERR_ERROR:
            return(VIR_LOG_ERROR);
    }
    return(VIR_LOG_ERROR);
}

static const char *virErrorDomainName(virErrorDomain domain) {
    const char *dom = "unknown";
    switch (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_XEN_INOTIFY:
            dom = "Xen Inotify ";
            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;
        case VIR_FROM_TEST:
            dom = "Test ";
            break;
        case VIR_FROM_REMOTE:
            dom = "Remote ";
            break;
        case VIR_FROM_SEXPR:
            dom = "S-Expr ";
            break;
        case VIR_FROM_PROXY:
            dom = "PROXY ";
            break;
        case VIR_FROM_CONF:
            dom = "Config ";
            break;
        case VIR_FROM_PHYP:
            dom = "IBM power hypervisor ";
            break;
        case VIR_FROM_OPENVZ:
            dom = "OpenVZ ";
            break;
        case VIR_FROM_XENXM:
            dom = "Xen XM ";
            break;
        case VIR_FROM_STATS_LINUX:
            dom = "Linux Stats ";
            break;
        case VIR_FROM_LXC:
            dom = "Linux Container ";
            break;
        case VIR_FROM_STORAGE:
            dom = "Storage ";
            break;
        case VIR_FROM_NETWORK:
            dom = "Network Config ";
            break;
        case VIR_FROM_DOMAIN:
            dom = "Domain Config ";
            break;
        case VIR_FROM_NODEDEV:
            dom = "Node Device ";
            break;
        case VIR_FROM_UML:
            dom = "UML ";
            break;
        case VIR_FROM_SECURITY:
            dom = "Security Labeling ";
            break;
        case VIR_FROM_VBOX:
            dom = "VBOX ";
            break;
        case VIR_FROM_INTERFACE:
            dom = "Interface ";
            break;
        case VIR_FROM_ONE:
            dom = "ONE ";
            break;
        case VIR_FROM_ESX:
            dom = "ESX ";
            break;
    }
    return(dom);
}


/*
 * Internal helper that is called when a thread exits, to
 * release the error object stored in the thread local
 */
static void
virLastErrFreeData(void *data)
{
    virErrorPtr err = data;
    if (!err)
        return;
    virResetError(err);
    VIR_FREE(err);
}


/**
 * virErrorInitialize:
 *
 * Initialize the error data (per thread)
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virErrorInitialize(void)
{
    return virThreadLocalInit(&virLastErr, virLastErrFreeData);
}


/*
 * Internal helper to ensure a generic error code is stored
 * in case where API returns failure, but forgot to set an
 * error
 */
static void
virErrorGenericFailure(virErrorPtr err)
{
    err->code = VIR_ERR_INTERNAL_ERROR;
    err->domain = VIR_FROM_NONE;
    err->level = VIR_ERR_ERROR;
    err->message = strdup(_("Unknown failure"));
}


/*
 * Internal helper to perform a deep copy of the an error
 */
static int
virCopyError(virErrorPtr from,
             virErrorPtr to)
{
    int ret = 0;
    if (!to)
        return 0;
    virResetError(to);
    if (!from)
        return 0;
    to->code = from->code;
    to->domain = from->domain;
    to->level = from->level;
    if (from->message && !(to->message = strdup(from->message)))
        ret = -1;
    if (from->str1 && !(to->str1 = strdup(from->str1)))
        ret = -1;
    if (from->str2 && !(to->str2 = strdup(from->str2)))
        ret = -1;
    if (from->str3 && !(to->str3 = strdup(from->str3)))
        ret = -1;
    to->int1 = from->int1;
    to->int2 = from->int2;
    /*
     * Delibrately not setting 'conn', 'dom', 'net' references
     */
    return ret;
}

static virErrorPtr
virLastErrorObject(void)
{
    virErrorPtr err;
    err = virThreadLocalGet(&virLastErr);
    if (!err) {
        if (VIR_ALLOC(err) < 0)
            return NULL;
        virThreadLocalSet(&virLastErr, err);
    }
    return err;
}


/**
 * virGetLastError:
 *
 * Provide a pointer to the last error caught at the library level
 *
 * The error object is kept in thread local storage, so separate
 * threads can safely access this concurrently.
 *
 * Returns a pointer to the last error or NULL if none occurred.
 */
virErrorPtr
virGetLastError(void)
{
    virErrorPtr err = virLastErrorObject();
    if (!err || err->code == VIR_ERR_OK)
        return NULL;
    return err;
}

/**
 * virCopyLastError:
 * @to: target to receive the copy
 *
 * Copy the content of the last error caught at the library level
 *
 * The error object is kept in thread local storage, so separate
 * threads can safely access this concurrently.
 *
 * 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)
{
    virErrorPtr err = virLastErrorObject();
    /* We can't guarentee caller has initialized it to zero */
    memset(to, 0, sizeof(*to));
    if (err)
        virCopyError(err, to);
    else
        virResetError(to);
    return to->code;
}

/**
 * virSaveLastError:
 *
 * Save the last error into a new error object.
 *
 * Returns a pointer to the copied error or NULL if allocation failed.
 * It is the caller's responsibility to free the error with
 * virFreeError().
 */
virErrorPtr
virSaveLastError(void)
{
    virErrorPtr to;

    if (VIR_ALLOC(to) < 0)
        return NULL;

    virCopyLastError(to);
    return to;
}

/**
 * virResetError:
 * @err: pointer to the virError to clean up
 *
 * Reset the error being pointed to
 */
void
virResetError(virErrorPtr err)
{
    if (err == NULL)
        return;
    free(err->message);
    free(err->str1);
    free(err->str2);
    free(err->str3);
    memset(err, 0, sizeof(virError));
}

/**
 * virFreeError:
 * @err: error to free
 *
 * Resets and frees the given error.
 */
void
virFreeError(virErrorPtr err)
{
    virResetError(err);
    VIR_FREE(err);
}

/**
 * virResetLastError:
 *
 * Reset the last error caught at the library level.
 *
 * The error object is kept in thread local storage, so separate
 * threads can safely access this concurrently, only resetting
 * their own error object.
 */
void
virResetLastError(void)
{
    virErrorPtr err = virLastErrorObject();
    if (err)
        virResetError(err);
}

/**
 * virConnGetLastError:
 * @conn: pointer to the hypervisor connection
 *
 * Provide a pointer to the last error caught on that connection
 *
 * This method is not protected against access from multiple
 * threads. In a multi-threaded application, always use the
 * global virGetLastError() API which is backed by thread
 * local storage.
 *
 * If the connection object was discovered to be invalid by
 * an API call, then the error will be reported against the
 * global error object.
 *
 * Since 0.6.0, all errors reported in the per-connection object
 * are also duplicated in the global error object. As such an
 * application can always use virGetLastError(). This method
 * remains for backwards compatability.
 *
 * Returns a pointer to the last error or NULL if none occurred.
 */
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
 *
 * This method is not protected against access from multiple
 * threads. In a multi-threaded application, always use the
 * global virGetLastError() API which is backed by thread
 * local storage.
 *
 * If the connection object was discovered to be invalid by
 * an API call, then the error will be reported against the
 * global error object.
 *
 * Since 0.6.0, all errors reported in the per-connection object
 * are also duplicated in the global error object. As such an
 * application can always use virGetLastError(). This method
 * remains for backwards compatability.
 *
 * 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)
{
    /* We can't guarentee caller has initialized it to zero */
    memset(to, 0, sizeof(*to));

    if (conn == NULL)
        return -1;
    virMutexLock(&conn->lock);
    if (conn->err.code == VIR_ERR_OK)
        virResetError(to);
    else
        virCopyError(&conn->err, to);
    virMutexUnlock(&conn->lock);
    return to->code;
}

/**
 * virConnResetLastError:
 * @conn: pointer to the hypervisor connection
 *
 * The error object is kept in thread local storage, so separate
 * threads can safely access this concurrently.
 *
 * Reset the last error caught on that connection
 */
void
virConnResetLastError(virConnectPtr conn)
{
    if (conn == NULL)
        return;
    virMutexLock(&conn->lock);
    virResetError(&conn->err);
    virMutexUnlock(&conn->lock);
}

/**
 * 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;
    virMutexLock(&conn->lock);
    conn->handler = handler;
    conn->userData = userData;
    virMutexUnlock(&conn->lock);
}

/**
 * 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;
    }
    dom = virErrorDomainName(err->domain);
    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);
}

/**
 * virSetGlobalError:
 * Internal helper to ensure the global error object
 * is initialized with a generic message if not already
 * set.
 */
void
virSetGlobalError(void)
{
    virErrorPtr err = virLastErrorObject();

    if (err && err->code == VIR_ERR_OK)
        virErrorGenericFailure(err);
}

/**
 * virSetConnError:
 * @conn: pointer to the hypervisor connection
 *
 * Internal helper to ensure the connection error object
 * is initialized from the global object.
 */
void
virSetConnError(virConnectPtr conn)
{
    virErrorPtr err = virLastErrorObject();

    if (err && err->code == VIR_ERR_OK)
        virErrorGenericFailure(err);

    if (conn) {
        virMutexLock(&conn->lock);
        if (err)
            virCopyError(err, &conn->err);
        else
            virErrorGenericFailure(&conn->err);
        virMutexUnlock(&conn->lock);
    }
}



/**
 * virRaiseErrorFull:
 * @conn: the connection to the hypervisor if available
 * @filename: filename where error was raised
 * @funcname: function name where error was raised
 * @linenr: line number where error was raised
 * @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
 * @fmt:  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
virRaiseErrorFull(virConnectPtr conn,
                  const char *filename ATTRIBUTE_UNUSED,
                  const char *funcname,
                  size_t linenr,
                  int domain,
                  int code,
                  virErrorLevel level,
                  const char *str1,
                  const char *str2,
                  const char *str3,
                  int int1,
                  int int2,
                  const char *fmt, ...)
{
    virErrorPtr to;
    void *userData = virUserData;
    virErrorFunc handler = virErrorHandler;
    char *str;

    /*
     * All errors are recorded in thread local storage
     * For compatability, public API calls will copy them
     * to the per-connection error object when neccessary
     */
    to = virLastErrorObject();
    if (!to)
        return; /* Hit OOM allocating thread error object, sod all we can do now */

    virResetError(to);

    if (code == VIR_ERR_OK)
        return;

    /*
     * try to find the best place to save and report the error
     */
    if (conn != NULL) {
        virMutexLock(&conn->lock);
        if (conn->handler != NULL) {
            handler = conn->handler;
            userData = conn->userData;
        }
        virMutexUnlock(&conn->lock);
    }

    /*
     * formats the message
     */
    if (fmt == NULL) {
        str = strdup(_("No error message provided"));
    } else {
        VIR_GET_VAR_STR(fmt, str);
    }

    /*
     * Hook up the error or warning to the logging facility
     * XXXX should we include filename as 'category' instead of domain name ?
     */
    virLogMessage(virErrorDomainName(domain), virErrorLevelPriority(level),
                  funcname, linenr, 1, "%s", str);

    /*
     * Save the information about the error
     */
    virResetError(to);
    /*
     * Delibrately not setting conn, dom & net fields since
     * they're utterly unsafe
     */
    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 parameter 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 = _("this function is not supported by the hypervisor");
            else
                errmsg = _("this function is not supported by the hypervisor: %s");
            break;
        case VIR_ERR_NO_CONNECT:
            if (info == NULL)
                errmsg = _("no hypervisor driver available");
            else
                errmsg = _("no hypervisor driver available for %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");
            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: /* DEPRECATED, use VIR_ERR_NO_SUPPORT */
            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;
        case VIR_ERR_RPC:
            if (info == NULL)
                errmsg = _("RPC error");
            else
                errmsg = "%s";
            break;
        case VIR_ERR_GNUTLS_ERROR:
            if (info == NULL)
                errmsg = _("GNUTLS call error");
            else
                errmsg = "%s";
            break;
        case VIR_WAR_NO_NETWORK:
            if (info == NULL)
                errmsg = _("Failed to find the network");
            else
                errmsg = _("Failed to find the network: %s");
            break;
        case VIR_ERR_NO_DOMAIN:
            if (info == NULL)
                errmsg = _("Domain not found");
            else
                errmsg = _("Domain not found: %s");
            break;
        case VIR_ERR_NO_NETWORK:
            if (info == NULL)
                errmsg = _("Network not found");
            else
                errmsg = _("Network not found: %s");
            break;
        case VIR_ERR_INVALID_MAC:
            if (info == NULL)
                errmsg = _("invalid MAC address");
            else
                errmsg = _("invalid MAC address: %s");
            break;
        case VIR_ERR_AUTH_FAILED:
            if (info == NULL)
                errmsg = _("authentication failed");
            else
                errmsg = _("authentication failed: %s");
            break;
        case VIR_ERR_NO_STORAGE_POOL:
            if (info == NULL)
                    errmsg = _("Storage pool not found");
            else
                    errmsg = _("Storage pool not found: %s");
            break;
        case VIR_ERR_NO_STORAGE_VOL:
            if (info == NULL)
                    errmsg = _("Storage volume not found");
            else
                    errmsg = _("Storage volume not found: %s");
            break;
        case VIR_ERR_INVALID_STORAGE_POOL:
            if (info == NULL)
                    errmsg = _("invalid storage pool pointer in");
            else
                    errmsg = _("invalid storage pool pointer in %s");
            break;
        case VIR_ERR_INVALID_STORAGE_VOL:
            if (info == NULL)
                    errmsg = _("invalid storage volume pointer in");
            else
                    errmsg = _("invalid storage volume pointer in %s");
            break;
        case VIR_WAR_NO_STORAGE:
            if (info == NULL)
                    errmsg = _("Failed to find a storage driver");
            else
                    errmsg = _("Failed to find a storage driver: %s");
            break;
        case VIR_WAR_NO_NODE:
            if (info == NULL)
                    errmsg = _("Failed to find a node driver");
            else
                    errmsg = _("Failed to find a node driver: %s");
            break;
        case VIR_ERR_INVALID_NODE_DEVICE:
            if (info == NULL)
                    errmsg = _("invalid node device pointer");
            else
                    errmsg = _("invalid node device pointer in %s");
            break;
        case VIR_ERR_NO_NODE_DEVICE:
            if (info == NULL)
                    errmsg = _("Node device not found");
            else
                    errmsg = _("Node device not found: %s");
            break;
        case VIR_ERR_NO_SECURITY_MODEL:
            if (info == NULL)
                    errmsg = _("Security model not found");
            else
                    errmsg = _("Security model not found: %s");
            break;
        case VIR_ERR_OPERATION_INVALID:
            if (info == NULL)
                    errmsg = _("Requested operation is not valid");
            else
                    errmsg = _("Requested operation is not valid: %s");
            break;
        case VIR_WAR_NO_INTERFACE:
            if (info == NULL)
                errmsg = _("Failed to find the interface");
            else
                errmsg = _("Failed to find the interface: %s");
            break;
        case VIR_ERR_NO_INTERFACE:
            if (info == NULL)
                errmsg = _("Interface not found");
            else
                errmsg = _("Interface not found: %s");
            break;
        case VIR_ERR_INVALID_INTERFACE:
            if (info == NULL)
                errmsg = _("invalid interface pointer in");
            else
                errmsg = _("invalid interface pointer in %s");
            break;
        case VIR_ERR_MULTIPLE_INTERFACES:
            if (info == NULL)
                errmsg = _("multiple matching interfaces found");
            else
                errmsg = _("multiple matching interfaces found: %s");
            break;
    }
    return (errmsg);
}

/**
 * virReportErrorHelper:
 *
 * @conn: the connection to the hypervisor if available
 * @domcode: the virErrorDomain indicating where it's coming from
 * @errcode: the virErrorNumber code for the error
 * @filename: Source file error is dispatched from
 * @funcname: Function error is dispatched from
 * @linenr: Line number error is dispatched from
 * @fmt:  the format string
 * @...:  extra parameters for the message display
 *
 * Helper function to do most of the grunt work for individual driver
 * ReportError
 */
void virReportErrorHelper(virConnectPtr conn,
                          int domcode,
                          int errcode,
                          const char *filename,
                          const char *funcname,
                          size_t linenr,
                          const char *fmt, ...)
{
    va_list args;
    char errorMessage[1024];
    const char *virerr;

    if (fmt) {
        va_start(args, fmt);
        vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args);
        va_end(args);
    } else {
        errorMessage[0] = '\0';
    }

    virerr = virErrorMsg(errcode, (errorMessage[0] ? errorMessage : NULL));
    virRaiseErrorFull(conn, filename, funcname, linenr,
                      domcode, errcode, VIR_ERR_ERROR,
                      virerr, errorMessage, NULL,
                      -1, -1, virerr, errorMessage);

}

/**
 * virStrerror:
 * @theerrno: the errno value
 * @errBuf: the buffer to save the error to
 * @errBufLen: the buffer length
 *
 * Generate an erro string for the given errno
 *
 * Returns a pointer to the error string, possibly indicating that the
 *         error is unknown
 */
const char *virStrerror(int theerrno, char *errBuf, size_t errBufLen)
{
#ifdef HAVE_STRERROR_R
# ifdef __USE_GNU
    /* Annoying linux specific API contract */
    return strerror_r(theerrno, errBuf, errBufLen);
# else
    strerror_r(theerrno, errBuf, errBufLen);
    return errBuf;
# endif
#else
    /* Mingw lacks strerror_r() and its strerror() is definitely not
     * threadsafe, so safest option is to just print the raw errno
     * value - we can at least reliably & safely look it up in the
     * header files for debug purposes
     */
    int n = snprintf(errBuf, errBufLen, "errno=%d", theerrno);
    return (0 < n && n < errBufLen
            ? errBuf : _("internal error: buffer too small"));
#endif
}

/**
 * virReportSystemErrorFull:
 * @conn: the hyperisor connection
 * @domcode: the virErrorDomain indicating where it's coming from
 * @theerrno: an errno number
 * @filename: filename where error was raised
 * @funcname: function name where error was raised
 * @linenr: line number where error was raised
 * @fmt:  the message to display/transmit
 * @...:  extra parameters for the message display
 *
 * Convenience internal routine called when a system error is detected.
 */
void virReportSystemErrorFull(virConnectPtr conn,
                              int domcode,
                              int theerrno,
                              const char *filename,
                              const char *funcname,
                              size_t linenr,
                              const char *fmt, ...)
{
    char strerror_buf[1024];
    char msgDetailBuf[1024];

    const char *errnoDetail = virStrerror(theerrno, strerror_buf,
                                          sizeof(strerror_buf));
    const char *msg = virErrorMsg(VIR_ERR_SYSTEM_ERROR, fmt);
    const char *msgDetail = NULL;

    if (fmt) {
        va_list args;
        int n;

        va_start(args, fmt);
        n = vsnprintf(msgDetailBuf, sizeof(msgDetailBuf), fmt, args);
        va_end(args);

        size_t len = strlen (msgDetailBuf);
        if (0 <= n && n + 2 + len < sizeof (msgDetailBuf)) {
          char *p = msgDetailBuf + n;
          stpcpy (stpcpy (p, ": "), errnoDetail);
          msgDetail = msgDetailBuf;
        }
    }

    if (!msgDetail)
        msgDetail = errnoDetail;

    virRaiseErrorFull(conn, filename, funcname, linenr,
                      domcode, VIR_ERR_SYSTEM_ERROR, VIR_ERR_ERROR,
                      msg, msgDetail, NULL, -1, -1, msg, msgDetail);
}

/**
 * virReportOOMErrorFull:
 * @conn: the hyperisor connection
 * @domcode: the virErrorDomain indicating where it's coming from
 * @filename: filename where error was raised
 * @funcname: function name where error was raised
 * @linenr: line number where error was raised
 *
 * Convenience internal routine called when an out of memory error is
 * detected
 */
void virReportOOMErrorFull(virConnectPtr conn,
                           int domcode,
                           const char *filename,
                           const char *funcname,
                           size_t linenr)
{
    const char *virerr;

    virerr = virErrorMsg(VIR_ERR_NO_MEMORY, NULL);
    virRaiseErrorFull(conn, filename, funcname, linenr,
                      domcode, VIR_ERR_NO_MEMORY, VIR_ERR_ERROR,
                      virerr, NULL, NULL, -1, -1, virerr, NULL);
}
