<commit_before>/*
 * xen_unified.c: Unified Xen driver.
 *
 * Copyright (C) 2007, 2008 Red Hat, Inc.
 *
 * See COPYING.LIB for the License of this software
 *
 * <NAME> <<EMAIL>>
 */

#include <config.h>

#ifdef WITH_XEN

/* Note:
 *
 * This driver provides a unified interface to the five
 * separate underlying Xen drivers (xen_internal, proxy_internal,
 * xend_internal, xs_internal and xm_internal).  Historically
 * the body of libvirt.c handled the five Xen drivers,
 * and contained Xen-specific code.
 */

#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <xen/dom0_ops.h>
#include <libxml/uri.h>

#include "internal.h"

#include "xen_unified.h"

#include "xen_internal.h"
#include "proxy_internal.h"
#include "xend_internal.h"
#include "xs_internal.h"
#include "xm_internal.h"
#include "xml.h"
#include "util.h"
#include "memory.h"

#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt,__VA_ARGS__)
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)

static int
xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info);
static int
xenUnifiedDomainGetMaxVcpus (virDomainPtr dom);
static int
xenUnifiedDomainGetVcpus (virDomainPtr dom,
                          virVcpuInfoPtr info, int maxinfo,
                          unsigned char *cpumaps, int maplen);

/* The five Xen drivers below us. */
static struct xenUnifiedDriver *drivers[XEN_UNIFIED_NR_DRIVERS] = {
    [XEN_UNIFIED_HYPERVISOR_OFFSET] = &xenHypervisorDriver,
    [XEN_UNIFIED_PROXY_OFFSET] = &xenProxyDriver,
    [XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver,
    [XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver,
    [XEN_UNIFIED_XM_OFFSET] = &xenXMDriver,
};

/**
 * xenUnifiedError:
 * @conn: the connection
 * @error: the error number
 * @info: extra information string
 *
 * Handle an error at the xend daemon interface
 */
static void
xenUnifiedError (virConnectPtr conn, virErrorNumber error, const char *info)
{
    const char *errmsg;

    errmsg = __virErrorMsg (error, info);
    __virRaiseError (conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR,
                     errmsg, info, NULL, 0, 0, errmsg, info);
}

/*
 * Helper functions currently used in the NUMA code
 * Those variables should not be accessed directly but through helper
 * functions xenNbCells() and xenNbCpu() available to all Xen backends
 */
static int nbNodeCells = -1;
static int nbNodeCpus = -1;

/**
 * xenNumaInit:
 * @conn: pointer to the hypervisor connection
 *
 * Initializer for previous variables. We currently assume that
 * the number of physical CPU and the number of NUMA cell is fixed
 * until reboot which might be false in future Xen implementations.
 */
static void
xenNumaInit(virConnectPtr conn) {
    virNodeInfo nodeInfo;
    int ret;

    ret = xenUnifiedNodeGetInfo(conn, &nodeInfo);
    if (ret < 0)
        return;
    nbNodeCells = nodeInfo.nodes;
    nbNodeCpus = nodeInfo.cpus;
}

/**
 * xenNbCells:
 * @conn: pointer to the hypervisor connection
 *
 * Number of NUMA cells present in the actual Node
 *
 * Returns the number of NUMA cells available on that Node
 */
int xenNbCells(virConnectPtr conn) {
    if (nbNodeCells < 0)
        xenNumaInit(conn);
    return(nbNodeCells);
}

/**
 * xenNbCpus:
 * @conn: pointer to the hypervisor connection
 *
 * Number of CPUs present in the actual Node
 *
 * Returns the number of CPUs available on that Node
 */
int xenNbCpus(virConnectPtr conn) {
    if (nbNodeCpus < 0)
        xenNumaInit(conn);
    return(nbNodeCpus);
}

/**
 * xenDomainUsedCpus:
 * @dom: the domain
 *
 * Analyze which set of CPUs are used by the domain and
 * return a string providing the ranges.
 *
 * Returns the string which needs to be freed by the caller or
 *         NULL if the domain uses all CPU or in case of error.
 */
char *
xenDomainUsedCpus(virDomainPtr dom)
{
    char *res = NULL;
    int nb_cpu, ncpus;
    int nb_vcpu;
    char *cpulist = NULL;
    unsigned char *cpumap = NULL;
    size_t cpumaplen;
    int nb = 0;
    int n, m;
    virVcpuInfoPtr cpuinfo = NULL;
    virNodeInfo nodeinfo;

    if (!VIR_IS_CONNECTED_DOMAIN(dom))
        return (NULL);

    nb_cpu = xenNbCpus(dom->conn);
    if (nb_cpu <= 0)
        return(NULL);
    nb_vcpu = xenUnifiedDomainGetMaxVcpus(dom);
    if (nb_vcpu <= 0)
        return(NULL);
    if (xenUnifiedNodeGetInfo(dom->conn, &nodeinfo) < 0)
        return(NULL);

    if (VIR_ALLOC_N(cpulist, nb_cpu) < 0)
        goto done;
    if (VIR_ALLOC_N(cpuinfo, nb_vcpu) < 0)
        goto done;
    cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo));
    if (xalloc_oversized(nb_vcpu, cpumaplen) ||
        VIR_ALLOC_N(cpumap, nb_vcpu * cpumaplen) < 0)
        goto done;

    if ((ncpus = xenUnifiedDomainGetVcpus(dom, cpuinfo, nb_vcpu,
                                          cpumap, cpumaplen)) >= 0) {
        for (n = 0 ; n < ncpus ; n++) {
            for (m = 0 ; m < nb_cpu; m++) {
                if ((cpulist[m] == 0) &&
                    (VIR_CPU_USABLE(cpumap, cpumaplen, n, m))) {
                    cpulist[m] = 1;
                    nb++;
                    /* if all CPU are used just return NULL */
                    if (nb == nb_cpu)
                        goto done;

                }
            }
        }
        res = virSaveCpuSet(dom->conn, cpulist, nb_cpu);
    }

done:
    VIR_FREE(cpulist);
    VIR_FREE(cpumap);
    VIR_FREE(cpuinfo);
    return(res);
}

/*----- Dispatch functions. -----*/

/* These dispatch functions follow the model used historically
 * by libvirt.c -- trying each low-level Xen driver in turn
 * until one succeeds.  However since we know what low-level
 * drivers can perform which functions, it is probably better
 * in future to optimise these dispatch functions to just call
 * the single function (or small number of appropriate functions)
 * in the low level drivers directly.
 */

static const char *
xenUnifiedProbe (void)
{
#ifdef __linux__
    if (virFileExists("/proc/xen"))
        return("xen:///");
#endif
#ifdef __sun__
    FILE *fh;

    if (fh = fopen("/dev/xen/domcaps", "r")) {
        fclose(fh);
        return("xen:///");
    }
#endif
    return(NULL);
}

static int
xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags)
{
    int i, ret = VIR_DRV_OPEN_DECLINED;
    xenUnifiedPrivatePtr priv;

    /* Refuse any scheme which isn't "xen://" or "http://". */
    if (uri->scheme &&
        STRCASENEQ(uri->scheme, "xen") &&
        STRCASENEQ(uri->scheme, "http"))
        return VIR_DRV_OPEN_DECLINED;

    /* xmlParseURI will parse a naked string like "foo" as a URI with
     * a NULL scheme.  That's not useful for us because we want to only
     * allow full pathnames (eg. ///var/lib/xen/xend-socket).  Decline
     * anything else.
     */
    if (!uri->scheme && (!uri->path || uri->path[0] != '/'))
        return VIR_DRV_OPEN_DECLINED;

    /* Refuse any xen:// URI with a server specified - allow remote to do it */
    if (uri->scheme && STRCASEEQ(uri->scheme, "xen") && uri->server)
        return VIR_DRV_OPEN_DECLINED;

    /* Allocate per-connection private data. */
    if (VIR_ALLOC(priv) < 0) {
        xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating private data");
        return VIR_DRV_OPEN_ERROR;
    }
    conn->privateData = priv;

    priv->handle = -1;
    priv->xendConfigVersion = -1;
    priv->type = -1;
    priv->len = -1;
    priv->addr = NULL;
    priv->xshandle = NULL;
    priv->proxy = -1;


    /* Hypervisor is only run as root & required to succeed */
    if (getuid() == 0) {
        DEBUG0("Trying hypervisor sub-driver");
        if (drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->open(conn, uri, auth, flags) ==
            VIR_DRV_OPEN_SUCCESS) {
            DEBUG0("Activated hypervisor sub-driver");
            priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] = 1;
        }
    }

    /* XenD is required to suceed if root.
     * If it fails as non-root, then the proxy driver may take over
     */
    DEBUG0("Trying XenD sub-driver");
    if (drivers[XEN_UNIFIED_XEND_OFFSET]->open(conn, uri, auth, flags) ==
        VIR_DRV_OPEN_SUCCESS) {
        DEBUG0("Activated XenD sub-driver");
        priv->opened[XEN_UNIFIED_XEND_OFFSET] = 1;

        /* XenD is active, so try the xm & xs drivers too, both requird to
         * succeed if root, optional otherwise */
        if (priv->xendConfigVersion <= 2) {
            DEBUG0("Trying XM sub-driver");
            if (drivers[XEN_UNIFIED_XM_OFFSET]->open(conn, uri, auth, flags) ==
                VIR_DRV_OPEN_SUCCESS) {
                DEBUG0("Activated XM sub-driver");
                priv->opened[XEN_UNIFIED_XM_OFFSET] = 1;
            }
        }
        DEBUG0("Trying XS sub-driver");
        if (drivers[XEN_UNIFIED_XS_OFFSET]->open(conn, uri, auth, flags) ==
            VIR_DRV_OPEN_SUCCESS) {
            DEBUG0("Activated XS sub-driver");
            priv->opened[XEN_UNIFIED_XS_OFFSET] = 1;
        } else {
            if (getuid() == 0)
                goto fail; /* XS is mandatory as root */
        }
    } else {
        if (getuid() == 0) {
            goto fail; /* XenD is mandatory as root */
        } else {
#if WITH_PROXY
            DEBUG0("Trying proxy sub-driver");
            if (drivers[XEN_UNIFIED_PROXY_OFFSET]->open(conn, uri, auth, flags) ==
                VIR_DRV_OPEN_SUCCESS) {
                DEBUG0("Activated proxy sub-driver");
                priv->opened[XEN_UNIFIED_PROXY_OFFSET] = 1;
            } else {
                goto fail; /* Proxy is mandatory if XenD failed */
            }
#else
            DEBUG0("Handing off for remote driver");
            ret = VIR_DRV_OPEN_DECLINED; /* Let remote_driver try instead */
            goto clean;
#endif
        }
    }

    return VIR_DRV_OPEN_SUCCESS;

fail:
    ret = VIR_DRV_OPEN_ERROR;
clean:
    DEBUG0("Failed to activate a mandatory sub-driver");
    for (i = 0 ; i < XEN_UNIFIED_NR_DRIVERS ; i++)
        if (priv->opened[i]) drivers[i]->close(conn);
    VIR_FREE(priv);
    return ret
}

#define GET_PRIVATE(conn) \
    xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) (conn)->privateData

static int
xenUnifiedClose (virConnectPtr conn)
{
    GET_PRIVATE(conn);
    int i;

    for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i)
        if (priv->opened[i] && drivers[i]->close)
            (void) drivers[i]->close (conn);

    free (conn->privateData);
    conn->privateData = NULL;

    return 0;
}

static const char *<commit_msg>Fix compile , Daniel<commit_after>/*
 * xen_unified.c: Unified Xen driver.
 *
 * Copyright (C) 2007, 2008 Red Hat, Inc.
 *
 * See COPYING.LIB for the License of this software
 *
 * <NAME> <<EMAIL>>
 */

#include <config.h>

#ifdef WITH_XEN

/* Note:
 *
 * This driver provides a unified interface to the five
 * separate underlying Xen drivers (xen_internal, proxy_internal,
 * xend_internal, xs_internal and xm_internal).  Historically
 * the body of libvirt.c handled the five Xen drivers,
 * and contained Xen-specific code.
 */

#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <xen/dom0_ops.h>
#include <libxml/uri.h>

#include "internal.h"

#include "xen_unified.h"

#include "xen_internal.h"
#include "proxy_internal.h"
#include "xend_internal.h"
#include "xs_internal.h"
#include "xm_internal.h"
#include "xml.h"
#include "util.h"
#include "memory.h"

#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt,__VA_ARGS__)
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)

static int
xenUnifiedNodeGetInfo (virConnectPtr conn, virNodeInfoPtr info);
static int
xenUnifiedDomainGetMaxVcpus (virDomainPtr dom);
static int
xenUnifiedDomainGetVcpus (virDomainPtr dom,
                          virVcpuInfoPtr info, int maxinfo,
                          unsigned char *cpumaps, int maplen);

/* The five Xen drivers below us. */
static struct xenUnifiedDriver *drivers[XEN_UNIFIED_NR_DRIVERS] = {
    [XEN_UNIFIED_HYPERVISOR_OFFSET] = &xenHypervisorDriver,
    [XEN_UNIFIED_PROXY_OFFSET] = &xenProxyDriver,
    [XEN_UNIFIED_XEND_OFFSET] = &xenDaemonDriver,
    [XEN_UNIFIED_XS_OFFSET] = &xenStoreDriver,
    [XEN_UNIFIED_XM_OFFSET] = &xenXMDriver,
};

/**
 * xenUnifiedError:
 * @conn: the connection
 * @error: the error number
 * @info: extra information string
 *
 * Handle an error at the xend daemon interface
 */
static void
xenUnifiedError (virConnectPtr conn, virErrorNumber error, const char *info)
{
    const char *errmsg;

    errmsg = __virErrorMsg (error, info);
    __virRaiseError (conn, NULL, NULL, VIR_FROM_XEN, error, VIR_ERR_ERROR,
                     errmsg, info, NULL, 0, 0, errmsg, info);
}

/*
 * Helper functions currently used in the NUMA code
 * Those variables should not be accessed directly but through helper
 * functions xenNbCells() and xenNbCpu() available to all Xen backends
 */
static int nbNodeCells = -1;
static int nbNodeCpus = -1;

/**
 * xenNumaInit:
 * @conn: pointer to the hypervisor connection
 *
 * Initializer for previous variables. We currently assume that
 * the number of physical CPU and the number of NUMA cell is fixed
 * until reboot which might be false in future Xen implementations.
 */
static void
xenNumaInit(virConnectPtr conn) {
    virNodeInfo nodeInfo;
    int ret;

    ret = xenUnifiedNodeGetInfo(conn, &nodeInfo);
    if (ret < 0)
        return;
    nbNodeCells = nodeInfo.nodes;
    nbNodeCpus = nodeInfo.cpus;
}

/**
 * xenNbCells:
 * @conn: pointer to the hypervisor connection
 *
 * Number of NUMA cells present in the actual Node
 *
 * Returns the number of NUMA cells available on that Node
 */
int xenNbCells(virConnectPtr conn) {
    if (nbNodeCells < 0)
        xenNumaInit(conn);
    return(nbNodeCells);
}

/**
 * xenNbCpus:
 * @conn: pointer to the hypervisor connection
 *
 * Number of CPUs present in the actual Node
 *
 * Returns the number of CPUs available on that Node
 */
int xenNbCpus(virConnectPtr conn) {
    if (nbNodeCpus < 0)
        xenNumaInit(conn);
    return(nbNodeCpus);
}

/**
 * xenDomainUsedCpus:
 * @dom: the domain
 *
 * Analyze which set of CPUs are used by the domain and
 * return a string providing the ranges.
 *
 * Returns the string which needs to be freed by the caller or
 *         NULL if the domain uses all CPU or in case of error.
 */
char *
xenDomainUsedCpus(virDomainPtr dom)
{
    char *res = NULL;
    int nb_cpu, ncpus;
    int nb_vcpu;
    char *cpulist = NULL;
    unsigned char *cpumap = NULL;
    size_t cpumaplen;
    int nb = 0;
    int n, m;
    virVcpuInfoPtr cpuinfo = NULL;
    virNodeInfo nodeinfo;

    if (!VIR_IS_CONNECTED_DOMAIN(dom))
        return (NULL);

    nb_cpu = xenNbCpus(dom->conn);
    if (nb_cpu <= 0)
        return(NULL);
    nb_vcpu = xenUnifiedDomainGetMaxVcpus(dom);
    if (nb_vcpu <= 0)
        return(NULL);
    if (xenUnifiedNodeGetInfo(dom->conn, &nodeinfo) < 0)
        return(NULL);

    if (VIR_ALLOC_N(cpulist, nb_cpu) < 0)
        goto done;
    if (VIR_ALLOC_N(cpuinfo, nb_vcpu) < 0)
        goto done;
    cpumaplen = VIR_CPU_MAPLEN(VIR_NODEINFO_MAXCPUS(nodeinfo));
    if (xalloc_oversized(nb_vcpu, cpumaplen) ||
        VIR_ALLOC_N(cpumap, nb_vcpu * cpumaplen) < 0)
        goto done;

    if ((ncpus = xenUnifiedDomainGetVcpus(dom, cpuinfo, nb_vcpu,
                                          cpumap, cpumaplen)) >= 0) {
        for (n = 0 ; n < ncpus ; n++) {
            for (m = 0 ; m < nb_cpu; m++) {
                if ((cpulist[m] == 0) &&
                    (VIR_CPU_USABLE(cpumap, cpumaplen, n, m))) {
                    cpulist[m] = 1;
                    nb++;
                    /* if all CPU are used just return NULL */
                    if (nb == nb_cpu)
                        goto done;

                }
            }
        }
        res = virSaveCpuSet(dom->conn, cpulist, nb_cpu);
    }

done:
    VIR_FREE(cpulist);
    VIR_FREE(cpumap);
    VIR_FREE(cpuinfo);
    return(res);
}

/*----- Dispatch functions. -----*/

/* These dispatch functions follow the model used historically
 * by libvirt.c -- trying each low-level Xen driver in turn
 * until one succeeds.  However since we know what low-level
 * drivers can perform which functions, it is probably better
 * in future to optimise these dispatch functions to just call
 * the single function (or small number of appropriate functions)
 * in the low level drivers directly.
 */

static const char *
xenUnifiedProbe (void)
{
#ifdef __linux__
    if (virFileExists("/proc/xen"))
        return("xen:///");
#endif
#ifdef __sun__
    FILE *fh;

    if (fh = fopen("/dev/xen/domcaps", "r")) {
        fclose(fh);
        return("xen:///");
    }
#endif
    return(NULL);
}

static int
xenUnifiedOpen (virConnectPtr conn, xmlURIPtr uri, virConnectAuthPtr auth, int flags)
{
    int i, ret = VIR_DRV_OPEN_DECLINED;
    xenUnifiedPrivatePtr priv;

    /* Refuse any scheme which isn't "xen://" or "http://". */
    if (uri->scheme &&
        STRCASENEQ(uri->scheme, "xen") &&
        STRCASENEQ(uri->scheme, "http"))
        return VIR_DRV_OPEN_DECLINED;

    /* xmlParseURI will parse a naked string like "foo" as a URI with
     * a NULL scheme.  That's not useful for us because we want to only
     * allow full pathnames (eg. ///var/lib/xen/xend-socket).  Decline
     * anything else.
     */
    if (!uri->scheme && (!uri->path || uri->path[0] != '/'))
        return VIR_DRV_OPEN_DECLINED;

    /* Refuse any xen:// URI with a server specified - allow remote to do it */
    if (uri->scheme && STRCASEEQ(uri->scheme, "xen") && uri->server)
        return VIR_DRV_OPEN_DECLINED;

    /* Allocate per-connection private data. */
    if (VIR_ALLOC(priv) < 0) {
        xenUnifiedError (NULL, VIR_ERR_NO_MEMORY, "allocating private data");
        return VIR_DRV_OPEN_ERROR;
    }
    conn->privateData = priv;

    priv->handle = -1;
    priv->xendConfigVersion = -1;
    priv->type = -1;
    priv->len = -1;
    priv->addr = NULL;
    priv->xshandle = NULL;
    priv->proxy = -1;


    /* Hypervisor is only run as root & required to succeed */
    if (getuid() == 0) {
        DEBUG0("Trying hypervisor sub-driver");
        if (drivers[XEN_UNIFIED_HYPERVISOR_OFFSET]->open(conn, uri, auth, flags) ==
            VIR_DRV_OPEN_SUCCESS) {
            DEBUG0("Activated hypervisor sub-driver");
            priv->opened[XEN_UNIFIED_HYPERVISOR_OFFSET] = 1;
        }
    }

    /* XenD is required to suceed if root.
     * If it fails as non-root, then the proxy driver may take over
     */
    DEBUG0("Trying XenD sub-driver");
    if (drivers[XEN_UNIFIED_XEND_OFFSET]->open(conn, uri, auth, flags) ==
        VIR_DRV_OPEN_SUCCESS) {
        DEBUG0("Activated XenD sub-driver");
        priv->opened[XEN_UNIFIED_XEND_OFFSET] = 1;

        /* XenD is active, so try the xm & xs drivers too, both requird to
         * succeed if root, optional otherwise */
        if (priv->xendConfigVersion <= 2) {
            DEBUG0("Trying XM sub-driver");
            if (drivers[XEN_UNIFIED_XM_OFFSET]->open(conn, uri, auth, flags) ==
                VIR_DRV_OPEN_SUCCESS) {
                DEBUG0("Activated XM sub-driver");
                priv->opened[XEN_UNIFIED_XM_OFFSET] = 1;
            }
        }
        DEBUG0("Trying XS sub-driver");
        if (drivers[XEN_UNIFIED_XS_OFFSET]->open(conn, uri, auth, flags) ==
            VIR_DRV_OPEN_SUCCESS) {
            DEBUG0("Activated XS sub-driver");
            priv->opened[XEN_UNIFIED_XS_OFFSET] = 1;
        } else {
            if (getuid() == 0)
                goto fail; /* XS is mandatory as root */
        }
    } else {
        if (getuid() == 0) {
            goto fail; /* XenD is mandatory as root */
        } else {
#if WITH_PROXY
            DEBUG0("Trying proxy sub-driver");
            if (drivers[XEN_UNIFIED_PROXY_OFFSET]->open(conn, uri, auth, flags) ==
                VIR_DRV_OPEN_SUCCESS) {
                DEBUG0("Activated proxy sub-driver");
                priv->opened[XEN_UNIFIED_PROXY_OFFSET] = 1;
            } else {
                goto fail; /* Proxy is mandatory if XenD failed */
            }
#else
            DEBUG0("Handing off for remote driver");
            ret = VIR_DRV_OPEN_DECLINED; /* Let remote_driver try instead */
            goto clean;
#endif
        }
    }

    return VIR_DRV_OPEN_SUCCESS;

fail:
    ret = VIR_DRV_OPEN_ERROR;
clean:
    DEBUG0("Failed to activate a mandatory sub-driver");
    for (i = 0 ; i < XEN_UNIFIED_NR_DRIVERS ; i++)
        if (priv->opened[i]) drivers[i]->close(conn);
    VIR_FREE(priv);
    return ret;
}

#define GET_PRIVATE(conn) \
    xenUnifiedPrivatePtr priv = (xenUnifiedPrivatePtr) (conn)->privateData

static int
xenUnifiedClose (virConnectPtr conn)
{
    GET_PRIVATE(conn);
    int i;

    for (i = 0; i < XEN_UNIFIED_NR_DRIVERS; ++i)
        if (priv->opened[i] && drivers[i]->close)
            (void) drivers[i]->close (conn);

    free (conn->privateData);
    conn->privateData = NULL;

    return 0;
}

static const char *
{'fraction_non_alphanumeric': 0.11354961832061068, 'fraction_numerical': 0.0057251908396946565, 'mean_word_length': 2.5855285665412246, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 105, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '185817', 'n_tokens_mistral': 7317, 'n_tokens_neox': 7036, 'n_words': 1938}