From 21dea2121ee1602e0b546b3e61b5a46c1ae2f54c Mon Sep 17 00:00:00 2001
From: Thilo Boehm <tboehm@linux.vnet.ibm.com>
Date: Wed, 11 Sep 2013 16:45:40 +0200
Subject: [PATCH 20/60] VSMS: Support for domains with console devices

An instance of KVM_ConsoleResourceAllocationSettingData can be added to
domain specification for VSMS DefineSystem() to define a console for a domain.
A console definition can not be modified or deleted.
It only can be added at system definition and deleted at system deletion.
If a KVM_ConsoleRASD is specified on a system definition,
no default graphics adapter definition is done.

Signed-off-by: Thilo Boehm <tboehm@linux.vnet.ibm.com>
Signed-off-by: Viktor Mihajlovski <mihajlov@linux.vnet.ibm.com>
Reviewed-by: Boris Fiuczynski <fiuczy@linux.vnet.ibm.com>
Signed-off-by: John Ferlan <jferlan@redhat.com>
---
 src/Virt_VirtualSystemManagementService.c | 314 +++++++++++++++++++++++++++---
 1 file changed, 291 insertions(+), 23 deletions(-)

diff --git a/src/Virt_VirtualSystemManagementService.c b/src/Virt_VirtualSystemManagementService.c
index 6629b35..d626691 100644
--- a/src/Virt_VirtualSystemManagementService.c
+++ b/src/Virt_VirtualSystemManagementService.c
@@ -1,5 +1,5 @@
 /*
- * Copyright IBM Corp. 2007
+ * Copyright IBM Corp. 2007, 2013
  *
  * Authors:
  *  Dan Smith <danms@us.ibm.com>
@@ -626,7 +626,8 @@ static bool default_input_device(struct domain *domain)

 static bool add_default_devs(struct domain *domain)
 {
-        if (domain->dev_graphics_ct < 1) {
+        if (domain->dev_graphics_ct < 1 &&
+            domain->dev_console_ct < 1) {
                 if (!default_graphics_device(domain))
                         return false;
         }
@@ -1339,47 +1340,294 @@ static int parse_sdl_address(const char *id,
         return ret;
 }

-static int parse_vnc_address(const char *id,
-                      char **ip,
-                      char **port)
+static int parse_ip_address(const char *id,
+                            char **ip,
+                            char **port)
 {
         int ret;
         char *tmp_ip = NULL;
         char *tmp_port = NULL;

-        CU_DEBUG("Entering parse_vnc_address, address is %s", id);
+        CU_DEBUG("Entering parse_ip_address, address is %s", id);
         if (strstr(id, "[") != NULL) {
                 /* its an ipv6 address */
                 ret = sscanf(id, "%a[^]]]:%as",  &tmp_ip, &tmp_port);
-                strcat(tmp_ip, "]");
+                if (tmp_ip != NULL) {
+                        tmp_ip = realloc(tmp_ip, strlen(tmp_ip) + 2);
+                        if (tmp_ip == NULL) {
+                                ret = 0;
+                                goto out;
+                        }
+                        strcat(tmp_ip, "]");
+                }
         } else {
                 ret = sscanf(id, "%a[^:]:%as", &tmp_ip, &tmp_port);
         }

-        if (ret != 2) {
+        /* ret == 2: address and port, ret == 1: address only */
+        if (ret < 1) {
                 ret = 0;
                 goto out;
         }

-        if (ip)
+        if (ip) {
                 *ip = strdup(tmp_ip);
+                CU_DEBUG("IP = '%s'",*ip);
+        }

-        if (port)
+        if (port && tmp_port) {
                 *port = strdup(tmp_port);
-
-        ret = 1;
+                CU_DEBUG("Port = '%s'",*port);
+        }

  out:
-        if (ip && port)
-                CU_DEBUG("Exiting parse_vnc_address, ip is %s, port is %s",
-                        *ip, *port);
-
         free(tmp_ip);
         free(tmp_port);

         return ret;
 }

+static bool parse_console_url(const char *url,
+                              char **protocol,
+                              char **host,
+                              char **port)
+{
+        bool success = false;
+        char *tmp_protocol = NULL;
+        char *tmp_address = NULL;
+
+        CU_DEBUG("Entering parse_console_url:'%s'", url);
+
+        if (sscanf(url,"%a[^:]://%as", &tmp_protocol, &tmp_address) != 2)
+                goto out;
+
+        if (parse_ip_address(tmp_address, host, port) < 1)
+                goto out;
+
+        if (protocol) {
+                *protocol = strdup(tmp_protocol);
+                CU_DEBUG("Protocol = '%s'", *protocol);
+        }
+
+        success = true;
+
+ out:
+        free(tmp_protocol);
+        free(tmp_address);
+
+        return success;
+}
+
+static const char *_unixsock_console_rasd_to_vdev(CMPIInstance *inst,
+                                                  struct console_device *cdev)
+{
+        const char *val = NULL;
+        const char *val2 = NULL;
+        char* protocol = NULL;
+
+        cdev->source_dev.unixsock.mode = NULL;
+        if (cu_get_str_prop(inst,"ConnectURL", &val) == CMPI_RC_OK) {
+                CU_DEBUG("ConnectURL = '%s'", val);
+                cdev->source_dev.unixsock.mode = strdup("connect");
+        }
+
+        if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) {
+                if (cdev->source_dev.unixsock.mode != NULL)
+                        return "ConsoleRASD: Only one of ConnectURL or BindURL "
+                                "is allowed for UNIX domain sockets.";
+                CU_DEBUG("BindURL = '%s'", val2);
+                cdev->source_dev.unixsock.mode = strdup("bind");
+                val = val2;
+        }
+
+        if (val) {
+                if (!parse_console_url(val, &protocol, &cdev->source_dev.unixsock.path, NULL))
+                        return "ConsoleRASD: Invalid ConnectURL or BindURL for "
+                                "UNIX domain socket client/server.";
+
+                if (protocol != NULL && !STREQC("file", protocol)) {
+                        CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol);
+                        free(protocol);
+                        return "ConsoleRASD: Protocol 'file' was not specified for "
+                                "ConnectURL or BindURL for UNIX domain socket client/server.";
+                }
+                free(protocol);
+        } else {
+                return "ConsoleRASD: ConnectURL or BindURL not specified for "
+                       "UNIX domain socket client/server.";
+        }
+
+        return NULL;
+}
+
+static const char *_udp_console_rasd_to_vdev(CMPIInstance *inst,
+                                             struct console_device *cdev)
+{
+         const char *val = NULL;
+         char* protocol = NULL;
+
+         if (cu_get_str_prop(inst, "ConnectURL", &val) != CMPI_RC_OK)
+                 return "ConsoleRASD: ConnectURL not specified for UDP network console.";
+
+         if (!parse_console_url(val, &protocol,
+                                &cdev->source_dev.udp.connect_host,
+                                &cdev->source_dev.udp.connect_service))
+                 return "ConsoleRASD: Invalid ConnectURL specified for UDP network console.";
+
+         if (protocol != NULL && !STREQC("udp", protocol)) {
+                 CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol);
+                 free(protocol);
+                 return "ConsoleRASD: Protocol 'udp' was not specified at "
+                         "ConnectURL for UDP network console.";
+         }
+
+         free(protocol);
+
+         if (cu_get_str_prop(inst, "BindURL", &val) != CMPI_RC_OK)
+                 return "ConsoleRASD: BindURL not specified for UDP network console.";
+
+         if (!parse_console_url(val, &protocol,
+                                &cdev->source_dev.udp.bind_host,
+                                &cdev->source_dev.udp.bind_service))
+                 return "ConsoleRASD: Invalid BindURL specified for UDP network console.";
+
+         if (protocol != NULL && !STREQC("udp", protocol)) {
+                 CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'", protocol);
+                 free(protocol);
+                 return "ConsoleRASD: Protocol 'udp' was not specified at BindURL "
+                         "for UDP network console.";
+         }
+
+         free(protocol);
+         return NULL;
+}
+
+static const char *_tcp_console_rasd_to_vdev(CMPIInstance *inst,
+                                             struct console_device *cdev)
+{
+         const char *val = NULL;
+         const char *val2 = NULL;
+
+         cdev->source_dev.tcp.mode = NULL;
+         if (cu_get_str_prop(inst, "ConnectURL", &val) == CMPI_RC_OK) {
+                 CU_DEBUG("ConnectURL = '%s'", val);
+                 cdev->source_dev.tcp.mode = strdup("connect");
+         }
+
+         if (cu_get_str_prop(inst, "BindURL", &val2) == CMPI_RC_OK) {
+                 if (cdev->source_dev.tcp.mode != NULL)
+                        return "ConsoleRASD: Only one of ConnectURL or BindURL "
+                                "is allowed for TCP sockets.";
+                 CU_DEBUG("BindURL = '%s'", val2);
+                 cdev->source_dev.tcp.mode = strdup("bind");
+                 val = val2;
+         }
+
+         if (val) {
+                 if (!parse_console_url(val,
+                                        &cdev->source_dev.tcp.protocol,
+                                        &cdev->source_dev.tcp.host,
+                                        &cdev->source_dev.tcp.service))
+                         return "ConsoleRASD: Invalid ConnectURL or BindURL for "
+                                 "TCP client/server console.";
+                 if (cdev->source_dev.tcp.service == NULL)
+                         return "ConsoleRASD: Missing TCP port for TCP client/server console.";
+         } else {
+                 return "ConsoleRASD: ConnectURL or BindURL not specified for "
+                         "TCP client/server console.";
+         }
+
+         if (cdev->source_dev.tcp.protocol != NULL) {
+                 if (STREQC("udp", cdev->source_dev.tcp.protocol)) {
+                         CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'",
+                                  cdev->source_dev.tcp.protocol);
+                         return "ConsoleRASD: Invalid protocol 'udp' was specified at "
+                                 "TCP client/server console.";
+                 } else  if (STREQC("file", cdev->source_dev.tcp.protocol)) {
+                         CU_DEBUG("Wrong ConsoleRASD protocol specified: '%s'",
+                                  cdev->source_dev.tcp.protocol);
+                         return "ConsoleRASD: Invalid protocol 'file' was specified at "
+                                 "TCP client/server console.";
+                 }
+         }
+
+         return NULL;
+}
+
+static const char *console_rasd_to_vdev(CMPIInstance *inst,
+                                        struct virt_device *dev)
+{
+        int rc = 0;
+        const char *msg = NULL;
+        const char *val = NULL;
+        struct console_device *cdev = &dev->dev.console;
+        uint16_t tmp;
+
+        rc = cu_get_u16_prop(inst, "SourceType", &tmp);
+        if (rc != CMPI_RC_OK)
+                return "ConsoleRASD: SourceType field not specified.";
+
+        if (tmp >= CIM_CHARDEV_SOURCE_TYPE_INVALIDTYPE)
+                return "ConsoleRASD: Invalid SourceType value";
+
+        cdev->source_type = tmp;
+        CU_DEBUG("Processing SourceType: %d", cdev->source_type);
+
+        /* property not required */
+        if (cu_get_str_prop(inst, "TargetType", &val) == CMPI_RC_OK)
+                cdev->target_type = strdup(val);
+        CU_DEBUG("TargetType is '%s'", cdev->target_type ? : "NULL" );
+
+        switch (cdev->source_type) {
+        case CIM_CHARDEV_SOURCE_TYPE_PTY:
+                /* property not required */
+                if (cu_get_str_prop(inst, "SourcePath", &val) == CMPI_RC_OK)
+                        cdev->source_dev.pty.path = strdup(val);
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_DEV:
+                if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK)
+                       return "ConsoleRASD: SourcePath not specified for Host device proxy.";
+                cdev->source_dev.dev.path = strdup(val);
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_FILE:
+                if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK)
+                        return "ConsoleRASD: SourcePath not specified for Device logfile.";
+                cdev->source_dev.file.path = strdup(val);
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_PIPE:
+                if (cu_get_str_prop(inst, "SourcePath", &val) != CMPI_RC_OK)
+                        return "ConsoleRASD: SourcePath not specified for Named pipe.";
+                cdev->source_dev.pipe.path = strdup(val);
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_UNIXSOCK:
+                msg = _unixsock_console_rasd_to_vdev(inst, cdev);
+                if (msg != NULL)
+                        return msg;
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_UDP:
+                msg = _udp_console_rasd_to_vdev(inst, cdev);
+                if (msg != NULL)
+                        return msg;
+                break;
+        case CIM_CHARDEV_SOURCE_TYPE_TCP:
+                msg = _tcp_console_rasd_to_vdev(inst, cdev);
+                if (msg != NULL)
+                        return msg;
+                break;
+
+        default:
+                /* Nothing to do for :
+                   CIM_CHARDEV_SOURCE_TYPE_STDIO
+                   CIM_CHARDEV_SOURCE_TYPE_NULL
+                   CIM_CHARDEV_SOURCE_TYPE_VC
+                   CIM_CHARDEV_SOURCE_TYPE_SPICEVMC
+                */
+                break;
+        }
+
+        return NULL;
+}
+
 static const char *graphics_rasd_to_vdev(CMPIInstance *inst,
                                          struct virt_device *dev)
 {
@@ -1394,7 +1642,7 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst,
         }
         dev->dev.graphics.type = strdup(val);

-        CU_DEBUG("graphics type = %s", dev->dev.graphics.type);
+        CU_DEBUG("graphics type = %s", dev->dev.graphics.type ? : "NULL");

         /* FIXME: Add logic to prevent address:port collisions */
         if (STREQC(dev->dev.graphics.type, "vnc")) {
@@ -1411,10 +1659,10 @@ static const char *graphics_rasd_to_vdev(CMPIInstance *inst,
                                 val = "127.0.0.1:-1";
                 }

-                ret = parse_vnc_address(val,
+                ret = parse_ip_address(val,
                                 &dev->dev.graphics.dev.vnc.host,
                                 &dev->dev.graphics.dev.vnc.port);
-                if (ret != 1) {
+                if (ret != 2) {
                         msg = "GraphicsRASD field Address not valid";
                         goto out;
                 }
@@ -1540,6 +1788,8 @@ static const char *_sysvirt_rasd_to_vdev(CMPIInstance *inst,
                 return proc_rasd_to_vdev(inst, dev);
         } else if (type == CIM_RES_TYPE_GRAPHICS) {
                 return graphics_rasd_to_vdev(inst, dev);
+        } else if (type == CIM_RES_TYPE_CONSOLE) {
+                return console_rasd_to_vdev(inst, dev);
         } else if (type == CIM_RES_TYPE_INPUT) {
                 return input_rasd_to_vdev(inst, dev);
         }
@@ -1601,7 +1851,7 @@ static const char *rasd_to_vdev(CMPIInstance *inst,
         return msg;
 }

-static char *add_device_nodup(struct virt_device *dev,
+static const char *add_device_nodup(struct virt_device *dev,
                               struct virt_device *list,
                               int max,
                               int *index)
@@ -1663,6 +1913,9 @@ static const char *classify_resources(CMPIArray *resources,
         if (!make_space(&domain->dev_graphics, domain->dev_graphics_ct, count))
                 return "Failed to alloc graphics list";

+        if (!make_space(&domain->dev_console, domain->dev_console_ct, count))
+                return "Failed to alloc console list";
+
         if (!make_space(&domain->dev_input, domain->dev_input_ct, count))
                 return "Failed to alloc input list";

@@ -1765,6 +2018,14 @@ static const char *classify_resources(CMPIArray *resources,
                                                 domain->dev_graphics,
                                                 gcount,
                                                 &domain->dev_graphics_ct);
+                } else if (type == CIM_RES_TYPE_CONSOLE) {
+                        msg = rasd_to_vdev(inst,
+                                           domain,
+                                           &domain->dev_console[domain->dev_console_ct],
+                                           ns,
+                                           p_error);
+                        if (msg == NULL)
+                                domain->dev_console_ct+=1;
                 } else if (type == CIM_RES_TYPE_INPUT) {
                         domain->dev_input_ct = 1;
                         msg = rasd_to_vdev(inst,
@@ -2570,6 +2831,9 @@ static struct virt_device **find_list(struct domain *dominfo,
         } else if (type == CIM_RES_TYPE_GRAPHICS) {
                 list = &dominfo->dev_graphics;
                 *count = &dominfo->dev_graphics_ct;
+        } else if (type == CIM_RES_TYPE_CONSOLE) {
+                list = &dominfo->dev_console;
+                *count = &dominfo->dev_console_ct;
         } else if (type == CIM_RES_TYPE_INPUT) {
                 list = &dominfo->dev_input;
                 *count = &dominfo->dev_input_ct;
@@ -2693,7 +2957,8 @@ static CMPIStatus resource_del(struct domain *dominfo,

                 if (STREQ(dev->id, devid)) {
                         if ((type == CIM_RES_TYPE_GRAPHICS) ||
-                           (type == CIM_RES_TYPE_INPUT))
+                            (type == CIM_RES_TYPE_CONSOLE)  ||
+                            (type == CIM_RES_TYPE_INPUT))
                                 cu_statusf(_BROKER, &s, CMPI_RC_OK, "");
                         else {
                                 s = _resource_dynamic(dominfo,
@@ -2774,7 +3039,9 @@ static CMPIStatus resource_add(struct domain *dominfo,
                 goto out;
         }

-        if ((type == CIM_RES_TYPE_GRAPHICS) || (type == CIM_RES_TYPE_INPUT)) {
+        if ((type == CIM_RES_TYPE_GRAPHICS) ||
+            (type == CIM_RES_TYPE_INPUT) ||
+            (type == CIM_RES_TYPE_CONSOLE)) {
                 (*count)++;
                 cu_statusf(_BROKER, &s, CMPI_RC_OK, "");
                 goto out;
@@ -2850,7 +3117,8 @@ static CMPIStatus resource_mod(struct domain *dominfo,
                         }

                         if ((type == CIM_RES_TYPE_GRAPHICS) ||
-                            (type == CIM_RES_TYPE_INPUT))
+                            (type == CIM_RES_TYPE_INPUT)    ||
+                            (type == CIM_RES_TYPE_CONSOLE))
                                 cu_statusf(_BROKER, &s, CMPI_RC_OK, "");
                         else {
 #if LIBVIR_VERSION_NUMBER < 9000
--
2.1.0
