/* ovirt viewer console application * Copyright (C) 2008 Red Hat Inc. * Written by Richard W.M. Jones * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * 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 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). * * These are passed by reference. They are allocated by the sender * (ie. the main thread) and freed by the receiver (ie. the WUI thread). */ enum message_type { QUIT, /* Tell the WUI thread to quit cleanly. */ CONNECT, /* Tell to connect (just fetch the URI). */ DISCONNECT, /* Tell to disconnect, forget state. */ LOGIN, /* Tell to login, and pass credentials. */ REFRESH_VM_LIST, /* Tell to refresh the VM list right away. */ }; struct message { enum message_type type; char *str1; char *str2; }; /* 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 start_wui_thread (void) { GError *error = NULL; 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 (); wui_gthread = g_thread_create (wui_thread, wui_thread_queue, TRUE, &error); if (error) { g_print ("%s\n", error->message); g_error_free (error); exit (1); } } void stop_wui_thread (void) { DEBUG ("stopping the WUI thread"); assert (wui_gthread != NULL); ASSERT_IS_MAIN_THREAD (); /* Send a quit message then wait for the WUI thread to join. * * This "nice" shutdown could cause the UI to hang for an * indefinite period (eg. if the WUI thread is engaged in some * long connection or download from the remote server). But * I want to keep it this way for the moment so that we can * diagnose problems with the WUI thread. * * (This could be solved with some sort of interruptible * join, but glib doesn't support that AFAICT). */ wui_thread_send_quit (); (void) g_thread_join (wui_gthread); g_async_queue_unref (wui_thread_queue); 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); } /* Send the connect message to the WUI thread. */ void 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); g_async_queue_push (wui_thread_queue, msg); } /* Send the disconnect message to the WUI thread. */ void 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); } /* Send the login message to the WUI thread. */ void 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); msg->str2 = g_strdup (password); g_async_queue_push (wui_thread_queue, msg); } /* Send the refresh VM list message to the WUI thread. */ void 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); } /* The current state. * * For safety, the main thread must lock this before reading, and the * WUI thread must lock this before writing. However the WUI thread * does not need to lock before reading, because no other thread * can modify it. */ static gboolean connected = FALSE; static gboolean logged_in = FALSE; static gboolean busy = FALSE; static GStaticMutex state_mutex; static void set_connected (gboolean); static void set_logged_in (gboolean); 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; static gboolean process_message (struct message *); /* The WUI thread. See main() above for explanation of * the threading model. */ static gpointer wui_thread (gpointer _queue) { GAsyncQueue *queue = (GAsyncQueue *) _queue; gboolean quit = FALSE; GTimeVal tv; gpointer _msg; struct message *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 * thread and carry them out. Also, if we are connected and logged * in then we periodically recheck the list of VMs. */ while (!quit) { set_busy (FALSE); if (logged_in) { g_get_current_time (&tv); g_time_val_add (&tv, secs_between_refresh * 1000000); _msg = g_async_queue_timed_pop (queue, &tv); } else _msg = g_async_queue_pop (queue); set_busy (TRUE); msg = (struct message *) _msg; if (msg) { 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); } else { /* No message, must have got a timeout instead, which means * we are logged in and we should refresh the list of VMs. * Note it's not an error if we temporarily lose contact * with the WUI. */ refresh_vm_list (); } } DEBUG ("WUI thread shutting down cleanly"); g_async_queue_unref (queue); g_thread_exit (NULL); return NULL; /* not reached? */ } /* The WUI thread calls this to safely update the state variables. * This also updates elements in the UI by setting idle callbacks * which are executed in the context of the main thread. */ 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 * current state. */ gboolean 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); return ret; } gboolean 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); return ret; } gboolean 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); return ret; } /* Process a message from the main thread. */ 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); set_connected (FALSE); set_logged_in (FALSE); 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; if (do_connect ()) { set_connected (TRUE); } else { set_connected (FALSE); set_logged_in (FALSE); } break; 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; case LOGIN: if (username) g_free (username); username = msg->str1; 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); if (refresh_vm_list ()) secs_between_refresh = 60; } else { set_logged_in (FALSE); } } break; case REFRESH_VM_LIST: if (connected && logged_in) { refresh_vm_list (); secs_between_refresh = 60; } break; default: DEBUG ("unknown message type %d", msg->type); abort (); } return 0; } /* 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); 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; } /* 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); 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; } } /* 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; }