1 /* ovirt viewer console application
2 * Copyright (C) 2008 Red Hat Inc.
3 * Written by Richard W.M. Jones <rjones@redhat.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* For an explanation of the threading model, please main(). */
30 #include <glib/gprintf.h>
32 #include <libxml/parser.h>
34 #include <curl/curl.h>
38 /* Private functions. */
39 static gpointer wui_thread (gpointer data);
40 static void wui_thread_send_quit (void);
41 static void do_curl_init (void);
42 static void write_fn_start_capture (void);
43 static char *write_fn_finish_capture (void);
44 static void write_fn_discard_capture_buffer (void);
45 static gboolean do_connect (void);
46 static gboolean do_login (void);
47 static gboolean refresh_vm_list (void);
48 static void parse_vmlist_from_xml (const char *xml);
49 static struct vm *parse_vm_from_xml (xmlNodePtr node);
51 /* Messages (main thread -> WUI thread only).
53 * These are passed by reference. They are allocated by the sender
54 * (ie. the main thread) and freed by the receiver (ie. the WUI thread).
57 QUIT, /* Tell the WUI thread to quit cleanly. */
58 CONNECT, /* Tell to connect (just fetch the URI). */
59 DISCONNECT, /* Tell to disconnect, forget state. */
60 LOGIN, /* Tell to login, and pass credentials. */
61 REFRESH_VM_LIST, /* Tell to refresh the VM list right away. */
65 enum message_type type;
70 /* Start the WUI thread. See main() for explanation of the threading model. */
71 static GThread *wui_gthread = NULL;
72 static GThread *main_gthread = NULL;
73 static GAsyncQueue *wui_thread_queue = NULL;
76 start_wui_thread (void)
80 DEBUG ("starting the WUI thread");
82 assert (wui_gthread == NULL);
84 main_gthread = g_thread_self ();
86 /* Create the message queue for main -> WUI thread communications. */
87 wui_thread_queue = g_async_queue_new ();
89 wui_gthread = g_thread_create (wui_thread, wui_thread_queue, TRUE, &error);
91 g_print ("%s\n", error->message);
98 stop_wui_thread (void)
100 DEBUG ("stopping the WUI thread");
102 assert (wui_gthread != NULL);
103 ASSERT_IS_MAIN_THREAD ();
105 /* Send a quit message then wait for the WUI thread to join.
107 * This "nice" shutdown could cause the UI to hang for an
108 * indefinite period (eg. if the WUI thread is engaged in some
109 * long connection or download from the remote server). But
110 * I want to keep it this way for the moment so that we can
111 * diagnose problems with the WUI thread.
113 * (This could be solved with some sort of interruptible
114 * join, but glib doesn't support that AFAICT).
116 wui_thread_send_quit ();
117 (void) g_thread_join (wui_gthread);
118 g_async_queue_unref (wui_thread_queue);
123 assert_is_wui_thread (const char *filename, int lineno)
125 if (g_thread_self () != wui_gthread) {
126 fprintf (stderr, "%s:%d: internal error: this function should only run in the context of the WUI thread\n", filename, lineno);
132 assert_is_main_thread (const char *filename, int lineno)
134 if (g_thread_self () != main_gthread) {
135 fprintf (stderr, "%s:%d: internal error: this function should only run in the context of the main thread\n", filename, lineno);
140 /* Send the quit message to the WUI thread. */
142 wui_thread_send_quit (void)
146 ASSERT_IS_MAIN_THREAD ();
148 msg = g_new (struct message, 1);
150 g_async_queue_push (wui_thread_queue, msg);
153 /* Send the connect message to the WUI thread. */
155 wui_thread_send_connect (const char *uri)
159 ASSERT_IS_MAIN_THREAD ();
161 msg = g_new (struct message, 1);
163 msg->str1 = g_strdup (uri);
164 g_async_queue_push (wui_thread_queue, msg);
167 /* Send the disconnect message to the WUI thread. */
169 wui_thread_send_disconnect (void)
173 ASSERT_IS_MAIN_THREAD ();
175 msg = g_new (struct message, 1);
176 msg->type = DISCONNECT;
177 g_async_queue_push (wui_thread_queue, msg);
180 /* Send the login message to the WUI thread. */
182 wui_thread_send_login (const char *username, const char *password)
186 ASSERT_IS_MAIN_THREAD ();
188 msg = g_new (struct message, 1);
190 msg->str1 = g_strdup (username);
191 msg->str2 = g_strdup (password);
192 g_async_queue_push (wui_thread_queue, msg);
195 /* Send the refresh VM list message to the WUI thread. */
197 wui_thread_send_refresh_vm_list (void)
201 ASSERT_IS_MAIN_THREAD ();
203 msg = g_new (struct message, 1);
204 msg->type = REFRESH_VM_LIST;
205 g_async_queue_push (wui_thread_queue, msg);
208 /* The current state.
210 * For safety, the main thread must lock this before reading, and the
211 * WUI thread must lock this before writing. However the WUI thread
212 * does not need to lock before reading, because no other thread
215 static gboolean connected = FALSE;
216 static gboolean logged_in = FALSE;
217 static gboolean busy = FALSE;
218 static GStaticMutex state_mutex;
220 static void set_connected (gboolean);
221 static void set_logged_in (gboolean);
222 static void set_busy (gboolean);
224 /* The private state of the WUI thread. */
225 static int secs_between_refresh = 60;
226 static CURL *curl = NULL;
227 static char curl_error_buffer[CURL_ERROR_SIZE];
228 static char *uri = NULL;
229 static char *username = NULL;
230 static char *password = NULL;
232 static gboolean process_message (struct message *);
234 /* The WUI thread. See main() above for explanation of
235 * the threading model.
238 wui_thread (gpointer _queue)
240 GAsyncQueue *queue = (GAsyncQueue *) _queue;
241 gboolean quit = FALSE;
246 DEBUG ("WUI thread starting up");
248 /* This checks wui_gthread global which is actually set in the
249 * main thread. Of course, it might not be set if the WUI thread
250 * runs first. Hence we sleep for the main thread to run. (XXX)
253 ASSERT_IS_WUI_THREAD ();
255 g_async_queue_ref (queue);
257 /* In the thread's loop we check for new instructions from the main
258 * thread and carry them out. Also, if we are connected and logged
259 * in then we periodically recheck the list of VMs.
265 g_get_current_time (&tv);
266 g_time_val_add (&tv, secs_between_refresh * 1000000);
267 _msg = g_async_queue_timed_pop (queue, &tv);
269 _msg = g_async_queue_pop (queue);
273 msg = (struct message *) _msg;
276 DEBUG ("received message with msg->type = %d", msg->type);
277 quit = process_message (msg);
278 /* Don't free any strings in the message - we've saved them. */
281 /* No message, must have got a timeout instead, which means
282 * we are logged in and we should refresh the list of VMs.
283 * Note it's not an error if we temporarily lose contact
290 DEBUG ("WUI thread shutting down cleanly");
292 g_async_queue_unref (queue);
293 g_thread_exit (NULL);
294 return NULL; /* not reached? */
297 /* The WUI thread calls this to safely update the state variables.
298 * This also updates elements in the UI by setting idle callbacks
299 * which are executed in the context of the main thread.
302 set_connected (gboolean new_connected)
304 ASSERT_IS_WUI_THREAD ();
306 g_static_mutex_lock (&state_mutex);
307 connected = new_connected;
308 g_static_mutex_unlock (&state_mutex);
311 g_idle_add (main_connected, NULL);
313 g_idle_add (main_disconnected, NULL);
317 set_logged_in (gboolean new_logged_in)
319 ASSERT_IS_WUI_THREAD ();
321 g_static_mutex_lock (&state_mutex);
322 logged_in = new_logged_in;
323 g_static_mutex_unlock (&state_mutex);
326 g_idle_add (main_logged_in, NULL);
328 g_idle_add (main_logged_out, NULL);
332 set_busy (gboolean new_busy)
334 ASSERT_IS_WUI_THREAD ();
336 g_static_mutex_lock (&state_mutex);
338 g_static_mutex_unlock (&state_mutex);
341 g_idle_add (main_busy, NULL);
343 g_idle_add (main_idle, NULL);
346 /* The main thread should call these functions to get the WUI thread's
350 wui_thread_is_connected (void)
354 ASSERT_IS_MAIN_THREAD ();
356 g_static_mutex_lock (&state_mutex);
358 g_static_mutex_unlock (&state_mutex);
363 wui_thread_is_logged_in (void)
367 ASSERT_IS_MAIN_THREAD ();
369 g_static_mutex_lock (&state_mutex);
371 g_static_mutex_unlock (&state_mutex);
376 wui_thread_is_busy (void)
380 ASSERT_IS_MAIN_THREAD ();
382 g_static_mutex_lock (&state_mutex);
384 g_static_mutex_unlock (&state_mutex);
388 /* Process a message from the main thread. */
390 process_message (struct message *msg)
392 ASSERT_IS_WUI_THREAD ();
396 write_fn_discard_capture_buffer ();
397 if (curl) curl_easy_cleanup (curl);
398 if (uri) g_free (uri);
399 if (username) g_free (username);
400 if (password) g_free (password);
401 set_connected (FALSE);
402 set_logged_in (FALSE);
406 write_fn_discard_capture_buffer ();
407 if (curl) curl_easy_cleanup (curl);
409 if (uri) g_free (uri);
413 set_connected (TRUE);
415 set_connected (FALSE);
416 set_logged_in (FALSE);
421 /* This just forgets the state. REST is connectionless. */
422 write_fn_discard_capture_buffer ();
423 if (curl) curl_easy_cleanup (curl);
425 if (uri) g_free (uri);
427 if (username) g_free (username);
429 if (password) g_free (password);
431 set_connected (FALSE);
432 set_logged_in (FALSE);
436 if (username) g_free (username);
437 username = msg->str1;
438 if (password) g_free (password);
439 password = msg->str2;
441 /* If we're not connected, this message just updates the
442 * username and password. Otherwise if we are connected,
443 * try to login and grab the initial list of VMs.
447 set_logged_in (TRUE);
448 if (refresh_vm_list ())
449 secs_between_refresh = 60;
451 set_logged_in (FALSE);
456 case REFRESH_VM_LIST:
457 if (connected && logged_in) {
459 secs_between_refresh = 60;
464 DEBUG ("unknown message type %d", msg->type);
471 /* Macro for easy handling of CURL errors. */
472 #define CURL_CHECK_ERROR(fn, args) \
474 CURLcode __r = fn args; \
475 if (__r != CURLE_OK) { \
476 fprintf (stderr, "%s: %s\n", #fn, curl_easy_strerror (__r)); \
481 /* Libcurl has a really crufty method for handling HTTP headers and
482 * data. We set these functions as callbacks, because the default
483 * callback functions print the data out to stderr. In order to
484 * capture the data, we have to keep state here.
487 static char *write_fn_buffer = NULL;
488 static ssize_t write_fn_len = -1;
491 write_fn (void *ptr, size_t size, size_t nmemb, void *stream)
493 int bytes = size * nmemb;
496 ASSERT_IS_WUI_THREAD ();
498 if (write_fn_len >= 0) { /* We're capturing. */
499 old_start = write_fn_len;
500 write_fn_len += bytes;
501 write_fn_buffer = g_realloc (write_fn_buffer, write_fn_len);
502 memcpy (write_fn_buffer + old_start, ptr, bytes);
508 /* Start capturing HTTP response data. */
510 write_fn_start_capture (void)
512 ASSERT_IS_WUI_THREAD ();
514 write_fn_discard_capture_buffer ();
515 write_fn_buffer = NULL;
519 /* Finish capture and return the capture buffer. Caller must free. */
521 write_fn_finish_capture (void)
525 ASSERT_IS_WUI_THREAD ();
527 /* Make sure the buffer is NUL-terminated before returning it. */
528 write_fn_buffer = g_realloc (write_fn_buffer, write_fn_len+1);
529 write_fn_buffer[write_fn_len] = '\0';
530 ret = write_fn_buffer;
532 write_fn_buffer = NULL;
537 /* Stop capturing and discard the capture buffer, if any. */
539 write_fn_discard_capture_buffer (void)
541 ASSERT_IS_WUI_THREAD ();
543 g_free (write_fn_buffer);
544 write_fn_buffer = NULL;
549 header_fn (void *ptr, size_t size, size_t nmemb, void *stream)
551 int bytes = size * nmemb;
553 ASSERT_IS_WUI_THREAD ();
558 /* Called from the message loop to initialize the CURL handle. */
562 DEBUG ("initializing libcurl");
564 ASSERT_IS_WUI_THREAD ();
566 curl = curl_easy_init ();
567 if (!curl) { /* This is probably quite bad, so abort. */
568 DEBUG ("curl_easy_init failed");
572 CURL_CHECK_ERROR (curl_easy_setopt,
573 (curl, CURLOPT_CAINFO, cainfo));
574 CURL_CHECK_ERROR (curl_easy_setopt,
575 (curl, CURLOPT_SSL_VERIFYHOST, check_cert ? 2 : 0));
576 CURL_CHECK_ERROR (curl_easy_setopt,
577 (curl, CURLOPT_SSL_VERIFYPEER, check_cert ? 1 : 0));
579 CURL_CHECK_ERROR (curl_easy_setopt,
580 (curl, CURLOPT_WRITEFUNCTION, write_fn));
581 CURL_CHECK_ERROR (curl_easy_setopt,
582 (curl, CURLOPT_HEADERFUNCTION, header_fn));
584 /* This enables error messages in curl_easy_perform. */
585 CURL_CHECK_ERROR (curl_easy_setopt,
586 (curl, CURLOPT_ERRORBUFFER, curl_error_buffer));
588 /* This enables cookie handling, using an internal cookiejar. */
589 CURL_CHECK_ERROR (curl_easy_setopt,
590 (curl, CURLOPT_COOKIEFILE, ""));
593 /* Called from the message loop. Try to connect to the current URI.
594 * Returns true on success.
603 DEBUG ("connecting to uri %s", uri);
604 ASSERT_IS_WUI_THREAD ();
606 /* Set the URI for libcurl. */
607 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, uri));
609 /* Try to fetch the URI. */
610 r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
612 /* Signal an error back to the main thread. */
613 error_str = g_strdup (curl_easy_strerror (r));
614 g_idle_add (main_connection_error, error_str);
618 CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
619 DEBUG ("HTTP return code is %ld", code);
620 if (code != 200 && code != 302 && code != 401) {
621 /* XXX If only glib had g_asprintf. */
622 error_str = g_strdup ("unexpected HTTP return code from server");
623 g_idle_add (main_connection_error, error_str);
630 /* Called from the message loop. Try to login to 'URI/login' with the
631 * current username and password. Returns true on success.
643 DEBUG ("logging in with username %s, password *****", username);
644 ASSERT_IS_WUI_THREAD ();
646 /* Generate the login URI from the base URI. */
647 len = strlen (uri) + 6 + 1;
648 login_uri = g_alloca (len);
649 snprintf (login_uri, len, "%s/login", uri);
651 DEBUG ("login URI %s", login_uri);
653 /* Set the URI for libcurl. */
654 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, login_uri));
656 /* Construct the username:password for CURL. */
657 len = strlen (username) + strlen (password) + 2;
658 userpwd = g_alloca (len);
659 snprintf (userpwd, len, "%s:%s", username, password);
661 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_USERPWD, userpwd));
663 /* HTTP Basic authentication is OK since we should be sending
664 * this only over HTTPS.
666 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC));
668 /* Follow redirects. */
669 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_FOLLOWLOCATION, (long) 1));
670 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_MAXREDIRS, (long) 10));
672 /* Try to fetch the URI. */
673 r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
675 /* Signal an error back to the main thread. */
676 error_str = g_strdup (curl_easy_strerror (r));
677 g_idle_add (main_login_error, error_str);
681 CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
682 DEBUG ("HTTP return code is %ld", code);
686 DEBUG ("login was successful");
690 error_str = g_strdup ("server rejected the username or password");
691 g_idle_add (main_login_error, error_str);
695 /* XXX If only glib had g_asprintf. */
696 error_str = g_strdup ("unexpected HTTP return code from server");
697 g_idle_add (main_login_error, error_str);
702 /* Called from the message loop. Refresh the list of VMs. */
704 refresh_vm_list (void)
713 DEBUG ("refreshing list of VMs");
714 ASSERT_IS_WUI_THREAD ();
716 /* Generate the vms URI from the base URI. */
717 len = strlen (uri) + 4 + 1;
718 vms_uri = g_alloca (len);
719 snprintf (vms_uri, len, "%s/vms", uri);
721 DEBUG ("vms URI %s", vms_uri);
723 /* Set the URI for libcurl. */
724 CURL_CHECK_ERROR (curl_easy_setopt, (curl, CURLOPT_URL, vms_uri));
726 /* We want to capture the output, so tell our write function
727 * to place the output into a buffer.
729 write_fn_start_capture ();
731 /* Try to fetch the URI. */
732 r = CURL_CHECK_ERROR (curl_easy_perform, (curl));
734 /* Signal an error back to the main thread. */
735 error_str = g_strdup (curl_easy_strerror (r));
736 g_idle_add (main_login_error, error_str);
740 CURL_CHECK_ERROR (curl_easy_getinfo, (curl, CURLINFO_RESPONSE_CODE, &code));
741 DEBUG ("HTTP return code is %ld", code);
746 /* Hmm - even though we previously logged in, the server is
747 * rejecting our attempts now with an authorization error.
748 * We move to the logged out state.
751 set_logged_in (FALSE);
752 error_str = g_strdup ("server rejected the username or password");
753 g_idle_add (main_login_error, error_str);
757 /* XXX If only glib had g_asprintf. */
758 error_str = g_strdup ("unexpected HTTP return code from server");
759 g_idle_add (main_status_error, error_str);
763 /* If we got here then we appear to have a correct
764 * XML document describing the list of VMs.
766 secs_between_refresh <<= 1;
768 xml = write_fn_finish_capture ();
770 parse_vmlist_from_xml (xml);
776 /* Functions to deal with the list of VMs, parsing it from the XML, etc.
778 * A vmlist is a GSList (glib singly-linked list) of vm structures.
779 * The caller must call free_vmlist to free up this list correctly.
783 free_vm (gpointer _vm, gpointer data)
785 struct vm *vm = (struct vm *) _vm;
787 g_free (vm->description);
790 g_free (vm->mac_addr);
795 free_vmlist (GSList *vmlist)
797 g_slist_foreach (vmlist, free_vm, NULL);
798 g_slist_free (vmlist);
802 copy_vm (struct vm *vm)
806 vm2 = g_memdup (vm, sizeof (*vm));
807 vm2->description = g_strdup (vm->description);
808 vm2->uuid = g_strdup (vm->uuid);
809 vm2->state = vm->state ? g_strdup (vm->state) : NULL;
810 vm2->mac_addr = vm->mac_addr ? g_strdup (vm->mac_addr) : NULL;
815 compare_vm (gconstpointer _vm1, gconstpointer _vm2)
817 const struct vm *vm1 = (const struct vm *) _vm1;
818 const struct vm *vm2 = (const struct vm *) _vm2;
820 return strcmp (vm1->description, vm2->description);
823 static GSList *vmlist = NULL;
824 static gboolean vmlist_valid = FALSE;
825 static GStaticMutex vmlist_mutex;
827 /* Called from the main thread to find out if we have a valid vmlist. */
829 wui_thread_has_valid_vmlist (void)
833 ASSERT_IS_MAIN_THREAD ();
835 g_static_mutex_lock (&vmlist_mutex);
837 g_static_mutex_unlock (&vmlist_mutex);
841 /* Called from the main thread to find return the current vmlist. This
842 * actually returns a deep copy of it that the caller must free.
845 duplicate_and_insert_vm (gpointer _vm, gpointer _ret)
847 struct vm *vm = (struct vm *) _vm;
848 GSList **ret = (GSList **) _ret;
850 *ret = g_slist_prepend (*ret, copy_vm (vm));
854 wui_thread_get_vmlist (GSList **ret)
858 ASSERT_IS_MAIN_THREAD ();
863 g_static_mutex_lock (&vmlist_mutex);
864 if (!vmlist_valid) goto done;
866 g_slist_foreach (vmlist, duplicate_and_insert_vm, ret);
867 *ret = g_slist_sort (*ret, compare_vm);
871 g_static_mutex_unlock (&vmlist_mutex);
875 /* Called from the message loop in the WUI thread, with an XML document
876 * which we turn into a list of VM structures, and update the vmlist
880 parse_vmlist_from_xml (const char *xml)
882 xmlDocPtr doc = NULL;
886 GSList *new_vmlist = NULL;
890 /*DEBUG ("XML =\n%s", xml);*/
891 ASSERT_IS_WUI_THREAD ();
893 /* We don't really expect that we won't be able to parse the XML ... */
895 doc = xmlReadMemory (xml, len, NULL, NULL, 0);
898 DEBUG ("error parsing XML document, xml =\n%s", xml);
899 error_str = g_strdup ("error parsing XML document from remote server");
900 g_idle_add (main_status_error, error_str);
904 root = xmlDocGetRootElement (doc);
906 DEBUG ("XML document was empty");
907 error_str = g_strdup ("XML document was empty");
908 g_idle_add (main_status_error, error_str);
912 /* We expect the root element will be either "nil-classes"
913 * or "vms", with the former indicating an empty list of VMs.
915 if (xmlStrcmp (root->name, (const xmlChar *) "nil-classes") == 0) {
916 g_static_mutex_lock (&vmlist_mutex);
918 free_vmlist (vmlist);
920 g_static_mutex_unlock (&vmlist_mutex);
922 /* Signal to the main UI thread that the list has been updated. */
923 g_idle_add (main_vmlist_updated, NULL);
928 if (xmlStrcmp (root->name, (const xmlChar *) "vms") != 0) {
929 DEBUG ("unexpected root node in XML document, xml =\n%s", xml);
930 error_str = g_strdup ("unexpected root node in XML document");
931 g_idle_add (main_status_error, error_str);
935 /* The document is <vms> with a list of <vm> elements which
936 * we process in turn.
938 for (node = root->xmlChildrenNode; node != NULL; node = node->next) {
939 if (xmlStrcmp (node->name, (const xmlChar *) "vm") == 0) {
940 vm = parse_vm_from_xml (node);
942 error_str = g_strdup ("could not parse <vm> element");
943 g_idle_add (main_status_error, error_str);
945 free_vmlist (new_vmlist);
948 new_vmlist = g_slist_prepend (new_vmlist, vm);
952 /* Successfully parsed all the <vm> nodes, so swap the old and new
955 g_static_mutex_lock (&vmlist_mutex);
957 free_vmlist (vmlist);
959 g_static_mutex_unlock (&vmlist_mutex);
961 /* Signal that the vmlist has been updated. */
962 g_idle_add (main_vmlist_updated, NULL);
965 /* Free up XML resources used before returning. */
966 if (doc) xmlFreeDoc (doc);
970 parse_vm_from_xml (xmlNodePtr node)
976 memset (&vm, 0, sizeof vm);
980 vm.forward_vnc_port = -1;
981 vm.mem_allocated = -1;
983 vm.vcpus_allocated = -1;
986 for (p = node->xmlChildrenNode; p != NULL; p = p->next) {
987 if (xmlStrcmp (p->name, (const xmlChar *) "description") == 0) {
988 str = xmlNodeGetContent (p);
990 vm.description = g_strdup ((char *) str);
994 else if (xmlStrcmp (p->name, (const xmlChar *) "host-id") == 0) {
995 str = xmlNodeGetContent (p);
997 vm.hostid = strtol ((char *) str, NULL, 10);
1001 else if (xmlStrcmp (p->name, (const xmlChar *) "id") == 0) {
1002 str = xmlNodeGetContent (p);
1004 vm.id = strtol ((char *) str, NULL, 10);
1008 else if (xmlStrcmp (p->name, (const xmlChar *) "memory-allocated") == 0) {
1009 str = xmlNodeGetContent (p);
1011 vm.mem_allocated = strtol ((char *) str, NULL, 10);
1015 else if (xmlStrcmp (p->name, (const xmlChar *) "memory-used") == 0) {
1016 str = xmlNodeGetContent (p);
1018 vm.mem_used = strtol ((char *) str, NULL, 10);
1022 else if (xmlStrcmp (p->name, (const xmlChar *) "num-vcpus-allocated") == 0) {
1023 str = xmlNodeGetContent (p);
1025 vm.vcpus_allocated = strtol ((char *) str, NULL, 10);
1029 else if (xmlStrcmp (p->name, (const xmlChar *) "num-vcpus-used") == 0) {
1030 str = xmlNodeGetContent (p);
1032 vm.vcpus_used = strtol ((char *) str, NULL, 10);
1036 else if (xmlStrcmp (p->name, (const xmlChar *) "state") == 0) {
1037 str = xmlNodeGetContent (p);
1039 vm.state = g_strdup ((char *) str);
1043 else if (xmlStrcmp (p->name, (const xmlChar *) "uuid") == 0) {
1044 str = xmlNodeGetContent (p);
1046 vm.uuid = g_strdup ((char *) str);
1050 else if (xmlStrcmp (p->name, (const xmlChar *) "vnc-port") == 0) {
1051 str = xmlNodeGetContent (p);
1053 vm.vnc_port = strtol ((char *) str, NULL, 10);
1057 else if (xmlStrcmp (p->name, (const xmlChar *) "forward-vnc-port") == 0) {
1058 str = xmlNodeGetContent (p);
1060 vm.forward_vnc_port = strtol ((char *) str, NULL, 10);
1064 else if (xmlStrcmp (p->name, (const xmlChar *) "vnic-mac-addr") == 0) {
1065 str = xmlNodeGetContent (p);
1067 vm.mac_addr = g_strdup ((char *) str);
1073 /* Make sure we've got the required fields. */
1075 if (vm.description == NULL)
1076 DEBUG ("required field \"description\" missing from <vm> structure");
1077 else if (vm.hostid == -1)
1078 DEBUG ("required field \"description\" missing from <vm> structure");
1079 else if (vm.id == -1)
1080 DEBUG ("required field \"description\" missing from <vm> structure");
1081 else if (vm.vnc_port == -1)
1082 DEBUG ("required field \"vnc-port\" missing from <vm> structure");
1083 else if (vm.forward_vnc_port == -1)
1084 DEBUG ("required field \"forward-vnc-port\" missing from <vm> structure");
1085 else if (vm.uuid == NULL)
1086 DEBUG ("required field \"uuid\" missing from <vm> structure");
1088 ret = g_memdup (&vm, sizeof vm);
1094 main_vmlist_has_running_vm(struct vm* _vm)
1096 // TODO ? get list and wait to be retreived
1097 // wui_thread_send_refresh_vm_list();
1100 GSList* res = g_slist_find_custom (vmlist, _vm, compare_vm);
1102 // return true if running
1103 if(res != NULL) return STREQ (((struct vm*) res->data)->state, "running");