Implement the qemu version of hello

This is the fifth post in the series “HOWTO: Add libvirt support for a qemu command”

Now begins the fun part where we can actually interact with qemu to implement the command. In this patch we first create a virDrvDomainHello function in the qemu driver. This function locates the domain we are targetting, issues a monitor command, and returns the result. We provide support for issuing the command using the qmp and hmp monitors.

Find the patch here.

diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 40bb4c0..de6c719 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -7079,6 +7079,50 @@ cleanup:
     return ret;
 }
 
+static char *
+qemuDomainHello(virDomainPtr dom,
+                virHelloParamsPtr params)
+{
+    struct qemud_driver *driver = dom->conn->privateData;
+    virDomainObjPtr vm = NULL;
+    char *greeting = NULL;
+    
+    qemuDriverLock(driver);
+    vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    qemuDriverUnlock(driver);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+        virUUIDFormat(dom->uuid, uuidstr);
+        qemuReportError(VIR_ERR_NO_DOMAIN,
+                        _("no domain with matching uuid '%s'"), uuidstr);
+        goto cleanup;
+    }
+
+    if (qemuDomainObjBeginJob(vm) < 0)
+        goto cleanup;
+
+    if (virDomainObjIsActive(vm)) {
+        qemuDomainObjPrivatePtr priv = vm->privateData;
+        qemuDomainObjEnterMonitor(vm);
+        greeting = qemuMonitorHello(priv->mon, params);
+        if (!greeting)
+            qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                            _("monitor command failed"));
+        qemuDomainObjExitMonitor(vm);
+    } else {
+        qemuReportError(VIR_ERR_OPERATION_INVALID,
+                        "%s", _("domain is not running"));
+    }
+
+    if (qemuDomainObjEndJob(vm) == 0)
+        vm = NULL;
+
+cleanup:
+    if (vm)
+        virDomainObjUnlock(vm);
+    return greeting;
+}
 
 static virDriver qemuDriver = {
     VIR_DRV_QEMU,
@@ -7191,7 +7235,7 @@ static virDriver qemuDriver = {
     qemuDomainSnapshotDelete, /* domainSnapshotDelete */
     qemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
     qemuDomainOpenConsole, /* domainOpenConsole */
-    NULL, /* domainHello */
+    qemuDomainHello, /* domainHello */
 };

Above is the top-level implementation for the qemu driver. The first thing we do is find the vm object corresponding to the domain passed in by performing a UUID lookup. Be careful to follow driver locking rules. Just follow the many existing examples in order to get this right. In order to issue commands to a qemu monitor, you must enter a job context. This is another form of locking to make sure that commands are executed synchronously. Again, look to other commands for examples of correct usage. Once in job context, we call into the monitor code to perform the monitor command. Let’s look at that part next.

diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 800f744..46b89ae 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -2189,6 +2189,24 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name)
     return ret;
 }
 
+char *qemuMonitorHello(qemuMonitorPtr mon, virHelloParams *params) {
+    char *ret;
+    
+    VIR_DEBUG("mon=%p, params=%p",mon,params);
+
+    if (!mon) {
+        qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+                        _("monitor must not be NULL"));
+        return NULL;
+    }
+
+    if (mon->json)
+        ret = qemuMonitorJSONHello(mon, params);
+    else
+        ret = qemuMonitorTextHello(mon, params);
+    return ret;
+}
+
 int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
                                 const char *cmd,
                                 char **reply,
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index c90219b..c1ecd10 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -418,6 +418,8 @@ int qemuMonitorCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+char *qemuMonitorHello(qemuMonitorPtr mon, virHelloParams *params);
+
 int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
                                 const char *cmd,
                                 char **reply,

The function qemuMonitorHello is (yet another) wrapper around actual functionality. Libvirt supports the qmp and hmp monitor protocols. This function selects the appropriate function depending on the protocol being used by the active monitor.

diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 6bd03d6..5f18cfa 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -2470,6 +2470,49 @@ cleanup:
     return ret;
 }
 
+char *qemuMonitorJSONHello(qemuMonitorPtr mon, virHelloParams *params) {
+    const char *response;
+    char *ret = NULL;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr obj;
+
+    if (params->lang == NULL)
+        cmd = qemuMonitorJSONMakeCommand("hello",
+                                         "b:exclaim", params->exclaim != 0,
+                                         NULL);
+    else
+        cmd = qemuMonitorJSONMakeCommand("hello",
+                                         "b:exclaim", params->exclaim != 0,
+                                         "s:lang", params->lang,
+                                         NULL);
+    if (!cmd)
+        return NULL;
+    
+    if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0 ||
+        qemuMonitorJSONCheckError(cmd, reply) < 0)
+        goto cleanup;
+
+    if (!(obj = virJSONValueObjectGet(reply, "return")) ||
+        obj->type != VIR_JSON_TYPE_OBJECT)
+        goto cleanup;
+
+    response = virJSONValueObjectGetString(obj, "response");
+    if (response == NULL) {
+        qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                        _("malformed response to hello command"));
+        goto cleanup;
+    }
+
+    /* Return a copy because we will be freeing reply below */
+    ret = strdup(response);
+    
+cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
 int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
                                     const char *cmd_str,
                                     char **reply_str,
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 086f0e1..e0cf5bd 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -199,6 +199,8 @@ int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+char *qemuMonitorJSONHello(qemuMonitorPtr mon, virHelloParams *params);
+
 int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
                                     const char *cmd_str,
                                     char **reply_str,

Next, implement the JSON version of the hello command (for qmp monitors). This file contains very useful functions for manipulating JSON objects. qemuMonitorJSONMakeCommand is used to build the proper JSON command string. Next, we send the command, receive the response, and check for errors. We then use the virJSONValueObjectGet family of functions to extract the returned string from the response. Be careful when returning pointers and be sure to think about how the API is supposed to work. We want to return a new string to the caller and transfer ownership of that string to the caller. In order to do this we must make a copy (because the JSON response is going to be freed next. The string we return will eventually be freed by the caller when it is no longer needed.

diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index 75b2995..452c43f 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -2607,6 +2607,36 @@ cleanup:
     return ret;
 }
 
+char *qemuMonitorTextHello(qemuMonitorPtr mon, virHelloParams *params)
+{
+    char *cmd;
+    char *reply = NULL;
+    int rc;
+
+    if (params->exclaim != 0)
+        rc = virAsprintf(&cmd, "hello -e %s",
+                         params->lang ? params->lang : "");
+    else
+        rc = virAsprintf(&cmd, "hello %s",
+                         params->lang ? params->lang : "");
+    
+    if (rc == -1) {
+        virReportOOMError();
+        return NULL;
+    }
+    
+    if (qemuMonitorHMPCommand(mon, cmd, &reply)) {
+        qemuReportError(VIR_ERR_OPERATION_FAILED,
+                         _("failed to perform command '%s'"),
+                         cmd);
+        VIR_FREE(reply);
+    }
+    VIR_FREE(cmd);
+    
+    /* We own the reference to reply, return it and let the caller free it */
+    return reply;
+}
+
 int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
                                     char **reply)
 {
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 0838a2b..6c5f4f2 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -195,6 +195,8 @@ int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name);
 int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name);
 
+char *qemuMonitorTextHello(qemuMonitorPtr mon, virHelloParams *params);
+
 int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd,
                                     char **reply);

Last but not least, we have the hmp implementation of the command. Hopefully some day we will only need to program one monitor protocol but for now there are two. First, build a command string based on the parameters. Then execute the command and check for errors. If successful, reply will contain the hmp reply. No further parsing of this string is required so we just return it. Why don’t we duplicate the string as we did for the JSON monitor implementation? In this case, qemuMonitorHMPCommand allocates the reply string and transfers control of the reference to its caller. Thus, it is safe to return the pointer directly and let the caller free it.

Up next… Add hello remote transport support

Advertisements

About aglitke

I am a software engineer working on Linux, open source software, and virtualization. I am proud to work at Red Hat on oVirt and Red Hat Virtualization with a focus on software defined storage. Other notable projects I have been involved in include: The Linux ppc64 architecture, Linux kernel crash dumps (kdump), Linux huge pages and libhugetlbfs, qemu, libvirt, and the Memory Overcommitment Manager.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s