* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/* For an explanation of the threading model, please main(). */
+
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <assert.h>
#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <libxml/parser.h>
#include <curl/curl.h>
static gpointer wui_thread (gpointer data);
static void wui_thread_send_quit (void);
static void do_curl_init (void);
+static void write_fn_start_capture (void);
+static char *write_fn_finish_capture (void);
+static void write_fn_discard_capture_buffer (void);
static gboolean do_connect (void);
static gboolean do_login (void);
-static void refresh_vm_list (void);
+static gboolean refresh_vm_list (void);
+static void parse_vmlist_from_xml (const char *xml);
+static struct vm *parse_vm_from_xml (xmlNodePtr node);
/* Messages (main thread -> WUI thread only).
*
/* Start the WUI thread. See main() for explanation of the threading model. */
static GThread *wui_gthread = NULL;
+static GThread *main_gthread = NULL;
static GAsyncQueue *wui_thread_queue = NULL;
void
DEBUG ("starting the WUI thread");
+ assert (wui_gthread == NULL);
+
+ main_gthread = g_thread_self ();
+
/* Create the message queue for main -> WUI thread communications. */
wui_thread_queue = g_async_queue_new ();
stop_wui_thread (void)
{
DEBUG ("stopping the WUI thread");
- assert (wui_gthread);
+
+ assert (wui_gthread != NULL);
+ ASSERT_IS_MAIN_THREAD ();
/* Send a quit message then wait for the WUI thread to join.
*
wui_gthread = NULL;
}
+void
+assert_is_wui_thread (const char *filename, int lineno)
+{
+ if (g_thread_self () != wui_gthread) {
+ fprintf (stderr, "%s:%d: internal error: this function should only run in the context of the WUI thread\n", filename, lineno);
+ abort ();
+ }
+}
+
+void
+assert_is_main_thread (const char *filename, int lineno)
+{
+ if (g_thread_self () != main_gthread) {
+ fprintf (stderr, "%s:%d: internal error: this function should only run in the context of the main thread\n", filename, lineno);
+ abort ();
+ }
+}
+
/* Send the quit message to the WUI thread. */
static void
wui_thread_send_quit (void)
{
struct message *msg;
+ ASSERT_IS_MAIN_THREAD ();
+
msg = g_new (struct message, 1);
msg->type = QUIT;
g_async_queue_push (wui_thread_queue, msg);
{
struct message *msg;
+ ASSERT_IS_MAIN_THREAD ();
+
msg = g_new (struct message, 1);
msg->type = CONNECT;
msg->str1 = g_strdup (uri);
{
struct message *msg;
+ ASSERT_IS_MAIN_THREAD ();
+
msg = g_new (struct message, 1);
msg->type = DISCONNECT;
g_async_queue_push (wui_thread_queue, msg);
{
struct message *msg;
+ ASSERT_IS_MAIN_THREAD ();
+
msg = g_new (struct message, 1);
msg->type = LOGIN;
msg->str1 = g_strdup (username);
{
struct message *msg;
+ ASSERT_IS_MAIN_THREAD ();
+
msg = g_new (struct message, 1);
msg->type = REFRESH_VM_LIST;
g_async_queue_push (wui_thread_queue, msg);
DEBUG ("WUI thread starting up");
+ /* This checks wui_gthread global which is actually set in the
+ * main thread. Of course, it might not be set if the WUI thread
+ * runs first. Hence we sleep for the main thread to run. (XXX)
+ */
+ g_usleep (100000);
+ ASSERT_IS_WUI_THREAD ();
+
g_async_queue_ref (queue);
/* In the thread's loop we check for new instructions from the main
static void
set_connected (gboolean new_connected)
{
+ ASSERT_IS_WUI_THREAD ();
+
g_static_mutex_lock (&state_mutex);
connected = new_connected;
g_static_mutex_unlock (&state_mutex);
+
+ if (connected)
+ g_idle_add (main_connected, NULL);
+ else
+ g_idle_add (main_disconnected, NULL);
}
static void
set_logged_in (gboolean new_logged_in)
{
+ ASSERT_IS_WUI_THREAD ();
+
g_static_mutex_lock (&state_mutex);
logged_in = new_logged_in;
g_static_mutex_unlock (&state_mutex);
+
+ if (logged_in)
+ g_idle_add (main_logged_in, NULL);
+ else
+ g_idle_add (main_logged_out, NULL);
}
static void
set_busy (gboolean new_busy)
{
+ ASSERT_IS_WUI_THREAD ();
+
g_static_mutex_lock (&state_mutex);
busy = new_busy;
g_static_mutex_unlock (&state_mutex);
+
+ if (busy)
+ g_idle_add (main_busy, NULL);
+ else
+ g_idle_add (main_idle, NULL);
}
/* The main thread should call these functions to get the WUI thread's
{
gboolean ret;
+ ASSERT_IS_MAIN_THREAD ();
+
g_static_mutex_lock (&state_mutex);
ret = connected;
g_static_mutex_unlock (&state_mutex);
{
gboolean ret;
+ ASSERT_IS_MAIN_THREAD ();
+
g_static_mutex_lock (&state_mutex);
ret = logged_in;
g_static_mutex_unlock (&state_mutex);
{
gboolean ret;
+ ASSERT_IS_MAIN_THREAD ();
+
g_static_mutex_lock (&state_mutex);
ret = busy;
g_static_mutex_unlock (&state_mutex);
static gboolean
process_message (struct message *msg)
{
+ ASSERT_IS_WUI_THREAD ();
+
switch (msg->type) {
case QUIT:
+ write_fn_discard_capture_buffer ();
if (curl) curl_easy_cleanup (curl);
if (uri) g_free (uri);
if (username) g_free (username);
return 1;
case CONNECT:
+ write_fn_discard_capture_buffer ();
if (curl) curl_easy_cleanup (curl);
do_curl_init ();
if (uri) g_free (uri);
case DISCONNECT:
/* This just forgets the state. REST is connectionless. */
+ write_fn_discard_capture_buffer ();
if (curl) curl_easy_cleanup (curl);
curl = NULL;
if (uri) g_free (uri);
if (connected) {
if (do_login ()) {
set_logged_in (TRUE);
- refresh_vm_list ();
+ if (refresh_vm_list ())
+ secs_between_refresh = 60;
} else {
set_logged_in (FALSE);
}
__r; \
})
+/* Libcurl has a really crufty method for handling HTTP headers and
+ * data. We set these functions as callbacks, because the default
+ * callback functions print the data out to stderr. In order to
+ * capture the data, we have to keep state here.
+ */
+
+static char *write_fn_buffer = NULL;
+static ssize_t write_fn_len = -1;
+
static size_t
write_fn (void *ptr, size_t size, size_t nmemb, void *stream)
{
int bytes = size * nmemb;
+ int old_start;
+
+ ASSERT_IS_WUI_THREAD ();
+
+ if (write_fn_len >= 0) { /* We're capturing. */
+ old_start = write_fn_len;
+ write_fn_len += bytes;
+ write_fn_buffer = g_realloc (write_fn_buffer, write_fn_len);
+ memcpy (write_fn_buffer + old_start, ptr, bytes);
+ }
+
return bytes;
}
+/* Start capturing HTTP response data. */
+static void
+write_fn_start_capture (void)
+{
+ ASSERT_IS_WUI_THREAD ();
+
+ write_fn_discard_capture_buffer ();
+ write_fn_buffer = NULL;
+ write_fn_len = 0;
+}
+
+/* Finish capture and return the capture buffer. Caller must free. */
+static char *
+write_fn_finish_capture (void)
+{
+ char *ret;
+
+ ASSERT_IS_WUI_THREAD ();
+
+ /* Make sure the buffer is NUL-terminated before returning it. */
+ write_fn_buffer = g_realloc (write_fn_buffer, write_fn_len+1);
+ write_fn_buffer[write_fn_len] = '\0';
+ ret = write_fn_buffer;
+
+ write_fn_buffer = NULL;
+ write_fn_len = -1;
+ return ret;
+}
+
+/* Stop capturing and discard the capture buffer, if any. */
+static void
+write_fn_discard_capture_buffer (void)
+{
+ ASSERT_IS_WUI_THREAD ();
+
+ g_free (write_fn_buffer);
+ write_fn_buffer = NULL;
+ write_fn_len = -1;
+}
+
static size_t
header_fn (void *ptr, size_t size, size_t nmemb, void *stream)
{
int bytes = size * nmemb;
+
+ ASSERT_IS_WUI_THREAD ();
+
return bytes;
}
{
DEBUG ("initializing libcurl");
+ ASSERT_IS_WUI_THREAD ();
+
curl = curl_easy_init ();
if (!curl) { /* This is probably quite bad, so abort. */
DEBUG ("curl_easy_init failed");
do_connect (void)
{
long code = 0;
+ CURLcode r;
+ char *error_str;
DEBUG ("connecting to uri %s", uri);
+ ASSERT_IS_WUI_THREAD ();
/* Set the URI for libcurl. */
CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, uri));
/* Try to fetch the URI. */
- if (CURL_CHECK_ERROR (curl_easy_perform, (curl)) != CURLE_OK)
+ r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
+ if (r != CURLE_OK) {
+ /* Signal an error back to the main thread. */
+ error_str = g_strdup (curl_easy_strerror (r));
+ g_idle_add (main_connection_error, error_str);
return FALSE;
+ }
CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
DEBUG ("HTTP return code is %ld", code);
- if (code != 200 || code != 302 || code != 401)
+ if (code != 200 && code != 302 && code != 401) {
+ /* XXX If only glib had g_asprintf. */
+ error_str = g_strdup ("unexpected HTTP return code from server");
+ g_idle_add (main_connection_error, error_str);
return FALSE;
+ }
return TRUE;
}
static gboolean
do_login (void)
{
+ int len;
+ char *login_uri;
+ char *userpwd;
+ char *error_str;
+ CURLcode r;
+ long code = 0;
+
DEBUG ("logging in with username %s, password *****", username);
- return FALSE;
+ ASSERT_IS_WUI_THREAD ();
+
+ /* Generate the login URI from the base URI. */
+ len = strlen (uri) + 6 + 1;
+ login_uri = g_alloca (len);
+ snprintf (login_uri, len, "%s/login", uri);
+
+ DEBUG ("login URI %s", login_uri);
+
+ /* Set the URI for libcurl. */
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, login_uri));
+
+ /* Construct the username:password for CURL. */
+ len = strlen (username) + strlen (password) + 2;
+ userpwd = g_alloca (len);
+ snprintf (userpwd, len, "%s:%s", username, password);
+
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_USERPWD, userpwd));
+
+ /* HTTP Basic authentication is OK since we should be sending
+ * this only over HTTPS.
+ */
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC));
+
+ /* Follow redirects. */
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_FOLLOWLOCATION, (long) 1));
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_MAXREDIRS, (long) 10));
+
+ // FIXME when ssl is introduced into ovirt-viewer, remove there two lines
+ CURL_CHECK_ERROR(curl_easy_setopt, (curl, CURLOPT_SSL_VERIFYHOST, 0));
+ CURL_CHECK_ERROR(curl_easy_setopt, (curl, CURLOPT_SSL_VERIFYPEER, 0));
+
+ /* Try to fetch the URI. */
+ r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
+ if (r != CURLE_OK) {
+ /* Signal an error back to the main thread. */
+ error_str = g_strdup (curl_easy_strerror (r));
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+ }
+
+ CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
+ DEBUG ("HTTP return code is %ld", code);
+ switch (code)
+ {
+ case 200:
+ DEBUG ("login was successful");
+ return TRUE;
+
+ case 401:
+ error_str = g_strdup ("server rejected the username or password");
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+
+ default:
+ /* XXX If only glib had g_asprintf. */
+ error_str = g_strdup ("unexpected HTTP return code from server");
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+ }
}
/* Called from the message loop. Refresh the list of VMs. */
-static void
+static gboolean
refresh_vm_list (void)
{
+ int len;
+ char *vms_uri;
+ char *error_str;
+ CURLcode r;
+ long code = 0;
+ char *xml;
+
DEBUG ("refreshing list of VMs");
+ ASSERT_IS_WUI_THREAD ();
+
+ /* Generate the vms URI from the base URI. */
+ len = strlen (uri) + 4 + 1;
+ vms_uri = g_alloca (len);
+ snprintf (vms_uri, len, "%s/vms", uri);
+
+ DEBUG ("vms URI %s", vms_uri);
+
+ /* Set the URI for libcurl. */
+ CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, vms_uri));
+
+ /* We want to capture the output, so tell our write function
+ * to place the output into a buffer.
+ */
+ write_fn_start_capture ();
+
+ /* Try to fetch the URI. */
+ r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
+ if (r != CURLE_OK) {
+ /* Signal an error back to the main thread. */
+ error_str = g_strdup (curl_easy_strerror (r));
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+ }
+
+ CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
+ DEBUG ("HTTP return code is %ld", code);
+ switch (code)
+ {
+ case 200: break;
+
+ /* Hmm - even though we previously logged in, the server is
+ * rejecting our attempts now with an authorization error.
+ * We move to the logged out state.
+ */
+ case 401:
+ set_logged_in (FALSE);
+ error_str = g_strdup ("server rejected the username or password");
+ g_idle_add (main_login_error, error_str);
+ return FALSE;
+
+ default:
+ /* XXX If only glib had g_asprintf. */
+ error_str = g_strdup ("unexpected HTTP return code from server");
+ g_idle_add (main_status_error, error_str);
+ return FALSE;
+ }
+
+ /* If we got here then we appear to have a correct
+ * XML document describing the list of VMs.
+ */
+ secs_between_refresh <<= 1;
+
+ xml = write_fn_finish_capture ();
+
+ parse_vmlist_from_xml (xml);
+ g_free (xml);
+
+ return TRUE;
+}
+
+/* Functions to deal with the list of VMs, parsing it from the XML, etc.
+ *
+ * A vmlist is a GSList (glib singly-linked list) of vm structures.
+ * The caller must call free_vmlist to free up this list correctly.
+ */
+
+static void
+free_vm (gpointer _vm, gpointer data)
+{
+ struct vm *vm = (struct vm *) _vm;
+
+ g_free (vm->description);
+ g_free (vm->state);
+ g_free (vm->uuid);
+ g_free (vm->mac_addr);
+ g_free (vm);
+}
+
+void
+free_vmlist (GSList *vmlist)
+{
+ g_slist_foreach (vmlist, free_vm, NULL);
+ g_slist_free (vmlist);
+}
+
+static struct vm *
+copy_vm (struct vm *vm)
+{
+ struct vm *vm2;
+
+ vm2 = g_memdup (vm, sizeof (*vm));
+ vm2->description = g_strdup (vm->description);
+ vm2->uuid = g_strdup (vm->uuid);
+ vm2->state = vm->state ? g_strdup (vm->state) : NULL;
+ vm2->mac_addr = vm->mac_addr ? g_strdup (vm->mac_addr) : NULL;
+ return vm2;
+}
+
+static int
+compare_vm (gconstpointer _vm1, gconstpointer _vm2)
+{
+ const struct vm *vm1 = (const struct vm *) _vm1;
+ const struct vm *vm2 = (const struct vm *) _vm2;
+
+ return strcmp (vm1->description, vm2->description);
+}
+
+static GSList *vmlist = NULL;
+static gboolean vmlist_valid = FALSE;
+static GStaticMutex vmlist_mutex;
+
+/* Called from the main thread to find out if we have a valid vmlist. */
+gboolean
+wui_thread_has_valid_vmlist (void)
+{
+ gboolean ret;
+
+ ASSERT_IS_MAIN_THREAD ();
+
+ g_static_mutex_lock (&vmlist_mutex);
+ ret = vmlist_valid;
+ g_static_mutex_unlock (&vmlist_mutex);
+ return ret;
+}
+
+/* Called from the main thread to find return the current vmlist. This
+ * actually returns a deep copy of it that the caller must free.
+ */
+static void
+duplicate_and_insert_vm (gpointer _vm, gpointer _ret)
+{
+ struct vm *vm = (struct vm *) _vm;
+ GSList **ret = (GSList **) _ret;
+
+ *ret = g_slist_prepend (*ret, copy_vm (vm));
+}
+
+gboolean
+wui_thread_get_vmlist (GSList **ret)
+{
+ gboolean r;
+
+ ASSERT_IS_MAIN_THREAD ();
+
+ r = FALSE;
+ *ret = NULL;
+
+ g_static_mutex_lock (&vmlist_mutex);
+ if (!vmlist_valid) goto done;
+
+ g_slist_foreach (vmlist, duplicate_and_insert_vm, ret);
+ *ret = g_slist_sort (*ret, compare_vm);
+
+ r = TRUE;
+ done:
+ g_static_mutex_unlock (&vmlist_mutex);
+ return r;
+}
+
+/* Called from the message loop in the WUI thread, with an XML document
+ * which we turn into a list of VM structures, and update the vmlist
+ * if we can.
+ */
+static void
+parse_vmlist_from_xml (const char *xml)
+{
+ xmlDocPtr doc = NULL;
+ xmlNodePtr root;
+ xmlNodePtr node;
+ char *error_str;
+ GSList *new_vmlist = NULL;
+ struct vm *vm;
+ int len;
+
+ /*DEBUG ("XML =\n%s", xml);*/
+ ASSERT_IS_WUI_THREAD ();
+
+ /* We don't really expect that we won't be able to parse the XML ... */
+ len = strlen (xml);
+ doc = xmlReadMemory (xml, len, NULL, NULL, 0);
+
+ if (!doc) {
+ DEBUG ("error parsing XML document, xml =\n%s", xml);
+ error_str = g_strdup ("error parsing XML document from remote server");
+ g_idle_add (main_status_error, error_str);
+ goto done;
+ }
+
+ root = xmlDocGetRootElement (doc);
+ if (!root) {
+ DEBUG ("XML document was empty");
+ error_str = g_strdup ("XML document was empty");
+ g_idle_add (main_status_error, error_str);
+ goto done;
+ }
+
+ /* We expect the root element will be either "nil-classes"
+ * or "vms", with the former indicating an empty list of VMs.
+ */
+ if (xmlStrcmp (root->name, (const xmlChar *) "nil-classes") == 0) {
+ g_static_mutex_lock (&vmlist_mutex);
+ vmlist_valid = TRUE;
+ free_vmlist (vmlist);
+ vmlist = NULL;
+ g_static_mutex_unlock (&vmlist_mutex);
+
+ /* Signal to the main UI thread that the list has been updated. */
+ g_idle_add (main_vmlist_updated, NULL);
+
+ goto done;
+ }
+
+ if (xmlStrcmp (root->name, (const xmlChar *) "vms") != 0) {
+ DEBUG ("unexpected root node in XML document, xml =\n%s", xml);
+ error_str = g_strdup ("unexpected root node in XML document");
+ g_idle_add (main_status_error, error_str);
+ goto done;
+ }
+
+ /* The document is <vms> with a list of <vm> elements which
+ * we process in turn.
+ */
+ for (node = root->xmlChildrenNode; node != NULL; node = node->next) {
+ if (xmlStrcmp (node->name, (const xmlChar *) "vm") == 0) {
+ vm = parse_vm_from_xml (node);
+ if (!vm) {
+ error_str = g_strdup ("could not parse <vm> element");
+ g_idle_add (main_status_error, error_str);
+
+ free_vmlist (new_vmlist);
+ goto done;
+ }
+ new_vmlist = g_slist_prepend (new_vmlist, vm);
+ }
+ }
+
+ /* Successfully parsed all the <vm> nodes, so swap the old and new
+ * vmlists.
+ */
+ g_static_mutex_lock (&vmlist_mutex);
+ vmlist_valid = TRUE;
+ free_vmlist (vmlist);
+ vmlist = new_vmlist;
+ g_static_mutex_unlock (&vmlist_mutex);
+
+ /* Signal that the vmlist has been updated. */
+ g_idle_add (main_vmlist_updated, NULL);
+
+ done:
+ /* Free up XML resources used before returning. */
+ if (doc) xmlFreeDoc (doc);
+}
+
+static struct vm *
+parse_vm_from_xml (xmlNodePtr node)
+{
+ xmlNodePtr p;
+ struct vm vm, *ret;
+ xmlChar *str;
+
+ memset (&vm, 0, sizeof vm);
+ vm.hostid = -1;
+ vm.id = -1;
+ vm.vnc_port = -1;
+ vm.forward_vnc_port = -1;
+ vm.mem_allocated = -1;
+ vm.mem_used = -1;
+ vm.vcpus_allocated = -1;
+ vm.vcpus_used = -1;
+
+ for (p = node->xmlChildrenNode; p != NULL; p = p->next) {
+ if (xmlStrcmp (p->name, (const xmlChar *) "description") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.description = g_strdup ((char *) str);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "host-id") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.hostid = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "id") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.id = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "memory-allocated") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.mem_allocated = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "memory-used") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.mem_used = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "num-vcpus-allocated") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.vcpus_allocated = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "num-vcpus-used") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.vcpus_used = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "state") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.state = g_strdup ((char *) str);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "uuid") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.uuid = g_strdup ((char *) str);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "vnc-port") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.vnc_port = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "forward-vnc-port") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.forward_vnc_port = strtol ((char *) str, NULL, 10);
+ xmlFree (str);
+ }
+ }
+ else if (xmlStrcmp (p->name, (const xmlChar *) "vnic-mac-addr") == 0) {
+ str = xmlNodeGetContent (p);
+ if (str != NULL) {
+ vm.mac_addr = g_strdup ((char *) str);
+ xmlFree (str);
+ }
+ }
+ }
+
+ /* Make sure we've got the required fields. */
+ ret = NULL;
+ if (vm.description == NULL)
+ DEBUG ("required field \"description\" missing from <vm> structure");
+ else if (vm.hostid == -1)
+ DEBUG ("required field \"description\" missing from <vm> structure");
+ else if (vm.id == -1)
+ DEBUG ("required field \"description\" missing from <vm> structure");
+ else if (vm.vnc_port == -1)
+ DEBUG ("required field \"vnc-port\" missing from <vm> structure");
+ else if (vm.forward_vnc_port == -1)
+ DEBUG ("required field \"forward-vnc-port\" missing from <vm> structure");
+ else if (vm.uuid == NULL)
+ DEBUG ("required field \"uuid\" missing from <vm> structure");
+ else
+ ret = g_memdup (&vm, sizeof vm);
+
+ return ret;
}