X-Git-Url: http://git.annexia.org/?a=blobdiff_plain;f=wui_thread.c;h=9dab95c2d29e49eacd0769aa476fc26c9649bd89;hb=838aa4673bea4bf0e33a9e5045e4deba393a317d;hp=49dc2709aa18c9c47bba857be534e841626474c3;hpb=dc0e64a4c244fa084eb7bfce4503404824ada6ef;p=ovirt-viewer.git diff --git a/wui_thread.c b/wui_thread.c index 49dc270..9dab95c 100644 --- a/wui_thread.c +++ b/wui_thread.c @@ -17,6 +17,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* For an explanation of the threading model, please main(). */ + #include #include @@ -27,6 +29,8 @@ #include #include +#include + #include #include "internal.h" @@ -42,6 +46,7 @@ static gboolean do_connect (void); static gboolean do_login (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). * @@ -64,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 @@ -73,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 (); @@ -88,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. * @@ -107,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); @@ -124,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); @@ -136,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); @@ -147,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); @@ -160,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); @@ -205,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 @@ -254,6 +301,8 @@ 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); @@ -267,6 +316,8 @@ set_connected (gboolean new_connected) 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); @@ -280,6 +331,8 @@ set_logged_in (gboolean new_logged_in) 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); @@ -298,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); @@ -309,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); @@ -320,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); @@ -330,6 +389,8 @@ 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 (); @@ -432,6 +493,8 @@ 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; @@ -446,6 +509,8 @@ write_fn (void *ptr, size_t size, size_t nmemb, void *stream) static void write_fn_start_capture (void) { + ASSERT_IS_WUI_THREAD (); + write_fn_discard_capture_buffer (); write_fn_buffer = NULL; write_fn_len = 0; @@ -455,7 +520,14 @@ write_fn_start_capture (void) static char * write_fn_finish_capture (void) { - char *ret = write_fn_buffer; + 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; @@ -466,6 +538,8 @@ write_fn_finish_capture (void) 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; @@ -475,6 +549,9 @@ 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; } @@ -484,6 +561,8 @@ 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"); @@ -522,6 +601,7 @@ do_connect (void) 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)); @@ -561,6 +641,7 @@ do_login (void) long code = 0; DEBUG ("logging in with username %s, password *****", username); + ASSERT_IS_WUI_THREAD (); /* Generate the login URI from the base URI. */ len = strlen (uri) + 6 + 1; @@ -630,6 +711,7 @@ refresh_vm_list (void) char *xml; DEBUG ("refreshing list of VMs"); + ASSERT_IS_WUI_THREAD (); /* Generate the vms URI from the base URI. */ len = strlen (uri) + 4 + 1; @@ -685,7 +767,6 @@ refresh_vm_list (void) xml = write_fn_finish_capture (); - /*DEBUG ("XML from /vms =\n%s", xml);*/ parse_vmlist_from_xml (xml); g_free (xml); @@ -724,9 +805,9 @@ copy_vm (struct vm *vm) vm2 = g_memdup (vm, sizeof (*vm)); vm2->description = g_strdup (vm->description); - vm2->state = g_strdup (vm->state); vm2->uuid = g_strdup (vm->uuid); - vm2->mac_addr = g_strdup (vm->mac_addr); + vm2->state = vm->state ? g_strdup (vm->state) : NULL; + vm2->mac_addr = vm->mac_addr ? g_strdup (vm->mac_addr) : NULL; return vm2; } @@ -749,6 +830,8 @@ 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); @@ -772,6 +855,8 @@ wui_thread_get_vmlist (GSList **ret) { gboolean r; + ASSERT_IS_MAIN_THREAD (); + r = FALSE; *ret = NULL; @@ -794,11 +879,213 @@ wui_thread_get_vmlist (GSList **ret) 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; }