X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=wui_thread.c;h=8bfa8ca369a8318332bf14aafc325621483fac25;hb=2c24cea28b2f4702105fcf4f54fc4e923697174a;hp=0db43c8829a41ba06a239ad0eddfe6917597be81;hpb=7e9794277b619bb232ffc874ee7efa4ead8ddb8e;p=ovirt-viewer.git diff --git a/wui_thread.c b/wui_thread.c index 0db43c8..8bfa8ca 100644 --- a/wui_thread.c +++ b/wui_thread.c @@ -17,22 +17,36 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* For an explanation of the threading model, please main(). */ + #include #include #include +#include #include #include +#include + +#include + +#include #include "internal.h" /* Private functions. */ 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). * @@ -55,6 +69,7 @@ struct message { /* 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 @@ -64,6 +79,10 @@ start_wui_thread (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 (); @@ -79,7 +98,9 @@ void 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. * @@ -98,12 +119,32 @@ stop_wui_thread (void) 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); @@ -115,6 +156,8 @@ wui_thread_send_connect (const char *uri) { struct message *msg; + ASSERT_IS_MAIN_THREAD (); + msg = g_new (struct message, 1); msg->type = CONNECT; msg->str1 = g_strdup (uri); @@ -127,6 +170,8 @@ wui_thread_send_disconnect (void) { struct message *msg; + ASSERT_IS_MAIN_THREAD (); + msg = g_new (struct message, 1); msg->type = DISCONNECT; g_async_queue_push (wui_thread_queue, msg); @@ -138,6 +183,8 @@ wui_thread_send_login (const char *username, const char *password) { struct message *msg; + ASSERT_IS_MAIN_THREAD (); + msg = g_new (struct message, 1); msg->type = LOGIN; msg->str1 = g_strdup (username); @@ -151,6 +198,8 @@ wui_thread_send_refresh_vm_list (void) { 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); @@ -174,6 +223,8 @@ static void set_busy (gboolean); /* The private state of the WUI thread. */ static int secs_between_refresh = 60; +static CURL *curl = NULL; +static char curl_error_buffer[CURL_ERROR_SIZE]; static char *uri = NULL; static char *username = NULL; static char *password = NULL; @@ -194,6 +245,13 @@ wui_thread (gpointer _queue) 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 @@ -215,7 +273,7 @@ wui_thread (gpointer _queue) msg = (struct message *) _msg; if (msg) { - DEBUG ("received message %d", msg->type); + DEBUG ("received message with msg->type = %d", msg->type); quit = process_message (msg); /* Don't free any strings in the message - we've saved them. */ g_free (msg); @@ -243,25 +301,46 @@ wui_thread (gpointer _queue) 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 @@ -272,6 +351,8 @@ wui_thread_is_connected (void) { gboolean ret; + ASSERT_IS_MAIN_THREAD (); + g_static_mutex_lock (&state_mutex); ret = connected; g_static_mutex_unlock (&state_mutex); @@ -283,6 +364,8 @@ wui_thread_is_logged_in (void) { gboolean ret; + ASSERT_IS_MAIN_THREAD (); + g_static_mutex_lock (&state_mutex); ret = logged_in; g_static_mutex_unlock (&state_mutex); @@ -294,6 +377,8 @@ wui_thread_is_busy (void) { gboolean ret; + ASSERT_IS_MAIN_THREAD (); + g_static_mutex_lock (&state_mutex); ret = busy; g_static_mutex_unlock (&state_mutex); @@ -304,8 +389,12 @@ wui_thread_is_busy (void) 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); if (password) g_free (password); @@ -314,6 +403,9 @@ process_message (struct message *msg) return 1; case CONNECT: + write_fn_discard_capture_buffer (); + if (curl) curl_easy_cleanup (curl); + do_curl_init (); if (uri) g_free (uri); uri = msg->str1; @@ -327,9 +419,15 @@ process_message (struct message *msg) 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); + uri = NULL; if (username) g_free (username); + username = NULL; if (password) g_free (password); + password = NULL; set_connected (FALSE); set_logged_in (FALSE); break; @@ -340,10 +438,15 @@ process_message (struct message *msg) if (password) g_free (password); password = msg->str2; + /* If we're not connected, this message just updates the + * username and password. Otherwise if we are connected, + * try to login and grab the initial list of VMs. + */ 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); } @@ -351,8 +454,10 @@ process_message (struct message *msg) break; case REFRESH_VM_LIST: - refresh_vm_list (); - secs_between_refresh = 60; + if (connected && logged_in) { + refresh_vm_list (); + secs_between_refresh = 60; + } break; default: @@ -363,27 +468,639 @@ process_message (struct message *msg) return 0; } -/* Try to connect to the current URI. Returns true on success. */ +/* Macro for easy handling of CURL errors. */ +#define CURL_CHECK_ERROR(fn, args) \ + ({ \ + CURLcode __r = fn args; \ + if (__r != CURLE_OK) { \ + fprintf (stderr, "%s: %s\n", #fn, curl_easy_strerror (__r)); \ + } \ + __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; +} + +/* Called from the message loop to initialize the CURL handle. */ +static void +do_curl_init (void) +{ + 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"); + abort (); + } + + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_CAINFO, cainfo)); + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_SSL_VERIFYHOST, check_cert ? 2 : 0)); + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_SSL_VERIFYPEER, check_cert ? 1 : 0)); + + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_WRITEFUNCTION, write_fn)); + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_HEADERFUNCTION, header_fn)); + + /* This enables error messages in curl_easy_perform. */ + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_ERRORBUFFER, curl_error_buffer)); + + /* This enables cookie handling, using an internal cookiejar. */ + CURL_CHECK_ERROR (curl_easy_setopt, + (curl, CURLOPT_COOKIEFILE, "")); +} + +/* Called from the message loop. Try to connect to the current URI. + * Returns true on success. + */ static gboolean do_connect (void) { + long code = 0; + CURLcode r; + char *error_str; + DEBUG ("connecting to uri %s", uri); - return FALSE; + ASSERT_IS_WUI_THREAD (); + + /* Set the URI for libcurl. */ + CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, uri)); + + /* 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_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) { + /* 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; } -/* Try to login to URI/login with the username and password given. - * Returns true on success. +/* Called from the message loop. Try to login to 'URI/login' with the + * current username and password. Returns true on success. */ 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)); + + /* 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; + } } -/* Refresh the list of VMs. */ -static void +/* Called from the message loop. Refresh the list of VMs. */ +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 with a list of 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 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 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 structure"); + else if (vm.hostid == -1) + DEBUG ("required field \"description\" missing from structure"); + else if (vm.id == -1) + DEBUG ("required field \"description\" missing from structure"); + else if (vm.vnc_port == -1) + DEBUG ("required field \"vnc-port\" missing from structure"); + else if (vm.forward_vnc_port == -1) + DEBUG ("required field \"forward-vnc-port\" missing from structure"); + else if (vm.uuid == NULL) + DEBUG ("required field \"uuid\" missing from structure"); + else + ret = g_memdup (&vm, sizeof vm); + + return ret; +} + +gboolean +main_vmlist_has_running_vm(struct vm* _vm) +{ + // TODO ? get list and wait to be retreived + // wui_thread_send_refresh_vm_list(); + + // find vm in list + GSList* res = g_slist_find_custom (vmlist, _vm, compare_vm); + + // return true if running + if(res != NULL) return STREQ (((struct vm*) res->data)->state, "running"); + + return FALSE; }