/*
 * Copyright (C) 2010-2015 Red Hat, Inc.
 * Copyright IBM Corp. 2009
 *
 * phyp_driver.c: ssh layer to access Power Hypervisors
 *
 * Authors:
 *  Eduardo Otubo <otubo at linux.vnet.ibm.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it 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.
 *
 * This library 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 this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */

#include <config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <libssh2.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <domain_event.h>
#include <poll.h>

#include "internal.h"
#include "virauth.h"
#include "datatypes.h"
#include "virbuffer.h"
#include "viralloc.h"
#include "virlog.h"
#include "driver.h"
#include "virerror.h"
#include "viruuid.h"
#include "domain_conf.h"
#include "storage_conf.h"
#include "nodeinfo.h"
#include "virfile.h"
#include "interface_conf.h"
#include "phyp_driver.h"
#include "virstring.h"

#define VIR_FROM_THIS VIR_FROM_PHYP

VIR_LOG_INIT("phyp.phyp_driver");

#define LPAR_EXEC_ERR (-1)
#define SSH_CONN_ERR (-2)         /* error while trying to connect to remote host */
#define SSH_CMD_ERR (-3)          /* error while trying to execute the remote cmd */

/* This is the lpar (domain) struct that relates
 * the ID with UUID generated by the API
 * */
typedef struct _lpar lpar_t;
typedef lpar_t *lparPtr;
struct _lpar {
    unsigned char uuid[VIR_UUID_BUFLEN];
    int id;
};

/* Struct that holds how many lpars (domains) we're
 * handling and a pointer to an array of lpar structs
 * */
typedef struct _uuid_table uuid_table_t;
typedef uuid_table_t *uuid_tablePtr;
struct _uuid_table {
    size_t nlpars;
    lparPtr *lpars;
};

/* This is the main structure of the driver
 * */
typedef struct _phyp_driver phyp_driver_t;
typedef phyp_driver_t *phyp_driverPtr;
struct _phyp_driver {
    LIBSSH2_SESSION *session;
    int sock;

    uuid_tablePtr uuid_table;
    virCapsPtr caps;
    virDomainXMLOptionPtr xmlopt;
    int vios_id;

    /* system_type:
     * 0 = hmc
     * 127 = ivm
     * */
    int system_type;
    char *managed_system;
};

/*
 * URI: phyp://user@[hmc|ivm]/managed_system
 * */

enum {
    HMC = 0,
    PHYP_IFACENAME_SIZE = 24,
    PHYP_MAC_SIZE = 12,
};

static int
waitsocket(int socket_fd, LIBSSH2_SESSION * session)
{
    struct pollfd fds[1];
    int dir;

    memset(fds, 0, sizeof(fds));
    fds[0].fd = socket_fd;

    /* now make sure we wait in the correct direction */
    dir = libssh2_session_block_directions(session);

    if (dir & LIBSSH2_SESSION_BLOCK_INBOUND)
        fds[0].events |= POLLIN;

    if (dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
        fds[0].events |= POLLOUT;

    return poll(fds, ARRAY_CARDINALITY(fds), -1);
}

/* this function is the layer that manipulates the ssh channel itself
 * and executes the commands on the remote machine */
static char *phypExec(LIBSSH2_SESSION *, const char *, int *, virConnectPtr)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3)
    ATTRIBUTE_NONNULL(4);
static char *
phypExec(LIBSSH2_SESSION *session, const char *cmd, int *exit_status,
         virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_CHANNEL *channel;
    virBuffer tex_ret = VIR_BUFFER_INITIALIZER;
    char *buffer = NULL;
    size_t buffer_size = 16384;
    int exitcode;
    int bytecount = 0;
    int sock = phyp_driver->sock;
    int rc = 0;

    if (VIR_ALLOC_N(buffer, buffer_size) < 0)
        return NULL;

    /* Exec non-blocking on the remove host */
    while ((channel = libssh2_channel_open_session(session)) == NULL &&
           libssh2_session_last_error(session, NULL, NULL, 0) ==
           LIBSSH2_ERROR_EAGAIN) {
        if (waitsocket(sock, session) < 0 && errno != EINTR) {
            virReportSystemError(errno, "%s",
                                 _("unable to wait on libssh2 socket"));
            goto err;
        }
    }

    if (channel == NULL)
        goto err;

    while ((rc = libssh2_channel_exec(channel, cmd)) ==
           LIBSSH2_ERROR_EAGAIN) {
        if (waitsocket(sock, session) < 0 && errno != EINTR) {
            virReportSystemError(errno, "%s",
                                 _("unable to wait on libssh2 socket"));
            goto err;
        }
    }

    if (rc != 0)
        goto err;

    for (;;) {
        /* loop until we block */
        do {
            rc = libssh2_channel_read(channel, buffer, buffer_size);
            if (rc > 0) {
                bytecount += rc;
                virBufferAdd(&tex_ret, buffer, -1);
            }
        }
        while (rc > 0);

        /* this is due to blocking that would occur otherwise so we loop on
         * this condition */
        if (rc == LIBSSH2_ERROR_EAGAIN) {
            if (waitsocket(sock, session) < 0 && errno != EINTR) {
                virReportSystemError(errno, "%s",
                                     _("unable to wait on libssh2 socket"));
                goto err;
            }
        } else {
            break;
        }
    }

    exitcode = 127;

    while ((rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN) {
        if (waitsocket(sock, session) < 0 && errno != EINTR) {
            virReportSystemError(errno, "%s",
                                 _("unable to wait on libssh2 socket"));
            goto err;
        }
    }

    if (rc == 0)
        exitcode = libssh2_channel_get_exit_status(channel);

    (*exit_status) = exitcode;
    libssh2_channel_free(channel);
    channel = NULL;
    VIR_FREE(buffer);

    if (virBufferCheckError(&tex_ret) < 0)
        return NULL;
    return virBufferContentAndReset(&tex_ret);

 err:
    (*exit_status) = SSH_CMD_ERR;
    virBufferFreeAndReset(&tex_ret);
    VIR_FREE(buffer);
    return NULL;
}

/* Convenience wrapper function */
static char *phypExecBuffer(LIBSSH2_SESSION *, virBufferPtr buf, int *,
                            virConnectPtr, bool) ATTRIBUTE_NONNULL(1)
    ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
static char *
phypExecBuffer(LIBSSH2_SESSION *session, virBufferPtr buf, int *exit_status,
               virConnectPtr conn, bool strip_newline)
{
    char *cmd;
    char *ret;

    if (virBufferCheckError(buf) < 0)
        return NULL;
    cmd = virBufferContentAndReset(buf);
    ret = phypExec(session, cmd, exit_status, conn);
    VIR_FREE(cmd);
    if (ret && *exit_status == 0 && strip_newline) {
        char *nl = strchr(ret, '\n');
        if (nl)
            *nl = '\0';
    }
    return ret;
}

/* Convenience wrapper function */
static int phypExecInt(LIBSSH2_SESSION *, virBufferPtr, virConnectPtr, int *)
    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4);
static int
phypExecInt(LIBSSH2_SESSION *session, virBufferPtr buf, virConnectPtr conn,
            int *result)
{
    char *str;
    int ret;
    char *char_ptr;

    str = phypExecBuffer(session, buf, &ret, conn, true);
    if (!str || ret) {
        VIR_FREE(str);
        return -1;
    }
    ret = virStrToLong_i(str, &char_ptr, 10, result);
    if (ret == 0 && *char_ptr)
        VIR_WARN("ignoring suffix during integer parsing of '%s'", str);
    VIR_FREE(str);
    return ret;
}

static int
phypGetSystemType(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *ret = NULL;
    int exit_status = 0;

    ret = phypExec(session, "lshmc -V", &exit_status, conn);

    VIR_FREE(ret);
    return exit_status;
}

static int
phypGetVIOSPartitionID(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int id = -1;
    char *managed_system = phyp_driver->managed_system;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAddLit(&buf, " -r lpar -F lpar_id,lpar_env"
                    "|sed -n '/vioserver/ {\n s/,.*$//\n p\n}'");
    phypExecInt(session, &buf, conn, &id);
    return id;
}


static virCapsPtr
phypCapsInit(void)
{
    virCapsPtr caps;
    virCapsGuestPtr guest;

    if ((caps = virCapabilitiesNew(virArchFromHost(),
                                   false, false)) == NULL)
        goto no_memory;

    /* Some machines have problematic NUMA toplogy causing
     * unexpected failures. We don't want to break the QEMU
     * driver in this scenario, so log errors & carry on
     */
    if (nodeCapsInitNUMA(caps) < 0) {
        virCapabilitiesFreeNUMAInfo(caps);
        VIR_WARN
            ("Failed to query host NUMA topology, disabling NUMA capabilities");
    }

    if ((guest = virCapabilitiesAddGuest(caps,
                                         VIR_DOMAIN_OSTYPE_LINUX,
                                         caps->host.arch,
                                         NULL, NULL, 0, NULL)) == NULL)
        goto no_memory;

    if (virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_PHYP,
                                      NULL, NULL, 0, NULL) == NULL)
        goto no_memory;

    return caps;

 no_memory:
    virObjectUnref(caps);
    return NULL;
}

/* This is a generic function that won't be used directly by
 * libvirt api. The function returns the number of domains
 * in different states: Running, Not Activated and all:
 *
 * type: 0 - Running
 *       1 - Not Activated
 *       * - All
 * */
static int
phypConnectNumOfDomainsGeneric(virConnectPtr conn, unsigned int type)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int ndom = -1;
    char *managed_system = phyp_driver->managed_system;
    const char *state;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (type == 0) {
        state = "|grep Running";
    } else if (type == 1) {
        if (system_type == HMC) {
            state = "|grep \"Not Activated\"";
        } else {
            state = "|grep \"Open Firmware\"";
        }
    } else {
        state = " ";
    }

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -F lpar_id,state %s |grep -c '^[0-9][0-9]*'",
                      state);
    phypExecInt(session, &buf, conn, &ndom);
    return ndom;
}

/* This is a generic function that won't be used directly by
 * libvirt api. The function returns the ids of domains
 * in different states: Running, and all:
 *
 * type: 0 - Running
 *       1 - all
 * */
static int
phypConnectListDomainsGeneric(virConnectPtr conn, int *ids, int nids,
                              unsigned int type)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    int got = -1;
    char *ret = NULL;
    char *line, *next_line;
    const char *state;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (type == 0)
        state = "|grep Running";
    else
        state = " ";

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -F lpar_id,state %s | sed -e 's/,.*$//'",
                      state);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    /* I need to parse the textual return in order to get the ids */
    line = ret;
    got = 0;
    while (*line && got < nids) {
        if (virStrToLong_i(line, &next_line, 10, &ids[got]) == -1) {
            VIR_ERROR(_("Cannot parse number from '%s'"), line);
            got = -1;
            goto cleanup;
        }
        got++;
        line = next_line;
        while (*line == '\n')
            line++; /* skip \n */
    }

 cleanup:
    VIR_FREE(ret);
    return got;
}

static int
phypUUIDTable_WriteFile(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    size_t i = 0;
    int fd = -1;
    char local_file[] = "./uuid_table";

    if ((fd = creat(local_file, 0755)) == -1)
        goto err;

    for (i = 0; i < uuid_table->nlpars; i++) {
        if (safewrite(fd, &uuid_table->lpars[i]->id,
                      sizeof(uuid_table->lpars[i]->id)) !=
            sizeof(uuid_table->lpars[i]->id)) {
            VIR_ERROR(_("Unable to write information to local file."));
            goto err;
        }

        if (safewrite(fd, uuid_table->lpars[i]->uuid, VIR_UUID_BUFLEN) !=
            VIR_UUID_BUFLEN) {
            VIR_ERROR(_("Unable to write information to local file."));
            goto err;
        }
    }

    if (VIR_CLOSE(fd) < 0) {
        virReportSystemError(errno, _("Could not close %s"),
                             local_file);
        goto err;
    }
    return 0;

 err:
    VIR_FORCE_CLOSE(fd);
    return -1;
}

static int
phypUUIDTable_Push(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    LIBSSH2_CHANNEL *channel = NULL;
    struct stat local_fileinfo;
    char buffer[1024];
    int rc = 0;
    FILE *f = NULL;
    size_t nread, sent;
    char *ptr;
    char local_file[] = "./uuid_table";
    char *remote_file = NULL;
    int ret = -1;

    if (virAsprintf(&remote_file, "/home/%s/libvirt_uuid_table",
                    NULLSTR(conn->uri->user)) < 0)
        goto cleanup;

    if (stat(local_file, &local_fileinfo) == -1) {
        VIR_WARN("Unable to stat local file.");
        goto cleanup;
    }

    if (!(f = fopen(local_file, "rb"))) {
        VIR_WARN("Unable to open local file.");
        goto cleanup;
    }

    do {
        channel =
            libssh2_scp_send(session, remote_file,
                             0x1FF & local_fileinfo.st_mode,
                             (unsigned long) local_fileinfo.st_size);

        if ((!channel) && (libssh2_session_last_errno(session) !=
                           LIBSSH2_ERROR_EAGAIN))
            goto cleanup;
    } while (!channel);

    do {
        nread = fread(buffer, 1, sizeof(buffer), f);
        if (nread <= 0) {
            if (feof(f)) {
                /* end of file */
                break;
            } else {
                VIR_ERROR(_("Failed to read from %s"), local_file);
                goto cleanup;
            }
        }
        ptr = buffer;
        sent = 0;

        do {
            /* write the same data over and over, until error or completion */
            rc = libssh2_channel_write(channel, ptr, nread);
            if (LIBSSH2_ERROR_EAGAIN == rc) {   /* must loop around */
                continue;
            } else if (rc > 0) {
                /* rc indicates how many bytes were written this time */
                sent += rc;
            }
            ptr += sent;
            nread -= sent;
        } while (rc > 0 && sent < nread);
    } while (1);

    ret = 0;

 cleanup:
    if (channel) {
        libssh2_channel_send_eof(channel);
        libssh2_channel_wait_eof(channel);
        libssh2_channel_wait_closed(channel);
        libssh2_channel_free(channel);
        channel = NULL;
    }
    VIR_FORCE_FCLOSE(f);
    return ret;
}

static int
phypUUIDTable_RemLpar(virConnectPtr conn, int id)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    size_t i = 0;

    for (i = 0; i <= uuid_table->nlpars; i++) {
        if (uuid_table->lpars[i]->id == id) {
            uuid_table->lpars[i]->id = -1;
            memset(uuid_table->lpars[i]->uuid, 0, VIR_UUID_BUFLEN);
        }
    }

    if (phypUUIDTable_WriteFile(conn) == -1)
        goto err;

    if (phypUUIDTable_Push(conn) == -1)
        goto err;

    return 0;

 err:
    return -1;
}

static int
phypUUIDTable_AddLpar(virConnectPtr conn, unsigned char *uuid, int id)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    lparPtr item = NULL;

    if (VIR_ALLOC(item) < 0)
        goto err;

    item->id = id;
    memcpy(item->uuid, uuid, VIR_UUID_BUFLEN);

    if (VIR_APPEND_ELEMENT_COPY(uuid_table->lpars, uuid_table->nlpars, item) < 0)
        goto err;

    if (phypUUIDTable_WriteFile(conn) == -1)
        goto err;

    if (phypUUIDTable_Push(conn) == -1)
        goto err;

    return 0;

 err:
    VIR_FREE(item);
    return -1;
}

static int
phypUUIDTable_ReadFile(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    size_t i = 0;
    int fd = -1;
    char local_file[] = "./uuid_table";
    int rc = 0;
    int id;

    if ((fd = open(local_file, O_RDONLY)) == -1) {
        VIR_WARN("Unable to read information from local file.");
        goto err;
    }

    /* Creating a new data base and writing to local file */
    if (VIR_ALLOC_N(uuid_table->lpars, uuid_table->nlpars) >= 0) {
        for (i = 0; i < uuid_table->nlpars; i++) {

            rc = read(fd, &id, sizeof(int));
            if (rc == sizeof(int)) {
                if (VIR_ALLOC(uuid_table->lpars[i]) < 0)
                    goto err;
                uuid_table->lpars[i]->id = id;
            } else {
                VIR_WARN
                    ("Unable to read from information from local file.");
                goto err;
            }

            rc = read(fd, uuid_table->lpars[i]->uuid, VIR_UUID_BUFLEN);
            if (rc != VIR_UUID_BUFLEN) {
                VIR_WARN("Unable to read information from local file.");
                goto err;
            }
        }
    }

    VIR_FORCE_CLOSE(fd);
    return 0;

 err:
    VIR_FORCE_CLOSE(fd);
    return -1;
}

static int
phypUUIDTable_Pull(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    LIBSSH2_CHANNEL *channel = NULL;
    struct stat fileinfo;
    char buffer[1024];
    int rc = 0;
    int fd = -1;
    int got = 0;
    int amount = 0;
    int total = 0;
    int sock = 0;
    char local_file[] = "./uuid_table";
    char *remote_file = NULL;
    int ret = -1;

    if (virAsprintf(&remote_file, "/home/%s/libvirt_uuid_table",
                    NULLSTR(conn->uri->user)) < 0)
        goto cleanup;

    /* Trying to stat the remote file. */
    do {
        channel = libssh2_scp_recv(session, remote_file, &fileinfo);

        if (!channel) {
            if (libssh2_session_last_errno(session) !=
                LIBSSH2_ERROR_EAGAIN) {
                goto cleanup;
            } else {
                if (waitsocket(sock, session) < 0 && errno != EINTR) {
                    virReportSystemError(errno, "%s",
                                         _("unable to wait on libssh2 socket"));
                    goto cleanup;
                }
            }
        }
    } while (!channel);

    /* Creating a new data base based on remote file */
    if ((fd = creat(local_file, 0755)) == -1)
        goto cleanup;

    /* Request a file via SCP */
    while (got < fileinfo.st_size) {
        do {
            amount = sizeof(buffer);

            if ((fileinfo.st_size - got) < amount)
                amount = fileinfo.st_size - got;

            rc = libssh2_channel_read(channel, buffer, amount);
            if (rc > 0) {
                if (safewrite(fd, buffer, rc) != rc)
                    VIR_WARN
                        ("Unable to write information to local file.");

                got += rc;
                total += rc;
            }
        } while (rc > 0);

        if ((rc == LIBSSH2_ERROR_EAGAIN)
            && (got < fileinfo.st_size)) {
            /* this is due to blocking that would occur otherwise
             * so we loop on this condition */

            /* now we wait */
            if (waitsocket(sock, session) < 0 && errno != EINTR) {
                virReportSystemError(errno, "%s",
                                     _("unable to wait on libssh2 socket"));
                goto cleanup;
            }
            continue;
        }
        break;
    }
    if (VIR_CLOSE(fd) < 0) {
        virReportSystemError(errno, _("Could not close %s"),
                             local_file);
        goto cleanup;
    }

    ret = 0;

 cleanup:
    if (channel) {
        libssh2_channel_send_eof(channel);
        libssh2_channel_wait_eof(channel);
        libssh2_channel_wait_closed(channel);
        libssh2_channel_free(channel);
        channel = NULL;
    }
    VIR_FORCE_CLOSE(fd);
    return ret;
}

static int
phypUUIDTable_Init(virConnectPtr conn)
{
    uuid_tablePtr uuid_table = NULL;
    phyp_driverPtr phyp_driver;
    int nids_numdomains = 0;
    int nids_listdomains = 0;
    int *ids = NULL;
    size_t i = 0;
    int ret = -1;
    bool table_created = false;

    if ((nids_numdomains = phypConnectNumOfDomainsGeneric(conn, 2)) < 0)
        goto cleanup;

    if (VIR_ALLOC_N(ids, nids_numdomains) < 0)
        goto cleanup;

    if ((nids_listdomains =
         phypConnectListDomainsGeneric(conn, ids, nids_numdomains, 1)) < 0)
        goto cleanup;

    /* exit early if there are no domains */
    if (nids_numdomains == 0 && nids_listdomains == 0) {
        ret = 0;
        goto cleanup;
    }
    if (nids_numdomains != nids_listdomains) {
        VIR_ERROR(_("Unable to determine number of domains."));
        goto cleanup;
    }

    phyp_driver = conn->privateData;
    uuid_table = phyp_driver->uuid_table;
    uuid_table->nlpars = nids_listdomains;

    /* try to get the table from server */
    if (phypUUIDTable_Pull(conn) == -1) {
        /* file not found in the server, creating a new one */
        table_created = true;
        if (VIR_ALLOC_N(uuid_table->lpars, uuid_table->nlpars) >= 0) {
            for (i = 0; i < uuid_table->nlpars; i++) {
                if (VIR_ALLOC(uuid_table->lpars[i]) < 0)
                    goto cleanup;
                uuid_table->lpars[i]->id = ids[i];

                if (virUUIDGenerate(uuid_table->lpars[i]->uuid) < 0)
                    VIR_WARN("Unable to generate UUID for domain %d",
                             ids[i]);
            }
        } else {
            goto cleanup;
        }

        if (phypUUIDTable_WriteFile(conn) == -1)
            goto cleanup;

        if (phypUUIDTable_Push(conn) == -1)
            goto cleanup;
    } else {
        if (phypUUIDTable_ReadFile(conn) == -1)
            goto cleanup;
    }

    ret = 0;

 cleanup:
    if (ret < 0 && table_created) {
        for (i = 0; i < uuid_table->nlpars; i++)
            VIR_FREE(uuid_table->lpars[i]);
        VIR_FREE(uuid_table->lpars);
    }
    VIR_FREE(ids);
    return ret;
}

static void
phypUUIDTable_Free(uuid_tablePtr uuid_table)
{
    size_t i;

    if (uuid_table == NULL)
        return;

    for (i = 0; i < uuid_table->nlpars; i++)
        VIR_FREE(uuid_table->lpars[i]);

    VIR_FREE(uuid_table->lpars);
    VIR_FREE(uuid_table);
}

#define SPECIALCHARACTER_CASES                                                \
    case '&': case ';': case '`': case '@': case '"': case '|': case '*':     \
    case '?': case '~': case '<': case '>': case '^': case '(': case ')':     \
    case '[': case ']': case '{': case '}': case '$': case '%': case '#':     \
    case '\\': case '\n': case '\r': case '\t':

static bool
contains_specialcharacters(const char *src)
{
    size_t len = strlen(src);
    size_t i = 0;

    if (len == 0)
        return false;

    for (i = 0; i < len; i++) {
        switch (src[i]) {
        SPECIALCHARACTER_CASES
            return true;
        default:
            continue;
        }
    }

    return false;
}

static char *
escape_specialcharacters(const char *src)
{
    size_t len = strlen(src);
    size_t i = 0, j = 0;
    char *dst;

    if (len == 0)
        return NULL;

    if (VIR_ALLOC_N(dst, len + 1) < 0)
        return NULL;

    for (i = 0; i < len; i++) {
        switch (src[i]) {
        SPECIALCHARACTER_CASES
            continue;
        default:
            dst[j] = src[i];
            j++;
        }
    }

    dst[j] = '\0';

    return dst;
}

static LIBSSH2_SESSION *
openSSHSession(virConnectPtr conn, virConnectAuthPtr auth,
               int *internal_socket)
{
    LIBSSH2_SESSION *session;
    const char *hostname = conn->uri->server;
    char *username = NULL;
    char *password = NULL;
    int sock = -1;
    int rc;
    struct addrinfo *ai = NULL, *cur;
    struct addrinfo hints;
    int ret;
    char *pubkey = NULL;
    char *pvtkey = NULL;
    char *userhome = virGetUserDirectory();
    struct stat pvt_stat, pub_stat;

    if (userhome == NULL)
        goto err;

    if (virAsprintf(&pubkey, "%s/.ssh/id_rsa.pub", userhome) < 0)
        goto err;

    if (virAsprintf(&pvtkey, "%s/.ssh/id_rsa", userhome) < 0)
        goto err;

    if (conn->uri->user != NULL) {
        if (VIR_STRDUP(username, conn->uri->user) < 0)
            goto err;
    } else {
        if (auth == NULL || auth->cb == NULL) {
            virReportError(VIR_ERR_AUTH_FAILED,
                           "%s", _("No authentication callback provided."));
            goto err;
        }

        username = virAuthGetUsername(conn, auth, "ssh", NULL, conn->uri->server);

        if (username == NULL) {
            virReportError(VIR_ERR_AUTH_FAILED, "%s",
                           _("Username request failed"));
            goto err;
        }
    }

    memset(&hints, 0, sizeof(hints));
    hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = 0;

    ret = getaddrinfo(hostname, "22", &hints, &ai);
    if (ret != 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Error while getting %s address info"), hostname);
        goto err;
    }

    cur = ai;
    while (cur != NULL) {
        sock = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
        if (sock >= 0) {
            if (connect(sock, cur->ai_addr, cur->ai_addrlen) == 0) {
                freeaddrinfo(ai);
                goto connected;
            }
            VIR_FORCE_CLOSE(sock);
        }
        cur = cur->ai_next;
    }

    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("Failed to connect to %s"), hostname);
    freeaddrinfo(ai);
    goto err;

 connected:

    (*internal_socket) = sock;

    /* Create a session instance */
    session = libssh2_session_init();
    if (!session)
        goto err;

    /* tell libssh2 we want it all done non-blocking */
    libssh2_session_set_blocking(session, 0);

    while ((rc = libssh2_session_startup(session, sock)) ==
           LIBSSH2_ERROR_EAGAIN);
    if (rc) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("Failure establishing SSH session."));
        goto disconnect;
    }

    /* Trying authentication by pubkey */
    if (stat(pvtkey, &pvt_stat) || stat(pubkey, &pub_stat)) {
        rc = LIBSSH2_ERROR_SOCKET_NONE;
        goto keyboard_interactive;
    }

    while ((rc =
            libssh2_userauth_publickey_fromfile(session, username,
                                                pubkey,
                                                pvtkey,
                                                NULL)) ==
           LIBSSH2_ERROR_EAGAIN);

 keyboard_interactive:
    if (rc == LIBSSH2_ERROR_SOCKET_NONE
        || rc == LIBSSH2_ERROR_PUBLICKEY_UNRECOGNIZED
        || rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED) {
        if (auth == NULL || auth->cb == NULL) {
            virReportError(VIR_ERR_AUTH_FAILED,
                           "%s", _("No authentication callback provided."));
            goto disconnect;
        }

        password = virAuthGetPassword(conn, auth, "ssh", username, conn->uri->server);

        if (password == NULL) {
            virReportError(VIR_ERR_AUTH_FAILED, "%s",
                           _("Password request failed"));
            goto disconnect;
        }

        while ((rc =
                libssh2_userauth_password(session, username,
                                          password)) ==
               LIBSSH2_ERROR_EAGAIN);

        if (rc) {
            virReportError(VIR_ERR_AUTH_FAILED,
                           "%s", _("Authentication failed"));
            goto disconnect;
        } else {
            goto exit;
        }

    } else if (rc == LIBSSH2_ERROR_NONE) {
        goto exit;

    } else if (rc == LIBSSH2_ERROR_ALLOC || rc == LIBSSH2_ERROR_SOCKET_SEND
               || rc == LIBSSH2_ERROR_SOCKET_TIMEOUT) {
        goto err;
    }

 disconnect:
    libssh2_session_disconnect(session, "Disconnecting...");
    libssh2_session_free(session);
 err:
    VIR_FORCE_CLOSE(sock);
    VIR_FREE(userhome);
    VIR_FREE(pubkey);
    VIR_FREE(pvtkey);
    VIR_FREE(username);
    VIR_FREE(password);
    return NULL;

 exit:
    VIR_FREE(userhome);
    VIR_FREE(pubkey);
    VIR_FREE(pvtkey);
    VIR_FREE(username);
    VIR_FREE(password);
    return session;
}


static int
phypDomainDefPostParse(virDomainDefPtr def,
                       virCapsPtr caps ATTRIBUTE_UNUSED,
                       void *opaque ATTRIBUTE_UNUSED)
{
    /* memory hotplug tunables are not supported by this driver */
    if (virDomainDefCheckUnsupportedMemoryHotplug(def) < 0)
        return -1;

    return 0;
}


static int
phypDomainDeviceDefPostParse(virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
                             const virDomainDef *def ATTRIBUTE_UNUSED,
                             virCapsPtr caps ATTRIBUTE_UNUSED,
                             void *opaque ATTRIBUTE_UNUSED)
{
    return 0;
}


virDomainDefParserConfig virPhypDriverDomainDefParserConfig = {
    .devicesPostParseCallback = phypDomainDeviceDefPostParse,
    .domainPostParseCallback = phypDomainDefPostParse,
};


static virDrvOpenStatus
phypConnectOpen(virConnectPtr conn,
                virConnectAuthPtr auth, unsigned int flags)
{
    LIBSSH2_SESSION *session = NULL;
    int internal_socket = -1;
    uuid_tablePtr uuid_table = NULL;
    phyp_driverPtr phyp_driver = NULL;
    char *char_ptr;
    char *managed_system = NULL;

    virCheckFlags(VIR_CONNECT_RO, VIR_DRV_OPEN_ERROR);

    if (!conn || !conn->uri)
        return VIR_DRV_OPEN_DECLINED;

    if (conn->uri->scheme == NULL || STRNEQ(conn->uri->scheme, "phyp"))
        return VIR_DRV_OPEN_DECLINED;

    if (conn->uri->server == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("Missing server name in phyp:// URI"));
        return VIR_DRV_OPEN_ERROR;
    }

    if (VIR_ALLOC(phyp_driver) < 0)
        goto failure;

    phyp_driver->sock = -1;

    if (VIR_ALLOC(uuid_table) < 0)
        goto failure;

    if (conn->uri->path) {
        /* need to shift one byte in order to remove the first "/" of URI component */
        if (VIR_STRDUP(managed_system,
                       conn->uri->path + (conn->uri->path[0] == '/')) < 0)
            goto failure;

        /* here we are handling only the first component of the path,
         * so skipping the second:
         * */
        char_ptr = strchr(managed_system, '/');

        if (char_ptr)
            *char_ptr = '\0';

        if (contains_specialcharacters(conn->uri->path)) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           "%s",
                           _("Error parsing 'path'. Invalid characters."));
            goto failure;
        }
    }

    if ((session = openSSHSession(conn, auth, &internal_socket)) == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("Error while opening SSH session."));
        goto failure;
    }

    phyp_driver->session = session;
    phyp_driver->sock = internal_socket;

    uuid_table->nlpars = 0;
    uuid_table->lpars = NULL;

    if (conn->uri->path)
        phyp_driver->managed_system = managed_system;

    phyp_driver->uuid_table = uuid_table;
    if ((phyp_driver->caps = phypCapsInit()) == NULL)
        goto failure;

    if (!(phyp_driver->xmlopt = virDomainXMLOptionNew(&virPhypDriverDomainDefParserConfig,
                                                      NULL, NULL)))
        goto failure;

    conn->privateData = phyp_driver;

    if ((phyp_driver->system_type = phypGetSystemType(conn)) == -1)
        goto failure;

    if (phypUUIDTable_Init(conn) == -1)
        goto failure;

    if (phyp_driver->system_type == HMC) {
        if ((phyp_driver->vios_id = phypGetVIOSPartitionID(conn)) == -1)
            goto failure;
    }

    return VIR_DRV_OPEN_SUCCESS;

 failure:
    VIR_FREE(managed_system);

    if (phyp_driver != NULL) {
        virObjectUnref(phyp_driver->caps);
        virObjectUnref(phyp_driver->xmlopt);
        VIR_FREE(phyp_driver);
    }

    phypUUIDTable_Free(uuid_table);

    if (session != NULL) {
        libssh2_session_disconnect(session, "Disconnecting...");
        libssh2_session_free(session);
    }

    VIR_FORCE_CLOSE(internal_socket);

    return VIR_DRV_OPEN_ERROR;
}

static int
phypConnectClose(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;

    libssh2_session_disconnect(session, "Disconnecting...");
    libssh2_session_free(session);

    virObjectUnref(phyp_driver->caps);
    virObjectUnref(phyp_driver->xmlopt);
    phypUUIDTable_Free(phyp_driver->uuid_table);
    VIR_FREE(phyp_driver->managed_system);
    VIR_FORCE_CLOSE(phyp_driver->sock);
    VIR_FREE(phyp_driver);
    return 0;
}


static int
phypConnectIsEncrypted(virConnectPtr conn ATTRIBUTE_UNUSED)
{
    /* Phyp uses an SSH tunnel, so is always encrypted */
    return 1;
}


static int
phypConnectIsSecure(virConnectPtr conn ATTRIBUTE_UNUSED)
{
    /* Phyp uses an SSH tunnel, so is always secure */
    return 1;
}


static int
phypConnectIsAlive(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    /* XXX we should be able to do something better but this is simple, safe,
     * and good enough for now. In worst case, the function will return true
     * even though the connection is not alive.
     */
    if (phyp_driver->session)
        return 1;
    else
        return 0;
}


static int
phypDomainIsUpdated(virDomainPtr conn ATTRIBUTE_UNUSED)
{
    return 0;
}

/* return the lpar_id given a name and a managed system name */
static int
phypGetLparID(LIBSSH2_SESSION * session, const char *managed_system,
              const char *name, virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    int system_type = phyp_driver->system_type;
    int lpar_id = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " --filter lpar_names=%s -F lpar_id", name);
    phypExecInt(session, &buf, conn, &lpar_id);
    return lpar_id;
}

/* return the lpar name given a lpar_id and a managed system name */
static char *
phypGetLparNAME(LIBSSH2_SESSION * session, const char *managed_system,
                unsigned int lpar_id, virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    int system_type = phyp_driver->system_type;
    char *ret = NULL;
    int exit_status = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " --filter lpar_ids=%d -F name", lpar_id);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}


/* Search into the uuid_table for a lpar_uuid given a lpar_id
 * and a managed system name
 *
 * return:  0 - record found
 *         -1 - not found
 * */
static int
phypGetLparUUID(unsigned char *uuid, int lpar_id, virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    lparPtr *lpars = uuid_table->lpars;
    size_t i = 0;

    for (i = 0; i < uuid_table->nlpars; i++) {
        if (lpars[i]->id == lpar_id) {
            memcpy(uuid, lpars[i]->uuid, VIR_UUID_BUFLEN);
            return 0;
        }
    }

    return -1;
}

/*
 * type:
 * 0 - maxmem
 * 1 - memory
 * */
static unsigned long
phypGetLparMem(virConnectPtr conn, const char *managed_system, int lpar_id,
               int type)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int memory = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (type != 1 && type != 0)
        return 0;

    virBufferAddLit(&buf, "lshwres");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r mem --level lpar -F %s --filter lpar_ids=%d",
                      type ? "curr_mem" : "curr_max_mem", lpar_id);
    phypExecInt(session, &buf, conn, &memory);
    return memory;
}

static unsigned long
phypGetLparCPUGeneric(virConnectPtr conn, const char *managed_system,
                      int lpar_id, int type)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int vcpus = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lshwres");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r proc --level lpar -F %s --filter lpar_ids=%d",
                      type ? "curr_max_procs" : "curr_procs", lpar_id);
    phypExecInt(session, &buf, conn, &vcpus);
    return vcpus;
}

static unsigned long
phypGetLparCPU(virConnectPtr conn, const char *managed_system, int lpar_id)
{
    return phypGetLparCPUGeneric(conn, managed_system, lpar_id, 0);
}

static int
phypDomainGetVcpusFlags(virDomainPtr dom, unsigned int flags)
{
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    char *managed_system = phyp_driver->managed_system;

    if (flags != (VIR_DOMAIN_VCPU_LIVE | VIR_DOMAIN_VCPU_MAXIMUM)) {
        virReportError(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
        return -1;
    }

    return phypGetLparCPUGeneric(dom->conn, managed_system, dom->id, 1);
}

static int
phypDomainGetMaxVcpus(virDomainPtr dom)
{
    return phypDomainGetVcpusFlags(dom, (VIR_DOMAIN_VCPU_LIVE |
                                         VIR_DOMAIN_VCPU_MAXIMUM));
}

static int
phypGetRemoteSlot(virConnectPtr conn, const char *managed_system,
                  const char *lpar_name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int remote_slot = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lshwres");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r virtualio --rsubtype scsi -F "
                      "remote_slot_num --filter lpar_names=%s", lpar_name);
    phypExecInt(session, &buf, conn, &remote_slot);
    return remote_slot;
}

/* XXX - is this needed? */
static char *phypGetBackingDevice(virConnectPtr, const char *, char *)
    ATTRIBUTE_UNUSED;
static char *
phypGetBackingDevice(virConnectPtr conn, const char *managed_system,
                     char *lpar_name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *ret = NULL;
    int remote_slot = 0;
    int exit_status = 0;
    char *char_ptr;
    char *backing_device = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if ((remote_slot =
         phypGetRemoteSlot(conn, managed_system, lpar_name)) == -1)
        return NULL;

    virBufferAddLit(&buf, "lshwres");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r virtualio --rsubtype scsi -F "
                      "backing_devices --filter slots=%d", remote_slot);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    /* here is a little trick to deal returns of this kind:
     *
     * 0x8100000000000000//lv01
     *
     * the information we really need is only lv01, so we
     * need to skip a lot of things on the string.
     * */
    char_ptr = strchr(ret, '/');

    if (char_ptr) {
        char_ptr++;
        if (char_ptr[0] == '/')
            char_ptr++;
        else
            goto cleanup;

        if (VIR_STRDUP(backing_device, char_ptr) < 0)
            goto cleanup;
    } else {
        backing_device = ret;
        ret = NULL;
    }

    char_ptr = strchr(backing_device, '\n');

    if (char_ptr)
        *char_ptr = '\0';

 cleanup:
    VIR_FREE(ret);

    return backing_device;
}

static char *
phypGetLparProfile(virConnectPtr conn, int lpar_id)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r prof --filter lpar_ids=%d -F name|head -n 1",
                      lpar_id);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}

static int
phypGetVIOSNextSlotNumber(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    char *profile = NULL;
    int slot = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (!(profile = phypGetLparProfile(conn, vios_id))) {
        VIR_ERROR(_("Unable to get VIOS profile name."));
        return -1;
    }

    virBufferAddLit(&buf, "lssyscfg");

    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);

    virBufferAsprintf(&buf, " -r prof --filter "
                      "profile_names=%s -F virtual_eth_adapters,"
                      "virtual_opti_pool_id,virtual_scsi_adapters,"
                      "virtual_serial_adapters|sed -e 's/\"//g' -e "
                      "'s/,/\\n/g'|sed -e 's/\\(^[0-9][0-9]\\*\\).*$/\\1/'"
                      "|sort|tail -n 1", profile);
    if (phypExecInt(session, &buf, conn, &slot) < 0)
        return -1;
    return slot + 1;
}

static int
phypCreateServerSCSIAdapter(virConnectPtr conn)
{
    int result = -1;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    char *profile = NULL;
    int slot = 0;
    char *vios_name = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (!
        (vios_name =
         phypGetLparNAME(session, managed_system, vios_id, conn))) {
        VIR_ERROR(_("Unable to get VIOS name"));
        goto cleanup;
    }

    if (!(profile = phypGetLparProfile(conn, vios_id))) {
        VIR_ERROR(_("Unable to get VIOS profile name."));
        goto cleanup;
    }

    if ((slot = phypGetVIOSNextSlotNumber(conn)) == -1) {
        VIR_ERROR(_("Unable to get free slot number"));
        goto cleanup;
    }

    /* Listing all the virtual_scsi_adapter interfaces, the new adapter must
     * be appended to this list
     * */
    virBufferAddLit(&buf, "lssyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r prof --filter lpar_ids=%d,profile_names=%s"
                      " -F virtual_scsi_adapters|sed -e s/\\\"//g",
                      vios_id, profile);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    /* Here I change the VIOS configuration to append the new adapter
     * with the free slot I got with phypGetVIOSNextSlotNumber.
     * */
    virBufferAddLit(&buf, "chsyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r prof -i 'name=%s,lpar_id=%d,"
                      "\"virtual_scsi_adapters=%s,%d/server/any/any/1\"'",
                      vios_name, vios_id, ret, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    /* Finally I add the new scsi adapter to VIOS using the same slot
     * I used in the VIOS configuration.
     * */
    virBufferAddLit(&buf, "chhwres -r virtualio --rsubtype scsi");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -p %s -o a -s %d -d 0 -a \"adapter_type=server\"",
                      vios_name, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    result = 0;

 cleanup:
    VIR_FREE(profile);
    VIR_FREE(vios_name);
    VIR_FREE(ret);

    return result;
}

static char *
phypGetVIOSFreeSCSIAdapter(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAddLit(&buf, "lsmap -all -field svsa backing -fmt , ");

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed '/,[^.*]/d; s/,//g; q'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}


static int
phypDomainAttachDevice(virDomainPtr domain, const char *xml)
{
    int result = -1;
    virConnectPtr conn = domain->conn;
    phyp_driverPtr phyp_driver = domain->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    char *scsi_adapter = NULL;
    int slot = 0;
    char *vios_name = NULL;
    char *profile = NULL;
    virDomainDeviceDefPtr dev = NULL;
    virDomainDefPtr def = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *domain_name = NULL;

    if (!(def = virDomainDefNew()))
        goto cleanup;

    domain_name = escape_specialcharacters(domain->name);

    if (domain_name == NULL)
        goto cleanup;

    def->os.type = VIR_DOMAIN_OSTYPE_LINUX;

    dev = virDomainDeviceDefParse(xml, def, phyp_driver->caps, NULL,
                                  VIR_DOMAIN_DEF_PARSE_INACTIVE);
    if (!dev)
        goto cleanup;

    if (!
        (vios_name =
         phypGetLparNAME(session, managed_system, vios_id, conn))) {
        VIR_ERROR(_("Unable to get VIOS name"));
        goto cleanup;
    }

    /* First, let's look for a free SCSI Adapter
     * */
    if (!(scsi_adapter = phypGetVIOSFreeSCSIAdapter(conn))) {
        /* If not found, let's create one.
         * */
        if (phypCreateServerSCSIAdapter(conn) == -1) {
            VIR_ERROR(_("Unable to create new virtual adapter"));
            goto cleanup;
        } else {
            if (!(scsi_adapter = phypGetVIOSFreeSCSIAdapter(conn))) {
                VIR_ERROR(_("Unable to create new virtual adapter"));
                goto cleanup;
            }
        }
    }

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "mkvdev -vdev %s -vadapter %s",
                      virDomainDiskGetSource(dev->data.disk), scsi_adapter);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    if (!(profile = phypGetLparProfile(conn, domain->id))) {
        VIR_ERROR(_("Unable to get VIOS profile name."));
        goto cleanup;
    }

    /* Let's get the slot number for the adapter we just created
     * */
    virBufferAddLit(&buf, "lshwres -r virtualio --rsubtype scsi");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " slot_num,backing_device|grep %s|cut -d, -f1",
                      virDomainDiskGetSource(dev->data.disk));
    if (phypExecInt(session, &buf, conn, &slot) < 0)
        goto cleanup;

    /* Listing all the virtual_scsi_adapter interfaces, the new adapter must
     * be appended to this list
     * */
    virBufferAddLit(&buf, "lssyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r prof --filter lpar_ids=%d,profile_names=%s"
                      " -F virtual_scsi_adapters|sed -e 's/\"//g'",
                      vios_id, profile);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    /* Here I change the LPAR configuration to append the new adapter
     * with the new slot we just created
     * */
    virBufferAddLit(&buf, "chsyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r prof -i 'name=%s,lpar_id=%d,"
                      "\"virtual_scsi_adapters=%s,%d/client/%d/%s/0\"'",
                      domain_name, domain->id, ret, slot,
                      vios_id, vios_name);
    if (phypExecInt(session, &buf, conn, &slot) < 0)
        goto cleanup;

    /* Finally I add the new scsi adapter to VIOS using the same slot
     * I used in the VIOS configuration.
     * */
    virBufferAddLit(&buf, "chhwres -r virtualio --rsubtype scsi");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -p %s -o a -s %d -d 0 -a \"adapter_type=server\"",
                      domain_name, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL) {
        VIR_ERROR(_
                   ("Possibly you don't have IBM Tools installed in your LPAR."
                    "Contact your support to enable this feature."));
        goto cleanup;
    }

    result = 0;

 cleanup:
    VIR_FREE(ret);
    virDomainDeviceDefFree(dev);
    virDomainDefFree(def);
    VIR_FREE(vios_name);
    VIR_FREE(scsi_adapter);
    VIR_FREE(profile);
    VIR_FREE(domain_name);

    return result;
}

static char *
phypStorageVolGetKey(virConnectPtr conn, const char *name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lslv %s -field lvid", name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed -e 's/^LV IDENTIFIER://' -e 's/ //g'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}

static char *
phypGetStoragePoolDevice(virConnectPtr conn, char *name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lssp -detail -sp %s -field name", name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed '1d; s/ //g'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}

static unsigned long int
phypGetStoragePoolSize(virConnectPtr conn, char *name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int sp_size = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lssp -detail -sp %s -field size", name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed '1d; s/ //g'");
    phypExecInt(session, &buf, conn, &sp_size);
    return sp_size;
}

static char *
phypBuildVolume(virConnectPtr conn, const char *lvname, const char *spname,
                unsigned int capacity)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int vios_id = phyp_driver->vios_id;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    char *ret = NULL;
    int exit_status = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *key = NULL;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "mklv -lv %s %s %d", lvname, spname, capacity);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0) {
        VIR_ERROR(_("Unable to create Volume: %s"), NULLSTR(ret));
        goto cleanup;
    }

    key = phypStorageVolGetKey(conn, lvname);

 cleanup:
    VIR_FREE(ret);

    return key;
}

static virStorageVolPtr
phypStorageVolLookupByName(virStoragePoolPtr pool, const char *volname)
{
    char *key;
    virStorageVolPtr vol;

    key = phypStorageVolGetKey(pool->conn, volname);

    if (key == NULL)
        return NULL;

    vol = virGetStorageVol(pool->conn, pool->name, volname, key, NULL, NULL);

    VIR_FREE(key);

    return vol;
}

static virStorageVolPtr
phypStorageVolCreateXML(virStoragePoolPtr pool,
                        const char *xml, unsigned int flags)
{
    virCheckFlags(0, NULL);

    virStorageVolDefPtr voldef = NULL;
    virStoragePoolDefPtr spdef = NULL;
    virStorageVolPtr vol = NULL;
    virStorageVolPtr dup_vol = NULL;
    char *key = NULL;

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

    /* Filling spdef manually
     * */
    if (pool->name != NULL) {
        spdef->name = pool->name;
    } else {
        VIR_ERROR(_("Unable to determine storage pool's name."));
        goto err;
    }

    if (memcpy(spdef->uuid, pool->uuid, VIR_UUID_BUFLEN) == NULL) {
        VIR_ERROR(_("Unable to determine storage pool's uuid."));
        goto err;
    }

    if ((spdef->capacity =
         phypGetStoragePoolSize(pool->conn, pool->name)) == -1) {
        VIR_ERROR(_("Unable to determine storage pools's size."));
        goto err;
    }

    /* Information not available */
    spdef->allocation = 0;
    spdef->available = 0;

    spdef->source.ndevice = 1;

    /*XXX source adapter not working properly, should show hdiskX */
    if ((spdef->source.adapter.data.scsi_host.name =
         phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) {
        VIR_ERROR(_("Unable to determine storage pools's source adapter."));
        goto err;
    }

    if ((voldef = virStorageVolDefParseString(spdef, xml, 0)) == NULL) {
        VIR_ERROR(_("Error parsing volume XML."));
        goto err;
    }

    /* checking if this name already exists on this system */
    if ((dup_vol = phypStorageVolLookupByName(pool, voldef->name)) != NULL) {
        VIR_ERROR(_("StoragePool name already exists."));
        virObjectUnref(dup_vol);
        goto err;
    }

    /* The key must be NULL, the Power Hypervisor creates a key
     * in the moment you create the volume.
     * */
    if (voldef->key) {
        VIR_ERROR(_("Key must be empty, Power Hypervisor will create one for you."));
        goto err;
    }

    if (!voldef->target.capacity) {
        VIR_ERROR(_("Capacity cannot be empty."));
        goto err;
    }

    key = phypBuildVolume(pool->conn, voldef->name, spdef->name,
                          voldef->target.capacity);

    if (key == NULL)
        goto err;

    if ((vol =
         virGetStorageVol(pool->conn, pool->name, voldef->name,
                          key, NULL, NULL)) == NULL)
        goto err;

    VIR_FREE(key);

    return vol;

 err:
    VIR_FREE(key);
    virStorageVolDefFree(voldef);
    virStoragePoolDefFree(spdef);
    virObjectUnref(vol);
    return NULL;
}

static char *
phypStorageVolGetPhysicalVolumeByStoragePool(virStorageVolPtr vol, char *sp)
{
    virConnectPtr conn = vol->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lssp -detail -sp %s -field pvname", sp);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed 1d");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0)
        VIR_FREE(ret);
    return ret;
}

static virStorageVolPtr
phypStorageVolLookupByPath(virConnectPtr conn, const char *volname)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    char *key = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    virStorageVolPtr vol = NULL;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lslv %s -field vgname", volname);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed -e 's/^VOLUME GROUP://g' -e 's/ //g'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    key = phypStorageVolGetKey(conn, volname);

    if (key == NULL)
        goto cleanup;

    vol = virGetStorageVol(conn, ret, volname, key, NULL, NULL);

 cleanup:
    VIR_FREE(ret);
    VIR_FREE(key);

    return vol;
}

static int
phypGetStoragePoolUUID(virConnectPtr conn, unsigned char *uuid,
                       const char *name)
{
    int result = -1;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lsdev -dev %s -attr vgserial_id", name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed '1,2d'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    if (memcpy(uuid, ret, VIR_UUID_BUFLEN) == NULL)
        goto cleanup;

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static virStoragePoolPtr
phypStoragePoolLookupByName(virConnectPtr conn, const char *name)
{
    unsigned char uuid[VIR_UUID_BUFLEN];

    if (phypGetStoragePoolUUID(conn, uuid, name) == -1)
        return NULL;

    return virGetStoragePool(conn, name, uuid, NULL, NULL);
}

static char *
phypStorageVolGetXMLDesc(virStorageVolPtr vol, unsigned int flags)
{
    virStorageVolDef voldef;
    virStoragePoolDef pool;
    virStoragePoolPtr sp;
    char *xml = NULL;

    virCheckFlags(0, NULL);

    memset(&voldef, 0, sizeof(virStorageVolDef));
    memset(&pool, 0, sizeof(virStoragePoolDef));

    sp = phypStoragePoolLookupByName(vol->conn, vol->pool);

    if (!sp)
        goto cleanup;

    if (sp->name != NULL) {
        pool.name = sp->name;
    } else {
        VIR_ERROR(_("Unable to determine storage sp's name."));
        goto cleanup;
    }

    if (memcpy(pool.uuid, sp->uuid, VIR_UUID_BUFLEN) == NULL) {
        VIR_ERROR(_("Unable to determine storage sp's uuid."));
        goto cleanup;
    }

    if ((pool.capacity = phypGetStoragePoolSize(sp->conn, sp->name)) == -1) {
        VIR_ERROR(_("Unable to determine storage sps's size."));
        goto cleanup;
    }

    /* Information not available */
    pool.allocation = 0;
    pool.available = 0;

    pool.source.ndevice = 1;

    if ((pool.source.adapter.data.scsi_host.name =
         phypGetStoragePoolDevice(sp->conn, sp->name)) == NULL) {
        VIR_ERROR(_("Unable to determine storage sps's source adapter."));
        goto cleanup;
    }

    if (vol->name != NULL) {
        voldef.name = vol->name;
    } else {
        VIR_ERROR(_("Unable to determine storage pool's name."));
        goto cleanup;
    }

    if (VIR_STRDUP(voldef.key, vol->key) < 0)
        goto cleanup;

    voldef.type = VIR_STORAGE_POOL_LOGICAL;

    xml = virStorageVolDefFormat(&pool, &voldef);

    VIR_FREE(voldef.key);

 cleanup:
    virObjectUnref(sp);
    return xml;
}

/* The Volume Group path here will be treated as suggested in the
 * email on the libvirt mailling list. As soon as I can't get the
 * path for every volume, the path will be a representation in
 * the form:
 *
 * /physical_volume/storage_pool/logical_volume
 *
 * */
static char *
phypStorageVolGetPath(virStorageVolPtr vol)
{
    virConnectPtr conn = vol->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    char *ret = NULL;
    char *path = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *pv;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lslv %s -field vgname", vol->name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAsprintf(&buf,
                      "|sed -e 's/^VOLUME GROUP://g' -e 's/ //g'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    pv = phypStorageVolGetPhysicalVolumeByStoragePool(vol, ret);

    if (!pv)
        goto cleanup;

    if (virAsprintf(&path, "/%s/%s/%s", pv, ret, vol->name) < 0)
        goto cleanup;

 cleanup:
    VIR_FREE(ret);
    VIR_FREE(path);

    return path;
}

static int
phypStoragePoolListVolumes(virStoragePoolPtr pool, char **const volumes,
                           int nvolumes)
{
    bool success = false;
    virConnectPtr conn = pool->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    int got = 0;
    size_t i;
    char *ret = NULL;
    char *volumes_list = NULL;
    char *char_ptr = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "lsvg -lv %s -field lvname", pool->name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|sed '1,2d'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    /* I need to parse the textual return in order to get the volumes */
    if (exit_status < 0 || ret == NULL) {
        goto cleanup;
    } else {
        volumes_list = ret;

        while (got < nvolumes) {
            char_ptr = strchr(volumes_list, '\n');

            if (char_ptr) {
                *char_ptr = '\0';
                if (VIR_STRDUP(volumes[got++], volumes_list) < 0)
                    goto cleanup;
                char_ptr++;
                volumes_list = char_ptr;
            } else {
                break;
            }
        }
    }

    success = true;

 cleanup:
    if (!success) {
        for (i = 0; i < got; i++)
            VIR_FREE(volumes[i]);

        got = -1;
    }
    VIR_FREE(ret);
    return got;
}

static int
phypStoragePoolNumOfVolumes(virStoragePoolPtr pool)
{
    virConnectPtr conn = pool->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int nvolumes = -1;
    char *managed_system = phyp_driver->managed_system;
    int vios_id = phyp_driver->vios_id;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);
    virBufferAsprintf(&buf, "lsvg -lv %s -field lvname", pool->name);
    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    virBufferAddLit(&buf, "|grep -c '^.*$'");
    if (phypExecInt(session, &buf, conn, &nvolumes) < 0)
        return -1;

    /* We need to remove 2 line from the header text output */
    return nvolumes - 2;
}

static int
phypStoragePoolDestroy(virStoragePoolPtr pool)
{
    int result = -1;
    virConnectPtr conn = pool->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int vios_id = phyp_driver->vios_id;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    char *ret = NULL;
    int exit_status = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "rmsp %s", pool->name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0) {
        VIR_ERROR(_("Unable to destroy Storage Pool: %s"), NULLSTR(ret));
        goto cleanup;
    }

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static int
phypBuildStoragePool(virConnectPtr conn, virStoragePoolDefPtr def)
{
    int result = -1;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virStoragePoolSource source = def->source;
    int vios_id = phyp_driver->vios_id;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    char *ret = NULL;
    int exit_status = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (source.adapter.type !=
        VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_SCSI_HOST) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Only 'scsi_host' adapter is supported"));
        goto cleanup;
    }

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAsprintf(&buf, "mksp -f %schild %s", def->name,
                      source.adapter.data.scsi_host.name);

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0) {
        VIR_ERROR(_("Unable to create Storage Pool: %s"), NULLSTR(ret));
        goto cleanup;
    }

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;

}

static int
phypConnectNumOfStoragePools(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    int nsp = -1;
    char *managed_system = phyp_driver->managed_system;
    int vios_id = phyp_driver->vios_id;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAddLit(&buf, "lsvg");

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');

    virBufferAddLit(&buf, "|grep -c '^.*$'");
    phypExecInt(session, &buf, conn, &nsp);
    return nsp;
}

static int
phypConnectListStoragePools(virConnectPtr conn, char **const pools, int npools)
{
    bool success = false;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    int got = 0;
    size_t i;
    char *ret = NULL;
    char *storage_pools = NULL;
    char *char_ptr = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (system_type == HMC)
        virBufferAsprintf(&buf, "viosvrcmd -m %s --id %d -c '",
                          managed_system, vios_id);

    virBufferAddLit(&buf, "lsvg");

    if (system_type == HMC)
        virBufferAddChar(&buf, '\'');
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    /* I need to parse the textual return in order to get the storage pools */
    if (exit_status < 0 || ret == NULL) {
        goto cleanup;
    } else {
        storage_pools = ret;

        while (got < npools) {
            char_ptr = strchr(storage_pools, '\n');

            if (char_ptr) {
                *char_ptr = '\0';
                if (VIR_STRDUP(pools[got++], storage_pools) < 0)
                    goto cleanup;
                char_ptr++;
                storage_pools = char_ptr;
            } else {
                break;
            }
        }
    }

    success = true;

 cleanup:
    if (!success) {
        for (i = 0; i < got; i++)
            VIR_FREE(pools[i]);

        got = -1;
    }
    VIR_FREE(ret);
    return got;
}

static virStoragePoolPtr
phypStoragePoolLookupByUUID(virConnectPtr conn,
                            const unsigned char *uuid)
{
    virStoragePoolPtr sp = NULL;
    int npools = 0;
    int gotpools = 0;
    char **pools = NULL;
    size_t i = 0;
    unsigned char *local_uuid = NULL;

    if (VIR_ALLOC_N(local_uuid, VIR_UUID_BUFLEN) < 0)
        goto err;

    if ((npools = phypConnectNumOfStoragePools(conn)) == -1)
        goto err;

    if (VIR_ALLOC_N(pools, npools) < 0)
        goto err;

    if ((gotpools = phypConnectListStoragePools(conn, pools, npools)) == -1)
        goto err;

    if (gotpools != npools) {
        virReportOOMError();
        goto err;
    }

    for (i = 0; i < gotpools; i++) {
        if (phypGetStoragePoolUUID(conn, local_uuid, pools[i]) == -1)
            continue;

        if (!memcmp(local_uuid, uuid, VIR_UUID_BUFLEN)) {
            sp = virGetStoragePool(conn, pools[i], uuid, NULL, NULL);
            VIR_FREE(local_uuid);
            VIR_FREE(pools);

            if (sp)
                return sp;
            else
                goto err;
        }
    }

 err:
    VIR_FREE(local_uuid);
    VIR_FREE(pools);
    return NULL;
}

static virStoragePoolPtr
phypStoragePoolCreateXML(virConnectPtr conn,
                         const char *xml, unsigned int flags)
{
    virCheckFlags(0, NULL);

    virStoragePoolDefPtr def = NULL;
    virStoragePoolPtr dup_sp = NULL;
    virStoragePoolPtr sp = NULL;

    if (!(def = virStoragePoolDefParseString(xml)))
        goto err;

    /* checking if this name already exists on this system */
    if ((dup_sp = phypStoragePoolLookupByName(conn, def->name)) != NULL) {
        VIR_WARN("StoragePool name already exists.");
        virObjectUnref(dup_sp);
        goto err;
    }

    /* checking if ID or UUID already exists on this system */
    if ((dup_sp = phypStoragePoolLookupByUUID(conn, def->uuid)) != NULL) {
        VIR_WARN("StoragePool uuid already exists.");
        virObjectUnref(dup_sp);
        goto err;
    }

    if ((sp = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL)) == NULL)
        goto err;

    if (phypBuildStoragePool(conn, def) == -1)
        goto err;

    return sp;

 err:
    virStoragePoolDefFree(def);
    virObjectUnref(sp);
    return NULL;
}

static char *
phypStoragePoolGetXMLDesc(virStoragePoolPtr pool, unsigned int flags)
{
    virCheckFlags(0, NULL);

    virStoragePoolDef def;
    memset(&def, 0, sizeof(virStoragePoolDef));

    if (pool->name != NULL) {
        def.name = pool->name;
    } else {
        VIR_ERROR(_("Unable to determine storage pool's name."));
        goto err;
    }

    if (memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN) == NULL) {
        VIR_ERROR(_("Unable to determine storage pool's uuid."));
        goto err;
    }

    if ((def.capacity =
         phypGetStoragePoolSize(pool->conn, pool->name)) == -1) {
        VIR_ERROR(_("Unable to determine storage pools's size."));
        goto err;
    }

    /* Information not available */
    def.allocation = 0;
    def.available = 0;

    def.source.ndevice = 1;

    /*XXX source adapter not working properly, should show hdiskX */
    if ((def.source.adapter.data.scsi_host.name =
         phypGetStoragePoolDevice(pool->conn, pool->name)) == NULL) {
        VIR_ERROR(_("Unable to determine storage pools's source adapter."));
        goto err;
    }

    return virStoragePoolDefFormat(&def);

 err:
    return NULL;
}

static int
phypInterfaceDestroy(virInterfacePtr iface,
                     unsigned int flags)
{
    virCheckFlags(0, -1);

    phyp_driverPtr phyp_driver = iface->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int exit_status = 0;
    int slot_num = 0;
    int lpar_id = 0;
    char *ret = NULL;
    int rv = -1;

    /* Getting the remote slot number */

    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth --level lpar "
                      " -F mac_addr,slot_num|"
                      " sed -n '/%s/ s/^.*,//p'", iface->mac);
    if (phypExecInt(session, &buf, iface->conn, &slot_num) < 0)
        goto cleanup;

    /* Getting the remote slot number */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth --level lpar "
                      " -F mac_addr,lpar_id|"
                      " sed -n '/%s/ s/^.*,//p'", iface->mac);
    if (phypExecInt(session, &buf, iface->conn, &lpar_id) < 0)
        goto cleanup;

    /* excluding interface */
    virBufferAddLit(&buf, "chhwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth"
                      " --id %d -o r -s %d", lpar_id, slot_num);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, iface->conn, false);

    if (exit_status < 0 || ret != NULL)
        goto cleanup;

    rv = 0;

 cleanup:
    VIR_FREE(ret);
    return rv;
}

static virInterfacePtr
phypInterfaceDefineXML(virConnectPtr conn, const char *xml,
                       unsigned int flags)
{
    virCheckFlags(0, NULL);

    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int exit_status = 0;
    int slot = 0;
    char *ret = NULL;
    char name[PHYP_IFACENAME_SIZE];
    char mac[PHYP_MAC_SIZE];
    virInterfaceDefPtr def;
    virInterfacePtr result = NULL;

    if (!(def = virInterfaceDefParseString(xml)))
        goto cleanup;

    /* Now need to get the next free slot number */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype slot --level slot"
                      " -Fslot_num --filter lpar_names=%s"
                      " |sort|tail -n 1", def->name);
    if (phypExecInt(session, &buf, conn, &slot) < 0)
        goto cleanup;

    /* The next free slot itself: */
    slot++;

    /* Now adding the new network interface */
    virBufferAddLit(&buf, "chhwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth"
                      " -p %s -o a -s %d -a port_vlan_id=1,"
                      "ieee_virtual_eth=0", def->name, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret != NULL)
        goto cleanup;

    /* Need to sleep a little while to wait for the HMC to
     * complete the execution of the command.
     * */
    sleep(1);

    /* Getting the new interface name */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype slot --level slot"
                      " |sed '/lpar_name=%s/!d; /slot_num=%d/!d; "
                      "s/^.*drc_name=//'", def->name, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL) {
        /* roll back and excluding interface if error*/
        virBufferAddLit(&buf, "chhwres ");
        if (system_type == HMC)
            virBufferAsprintf(&buf, "-m %s ", managed_system);

        virBufferAsprintf(&buf,
                " -r virtualio --rsubtype eth"
                " -p %s -o r -s %d", def->name, slot);
        VIR_FREE(ret);
        ret = phypExecBuffer(session, &buf, &exit_status, conn, false);
        goto cleanup;
    }

    memcpy(name, ret, PHYP_IFACENAME_SIZE-1);

    /* Getting the new interface mac addr */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      "-r virtualio --rsubtype eth --level lpar "
                      " |sed '/lpar_name=%s/!d; /slot_num=%d/!d; "
                      "s/^.*mac_addr=//'", def->name, slot);
    VIR_FREE(ret);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    memcpy(mac, ret, PHYP_MAC_SIZE-1);

    result = virGetInterface(conn, name, mac);

 cleanup:
    VIR_FREE(ret);
    virInterfaceDefFree(def);
    return result;
}

static virInterfacePtr
phypInterfaceLookupByName(virConnectPtr conn, const char *name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int exit_status = 0;
    char *ret = NULL;
    int slot = 0;
    int lpar_id = 0;
    char mac[PHYP_MAC_SIZE];
    virInterfacePtr result = NULL;

    /*Getting the slot number for the interface */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype slot --level slot "
                      " -F drc_name,slot_num |"
                      " sed -n '/%s/ s/^.*,//p'", name);
    if (phypExecInt(session, &buf, conn, &slot) < 0)
        goto cleanup;

    /*Getting the lpar_id for the interface */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype slot --level slot "
                      " -F drc_name,lpar_id |"
                      " sed -n '/%s/ s/^.*,//p'", name);
    if (phypExecInt(session, &buf, conn, &lpar_id) < 0)
        goto cleanup;

    /*Getting the interface mac */
    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth --level lpar "
                      " -F lpar_id,slot_num,mac_addr|"
                      " sed -n '/%d,%d/ s/^.*,//p'", lpar_id, slot);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    memcpy(mac, ret, PHYP_MAC_SIZE-1);

    result = virGetInterface(conn, name, ret);

 cleanup:
    VIR_FREE(ret);
    return result;
}

static int
phypInterfaceIsActive(virInterfacePtr iface)
{
    phyp_driverPtr phyp_driver = iface->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int state = -1;

    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      " -r virtualio --rsubtype eth --level lpar "
                      " -F mac_addr,state |"
                      " sed -n '/%s/ s/^.*,//p'", iface->mac);
    phypExecInt(session, &buf, iface->conn, &state);
    return state;
}

static int
phypConnectListInterfaces(virConnectPtr conn, char **const names, int nnames)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int vios_id = phyp_driver->vios_id;
    int exit_status = 0;
    int got = 0;
    size_t i;
    char *ret = NULL;
    char *networks = NULL;
    char *char_ptr = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;
    bool success = false;

    virBufferAddLit(&buf, "lshwres");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r virtualio --rsubtype slot  --level slot|"
                      " sed '/eth/!d; /lpar_id=%d/d; s/^.*drc_name=//g'",
                      vios_id);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    /* I need to parse the textual return in order to get the network
     * interfaces */
    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    networks = ret;

    while (got < nnames) {
        char_ptr = strchr(networks, '\n');

        if (char_ptr) {
            *char_ptr = '\0';
            if (VIR_STRDUP(names[got++], networks) < 0)
                goto cleanup;
            char_ptr++;
            networks = char_ptr;
        } else {
            break;
        }
    }

 cleanup:
    if (!success) {
        for (i = 0; i < got; i++)
            VIR_FREE(names[i]);
    }
    VIR_FREE(ret);
    return got;
}

static int
phypConnectNumOfInterfaces(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    int system_type = phyp_driver->system_type;
    int vios_id = phyp_driver->vios_id;
    int nnets = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lshwres ");
    if (system_type == HMC)
        virBufferAsprintf(&buf, "-m %s ", managed_system);

    virBufferAsprintf(&buf,
                      "-r virtualio --rsubtype eth --level lpar|"
                      "grep -v lpar_id=%d|grep -c lpar_name", vios_id);
    phypExecInt(session, &buf, conn, &nnets);
    return nnets;
}

static int
phypGetLparState(virConnectPtr conn, unsigned int lpar_id)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *ret = NULL;
    int exit_status = 0;
    char *managed_system = phyp_driver->managed_system;
    int state = VIR_DOMAIN_NOSTATE;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -F state --filter lpar_ids=%d", lpar_id);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    if (STREQ(ret, "Running"))
        state = VIR_DOMAIN_RUNNING;
    else if (STREQ(ret, "Not Activated"))
        state = VIR_DOMAIN_SHUTOFF;
    else if (STREQ(ret, "Shutting Down"))
        state = VIR_DOMAIN_SHUTDOWN;

 cleanup:
    VIR_FREE(ret);
    return state;
}

/* XXX - is this needed? */
static int phypDiskType(virConnectPtr, char *) ATTRIBUTE_UNUSED;
static int
phypDiskType(virConnectPtr conn, char *backing_device)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *ret = NULL;
    int exit_status = 0;
    char *managed_system = phyp_driver->managed_system;
    int vios_id = phyp_driver->vios_id;
    int disk_type = -1;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "viosvrcmd");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -p %d -c \"lssp -field name type "
                      "-fmt , -all|sed -n '/%s/ {\n s/^.*,//\n p\n}'\"",
                      vios_id, backing_device);
    ret = phypExecBuffer(session, &buf, &exit_status, conn, true);

    if (exit_status < 0 || ret == NULL)
        goto cleanup;

    if (STREQ(ret, "LVPOOL"))
        disk_type = VIR_STORAGE_TYPE_BLOCK;
    else if (STREQ(ret, "FBPOOL"))
        disk_type = VIR_STORAGE_TYPE_FILE;

 cleanup:
    VIR_FREE(ret);
    return disk_type;
}

static int
phypConnectNumOfDefinedDomains(virConnectPtr conn)
{
    return phypConnectNumOfDomainsGeneric(conn, 1);
}

static int
phypConnectNumOfDomains(virConnectPtr conn)
{
    return phypConnectNumOfDomainsGeneric(conn, 0);
}

static int
phypConnectListDomains(virConnectPtr conn, int *ids, int nids)
{
    return phypConnectListDomainsGeneric(conn, ids, nids, 0);
}

static int
phypConnectListDefinedDomains(virConnectPtr conn, char **const names, int nnames)
{
    bool success = false;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    int got = 0;
    size_t i;
    char *ret = NULL;
    char *domains = NULL;
    char *char_ptr = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "lssyscfg -r lpar");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAddLit(&buf, " -F name,state"
                      "|sed -n '/Not Activated/ {\n s/,.*$//\n p\n}'");
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    /* I need to parse the textual return in order to get the domains */
    if (exit_status < 0 || ret == NULL) {
        goto cleanup;
    } else {
        domains = ret;

        while (got < nnames) {
            char_ptr = strchr(domains, '\n');

            if (char_ptr) {
                *char_ptr = '\0';
                if (VIR_STRDUP(names[got++], domains) < 0)
                    goto cleanup;
                char_ptr++;
                domains = char_ptr;
            } else {
                break;
            }
        }
    }

    success = true;

 cleanup:
    if (!success) {
        for (i = 0; i < got; i++)
            VIR_FREE(names[i]);

        got = -1;
    }
    VIR_FREE(ret);
    return got;
}

static virDomainPtr
phypDomainLookupByName(virConnectPtr conn, const char *lpar_name)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virDomainPtr dom = NULL;
    int lpar_id = 0;
    char *managed_system = phyp_driver->managed_system;
    unsigned char lpar_uuid[VIR_UUID_BUFLEN];

    lpar_id = phypGetLparID(session, managed_system, lpar_name, conn);
    if (lpar_id == -1)
        return NULL;

    if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1)
        return NULL;

    dom = virGetDomain(conn, lpar_name, lpar_uuid);

    if (dom)
        dom->id = lpar_id;

    return dom;
}

static virDomainPtr
phypDomainLookupByID(virConnectPtr conn, int lpar_id)
{
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virDomainPtr dom = NULL;
    char *managed_system = phyp_driver->managed_system;
    unsigned char lpar_uuid[VIR_UUID_BUFLEN];

    char *lpar_name = phypGetLparNAME(session, managed_system, lpar_id,
                                      conn);

    if (phypGetLparUUID(lpar_uuid, lpar_id, conn) == -1)
        goto cleanup;

    dom = virGetDomain(conn, lpar_name, lpar_uuid);

    if (dom)
        dom->id = lpar_id;

 cleanup:
    VIR_FREE(lpar_name);

    return dom;
}

static char *
phypDomainGetXMLDesc(virDomainPtr dom, unsigned int flags)
{
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virDomainDef def;
    char *managed_system = phyp_driver->managed_system;
    unsigned long long memory;

    /* Flags checked by virDomainDefFormat */

    memset(&def, 0, sizeof(virDomainDef));

    def.virtType = VIR_DOMAIN_VIRT_PHYP;
    def.id = dom->id;

    char *lpar_name = phypGetLparNAME(session, managed_system, def.id,
                                      dom->conn);

    if (lpar_name == NULL) {
        VIR_ERROR(_("Unable to determine domain's name."));
        goto err;
    }

    if (phypGetLparUUID(def.uuid, dom->id, dom->conn) == -1) {
        VIR_ERROR(_("Unable to generate random uuid."));
        goto err;
    }

    if ((memory = phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0) {
        VIR_ERROR(_("Unable to determine domain's max memory."));
        goto err;
    }

    virDomainDefSetMemoryInitial(&def, memory);

    if ((def.mem.cur_balloon =
         phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0) {
        VIR_ERROR(_("Unable to determine domain's memory."));
        goto err;
    }

    if ((def.maxvcpus = def.vcpus =
         phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0) {
        VIR_ERROR(_("Unable to determine domain's CPU."));
        goto err;
    }

    return virDomainDefFormat(&def,
                              virDomainDefFormatConvertXMLFlags(flags));

 err:
    return NULL;
}

static int
phypDomainResume(virDomainPtr dom)
{
    int result = -1;
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "chsysstate");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r lpar -o on --id %d -f %s",
                      dom->id, dom->name);
    ret = phypExecBuffer(session, &buf, &exit_status, dom->conn, false);

    if (exit_status < 0)
        goto cleanup;

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static int
phypDomainReboot(virDomainPtr dom, unsigned int flags)
{
    int result = -1;
    virConnectPtr conn = dom->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virCheckFlags(0, -1);

    virBufferAddLit(&buf, "chsysstate");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf,
                      " -r lpar -o shutdown --id %d --immed --restart",
                      dom->id);
    ret = phypExecBuffer(session, &buf, &exit_status, dom->conn, false);

    if (exit_status < 0)
        goto cleanup;

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static int
phypDomainShutdown(virDomainPtr dom)
{
    int result = -1;
    virConnectPtr conn = dom->conn;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virBufferAddLit(&buf, "chsysstate");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r lpar -o shutdown --id %d", dom->id);
    ret = phypExecBuffer(session, &buf, &exit_status, dom->conn, false);

    if (exit_status < 0)
        goto cleanup;

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static int
phypDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info)
{
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    char *managed_system = phyp_driver->managed_system;

    info->state = phypGetLparState(dom->conn, dom->id);

    if ((info->maxMem =
         phypGetLparMem(dom->conn, managed_system, dom->id, 0)) == 0)
        VIR_WARN("Unable to determine domain's max memory.");

    if ((info->memory =
         phypGetLparMem(dom->conn, managed_system, dom->id, 1)) == 0)
        VIR_WARN("Unable to determine domain's memory.");

    if ((info->nrVirtCpu =
         phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0)
        VIR_WARN("Unable to determine domain's CPU.");

    return 0;
}

static int
phypDomainGetState(virDomainPtr dom,
                   int *state,
                   int *reason,
                   unsigned int flags)
{
    virCheckFlags(0, -1);

    *state = phypGetLparState(dom->conn, dom->id);
    if (reason)
        *reason = 0;

    return 0;
}

static int
phypDomainDestroyFlags(virDomainPtr dom,
                       unsigned int flags)
{
    int result = -1;
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    char *ret = NULL;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    virCheckFlags(0, -1);

    virBufferAddLit(&buf, "rmsyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r lpar --id %d", dom->id);
    ret = phypExecBuffer(session, &buf, &exit_status, dom->conn, false);

    if (exit_status < 0)
        goto cleanup;

    if (phypUUIDTable_RemLpar(dom->conn, dom->id) == -1)
        goto cleanup;

    dom->id = -1;
    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static int
phypDomainDestroy(virDomainPtr dom)
{
    return phypDomainDestroyFlags(dom, 0);
}

static int
phypBuildLpar(virConnectPtr conn, virDomainDefPtr def)
{
    int result = -1;
    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    char *ret = NULL;
    int exit_status = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (!def->mem.cur_balloon) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Field <currentMemory> on the domain XML file is "
                         "missing or has invalid value"));
        goto cleanup;
    }

    if (!virDomainDefGetMemoryInitial(def)) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Field <memory> on the domain XML file is missing or "
                         "has invalid value"));
        goto cleanup;
    }

    if (def->ndisks < 1) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Domain XML must contain at least one <disk> element."));
        goto cleanup;
    }

    if (!virDomainDiskGetSource(def->disks[0])) {
        virReportError(VIR_ERR_XML_ERROR, "%s",
                       _("Field <src> under <disk> on the domain XML file is "
                         "missing."));
        goto cleanup;
    }

    virBufferAddLit(&buf, "mksyscfg");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " -r lpar -p %s -i min_mem=%lld,desired_mem=%lld,"
                      "max_mem=%lld,desired_procs=%d,virtual_scsi_adapters=%s",
                      def->name, def->mem.cur_balloon,
                      def->mem.cur_balloon,
                      virDomainDefGetMemoryInitial(def),
                      (int) def->vcpus, virDomainDiskGetSource(def->disks[0]));
    ret = phypExecBuffer(session, &buf, &exit_status, conn, false);

    if (exit_status < 0) {
        VIR_ERROR(_("Unable to create LPAR. Reason: '%s'"), NULLSTR(ret));
        goto cleanup;
    }

    if (phypUUIDTable_AddLpar(conn, def->uuid, def->id) == -1) {
        VIR_ERROR(_("Unable to add LPAR to the table"));
        goto cleanup;
    }

    result = 0;

 cleanup:
    VIR_FREE(ret);

    return result;
}

static virDomainPtr
phypDomainCreateXML(virConnectPtr conn,
                    const char *xml, unsigned int flags)
{
    virCheckFlags(0, NULL);

    phyp_driverPtr phyp_driver = conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    virDomainDefPtr def = NULL;
    virDomainPtr dom = NULL;
    uuid_tablePtr uuid_table = phyp_driver->uuid_table;
    lparPtr *lpars = uuid_table->lpars;
    size_t i = 0;
    char *managed_system = phyp_driver->managed_system;
    unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;

    virCheckFlags(VIR_DOMAIN_START_VALIDATE, NULL);

    if (flags & VIR_DOMAIN_START_VALIDATE)
        parse_flags |= VIR_DOMAIN_DEF_PARSE_VALIDATE;

    if (!(def = virDomainDefParseString(xml, phyp_driver->caps,
                                        phyp_driver->xmlopt,
                                        parse_flags)))
        goto err;

    /* checking if this name already exists on this system */
    if (phypGetLparID(session, managed_system, def->name, conn) != -1) {
        VIR_WARN("LPAR name already exists.");
        goto err;
    }

    /* checking if ID or UUID already exists on this system */
    for (i = 0; i < uuid_table->nlpars; i++) {
        if (lpars[i]->id == def->id || lpars[i]->uuid == def->uuid) {
            VIR_WARN("LPAR ID or UUID already exists.");
            goto err;
        }
    }

    if ((dom = virGetDomain(conn, def->name, def->uuid)) == NULL)
        goto err;

    if (phypBuildLpar(conn, def) == -1)
        goto err;

    if (phypDomainResume(dom) == -1)
        goto err;

    return dom;

 err:
    virDomainDefFree(def);
    virObjectUnref(dom);
    return NULL;
}

static char *
phypConnectGetCapabilities(virConnectPtr conn)
{
    phyp_driverPtr phyp_driver = conn->privateData;

    return virCapabilitiesFormatXML(phyp_driver->caps);
}

static int
phypDomainSetVcpusFlags(virDomainPtr dom, unsigned int nvcpus,
                        unsigned int flags)
{
    phyp_driverPtr phyp_driver = dom->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    int system_type = phyp_driver->system_type;
    char *managed_system = phyp_driver->managed_system;
    int exit_status = 0;
    char *ret = NULL;
    char operation;
    unsigned long ncpus = 0;
    unsigned int amount = 0;
    virBuffer buf = VIR_BUFFER_INITIALIZER;

    if (flags != VIR_DOMAIN_VCPU_LIVE) {
        virReportError(VIR_ERR_INVALID_ARG, _("unsupported flags: (0x%x)"), flags);
        return -1;
    }

    if ((ncpus = phypGetLparCPU(dom->conn, managed_system, dom->id)) == 0)
        return 0;

    if (nvcpus > phypDomainGetMaxVcpus(dom)) {
        VIR_ERROR(_("You are trying to set a number of CPUs bigger than "
                     "the max possible."));
        return 0;
    }

    if (ncpus > nvcpus) {
        operation = 'r';
        amount = nvcpus - ncpus;
    } else if (ncpus < nvcpus) {
        operation = 'a';
        amount = nvcpus - ncpus;
    } else {
        return 0;
    }

    virBufferAddLit(&buf, "chhwres -r proc");
    if (system_type == HMC)
        virBufferAsprintf(&buf, " -m %s", managed_system);
    virBufferAsprintf(&buf, " --id %d -o %c --procunits %d 2>&1 |sed "
                      "-e 's/^.*\\([0-9][0-9]*.[0-9][0-9]*\\).*$/\\1/'",
                      dom->id, operation, amount);
    ret = phypExecBuffer(session, &buf, &exit_status, dom->conn, false);

    if (exit_status < 0) {
        VIR_ERROR(_
                   ("Possibly you don't have IBM Tools installed in your LPAR."
                    " Contact your support to enable this feature."));
    }

    VIR_FREE(ret);
    return 0;

}

static int
phypDomainSetVcpus(virDomainPtr dom, unsigned int nvcpus)
{
    return phypDomainSetVcpusFlags(dom, nvcpus, VIR_DOMAIN_VCPU_LIVE);
}

static int
phypDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
{

    phyp_driverPtr phyp_driver = dom->conn->privateData;
    LIBSSH2_SESSION *session = phyp_driver->session;
    char *managed_system = phyp_driver->managed_system;
    char *lpar_name = NULL;
    int ret = -1;

    virCheckFlags(0, -1);

    lpar_name = phypGetLparNAME(session, managed_system, dom->id, dom->conn);

    if (lpar_name == NULL) {
        VIR_ERROR(_("Unable to determine domain's name."));
        goto cleanup;
    }

    ret = 0;

 cleanup:
    VIR_FREE(lpar_name);
    return ret;
}

static virHypervisorDriver phypHypervisorDriver = {
    .name = "PHYP",
    .connectOpen = phypConnectOpen, /* 0.7.0 */
    .connectClose = phypConnectClose, /* 0.7.0 */
    .connectGetCapabilities = phypConnectGetCapabilities, /* 0.7.3 */
    .connectListDomains = phypConnectListDomains, /* 0.7.0 */
    .connectNumOfDomains = phypConnectNumOfDomains, /* 0.7.0 */
    .domainCreateXML = phypDomainCreateXML, /* 0.7.3 */
    .domainLookupByID = phypDomainLookupByID, /* 0.7.0 */
    .domainLookupByName = phypDomainLookupByName, /* 0.7.0 */
    .domainResume = phypDomainResume, /* 0.7.0 */
    .domainShutdown = phypDomainShutdown, /* 0.7.0 */
    .domainReboot = phypDomainReboot, /* 0.9.1 */
    .domainDestroy = phypDomainDestroy, /* 0.7.3 */
    .domainDestroyFlags = phypDomainDestroyFlags, /* 0.9.4 */
    .domainGetInfo = phypDomainGetInfo, /* 0.7.0 */
    .domainGetState = phypDomainGetState, /* 0.9.2 */
    .domainSetVcpus = phypDomainSetVcpus, /* 0.7.3 */
    .domainSetVcpusFlags = phypDomainSetVcpusFlags, /* 0.8.5 */
    .domainGetVcpusFlags = phypDomainGetVcpusFlags, /* 0.8.5 */
    .domainGetMaxVcpus = phypDomainGetMaxVcpus, /* 0.7.3 */
    .domainGetXMLDesc = phypDomainGetXMLDesc, /* 0.7.0 */
    .connectListDefinedDomains = phypConnectListDefinedDomains, /* 0.7.0 */
    .connectNumOfDefinedDomains = phypConnectNumOfDefinedDomains, /* 0.7.0 */
    .domainAttachDevice = phypDomainAttachDevice, /* 0.8.2 */
    .connectIsEncrypted = phypConnectIsEncrypted, /* 0.7.3 */
    .connectIsSecure = phypConnectIsSecure, /* 0.7.3 */
    .domainIsUpdated = phypDomainIsUpdated, /* 0.8.6 */
    .connectIsAlive = phypConnectIsAlive, /* 0.9.8 */
    .domainHasManagedSaveImage = phypDomainHasManagedSaveImage, /* 1.2.13 */
};

static virStorageDriver phypStorageDriver = {
    .connectNumOfStoragePools = phypConnectNumOfStoragePools, /* 0.8.2 */
    .connectListStoragePools = phypConnectListStoragePools, /* 0.8.2 */
    .storagePoolLookupByName = phypStoragePoolLookupByName, /* 0.8.2 */
    .storagePoolLookupByUUID = phypStoragePoolLookupByUUID, /* 0.8.2 */
    .storagePoolCreateXML = phypStoragePoolCreateXML, /* 0.8.2 */
    .storagePoolDestroy = phypStoragePoolDestroy, /* 0.8.2 */
    .storagePoolGetXMLDesc = phypStoragePoolGetXMLDesc, /* 0.8.2 */
    .storagePoolNumOfVolumes = phypStoragePoolNumOfVolumes, /* 0.8.2 */
    .storagePoolListVolumes = phypStoragePoolListVolumes, /* 0.8.2 */

    .storageVolLookupByName = phypStorageVolLookupByName, /* 0.8.2 */
    .storageVolLookupByPath = phypStorageVolLookupByPath, /* 0.8.2 */
    .storageVolCreateXML = phypStorageVolCreateXML, /* 0.8.2 */
    .storageVolGetXMLDesc = phypStorageVolGetXMLDesc, /* 0.8.2 */
    .storageVolGetPath = phypStorageVolGetPath, /* 0.8.2 */
};

static virInterfaceDriver phypInterfaceDriver = {
    .connectNumOfInterfaces = phypConnectNumOfInterfaces, /* 0.9.1 */
    .connectListInterfaces = phypConnectListInterfaces, /* 0.9.1 */
    .interfaceLookupByName = phypInterfaceLookupByName, /* 0.9.1 */
    .interfaceDefineXML = phypInterfaceDefineXML, /* 0.9.1 */
    .interfaceDestroy = phypInterfaceDestroy, /* 0.9.1 */
    .interfaceIsActive = phypInterfaceIsActive /* 0.9.1 */
};

static virConnectDriver phypConnectDriver = {
    .hypervisorDriver = &phypHypervisorDriver,
    .interfaceDriver = &phypInterfaceDriver,
    .storageDriver = &phypStorageDriver,
};

int
phypRegister(void)
{
    return virRegisterConnectDriver(&phypConnectDriver,
                                    false);
}
