added autogen script to run autotools to generate configure and necessary make inputs
[ovirt-viewer.git] / wui_thread.c
index 49dc270..c51c43f 100644 (file)
@@ -17,6 +17,8 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/* For an explanation of the threading model, please main(). */
+
 #include <config.h>
 
 #include <stdio.h>
@@ -27,6 +29,8 @@
 #include <glib.h>
 #include <glib/gprintf.h>
 
+#include <libxml/parser.h>
+
 #include <curl/curl.h>
 
 #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;
@@ -588,6 +669,10 @@ do_login (void)
   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) {
@@ -630,6 +715,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 +771,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 +809,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 +834,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 +859,8 @@ wui_thread_get_vmlist (GSList **ret)
 {
   gboolean r;
 
+  ASSERT_IS_MAIN_THREAD ();
+
   r = FALSE;
   *ret = NULL;
 
@@ -794,11 +883,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 <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;
 }