[Ovirt-devel] [PATCH viewer] project cleanup, move sources in a src/ subdir and remove currently unused files
Mohammed Morsi
mmorsi at redhat.com
Thu Jul 23 23:01:45 UTC 2009
---
INSTALL | 237 -----------
Makefile.am | 11 +-
README | 6 +-
configure.ac | 4 +-
internal.h | 220 ----------
main.c | 1148 -----------------------------------------------------
ovirt-viewer.spec | 2 +-
src/Makefile.am | 5 +
src/internal.h | 220 ++++++++++
src/main.c | 1148 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/tunnel.c | 327 +++++++++++++++
src/wui_thread.c | 1106 +++++++++++++++++++++++++++++++++++++++++++++++++++
tunnel.c | 327 ---------------
wui_thread.c | 1106 ---------------------------------------------------
14 files changed, 2817 insertions(+), 3050 deletions(-)
delete mode 100644 AUTHORS
delete mode 100644 ChangeLog
delete mode 100644 INSTALL
delete mode 100644 NEWS
delete mode 100644 internal.h
delete mode 100644 main.c
create mode 100644 src/Makefile.am
create mode 100644 src/internal.h
create mode 100644 src/main.c
create mode 100644 src/tunnel.c
create mode 100644 src/wui_thread.c
delete mode 100644 tunnel.c
delete mode 100644 wui_thread.c
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index e69de29..0000000
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index e69de29..0000000
diff --git a/INSTALL b/INSTALL
deleted file mode 100644
index d3c5b40..0000000
--- a/INSTALL
+++ /dev/null
@@ -1,237 +0,0 @@
-Installation Instructions
-*************************
-
-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
-2006, 2007 Free Software Foundation, Inc.
-
-This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
-
-Basic Installation
-==================
-
-Briefly, the shell commands `./configure; make; make install' should
-configure, build, and install this package. The following
-more-detailed instructions are generic; see the `README' file for
-instructions specific to this package.
-
- The `configure' shell script attempts to guess correct values for
-various system-dependent variables used during compilation. It uses
-those values to create a `Makefile' in each directory of the package.
-It may also create one or more `.h' files containing system-dependent
-definitions. Finally, it creates a shell script `config.status' that
-you can run in the future to recreate the current configuration, and a
-file `config.log' containing compiler output (useful mainly for
-debugging `configure').
-
- It can also use an optional file (typically called `config.cache'
-and enabled with `--cache-file=config.cache' or simply `-C') that saves
-the results of its tests to speed up reconfiguring. Caching is
-disabled by default to prevent problems with accidental use of stale
-cache files.
-
- If you need to do unusual things to compile the package, please try
-to figure out how `configure' could check whether to do them, and mail
-diffs or instructions to the address given in the `README' so they can
-be considered for the next release. If you are using the cache, and at
-some point `config.cache' contains results you don't want to keep, you
-may remove or edit it.
-
- The file `configure.ac' (or `configure.in') is used to create
-`configure' by a program called `autoconf'. You need `configure.ac' if
-you want to change it or regenerate `configure' using a newer version
-of `autoconf'.
-
-The simplest way to compile this package is:
-
- 1. `cd' to the directory containing the package's source code and type
- `./configure' to configure the package for your system.
-
- Running `configure' might take a while. While running, it prints
- some messages telling which features it is checking for.
-
- 2. Type `make' to compile the package.
-
- 3. Optionally, type `make check' to run any self-tests that come with
- the package.
-
- 4. Type `make install' to install the programs and any data files and
- documentation.
-
- 5. You can remove the program binaries and object files from the
- source code directory by typing `make clean'. To also remove the
- files that `configure' created (so you can compile the package for
- a different kind of computer), type `make distclean'. There is
- also a `make maintainer-clean' target, but that is intended mainly
- for the package's developers. If you use it, you may have to get
- all sorts of other programs in order to regenerate files that came
- with the distribution.
-
- 6. Often, you can also type `make uninstall' to remove the installed
- files again.
-
-Compilers and Options
-=====================
-
-Some systems require unusual options for compilation or linking that the
-`configure' script does not know about. Run `./configure --help' for
-details on some of the pertinent environment variables.
-
- You can give `configure' initial values for configuration parameters
-by setting variables in the command line or in the environment. Here
-is an example:
-
- ./configure CC=c99 CFLAGS=-g LIBS=-lposix
-
- *Note Defining Variables::, for more details.
-
-Compiling For Multiple Architectures
-====================================
-
-You can compile the package for more than one kind of computer at the
-same time, by placing the object files for each architecture in their
-own directory. To do this, you can use GNU `make'. `cd' to the
-directory where you want the object files and executables to go and run
-the `configure' script. `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
-
- With a non-GNU `make', it is safer to compile the package for one
-architecture at a time in the source code directory. After you have
-installed the package for one architecture, use `make distclean' before
-reconfiguring for another architecture.
-
-Installation Names
-==================
-
-By default, `make install' installs the package's commands under
-`/usr/local/bin', include files under `/usr/local/include', etc. You
-can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX'.
-
- You can specify separate installation prefixes for
-architecture-specific files and architecture-independent files. If you
-pass the option `--exec-prefix=PREFIX' to `configure', the package uses
-PREFIX as the prefix for installing programs and libraries.
-Documentation and other data files still use the regular prefix.
-
- In addition, if you use an unusual directory layout you can give
-options like `--bindir=DIR' to specify different values for particular
-kinds of files. Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
-
- If the package supports it, you can cause programs to be installed
-with an extra prefix or suffix on their names by giving `configure' the
-option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
-
-Optional Features
-=================
-
-Some packages pay attention to `--enable-FEATURE' options to
-`configure', where FEATURE indicates an optional part of the package.
-They may also pay attention to `--with-PACKAGE' options, where PACKAGE
-is something like `gnu-as' or `x' (for the X Window System). The
-`README' should mention any `--enable-' and `--with-' options that the
-package recognizes.
-
- For packages that use the X Window System, `configure' can usually
-find the X include and library files automatically, but if it doesn't,
-you can use the `configure' options `--x-includes=DIR' and
-`--x-libraries=DIR' to specify their locations.
-
-Specifying the System Type
-==========================
-
-There may be some features `configure' cannot figure out automatically,
-but needs to determine by the type of machine the package will run on.
-Usually, assuming the package is built to be run on the _same_
-architectures, `configure' can figure that out, but if it prints a
-message saying it cannot guess the machine type, give it the
-`--build=TYPE' option. TYPE can either be a short name for the system
-type, such as `sun4', or a canonical name which has the form:
-
- CPU-COMPANY-SYSTEM
-
-where SYSTEM can have one of these forms:
-
- OS KERNEL-OS
-
- See the file `config.sub' for the possible values of each field. If
-`config.sub' isn't included in this package, then this package doesn't
-need to know the machine type.
-
- If you are _building_ compiler tools for cross-compiling, you should
-use the option `--target=TYPE' to select the type of system they will
-produce code for.
-
- If you want to _use_ a cross compiler, that generates code for a
-platform different from the build platform, you should specify the
-"host" platform (i.e., that on which the generated programs will
-eventually be run) with `--host=TYPE'.
-
-Sharing Defaults
-================
-
-If you want to set default values for `configure' scripts to share, you
-can create a site shell script called `config.site' that gives default
-values for variables like `CC', `cache_file', and `prefix'.
-`configure' looks for `PREFIX/share/config.site' if it exists, then
-`PREFIX/etc/config.site' if it exists. Or, you can set the
-`CONFIG_SITE' environment variable to the location of the site script.
-A warning: not all `configure' scripts look for a site script.
-
-Defining Variables
-==================
-
-Variables not defined in a site shell script can be set in the
-environment passed to `configure'. However, some packages may run
-configure again during the build, and the customized values of these
-variables may be lost. In order to avoid this problem, you should set
-them in the `configure' command line, using `VAR=value'. For example:
-
- ./configure CC=/usr/local2/bin/gcc
-
-causes the specified `gcc' to be used as the C compiler (unless it is
-overridden in the site shell script).
-
-Unfortunately, this technique does not work for `CONFIG_SHELL' due to
-an Autoconf bug. Until the bug is fixed you can use this workaround:
-
- CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
-
-`configure' Invocation
-======================
-
-`configure' recognizes the following options to control how it operates.
-
-`--help'
-`-h'
- Print a summary of the options to `configure', and exit.
-
-`--version'
-`-V'
- Print the version of Autoconf used to generate the `configure'
- script, and exit.
-
-`--cache-file=FILE'
- Enable the cache: use and save the results of the tests in FILE,
- traditionally `config.cache'. FILE defaults to `/dev/null' to
- disable caching.
-
-`--config-cache'
-`-C'
- Alias for `--cache-file=config.cache'.
-
-`--quiet'
-`--silent'
-`-q'
- Do not print messages saying which checks are being made. To
- suppress all normal output, redirect it to `/dev/null' (any error
- messages will still be shown).
-
-`--srcdir=DIR'
- Look for the package's source code in directory DIR. Usually
- `configure' can determine that directory automatically.
-
-`configure' also accepts some other, not widely useful, options. Run
-`configure --help' for more details.
-
diff --git a/Makefile.am b/Makefile.am
index b5a3e01..50d85c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,6 @@
# ovirt viewer console application
# Copyright (C) 2008 Red Hat Inc.
-# Written by Richard W.M. Jones <rjones at redhat.com>
+# Written by Richard W.M. Jones <rjones at redhat.com>, Mohammed Morsi <mmorsi at redhat.com>
#
# 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
@@ -16,15 +16,10 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-bin_PROGRAMS = ovirt-viewer
+AUTOMAKE_OPTIONS = foreign
-ovirt_viewer_SOURCES = main.c wui_thread.c tunnel.c internal.h
-ovirt_viewer_CFLAGS = $(OVIRT_VIEWER_CFLAGS) -DCAINFO='"$(CAINFO)"'
-ovirt_viewer_LDADD = $(OVIRT_VIEWER_LIBS)
+SUBDIRS = src
EXTRA_DIST = $(PACKAGE).spec
DISTCLEAN_FILES = $(PACKAGE).spec
-
-valgrind:
- valgrind --leak-check=full ./ovirt-viewer$(EXEEXT) --debug
diff --git a/NEWS b/NEWS
deleted file mode 100644
index e69de29..0000000
diff --git a/README b/README
index cff4b1f..3b98bc7 100644
--- a/README
+++ b/README
@@ -13,8 +13,12 @@ consoles from Linux, Unix or Windows-based desktops.
Installation
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
-Read INSTALL for complete instructions, or briefly:
1. ./autogen.sh
2. ./configure
3. make
4. ./ovirt-viewer
+
+Authors
+âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
+Richard W.M. Jones <rjones at redhat.com>
+Mohammed Morsi <mmorsi at redhat.com>
diff --git a/configure.ac b/configure.ac
index 13276ea..95b8b8b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -16,7 +16,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-AC_INIT(ovirt-viewer, 0.1)
+AC_INIT(ovirt-viewer, 1.0.0)
AM_INIT_AUTOMAKE
dnl Basic C compiler environment.
@@ -57,5 +57,5 @@ fi
dnl Output.
AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([Makefile src/Makefile])
AC_OUTPUT
diff --git a/internal.h b/internal.h
deleted file mode 100644
index bd65922..0000000
--- a/internal.h
+++ /dev/null
@@ -1,220 +0,0 @@
-/* ovirt viewer console application
- * Copyright (C) 2008 Red Hat Inc.
- * Written by Richard W.M. Jones <rjones at redhat.com>
- *
- * 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.
- */
-
-#ifndef OVIRT_VIEWER_INTERNAL_H
-#define OVIRT_VIEWER_INTERNAL_H
-
-#ifndef G_THREADS_ENABLED
-#error "This program requires GLib threads, and cannot be compiled without."
-#endif
-
-/* Debugging messages are always compiled in, but have to
- * be turned on using the --debug command line switch.
- */
-extern gboolean debug;
-
-#define DEBUG(fs,...) \
- do { \
- if (debug) { \
- fprintf (stderr, "%s:%d: [thread %p] ", __FILE__, __LINE__, \
- g_thread_self ()); \
- fprintf (stderr, (fs), ## __VA_ARGS__); \
- fprintf (stderr, "\n"); \
- } \
- } while (0)
-
-/* Verbose messages are always compiled in, but have to
- * be turned on using the --verbose command line switch.
- */
-extern gboolean verbose;
-
-#define VERBOSE(fs,...) \
- do { \
- if (verbose) { \
- fprintf (stderr, "%s:%d: [thread %p] ", __FILE__, __LINE__, \
- g_thread_self ()); \
- fprintf (stderr, (fs), ## __VA_ARGS__); \
- fprintf (stderr, "\n"); \
- } \
- } while (0)
-
-/* String equality tests, suggested by Jim Meyering. */
-#define STREQ(a,b) (strcmp((a),(b)) == 0)
-#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
-#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
-#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
-#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
-#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
-#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
-#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
-#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
-
-extern const char *cainfo;
-extern gboolean check_cert;
-
-/* server we're connecting to */
-extern const char* hostname;
-
-/* port which to connect to the server via vnc */
-extern int ovirt_server_vnc_port;
-
-/* vm currently in focus */
-extern struct vm* vm_in_focus;
-
-/* Communications between the main thread and the WUI thread. For
- * an explanation of the threading model, please see the comment in
- * main().
- */
-
-extern void start_wui_thread (void);
-extern void stop_wui_thread (void);
-
-extern void assert_is_main_thread (const char *, int);
-extern void assert_is_wui_thread (const char *, int);
-
-#define ASSERT_IS_MAIN_THREAD() assert_is_main_thread(__FILE__,__LINE__)
-#define ASSERT_IS_WUI_THREAD() assert_is_wui_thread(__FILE__,__LINE__)
-
-/* These are messages (instructions) which can be sent from the main
- * thread to the WUI thread.
- */
-
-/* Start connecting to WUI, and set the base URI. */
-extern void wui_thread_send_connect (const char *uri);
-
-/* Disconnect, forget URI, credentials, VMs etc. */
-extern void wui_thread_send_disconnect (void);
-
-/* Set the username and password and tell the WUI to try to log in. */
-extern void wui_thread_send_login (const char *username, const char *password);
-
-/* Tell the WUI thread to refresh the VM list. Note that the WUI
- * thread does this automatically anyway after a successful login, and
- * it also periodically updates the list. This call just tells it to
- * do so right away.
- */
-extern void wui_thread_send_refresh_vm_list (void);
-
-/* Retrieve the list of VMs.
- *
- * wui_thread_get_vmlist returns TRUE if there was a valid list of
- * VMs (even if it is empty), or FALSE if we don't have a valid list
- * of VMs to return.
- *
- * NB: Caller must call free_vmlist once it is done with the list.
- *
- */
-extern gboolean wui_thread_get_vmlist (GSList **ret);
-extern void free_vmlist (GSList *vmlist);
-
-struct vm {
- char *description;
- int hostid;
- int id;
- int vnc_port;
- int forward_vnc_port;
- char *uuid; /* Printable UUID. */
- char *state;
-
- /* Only the fields above this point are required. The remainder may
- * be NULL / -1 to indicate they were missing in the data we got
- * back from the WUI.
- */
-
- long mem_allocated; /* Kbytes */
- long mem_used; /* Kbytes */
- int vcpus_allocated;
- int vcpus_used;
- char *mac_addr; /* Printable MAC addr. */
-};
-
-/* Returns true if the WUI thread thinks it is connected to a remote
- * WUI. REST is connectionless so really this means that we
- * successfully made an HTTP/HTTPS request "recently", and we haven't
- * seen any errors above a certain threshold.
- */
-extern gboolean wui_thread_is_connected (void);
-
-/* Returns true if we successfully logged in with the username
- * and password supplied in a recent request, and we haven't
- * received any authorization failures since.
- */
-extern gboolean wui_thread_is_logged_in (void);
-
-/* Returns true if we have a valid list of VMs. Note that because
- * of race conditions, this doesn't guarantee that wui_thread_get_vmlist
- * will work.
- */
-extern gboolean wui_thread_has_valid_vmlist (void);
-
-/* Returns true if the WUI thread is busy performing a request
- * at the moment.
- */
-extern gboolean wui_thread_is_busy (void);
-
-
-/* Communications between the main thread and the tunnel thread.*/
-extern void start_tunnel (void);
-extern void stop_tunnel (void);
-
-/* port which local tunnel is listening on */
-extern int tunnel_port;
-
-
-/* Returns true if the main vm list contains a
- * running vm w/ the same name as specified one
- */
-extern gboolean main_vmlist_has_running_vm(struct vm*);
-
-/* Callbacks from the WUI thread to the main thread. The WUI thread
- * adds these to the Glib main loop using g_idle_add, which means they
- * actually get executed in the context of the main thread.
- */
-
-/* The WUI thread has changed its state to connected. */
-extern gboolean main_connected (gpointer);
-
-/* The WUI thread has changed its state to disconnected. */
-extern gboolean main_disconnected (gpointer);
-
-/* The WUI thread has changed its state to logged in. */
-extern gboolean main_logged_in (gpointer);
-
-/* The WUI thread has changed its state to logged out. */
-extern gboolean main_logged_out (gpointer);
-
-/* The WUI thread has changed its state to busy. */
-extern gboolean main_busy (gpointer);
-
-/* The WUI thread has changed its state to idle. */
-extern gboolean main_idle (gpointer);
-
-/* The WUI thread had a connection problem. */
-extern gboolean main_connection_error (gpointer str);
-
-/* The WUI thread had a login problem. */
-extern gboolean main_login_error (gpointer str);
-
-/* The WUI thread reports a general status error. */
-extern gboolean main_status_error (gpointer str);
-
-/* The WUI thread has updated the vm list. */
-extern gboolean main_vmlist_updated (gpointer);
-
-#endif /* OVIRT_VIEWER_INTERNAL_H */
diff --git a/main.c b/main.c
deleted file mode 100644
index dd26c93..0000000
--- a/main.c
+++ /dev/null
@@ -1,1148 +0,0 @@
-/* ovirt viewer console application
- * Copyright (C) 2008 Red Hat Inc.
- * Written by Richard W.M. Jones <rjones at redhat.com>
- *
- * 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.
- */
-
-#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <glib.h>
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-#include <vncdisplay.h>
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#include <arpa/inet.h>
-
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-
-#ifdef HAVE_WINDOWS_H
-#include <windows.h>
-#endif
-
-#include "internal.h"
-
-/*#define HTTPS "https"*/
-#define HTTPS "http"
-
-gboolean debug = 0;
-
-gboolean verbose = 0;
-
-/* Usually /etc/pki/tls/certs/ca-bundle.crt unless overridden during
- * configure or on the command line.
- */
-const char *cainfo = CAINFO;
-gboolean check_cert = FALSE; // do we want this enabled by default ?
- // would require a CA by default (self-signed wont work)
- // (don't set to true, change var/flag to no_check_cert)
-
-/* The WUI thread has updated the vm list. Here in the main thread
-* we keep our own copy of the vmlist.
-*/
-static GSList *vmlist = NULL;
-
-/* internal.h shared constructs */
-const char* hostname;
-struct vm* vm_in_focus;
-int ovirt_server_vnc_port = 5900;
-
-/* Private functions. */
-static void start_ui (void);
-static GtkWidget *menu_item_new (int which_menu);
-static void refresh_menu_vm_list (GtkWidget *, gpointer);
-static void connect_to_wui_on_enter (GtkWidget *, gpointer);
-static void connect_to_wui (GtkWidget *, gpointer);
-static void send_key_to_vm (GtkWidget *widget, gpointer _keyComboDef);
-static void login_to_wui_on_enter (GtkWidget *, gpointer);
-static void login_to_wui (GtkWidget *, gpointer);
-static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
-static void destroy (GtkWidget *widget, gpointer data);
-static void clear_connectmenu (void);
-static void help_about (GtkWidget *menu);
-static void viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc);
-#if 0
-static void viewer_quit (GtkWidget *src, GtkWidget *vnc);
-#endif
-static void viewer_connected (GtkWidget *vnc);
-static void viewer_initialized (GtkWidget *vnc, GtkWidget *data);
-static void viewer_disconnected (GtkWidget *vnc);
-static void viewer_credential (GtkWidget *vnc, GValueArray *credList);
-static int viewer_open_vnc_socket (const char *vnchost, int vncport);
-static void add_vm_to_connectmenu (gpointer _vm, gpointer data);
-
-/* For any widgets accessed from multiple functions. */
-static GtkWidget *window;
-static GtkWidget *connectitem;
-static GtkWidget *connectmenu;
-static GtkWidget *no_connections;
-static GtkWidget *refresh_vmlist;
-static GtkWidget *refresh_vmlist_separator;
-static GtkWidget *connection_area;
-static GtkWidget *ca_hostname;
-static GtkWidget *ca_button;
-static GtkWidget *ca_error;
-static GtkWidget *login_area;
-static GtkWidget *la_username;
-static GtkWidget *la_password;
-static GtkWidget *la_button;
-static GtkWidget *la_error;
-static GtkWidget *notebook;
-static GtkWidget *statusbar;
-static guint statusbar_ctx;
-static GdkCursor *busy_cursor;
-
-/* Menus. */
-enum menuNums {
- CONNECT_MENU,
- VIEW_MENU,
- SEND_KEY_MENU,
- WINDOW_MENU,
- HELP_MENU,
-};
-
-struct menuItem {
- guint menu;
- GtkWidget *label;
- const char *ungrabbed_text;
- const char *grabbed_text;
-};
-
-static struct menuItem menuItems[] = {
- { CONNECT_MENU, NULL, "_Connect", "Connect" },
- { VIEW_MENU, NULL, "_View", "View" },
- { SEND_KEY_MENU, NULL, "_Send Key", "Send Key" },
- { WINDOW_MENU, NULL, "_Window", "Window" },
- { HELP_MENU, NULL, "_Help", "Help" }
-};
-
-#define MAX_KEY_COMBO 3
- struct keyComboDef {
- guint keys[MAX_KEY_COMBO];
- guint nkeys;
- const char *label;
- };
-
-#define NUM_KEY_COMBOS 17
-static struct keyComboDef keyCombos[] = {
- { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+Del"},
- { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+Backspace"},
- { {}, 0, "" },
- { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F1"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F2"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F3"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F4"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F5"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F6"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F7"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F8"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F9"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F10"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
- { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
- { {}, 0, "" },
- { { GDK_Print }, 1, "PrintScreen"},
-};
-
-/* Window title. */
-static const char *title = "oVirt Viewer";
-
-// when running vm
-// 47 chars
-static const char *title_vm =
- "oVirt Viewer: (ctrl+alt to grab/release mouse) ";
-
-/* Gtk widget styles. Avoid installation hassles by keeping this
- * inside the binary. It can still be overridden by the user (who
- * will do that?)
- */
-static const char *styles =
- "style \"ovirt-viewer-yellow-box\"\n"
- "{\n"
- " bg[NORMAL] = shade (1.5, \"yellow\")\n"
- "}\n"
- "widget \"*.ovirt-viewer-connection-area\" style \"ovirt-viewer-yellow-box\"\n"
- ;
-
-/* Command-line arguments. */
-static int print_version = 0;
-
-static const char *help_msg =
- "Use '" PACKAGE " --help' to see a list of available command line options";
-
-static const GOptionEntry options[] = {
- { "port", 'p', 0, G_OPTION_ARG_INT, &ovirt_server_vnc_port,
- "set port which to connect to server via vnc", NULL },
- { "cainfo", 0, 0, G_OPTION_ARG_STRING, &cainfo,
- "set the path of the CA certificate bundle", NULL },
- { "check-certificate", 0, 0, G_OPTION_ARG_NONE, &check_cert,
- "check the SSL certificate of the server", NULL },
- { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
- "turn on debugging messages", NULL },
- { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
- "turn on verbose messages", NULL },
- { "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
- "display version and exit", NULL },
- { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
-};
-
-int
-main (int argc, char *argv[])
-{
- GOptionContext *context;
- GError *error = NULL;
-
- /* Initialize GLib threads before anything else.
- *
- * There is one main thread, which is used for all Gtk interactions
- * and to keep the UI responsive, and one WUI thread. The WUI
- * thread is used to connect to the WUI, log in, and maintain the list
- * of virtual machines. The WUI thread is the only thread allowed
- * to use the CURL library.
- *
- * The main thread sends instructions to the WUI thread (like "connect",
- * "disconnect", etc.) using a simple message-passing protocol and
- * a GAsyncQueue.
- *
- * The WUI thread keeps the UI updated by adding idle events which are
- * processed in the main thread - see:
- * http://mail.gnome.org/archives/gtk-app-devel-list/2007-March/msg00232.html
- *
- * A tunnel thread is also started to locally listen for vnc packets
- * and make them proxyable, adding the vm name, before forwarding onto
- * the server
- *
- * Note that under Win32 you must confine all Gtk/Gdk interactions
- * to a single thread - see:
- * http://developer.gimp.org/api/2.0/gdk/gdk-Threads.html
- */
- if (!g_thread_supported ()) {
- g_thread_init (NULL);
-#ifndef WIN32
- gdk_threads_init ();
-#endif
- } else {
- fprintf (stderr, "GLib threads not supported or not working.");
- exit (1);
- }
-
- gtk_init (&argc, &argv);
-
- /* Parse command-line options. */
- context = g_option_context_new ("oVirt viewer");
- g_option_context_add_main_entries (context, options, NULL);
- g_option_context_add_group (context, gtk_get_option_group (TRUE));
- g_option_context_add_group (context, vnc_display_get_option_group ());
- g_option_context_parse (context, &argc, &argv, &error);
-
- if (error) {
- g_print ("%s\n%s\n", error->message, help_msg);
- g_error_free (error);
- exit (1);
- }
-
- if (print_version) {
- printf ("%s %s\n", PACKAGE, VERSION);
- exit (0);
- }
-
- start_wui_thread ();
- start_ui ();
-
- DEBUG ("entering the Gtk main loop");
-
- gtk_main ();
-
- stop_wui_thread ();
- stop_tunnel();
-
- exit (0);
-}
-
-/* Create the viewer window, menus. */
-static void
-start_ui (void)
-{
- int i;
- GtkWidget *vbox;
- GtkWidget *menubar;
- GtkWidget *view;
- GtkWidget *viewmenu;
- GtkWidget *sendkey;
- GtkWidget *sendkeymenu;
- GtkWidget *sendkeymenuitem;
- GtkWidget *wind;
- GtkWidget *windmenu;
- GtkWidget *help;
- GtkWidget *helpmenu;
- GtkWidget *about;
- GtkWidget *ca_vbox;
- GtkWidget *ca_hbox;
- GtkWidget *ca_label;
- GtkWidget *la_vbox;
- GtkWidget *la_hbox;
- GtkWidget *la_label;
-
- DEBUG ("creating viewer windows and menus");
-
- /* Parse styles. */
- gtk_rc_parse_string (styles);
-
- /* Busy cursor, used by main_busy() function.
- * XXX This cursor is crap - how can we use the Bluecurve/theme cursor?
- */
- busy_cursor = gdk_cursor_new (GDK_WATCH);
-
- /* Window. */
- window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
- gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
- gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
- gtk_window_set_title (GTK_WINDOW (window), title);
-
- g_signal_connect (G_OBJECT (window), "delete_event",
- G_CALLBACK (delete_event), NULL);
- g_signal_connect (G_OBJECT (window), "destroy",
- G_CALLBACK (destroy), NULL);
-
- /* VBox for layout within the window. */
- vbox = gtk_vbox_new (FALSE, 0);
-
- /* Menubar. */
- menubar = gtk_menu_bar_new ();
- connectitem = menu_item_new (CONNECT_MENU);
- connectmenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (connectitem), connectmenu);
-
- no_connections = gtk_menu_item_new_with_label ("Not connected");
- gtk_menu_append (GTK_MENU (connectmenu), no_connections);
- gtk_widget_set_sensitive (no_connections, FALSE);
-
- /* This is not added to the menu yet, but will be when we are
- * connected.
- */
- refresh_vmlist =
- gtk_menu_item_new_with_label ("Refresh list of virtual machines");
- g_object_ref (refresh_vmlist);
- refresh_vmlist_separator = gtk_separator_menu_item_new ();
- g_object_ref (refresh_vmlist_separator);
-
- g_signal_connect (G_OBJECT (refresh_vmlist), "activate",
- G_CALLBACK (refresh_menu_vm_list), NULL);
-
-#if 0
- screenshot = gtk_menu_item_new_with_mnemonic ("_Screenshot");
- gtk_menu_append (GTK_MENU (filemenu), screenshot);
- g_signal_connect (screenshot, "activate",
- GTK_SIGNAL_FUNC (take_screenshot), NULL);
-#endif
-
- view = menu_item_new (VIEW_MENU);
- viewmenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (view), viewmenu);
-
- sendkey = menu_item_new (SEND_KEY_MENU);
- sendkeymenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (sendkey), sendkeymenu);
-
- for(i = 0; i < NUM_KEY_COMBOS; ++i){
- sendkeymenuitem = gtk_menu_item_new_with_label (keyCombos[i].label);
- gtk_menu_append (GTK_MENU (sendkeymenu), sendkeymenuitem);
- g_signal_connect (G_OBJECT (sendkeymenuitem), "activate",
- G_CALLBACK (send_key_to_vm), &(keyCombos[i]));
- }
-
- wind = menu_item_new (WINDOW_MENU);
- windmenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (wind), windmenu);
-
- help = menu_item_new (HELP_MENU);
- helpmenu = gtk_menu_new ();
- gtk_menu_item_set_submenu (GTK_MENU_ITEM (help), helpmenu);
-
- about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
- gtk_menu_append(GTK_MENU(helpmenu), about);
- g_signal_connect(about, "activate", GTK_SIGNAL_FUNC (help_about), NULL);
-
- gtk_menu_bar_append (GTK_MENU_BAR (menubar), connectitem);
- gtk_menu_bar_append (GTK_MENU_BAR (menubar), view);
- gtk_menu_bar_append (GTK_MENU_BAR (menubar), sendkey);
- gtk_menu_bar_append (GTK_MENU_BAR (menubar), wind);
- gtk_menu_bar_append (GTK_MENU_BAR (menubar), help);
-
- /* For login dialogs, etc., usually invisible. */
- connection_area = gtk_event_box_new ();
- ca_vbox = gtk_vbox_new (FALSE, 0);
- ca_label = gtk_label_new ("Give the name of the oVirt management server:");
- ca_hbox = gtk_hbox_new (FALSE, 0);
- ca_hostname = gtk_entry_new ();
- gtk_entry_set_width_chars (GTK_ENTRY (ca_hostname), 24);
- ca_button = gtk_button_new_with_label ("Connect");
- ca_error = gtk_label_new (NULL);
- gtk_box_pack_start (GTK_BOX (ca_hbox), ca_hostname, FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (ca_hbox), ca_button, FALSE, FALSE, 0);
- gtk_box_pack_start (GTK_BOX (ca_vbox), ca_label, FALSE, FALSE, 4);
- gtk_box_pack_start (GTK_BOX (ca_vbox), ca_hbox, TRUE, FALSE, 4);
- gtk_box_pack_start (GTK_BOX (ca_vbox), ca_error, TRUE, FALSE, 4);
- gtk_container_add (GTK_CONTAINER (connection_area), ca_vbox);
-
- gtk_widget_set_name (connection_area, "ovirt-viewer-connection-area");
-
- g_signal_connect (G_OBJECT (ca_hostname), "key-release-event",
- G_CALLBACK (connect_to_wui_on_enter), NULL);
- g_signal_connect (G_OBJECT (ca_button), "clicked",
- G_CALLBACK (connect_to_wui), NULL);
-
- login_area = gtk_event_box_new ();
- la_vbox = gtk_vbox_new (FALSE, 0);
- la_hbox = gtk_hbox_new (FALSE, 0);
- la_label = gtk_label_new ("Username / password:");
- la_username = gtk_entry_new ();
- gtk_entry_set_width_chars (GTK_ENTRY (la_username), 12);
- la_password = gtk_entry_new ();
- gtk_entry_set_width_chars (GTK_ENTRY (la_password), 12);
- gtk_entry_set_visibility (GTK_ENTRY (la_password), FALSE);
- la_button = gtk_button_new_with_label ("Login");
- la_error = gtk_label_new (NULL);
- gtk_container_add (GTK_CONTAINER (la_hbox), la_username);
- gtk_container_add (GTK_CONTAINER (la_hbox), la_password);
- gtk_container_add (GTK_CONTAINER (la_hbox), la_button);
- gtk_box_pack_start (GTK_BOX (la_vbox), la_label, TRUE, FALSE, 4);
- gtk_box_pack_start (GTK_BOX (la_vbox), la_hbox, TRUE, FALSE, 4);
- gtk_box_pack_start (GTK_BOX (la_vbox), la_error, TRUE, FALSE, 4);
- gtk_container_add (GTK_CONTAINER (login_area), la_vbox);
-
- gtk_widget_set_name (login_area, "ovirt-viewer-login-area");
-
- g_signal_connect (G_OBJECT (la_username), "key-release-event",
- G_CALLBACK (login_to_wui_on_enter), NULL);
- g_signal_connect (G_OBJECT (la_password), "key-release-event",
- G_CALLBACK (login_to_wui_on_enter), NULL);
- g_signal_connect (G_OBJECT (la_button), "clicked",
- G_CALLBACK (login_to_wui), NULL);
-
- /* Tabbed notebook. */
- notebook = gtk_notebook_new ();
- /*gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);*/
- gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE);
- gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
-
- /* Status bar. */
- statusbar = gtk_statusbar_new ();
- statusbar_ctx = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
- "context");
- gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, "");
-
- /* Packing. */
- gtk_container_add (GTK_CONTAINER (window), vbox);
- gtk_container_add_with_properties (GTK_CONTAINER (vbox), menubar,
- "expand", FALSE, NULL);
- gtk_container_add_with_properties (GTK_CONTAINER (vbox), connection_area,
- "expand", FALSE, "fill", TRUE, NULL);
- gtk_container_add_with_properties (GTK_CONTAINER (vbox), login_area,
- "expand", FALSE, "fill", TRUE, NULL);
- gtk_container_add_with_properties (GTK_CONTAINER (vbox), notebook,
- "expand", TRUE, NULL);
- gtk_container_add_with_properties (GTK_CONTAINER (vbox), statusbar,
- "expand", FALSE, NULL);
-
- /* Show widgets. */
- gtk_widget_show_all (window);
-
- if (wui_thread_is_connected ())
- gtk_widget_hide (connection_area);
- if (!wui_thread_is_connected () || wui_thread_is_logged_in ())
- gtk_widget_hide (login_area);
-}
-
-static GtkWidget *
-menu_item_new (int which_menu)
-{
- GtkWidget *widget;
- GtkWidget *label;
- const char *text;
-
- text = menuItems[which_menu].ungrabbed_text;
-
- widget = gtk_menu_item_new ();
- label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
- gtk_label_set_text_with_mnemonic (GTK_LABEL (label), text);
- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
-
- gtk_container_add (GTK_CONTAINER (widget), label);
- gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), widget);
- gtk_widget_show (label);
-
- menuItems[which_menu].label = label;
-
- return widget;
-}
-
-static gboolean
-delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
-{
- DEBUG ("delete_event");
- return FALSE;
-}
-
-static void
-destroy (GtkWidget *widget, gpointer data)
-{
- DEBUG ("destroy");
- gtk_main_quit ();
-}
-
-static void
-help_about (GtkWidget *menu)
-{
- GtkWidget *about;
- const char *authors[] = {
- "Richard W.M. Jones <rjones at redhat.com>",
- "Daniel P. Berrange <berrange at redhat.com>",
- "Mohammed Morsi <mmorsi at redhat.com>",
- NULL
- };
-
- about = gtk_about_dialog_new();
-
- gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "oVirt Viewer");
- gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
- gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://ovirt.org/");
- gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "oVirt website");
- gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
- gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),
- "This program is free software; you can redistribute it and/or modify\n" \
- "it under the terms of the GNU General Public License as published by\n" \
- "the Free Software Foundation; either version 2 of the License, or\n" \
- "(at your option) any later version.\n" \
- "\n" \
- "This program is distributed in the hope that it will be useful,\n" \
- "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
- "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \
- "GNU General Public License for more details.\n" \
- "\n" \
- "You should have received a copy of the GNU General Public License\n" \
- "along with this program; if not, write to the Free Software\n" \
- "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n");
-
- gtk_dialog_run(GTK_DIALOG(about));
- gtk_widget_destroy(about);
-}
-
-static void
-refresh_menu_vm_list(GtkWidget *widget, gpointer data)
-{
- wui_thread_send_refresh_vm_list();
-}
-
-static void
-connect_to_wui_on_enter (GtkWidget *widget, gpointer data)
-{
- // if key released was not 'enter' key
- if(((GdkEventKey *)data)->type == GDK_KEY_RELEASE && (((GdkEventKey *)data)->keyval & 0xFF) != 13 ) return;
-
- connect_to_wui(widget, data);
-}
-
-static void
-connect_to_wui (GtkWidget *widget, gpointer data)
-{
- char *uri;
- int len;
-
- hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
- if (STREQ (hostname, "")) return;
-
- /* https:// + hostname + /ovirt + \0 */
- len = 8 + strlen (hostname) + 6 + 1;
- uri = g_alloca (len);
- snprintf (uri, len, HTTPS "://%s/ovirt", hostname);
-
- wui_thread_send_connect (uri);
- start_tunnel();
-}
-
-static void
-login_to_wui_on_enter (GtkWidget *widget, gpointer data)
-{
- // if key released was not 'enter' key
- if(((GdkEventKey *)data)->type == GDK_KEY_RELEASE && (((GdkEventKey *)data)->keyval & 0xFF) != 13 ) return;
-
- login_to_wui(widget, data);
-}
-
-static void
-login_to_wui (GtkWidget *widget, gpointer data)
-{
- const char *username, *password;
-
- username = gtk_entry_get_text (GTK_ENTRY (la_username));
- if (STREQ (username, "")) return;
- password = gtk_entry_get_text (GTK_ENTRY (la_password));
-
- wui_thread_send_login (username, password);
-}
-
-/* Connect to a virtual machine. This callback is called from the
- * connect menu. It searches the notebook of gtk-vnc widgets to see
- * if we have already connected to this machine, and if not it
- * makes a new connection.
- */
-static void
-connect_to_vm (GtkWidget *widget, gpointer _vm)
-{
- struct vm *vm = (struct vm *) _vm;
- int n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
- int i, uuidlen, len, fd;
- GtkWidget *child;
- const char *label;
- char *label2;
- char new_title[97]; // 47 chars for title + 50 for vm name
-
- DEBUG ("searching tabs for uuid %s", vm->uuid);
-
- uuidlen = strlen (vm->uuid);
-
- /* Search the tabs for this UUID, and if found, switch to it and return. */
- for (i = 0; i < n; ++i) {
- child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
- label = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (notebook), child);
- len = strlen (label);
- if (len >= uuidlen &&
- STREQ (label + len - uuidlen, vm->uuid)) {
- DEBUG ("found on tab %d", i);
- gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
- return;
- }
- }
-
- DEBUG ("not found, creating new tab");
-
- // before we open vnc connection, make sure vm is running
- gboolean vm_running = main_vmlist_has_running_vm(vm);
-
- if(!vm_running){
- main_status_error (g_strdup ("VM not running"));
- return;
- }
-
- // FIXME on notebook tab switch, change vm_in_focus
- vm_in_focus = vm;
-
- /* This VM isn't in the notebook already, so create a new console. */
- DEBUG ("connecting to local tunnel on port %i", tunnel_port);
- fd = viewer_open_vnc_socket("127.0.0.1", tunnel_port);
- if (fd == -1) return; /* We've already given an error. */
-
- child = vnc_display_new ();
- if (! vnc_display_open_fd (VNC_DISPLAY (child), fd)) {
- main_status_error (g_strdup ("internal error in Gtk-VNC widget"));
- return;
- }
-
- main_status_error(g_strdup(""));
-
- for(i = 0; i < 47; ++i) new_title[i] = title_vm[i];
- for(i = 0; i < 50; ++i){
- if(vm->description[i] == '\0') break;
- new_title[47 + i] = vm->description[i];
- }
- new_title[i < 50 ? i+47 : 96] = '\0';
- gtk_window_set_title (GTK_WINDOW (window), new_title);
-
- /*
- gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-grab",
- GTK_SIGNAL_FUNC(viewer_grab), window);
- gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-ungrab",
- GTK_SIGNAL_FUNC(viewer_ungrab), window);
- */
-
- gtk_signal_connect(GTK_OBJECT(child), "delete-event",
- GTK_SIGNAL_FUNC(viewer_shutdown), child);
-
- gtk_signal_connect(GTK_OBJECT(child), "vnc-connected",
- GTK_SIGNAL_FUNC(viewer_connected), NULL);
- gtk_signal_connect(GTK_OBJECT(child), "vnc-initialized",
- GTK_SIGNAL_FUNC(viewer_initialized), NULL);
- gtk_signal_connect(GTK_OBJECT(child), "vnc-disconnected",
- GTK_SIGNAL_FUNC(viewer_disconnected), NULL);
-
- g_signal_connect(GTK_OBJECT(child), "vnc-auth-credential",
- GTK_SIGNAL_FUNC(viewer_credential), NULL);
-
- /* NB. We have to do this before adding it to the notebook. */
- gtk_widget_show (child);
-
- /* Choose a tab label, which MUST end with the uuid string, since
- * we use the tab label to store uuid.
- */
- len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1;
- label2 = g_alloca (len);
- snprintf (label2, len, "%s %s", vm->description, vm->uuid);
-
- i = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child, NULL);
- gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (notebook), child, label2);
- gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
-
- DEBUG ("finished creating new tab");
-}
-
-/* Send key to a virtual machine. This callback is called from the
- * send key menu. If finds vm in focus, and sends specified key to it
- */
-static void
-send_key_to_vm (GtkWidget *widget, gpointer _keyComboDef)
-{
- GtkWidget* viewer;
- struct keyComboDef* key_combo = (struct keyComboDef*) _keyComboDef;
- int c = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
-
- if(c < 0){
- return;
- }
-
- viewer = gtk_notebook_get_nth_page(GTK_NOTEBOOK (notebook), c);
- if(viewer != NULL){
- DEBUG ("sending keys to vm");
- vnc_display_send_keys(VNC_DISPLAY(viewer),
- key_combo->keys,
- key_combo->nkeys);
- }
-}
-
-
-/*
-static void viewer_grab(GtkWidget *vnc, GtkWidget *window)
-{
- int i;
-
- viewer_set_title(VNC_DISPLAY(vnc), window, TRUE);
-
- for (i = 0 ; i < LAST_MENU; i++) {
- gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].grabbed_text);
- }
-}
-
-static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window)
-{
- int i;
-
- viewer_set_title(VNC_DISPLAY(vnc), window, FALSE);
-
- for (i = 0 ; i < LAST_MENU; i++) {
- gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].ungrabbed_text);
- }
-}
-*/
-
-static void
-viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc)
-{
- vnc_display_close (VNC_DISPLAY(vnc));
-
- /* Just close the notebook tab for now. XXX */
- gtk_notebook_remove_page (GTK_NOTEBOOK (notebook),
- gtk_notebook_page_num (GTK_NOTEBOOK (notebook),
- vnc));
-}
-
-#if 0
-static void
-viewer_quit (GtkWidget *src, GtkWidget *vnc)
-{
- viewer_shutdown (src, NULL, vnc);
-}
-#endif
-
-static void
-viewer_connected (GtkWidget *vnc)
-{
- DEBUG ("Connected to server");
-}
-
-static void
-viewer_initialized (GtkWidget *vnc, GtkWidget *data)
-{
- DEBUG ("Connection initialized");
-}
-
-static void
-viewer_disconnected (GtkWidget *vnc)
-{
- DEBUG ("Disconnected from server");
-}
-
-static void
-viewer_credential (GtkWidget *vnc, GValueArray *credList)
-{
- GtkWidget *dialog = NULL;
- int response;
- unsigned int i, prompt = 0;
- const char **data;
-
- DEBUG ("Got credential request for %d credential(s)",
- credList->n_values);
-
- data = g_new0(const char *, credList->n_values);
-
- for (i = 0 ; i < credList->n_values ; i++) {
- GValue *cred = g_value_array_get_nth(credList, i);
- switch (g_value_get_enum(cred)) {
- case VNC_DISPLAY_CREDENTIAL_USERNAME:
- case VNC_DISPLAY_CREDENTIAL_PASSWORD:
- prompt++;
- break;
- case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
- data[i] = "libvirt";
- default:
- break;
- }
- }
-
- if (prompt) {
- GtkWidget **label, **entry, *box, *vbox;
- int row;
- dialog = gtk_dialog_new_with_buttons("Authentication required",
- NULL,
- 0,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL,
- GTK_STOCK_OK,
- GTK_RESPONSE_OK,
- NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
-
- box = gtk_table_new(credList->n_values, 2, FALSE);
- label = g_new(GtkWidget *, prompt);
- entry = g_new(GtkWidget *, prompt);
-
- for (i = 0, row =0 ; i < credList->n_values ; i++) {
- GValue *cred = g_value_array_get_nth(credList, i);
- switch (g_value_get_enum(cred)) {
- case VNC_DISPLAY_CREDENTIAL_USERNAME:
- label[row] = gtk_label_new("Username:");
- break;
- case VNC_DISPLAY_CREDENTIAL_PASSWORD:
- label[row] = gtk_label_new("Password:");
- break;
- default:
- continue;
- }
- entry[row] = gtk_entry_new();
- if (g_value_get_enum(cred) == VNC_DISPLAY_CREDENTIAL_PASSWORD)
- gtk_entry_set_visibility(GTK_ENTRY(entry[row]), FALSE);
-
- gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
- gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
- row++;
- }
-
- vbox = gtk_bin_get_child(GTK_BIN(dialog));
- gtk_container_add(GTK_CONTAINER(vbox), box);
-
- gtk_widget_show_all(dialog);
- response = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_hide(GTK_WIDGET(dialog));
-
- if (response == GTK_RESPONSE_OK) {
- for (i = 0, row = 0 ; i < credList->n_values ; i++) {
- GValue *cred = g_value_array_get_nth(credList, i);
- switch (g_value_get_enum(cred)) {
- case VNC_DISPLAY_CREDENTIAL_USERNAME:
- case VNC_DISPLAY_CREDENTIAL_PASSWORD:
- data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row]));
- break;
- }
- }
- }
- }
-
- for (i = 0 ; i < credList->n_values ; i++) {
- GValue *cred = g_value_array_get_nth(credList, i);
- if (data[i]) {
- if (vnc_display_set_credential(VNC_DISPLAY(vnc),
- g_value_get_enum(cred),
- data[i])) {
- DEBUG("Failed to set credential type %d",
- g_value_get_enum(cred));
- vnc_display_close(VNC_DISPLAY(vnc));
- }
- } else {
- DEBUG("Unsupported credential type %d",
- g_value_get_enum(cred));
- vnc_display_close(VNC_DISPLAY(vnc));
- }
- }
-
- g_free(data);
- if (dialog)
- gtk_widget_destroy(GTK_WIDGET(dialog));
-}
-
-#if defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS)
-
-static int
-viewer_open_vnc_socket(const char* vnchost, int vncport)
-{
- int result, socketfd;
- char port[10];
- struct addrinfo* vnc_addr;
-
- sprintf(port, "%d", vncport);
-
- result = getaddrinfo(vnchost, port, NULL, &vnc_addr);
- if(result != 0 || vnc_addr == NULL)
- return -1;
-
- // just use first found, ignoring rest
- socketfd = socket(vnc_addr->ai_family,
- vnc_addr->ai_socktype,
- vnc_addr->ai_protocol);
-
- if(connect(socketfd, vnc_addr->ai_addr, vnc_addr->ai_addrlen) <0)
- socketfd = -1;
-
- freeaddrinfo(vnc_addr);
-
- return socketfd;
-}
-
-#endif /* defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) */
-
-/* Remove all menu items from the Connect menu. */
-static void
-remove_menu_item (GtkWidget *menu_item, gpointer data)
-{
- gtk_container_remove (GTK_CONTAINER (connectmenu), menu_item);
-}
-
-static void
-clear_connectmenu (void)
-{
- DEBUG ("clear Connect menu");
- gtk_container_foreach (GTK_CONTAINER (connectmenu), remove_menu_item, NULL);
-}
-
-/* The WUI thread has changed its state to connected. */
-gboolean
-main_connected (gpointer data)
-{
- DEBUG ("connected");
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_label_set_text (GTK_LABEL (ca_error), NULL);
-
- gtk_widget_hide (connection_area);
- if (!wui_thread_is_logged_in ())
- gtk_widget_show (login_area);
- return FALSE;
-}
-
-/* The WUI thread has changed its state to disconnected. */
-gboolean
-main_disconnected (gpointer data)
-{
- DEBUG ("disconnected");
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_widget_show (connection_area);
- gtk_widget_hide (login_area);
-
- clear_connectmenu ();
- gtk_menu_append (GTK_MENU (connectmenu), no_connections);
- gtk_widget_show_all (connectmenu);
-
- return FALSE;
-}
-
-/* The WUI thread has changed its state to logged in. */
-gboolean
-main_logged_in (gpointer data)
-{
- DEBUG ("logged in");
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_widget_hide (login_area);
- return FALSE;
-}
-
-/* The WUI thread has changed its state to logged out. */
-gboolean
-main_logged_out (gpointer data)
-{
- DEBUG ("logged out");
- ASSERT_IS_MAIN_THREAD ();
-
- if (wui_thread_is_connected ())
- gtk_widget_show (login_area);
- return FALSE;
-}
-
-/* The WUI thread has changed its state to busy. */
-gboolean
-main_busy (gpointer data)
-{
- GdkWindow *gdk_window;
-
- DEBUG ("busy");
- ASSERT_IS_MAIN_THREAD ();
-
- gdk_window = gtk_widget_get_window (window);
- if (gdk_window) {
- gdk_window_set_cursor (gdk_window, busy_cursor);
- gdk_flush ();
- }
-
- return FALSE;
-}
-
-/* The WUI thread has changed its state to idle. */
-gboolean
-main_idle (gpointer data)
-{
- GdkWindow *gdk_window;
-
- DEBUG ("idle");
- ASSERT_IS_MAIN_THREAD ();
-
- gdk_window = gtk_widget_get_window (window);
- if (gdk_window) {
- gdk_window_set_cursor (gdk_window, NULL);
- gdk_flush ();
- }
-
- return FALSE;
-}
-
-/* The WUI thread had a connection error. This function must
- * free the string.
- */
-gboolean
-main_connection_error (gpointer _str)
-{
- char *str = (char *) _str;
-
- DEBUG ("connection error: %s", str);
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_label_set_text (GTK_LABEL (ca_error), str);
- g_free (str);
-
- return FALSE;
-}
-
-/* The WUI thread had a login error. This function must
- * free the string.
- */
-gboolean
-main_login_error (gpointer _str)
-{
- char *str = (char *) _str;
-
- DEBUG ("login error: %s", str);
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_label_set_text (GTK_LABEL (la_error), str);
- g_free (str);
-
- return FALSE;
-}
-
-/* The WUI thread reports a general status error. This function
- * must free the string.
- */
-gboolean
-main_status_error (gpointer _str)
-{
- char *str = (char *) _str;
-
- DEBUG ("status error: %s", str);
- ASSERT_IS_MAIN_THREAD ();
-
- gtk_statusbar_pop (GTK_STATUSBAR (statusbar), statusbar_ctx);
- gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, str);
- g_free (str);
-
- return FALSE;
-}
-
-gboolean
-main_vmlist_updated (gpointer data)
-{
- GSList *new_vmlist;
-
- DEBUG ("vmlist updated");
- ASSERT_IS_MAIN_THREAD ();
-
- /* Get the new vmlist. */
- if (wui_thread_get_vmlist (&new_vmlist)) {
- /* Free the previous vmlist. This invalidates all the vm pointers
- * contained in the Connect menu callbacks, but we're going to
- * delete those callbacks and create news ones in a moment anyway ...
- */
- free_vmlist (vmlist);
-
- vmlist = new_vmlist;
-
- clear_connectmenu ();
-
- gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist);
-
- if (vmlist != NULL) {
- gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator);
- g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL);
- }
-
- /* Grrrr Gtk is stupid. */
- gtk_widget_show_all (connectmenu);
- }
-
- return FALSE;
-}
-
-static void
-add_vm_to_connectmenu (gpointer _vm, gpointer data)
-{
- struct vm *vm = (struct vm *) _vm;
- GtkWidget *item;
-
- // TODO only present running vms ?
- // if(vm->state == "running")
-
- DEBUG ("adding %s to Connect menu", vm->description);
-
- item = gtk_menu_item_new_with_label (vm->description);
- gtk_menu_append (GTK_MENU (connectmenu), item);
-
- g_signal_connect (G_OBJECT (item), "activate",
- G_CALLBACK (connect_to_vm), vm);
-}
diff --git a/ovirt-viewer.spec b/ovirt-viewer.spec
index a4d826e..1db5994 100644
--- a/ovirt-viewer.spec
+++ b/ovirt-viewer.spec
@@ -51,7 +51,7 @@ make %{?_smp_mflags}
%install
rm -rf $RPM_BUILD_ROOT
-%{__install} -Dp -m0755 %{pbuild}/ovirt-viewer %{buildroot}%{_bindir}/ovirt-viewer
+%{__install} -Dp -m0755 %{pbuild}/src/ovirt-viewer %{buildroot}%{_bindir}/ovirt-viewer
%clean
rm -rf $RPM_BUILD_ROOT
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..cdd8f40
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,5 @@
+bin_PROGRAMS = ovirt-viewer
+
+ovirt_viewer_SOURCES = main.c wui_thread.c tunnel.c internal.h
+ovirt_viewer_CFLAGS = $(OVIRT_VIEWER_CFLAGS) $(CFLAGS) -DCAINFO='"$(CAINFO)"'
+ovirt_viewer_LDADD = $(OVIRT_VIEWER_LIBS)
diff --git a/src/internal.h b/src/internal.h
new file mode 100644
index 0000000..bd65922
--- /dev/null
+++ b/src/internal.h
@@ -0,0 +1,220 @@
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Richard W.M. Jones <rjones at redhat.com>
+ *
+ * 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.
+ */
+
+#ifndef OVIRT_VIEWER_INTERNAL_H
+#define OVIRT_VIEWER_INTERNAL_H
+
+#ifndef G_THREADS_ENABLED
+#error "This program requires GLib threads, and cannot be compiled without."
+#endif
+
+/* Debugging messages are always compiled in, but have to
+ * be turned on using the --debug command line switch.
+ */
+extern gboolean debug;
+
+#define DEBUG(fs,...) \
+ do { \
+ if (debug) { \
+ fprintf (stderr, "%s:%d: [thread %p] ", __FILE__, __LINE__, \
+ g_thread_self ()); \
+ fprintf (stderr, (fs), ## __VA_ARGS__); \
+ fprintf (stderr, "\n"); \
+ } \
+ } while (0)
+
+/* Verbose messages are always compiled in, but have to
+ * be turned on using the --verbose command line switch.
+ */
+extern gboolean verbose;
+
+#define VERBOSE(fs,...) \
+ do { \
+ if (verbose) { \
+ fprintf (stderr, "%s:%d: [thread %p] ", __FILE__, __LINE__, \
+ g_thread_self ()); \
+ fprintf (stderr, (fs), ## __VA_ARGS__); \
+ fprintf (stderr, "\n"); \
+ } \
+ } while (0)
+
+/* String equality tests, suggested by Jim Meyering. */
+#define STREQ(a,b) (strcmp((a),(b)) == 0)
+#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0)
+#define STRNEQ(a,b) (strcmp((a),(b)) != 0)
+#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0)
+#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0)
+#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0)
+#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0)
+#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0)
+#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0)
+
+extern const char *cainfo;
+extern gboolean check_cert;
+
+/* server we're connecting to */
+extern const char* hostname;
+
+/* port which to connect to the server via vnc */
+extern int ovirt_server_vnc_port;
+
+/* vm currently in focus */
+extern struct vm* vm_in_focus;
+
+/* Communications between the main thread and the WUI thread. For
+ * an explanation of the threading model, please see the comment in
+ * main().
+ */
+
+extern void start_wui_thread (void);
+extern void stop_wui_thread (void);
+
+extern void assert_is_main_thread (const char *, int);
+extern void assert_is_wui_thread (const char *, int);
+
+#define ASSERT_IS_MAIN_THREAD() assert_is_main_thread(__FILE__,__LINE__)
+#define ASSERT_IS_WUI_THREAD() assert_is_wui_thread(__FILE__,__LINE__)
+
+/* These are messages (instructions) which can be sent from the main
+ * thread to the WUI thread.
+ */
+
+/* Start connecting to WUI, and set the base URI. */
+extern void wui_thread_send_connect (const char *uri);
+
+/* Disconnect, forget URI, credentials, VMs etc. */
+extern void wui_thread_send_disconnect (void);
+
+/* Set the username and password and tell the WUI to try to log in. */
+extern void wui_thread_send_login (const char *username, const char *password);
+
+/* Tell the WUI thread to refresh the VM list. Note that the WUI
+ * thread does this automatically anyway after a successful login, and
+ * it also periodically updates the list. This call just tells it to
+ * do so right away.
+ */
+extern void wui_thread_send_refresh_vm_list (void);
+
+/* Retrieve the list of VMs.
+ *
+ * wui_thread_get_vmlist returns TRUE if there was a valid list of
+ * VMs (even if it is empty), or FALSE if we don't have a valid list
+ * of VMs to return.
+ *
+ * NB: Caller must call free_vmlist once it is done with the list.
+ *
+ */
+extern gboolean wui_thread_get_vmlist (GSList **ret);
+extern void free_vmlist (GSList *vmlist);
+
+struct vm {
+ char *description;
+ int hostid;
+ int id;
+ int vnc_port;
+ int forward_vnc_port;
+ char *uuid; /* Printable UUID. */
+ char *state;
+
+ /* Only the fields above this point are required. The remainder may
+ * be NULL / -1 to indicate they were missing in the data we got
+ * back from the WUI.
+ */
+
+ long mem_allocated; /* Kbytes */
+ long mem_used; /* Kbytes */
+ int vcpus_allocated;
+ int vcpus_used;
+ char *mac_addr; /* Printable MAC addr. */
+};
+
+/* Returns true if the WUI thread thinks it is connected to a remote
+ * WUI. REST is connectionless so really this means that we
+ * successfully made an HTTP/HTTPS request "recently", and we haven't
+ * seen any errors above a certain threshold.
+ */
+extern gboolean wui_thread_is_connected (void);
+
+/* Returns true if we successfully logged in with the username
+ * and password supplied in a recent request, and we haven't
+ * received any authorization failures since.
+ */
+extern gboolean wui_thread_is_logged_in (void);
+
+/* Returns true if we have a valid list of VMs. Note that because
+ * of race conditions, this doesn't guarantee that wui_thread_get_vmlist
+ * will work.
+ */
+extern gboolean wui_thread_has_valid_vmlist (void);
+
+/* Returns true if the WUI thread is busy performing a request
+ * at the moment.
+ */
+extern gboolean wui_thread_is_busy (void);
+
+
+/* Communications between the main thread and the tunnel thread.*/
+extern void start_tunnel (void);
+extern void stop_tunnel (void);
+
+/* port which local tunnel is listening on */
+extern int tunnel_port;
+
+
+/* Returns true if the main vm list contains a
+ * running vm w/ the same name as specified one
+ */
+extern gboolean main_vmlist_has_running_vm(struct vm*);
+
+/* Callbacks from the WUI thread to the main thread. The WUI thread
+ * adds these to the Glib main loop using g_idle_add, which means they
+ * actually get executed in the context of the main thread.
+ */
+
+/* The WUI thread has changed its state to connected. */
+extern gboolean main_connected (gpointer);
+
+/* The WUI thread has changed its state to disconnected. */
+extern gboolean main_disconnected (gpointer);
+
+/* The WUI thread has changed its state to logged in. */
+extern gboolean main_logged_in (gpointer);
+
+/* The WUI thread has changed its state to logged out. */
+extern gboolean main_logged_out (gpointer);
+
+/* The WUI thread has changed its state to busy. */
+extern gboolean main_busy (gpointer);
+
+/* The WUI thread has changed its state to idle. */
+extern gboolean main_idle (gpointer);
+
+/* The WUI thread had a connection problem. */
+extern gboolean main_connection_error (gpointer str);
+
+/* The WUI thread had a login problem. */
+extern gboolean main_login_error (gpointer str);
+
+/* The WUI thread reports a general status error. */
+extern gboolean main_status_error (gpointer str);
+
+/* The WUI thread has updated the vm list. */
+extern gboolean main_vmlist_updated (gpointer);
+
+#endif /* OVIRT_VIEWER_INTERNAL_H */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..dd26c93
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,1148 @@
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Richard W.M. Jones <rjones at redhat.com>
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <vncdisplay.h>
+
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#include <arpa/inet.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+#endif
+
+#include "internal.h"
+
+/*#define HTTPS "https"*/
+#define HTTPS "http"
+
+gboolean debug = 0;
+
+gboolean verbose = 0;
+
+/* Usually /etc/pki/tls/certs/ca-bundle.crt unless overridden during
+ * configure or on the command line.
+ */
+const char *cainfo = CAINFO;
+gboolean check_cert = FALSE; // do we want this enabled by default ?
+ // would require a CA by default (self-signed wont work)
+ // (don't set to true, change var/flag to no_check_cert)
+
+/* The WUI thread has updated the vm list. Here in the main thread
+* we keep our own copy of the vmlist.
+*/
+static GSList *vmlist = NULL;
+
+/* internal.h shared constructs */
+const char* hostname;
+struct vm* vm_in_focus;
+int ovirt_server_vnc_port = 5900;
+
+/* Private functions. */
+static void start_ui (void);
+static GtkWidget *menu_item_new (int which_menu);
+static void refresh_menu_vm_list (GtkWidget *, gpointer);
+static void connect_to_wui_on_enter (GtkWidget *, gpointer);
+static void connect_to_wui (GtkWidget *, gpointer);
+static void send_key_to_vm (GtkWidget *widget, gpointer _keyComboDef);
+static void login_to_wui_on_enter (GtkWidget *, gpointer);
+static void login_to_wui (GtkWidget *, gpointer);
+static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
+static void destroy (GtkWidget *widget, gpointer data);
+static void clear_connectmenu (void);
+static void help_about (GtkWidget *menu);
+static void viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc);
+#if 0
+static void viewer_quit (GtkWidget *src, GtkWidget *vnc);
+#endif
+static void viewer_connected (GtkWidget *vnc);
+static void viewer_initialized (GtkWidget *vnc, GtkWidget *data);
+static void viewer_disconnected (GtkWidget *vnc);
+static void viewer_credential (GtkWidget *vnc, GValueArray *credList);
+static int viewer_open_vnc_socket (const char *vnchost, int vncport);
+static void add_vm_to_connectmenu (gpointer _vm, gpointer data);
+
+/* For any widgets accessed from multiple functions. */
+static GtkWidget *window;
+static GtkWidget *connectitem;
+static GtkWidget *connectmenu;
+static GtkWidget *no_connections;
+static GtkWidget *refresh_vmlist;
+static GtkWidget *refresh_vmlist_separator;
+static GtkWidget *connection_area;
+static GtkWidget *ca_hostname;
+static GtkWidget *ca_button;
+static GtkWidget *ca_error;
+static GtkWidget *login_area;
+static GtkWidget *la_username;
+static GtkWidget *la_password;
+static GtkWidget *la_button;
+static GtkWidget *la_error;
+static GtkWidget *notebook;
+static GtkWidget *statusbar;
+static guint statusbar_ctx;
+static GdkCursor *busy_cursor;
+
+/* Menus. */
+enum menuNums {
+ CONNECT_MENU,
+ VIEW_MENU,
+ SEND_KEY_MENU,
+ WINDOW_MENU,
+ HELP_MENU,
+};
+
+struct menuItem {
+ guint menu;
+ GtkWidget *label;
+ const char *ungrabbed_text;
+ const char *grabbed_text;
+};
+
+static struct menuItem menuItems[] = {
+ { CONNECT_MENU, NULL, "_Connect", "Connect" },
+ { VIEW_MENU, NULL, "_View", "View" },
+ { SEND_KEY_MENU, NULL, "_Send Key", "Send Key" },
+ { WINDOW_MENU, NULL, "_Window", "Window" },
+ { HELP_MENU, NULL, "_Help", "Help" }
+};
+
+#define MAX_KEY_COMBO 3
+ struct keyComboDef {
+ guint keys[MAX_KEY_COMBO];
+ guint nkeys;
+ const char *label;
+ };
+
+#define NUM_KEY_COMBOS 17
+static struct keyComboDef keyCombos[] = {
+ { { GDK_Control_L, GDK_Alt_L, GDK_Delete }, 3, "Ctrl+Alt+Del"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_BackSpace }, 3, "Ctrl+Alt+Backspace"},
+ { {}, 0, "" },
+ { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F1"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F2"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F3"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F4"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F5"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F6"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F7"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F8"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F9"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F10"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F11"},
+ { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F12"},
+ { {}, 0, "" },
+ { { GDK_Print }, 1, "PrintScreen"},
+};
+
+/* Window title. */
+static const char *title = "oVirt Viewer";
+
+// when running vm
+// 47 chars
+static const char *title_vm =
+ "oVirt Viewer: (ctrl+alt to grab/release mouse) ";
+
+/* Gtk widget styles. Avoid installation hassles by keeping this
+ * inside the binary. It can still be overridden by the user (who
+ * will do that?)
+ */
+static const char *styles =
+ "style \"ovirt-viewer-yellow-box\"\n"
+ "{\n"
+ " bg[NORMAL] = shade (1.5, \"yellow\")\n"
+ "}\n"
+ "widget \"*.ovirt-viewer-connection-area\" style \"ovirt-viewer-yellow-box\"\n"
+ ;
+
+/* Command-line arguments. */
+static int print_version = 0;
+
+static const char *help_msg =
+ "Use '" PACKAGE " --help' to see a list of available command line options";
+
+static const GOptionEntry options[] = {
+ { "port", 'p', 0, G_OPTION_ARG_INT, &ovirt_server_vnc_port,
+ "set port which to connect to server via vnc", NULL },
+ { "cainfo", 0, 0, G_OPTION_ARG_STRING, &cainfo,
+ "set the path of the CA certificate bundle", NULL },
+ { "check-certificate", 0, 0, G_OPTION_ARG_NONE, &check_cert,
+ "check the SSL certificate of the server", NULL },
+ { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
+ "turn on debugging messages", NULL },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ "turn on verbose messages", NULL },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
+ "display version and exit", NULL },
+ { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
+};
+
+int
+main (int argc, char *argv[])
+{
+ GOptionContext *context;
+ GError *error = NULL;
+
+ /* Initialize GLib threads before anything else.
+ *
+ * There is one main thread, which is used for all Gtk interactions
+ * and to keep the UI responsive, and one WUI thread. The WUI
+ * thread is used to connect to the WUI, log in, and maintain the list
+ * of virtual machines. The WUI thread is the only thread allowed
+ * to use the CURL library.
+ *
+ * The main thread sends instructions to the WUI thread (like "connect",
+ * "disconnect", etc.) using a simple message-passing protocol and
+ * a GAsyncQueue.
+ *
+ * The WUI thread keeps the UI updated by adding idle events which are
+ * processed in the main thread - see:
+ * http://mail.gnome.org/archives/gtk-app-devel-list/2007-March/msg00232.html
+ *
+ * A tunnel thread is also started to locally listen for vnc packets
+ * and make them proxyable, adding the vm name, before forwarding onto
+ * the server
+ *
+ * Note that under Win32 you must confine all Gtk/Gdk interactions
+ * to a single thread - see:
+ * http://developer.gimp.org/api/2.0/gdk/gdk-Threads.html
+ */
+ if (!g_thread_supported ()) {
+ g_thread_init (NULL);
+#ifndef WIN32
+ gdk_threads_init ();
+#endif
+ } else {
+ fprintf (stderr, "GLib threads not supported or not working.");
+ exit (1);
+ }
+
+ gtk_init (&argc, &argv);
+
+ /* Parse command-line options. */
+ context = g_option_context_new ("oVirt viewer");
+ g_option_context_add_main_entries (context, options, NULL);
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_group (context, vnc_display_get_option_group ());
+ g_option_context_parse (context, &argc, &argv, &error);
+
+ if (error) {
+ g_print ("%s\n%s\n", error->message, help_msg);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (print_version) {
+ printf ("%s %s\n", PACKAGE, VERSION);
+ exit (0);
+ }
+
+ start_wui_thread ();
+ start_ui ();
+
+ DEBUG ("entering the Gtk main loop");
+
+ gtk_main ();
+
+ stop_wui_thread ();
+ stop_tunnel();
+
+ exit (0);
+}
+
+/* Create the viewer window, menus. */
+static void
+start_ui (void)
+{
+ int i;
+ GtkWidget *vbox;
+ GtkWidget *menubar;
+ GtkWidget *view;
+ GtkWidget *viewmenu;
+ GtkWidget *sendkey;
+ GtkWidget *sendkeymenu;
+ GtkWidget *sendkeymenuitem;
+ GtkWidget *wind;
+ GtkWidget *windmenu;
+ GtkWidget *help;
+ GtkWidget *helpmenu;
+ GtkWidget *about;
+ GtkWidget *ca_vbox;
+ GtkWidget *ca_hbox;
+ GtkWidget *ca_label;
+ GtkWidget *la_vbox;
+ GtkWidget *la_hbox;
+ GtkWidget *la_label;
+
+ DEBUG ("creating viewer windows and menus");
+
+ /* Parse styles. */
+ gtk_rc_parse_string (styles);
+
+ /* Busy cursor, used by main_busy() function.
+ * XXX This cursor is crap - how can we use the Bluecurve/theme cursor?
+ */
+ busy_cursor = gdk_cursor_new (GDK_WATCH);
+
+ /* Window. */
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 800, 600);
+ gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
+ gtk_window_set_title (GTK_WINDOW (window), title);
+
+ g_signal_connect (G_OBJECT (window), "delete_event",
+ G_CALLBACK (delete_event), NULL);
+ g_signal_connect (G_OBJECT (window), "destroy",
+ G_CALLBACK (destroy), NULL);
+
+ /* VBox for layout within the window. */
+ vbox = gtk_vbox_new (FALSE, 0);
+
+ /* Menubar. */
+ menubar = gtk_menu_bar_new ();
+ connectitem = menu_item_new (CONNECT_MENU);
+ connectmenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (connectitem), connectmenu);
+
+ no_connections = gtk_menu_item_new_with_label ("Not connected");
+ gtk_menu_append (GTK_MENU (connectmenu), no_connections);
+ gtk_widget_set_sensitive (no_connections, FALSE);
+
+ /* This is not added to the menu yet, but will be when we are
+ * connected.
+ */
+ refresh_vmlist =
+ gtk_menu_item_new_with_label ("Refresh list of virtual machines");
+ g_object_ref (refresh_vmlist);
+ refresh_vmlist_separator = gtk_separator_menu_item_new ();
+ g_object_ref (refresh_vmlist_separator);
+
+ g_signal_connect (G_OBJECT (refresh_vmlist), "activate",
+ G_CALLBACK (refresh_menu_vm_list), NULL);
+
+#if 0
+ screenshot = gtk_menu_item_new_with_mnemonic ("_Screenshot");
+ gtk_menu_append (GTK_MENU (filemenu), screenshot);
+ g_signal_connect (screenshot, "activate",
+ GTK_SIGNAL_FUNC (take_screenshot), NULL);
+#endif
+
+ view = menu_item_new (VIEW_MENU);
+ viewmenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (view), viewmenu);
+
+ sendkey = menu_item_new (SEND_KEY_MENU);
+ sendkeymenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (sendkey), sendkeymenu);
+
+ for(i = 0; i < NUM_KEY_COMBOS; ++i){
+ sendkeymenuitem = gtk_menu_item_new_with_label (keyCombos[i].label);
+ gtk_menu_append (GTK_MENU (sendkeymenu), sendkeymenuitem);
+ g_signal_connect (G_OBJECT (sendkeymenuitem), "activate",
+ G_CALLBACK (send_key_to_vm), &(keyCombos[i]));
+ }
+
+ wind = menu_item_new (WINDOW_MENU);
+ windmenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (wind), windmenu);
+
+ help = menu_item_new (HELP_MENU);
+ helpmenu = gtk_menu_new ();
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (help), helpmenu);
+
+ about = gtk_image_menu_item_new_from_stock(GTK_STOCK_ABOUT, NULL);
+ gtk_menu_append(GTK_MENU(helpmenu), about);
+ g_signal_connect(about, "activate", GTK_SIGNAL_FUNC (help_about), NULL);
+
+ gtk_menu_bar_append (GTK_MENU_BAR (menubar), connectitem);
+ gtk_menu_bar_append (GTK_MENU_BAR (menubar), view);
+ gtk_menu_bar_append (GTK_MENU_BAR (menubar), sendkey);
+ gtk_menu_bar_append (GTK_MENU_BAR (menubar), wind);
+ gtk_menu_bar_append (GTK_MENU_BAR (menubar), help);
+
+ /* For login dialogs, etc., usually invisible. */
+ connection_area = gtk_event_box_new ();
+ ca_vbox = gtk_vbox_new (FALSE, 0);
+ ca_label = gtk_label_new ("Give the name of the oVirt management server:");
+ ca_hbox = gtk_hbox_new (FALSE, 0);
+ ca_hostname = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (ca_hostname), 24);
+ ca_button = gtk_button_new_with_label ("Connect");
+ ca_error = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (ca_hbox), ca_hostname, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (ca_hbox), ca_button, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (ca_vbox), ca_label, FALSE, FALSE, 4);
+ gtk_box_pack_start (GTK_BOX (ca_vbox), ca_hbox, TRUE, FALSE, 4);
+ gtk_box_pack_start (GTK_BOX (ca_vbox), ca_error, TRUE, FALSE, 4);
+ gtk_container_add (GTK_CONTAINER (connection_area), ca_vbox);
+
+ gtk_widget_set_name (connection_area, "ovirt-viewer-connection-area");
+
+ g_signal_connect (G_OBJECT (ca_hostname), "key-release-event",
+ G_CALLBACK (connect_to_wui_on_enter), NULL);
+ g_signal_connect (G_OBJECT (ca_button), "clicked",
+ G_CALLBACK (connect_to_wui), NULL);
+
+ login_area = gtk_event_box_new ();
+ la_vbox = gtk_vbox_new (FALSE, 0);
+ la_hbox = gtk_hbox_new (FALSE, 0);
+ la_label = gtk_label_new ("Username / password:");
+ la_username = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (la_username), 12);
+ la_password = gtk_entry_new ();
+ gtk_entry_set_width_chars (GTK_ENTRY (la_password), 12);
+ gtk_entry_set_visibility (GTK_ENTRY (la_password), FALSE);
+ la_button = gtk_button_new_with_label ("Login");
+ la_error = gtk_label_new (NULL);
+ gtk_container_add (GTK_CONTAINER (la_hbox), la_username);
+ gtk_container_add (GTK_CONTAINER (la_hbox), la_password);
+ gtk_container_add (GTK_CONTAINER (la_hbox), la_button);
+ gtk_box_pack_start (GTK_BOX (la_vbox), la_label, TRUE, FALSE, 4);
+ gtk_box_pack_start (GTK_BOX (la_vbox), la_hbox, TRUE, FALSE, 4);
+ gtk_box_pack_start (GTK_BOX (la_vbox), la_error, TRUE, FALSE, 4);
+ gtk_container_add (GTK_CONTAINER (login_area), la_vbox);
+
+ gtk_widget_set_name (login_area, "ovirt-viewer-login-area");
+
+ g_signal_connect (G_OBJECT (la_username), "key-release-event",
+ G_CALLBACK (login_to_wui_on_enter), NULL);
+ g_signal_connect (G_OBJECT (la_password), "key-release-event",
+ G_CALLBACK (login_to_wui_on_enter), NULL);
+ g_signal_connect (G_OBJECT (la_button), "clicked",
+ G_CALLBACK (login_to_wui), NULL);
+
+ /* Tabbed notebook. */
+ notebook = gtk_notebook_new ();
+ /*gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_LEFT);*/
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), TRUE);
+ gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
+
+ /* Status bar. */
+ statusbar = gtk_statusbar_new ();
+ statusbar_ctx = gtk_statusbar_get_context_id (GTK_STATUSBAR (statusbar),
+ "context");
+ gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, "");
+
+ /* Packing. */
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), menubar,
+ "expand", FALSE, NULL);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), connection_area,
+ "expand", FALSE, "fill", TRUE, NULL);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), login_area,
+ "expand", FALSE, "fill", TRUE, NULL);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), notebook,
+ "expand", TRUE, NULL);
+ gtk_container_add_with_properties (GTK_CONTAINER (vbox), statusbar,
+ "expand", FALSE, NULL);
+
+ /* Show widgets. */
+ gtk_widget_show_all (window);
+
+ if (wui_thread_is_connected ())
+ gtk_widget_hide (connection_area);
+ if (!wui_thread_is_connected () || wui_thread_is_logged_in ())
+ gtk_widget_hide (login_area);
+}
+
+static GtkWidget *
+menu_item_new (int which_menu)
+{
+ GtkWidget *widget;
+ GtkWidget *label;
+ const char *text;
+
+ text = menuItems[which_menu].ungrabbed_text;
+
+ widget = gtk_menu_item_new ();
+ label = g_object_new (GTK_TYPE_ACCEL_LABEL, NULL);
+ gtk_label_set_text_with_mnemonic (GTK_LABEL (label), text);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
+
+ gtk_container_add (GTK_CONTAINER (widget), label);
+ gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (label), widget);
+ gtk_widget_show (label);
+
+ menuItems[which_menu].label = label;
+
+ return widget;
+}
+
+static gboolean
+delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
+{
+ DEBUG ("delete_event");
+ return FALSE;
+}
+
+static void
+destroy (GtkWidget *widget, gpointer data)
+{
+ DEBUG ("destroy");
+ gtk_main_quit ();
+}
+
+static void
+help_about (GtkWidget *menu)
+{
+ GtkWidget *about;
+ const char *authors[] = {
+ "Richard W.M. Jones <rjones at redhat.com>",
+ "Daniel P. Berrange <berrange at redhat.com>",
+ "Mohammed Morsi <mmorsi at redhat.com>",
+ NULL
+ };
+
+ about = gtk_about_dialog_new();
+
+ gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), "oVirt Viewer");
+ gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
+ gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://ovirt.org/");
+ gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(about), "oVirt website");
+ gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
+ gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about),
+ "This program is free software; you can redistribute it and/or modify\n" \
+ "it under the terms of the GNU General Public License as published by\n" \
+ "the Free Software Foundation; either version 2 of the License, or\n" \
+ "(at your option) any later version.\n" \
+ "\n" \
+ "This program is distributed in the hope that it will be useful,\n" \
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" \
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" \
+ "GNU General Public License for more details.\n" \
+ "\n" \
+ "You should have received a copy of the GNU General Public License\n" \
+ "along with this program; if not, write to the Free Software\n" \
+ "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n");
+
+ gtk_dialog_run(GTK_DIALOG(about));
+ gtk_widget_destroy(about);
+}
+
+static void
+refresh_menu_vm_list(GtkWidget *widget, gpointer data)
+{
+ wui_thread_send_refresh_vm_list();
+}
+
+static void
+connect_to_wui_on_enter (GtkWidget *widget, gpointer data)
+{
+ // if key released was not 'enter' key
+ if(((GdkEventKey *)data)->type == GDK_KEY_RELEASE && (((GdkEventKey *)data)->keyval & 0xFF) != 13 ) return;
+
+ connect_to_wui(widget, data);
+}
+
+static void
+connect_to_wui (GtkWidget *widget, gpointer data)
+{
+ char *uri;
+ int len;
+
+ hostname = gtk_entry_get_text (GTK_ENTRY (ca_hostname));
+ if (STREQ (hostname, "")) return;
+
+ /* https:// + hostname + /ovirt + \0 */
+ len = 8 + strlen (hostname) + 6 + 1;
+ uri = g_alloca (len);
+ snprintf (uri, len, HTTPS "://%s/ovirt", hostname);
+
+ wui_thread_send_connect (uri);
+ start_tunnel();
+}
+
+static void
+login_to_wui_on_enter (GtkWidget *widget, gpointer data)
+{
+ // if key released was not 'enter' key
+ if(((GdkEventKey *)data)->type == GDK_KEY_RELEASE && (((GdkEventKey *)data)->keyval & 0xFF) != 13 ) return;
+
+ login_to_wui(widget, data);
+}
+
+static void
+login_to_wui (GtkWidget *widget, gpointer data)
+{
+ const char *username, *password;
+
+ username = gtk_entry_get_text (GTK_ENTRY (la_username));
+ if (STREQ (username, "")) return;
+ password = gtk_entry_get_text (GTK_ENTRY (la_password));
+
+ wui_thread_send_login (username, password);
+}
+
+/* Connect to a virtual machine. This callback is called from the
+ * connect menu. It searches the notebook of gtk-vnc widgets to see
+ * if we have already connected to this machine, and if not it
+ * makes a new connection.
+ */
+static void
+connect_to_vm (GtkWidget *widget, gpointer _vm)
+{
+ struct vm *vm = (struct vm *) _vm;
+ int n = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
+ int i, uuidlen, len, fd;
+ GtkWidget *child;
+ const char *label;
+ char *label2;
+ char new_title[97]; // 47 chars for title + 50 for vm name
+
+ DEBUG ("searching tabs for uuid %s", vm->uuid);
+
+ uuidlen = strlen (vm->uuid);
+
+ /* Search the tabs for this UUID, and if found, switch to it and return. */
+ for (i = 0; i < n; ++i) {
+ child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), i);
+ label = gtk_notebook_get_tab_label_text (GTK_NOTEBOOK (notebook), child);
+ len = strlen (label);
+ if (len >= uuidlen &&
+ STREQ (label + len - uuidlen, vm->uuid)) {
+ DEBUG ("found on tab %d", i);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
+ return;
+ }
+ }
+
+ DEBUG ("not found, creating new tab");
+
+ // before we open vnc connection, make sure vm is running
+ gboolean vm_running = main_vmlist_has_running_vm(vm);
+
+ if(!vm_running){
+ main_status_error (g_strdup ("VM not running"));
+ return;
+ }
+
+ // FIXME on notebook tab switch, change vm_in_focus
+ vm_in_focus = vm;
+
+ /* This VM isn't in the notebook already, so create a new console. */
+ DEBUG ("connecting to local tunnel on port %i", tunnel_port);
+ fd = viewer_open_vnc_socket("127.0.0.1", tunnel_port);
+ if (fd == -1) return; /* We've already given an error. */
+
+ child = vnc_display_new ();
+ if (! vnc_display_open_fd (VNC_DISPLAY (child), fd)) {
+ main_status_error (g_strdup ("internal error in Gtk-VNC widget"));
+ return;
+ }
+
+ main_status_error(g_strdup(""));
+
+ for(i = 0; i < 47; ++i) new_title[i] = title_vm[i];
+ for(i = 0; i < 50; ++i){
+ if(vm->description[i] == '\0') break;
+ new_title[47 + i] = vm->description[i];
+ }
+ new_title[i < 50 ? i+47 : 96] = '\0';
+ gtk_window_set_title (GTK_WINDOW (window), new_title);
+
+ /*
+ gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-grab",
+ GTK_SIGNAL_FUNC(viewer_grab), window);
+ gtk_signal_connect(GTK_OBJECT(child), "vnc-pointer-ungrab",
+ GTK_SIGNAL_FUNC(viewer_ungrab), window);
+ */
+
+ gtk_signal_connect(GTK_OBJECT(child), "delete-event",
+ GTK_SIGNAL_FUNC(viewer_shutdown), child);
+
+ gtk_signal_connect(GTK_OBJECT(child), "vnc-connected",
+ GTK_SIGNAL_FUNC(viewer_connected), NULL);
+ gtk_signal_connect(GTK_OBJECT(child), "vnc-initialized",
+ GTK_SIGNAL_FUNC(viewer_initialized), NULL);
+ gtk_signal_connect(GTK_OBJECT(child), "vnc-disconnected",
+ GTK_SIGNAL_FUNC(viewer_disconnected), NULL);
+
+ g_signal_connect(GTK_OBJECT(child), "vnc-auth-credential",
+ GTK_SIGNAL_FUNC(viewer_credential), NULL);
+
+ /* NB. We have to do this before adding it to the notebook. */
+ gtk_widget_show (child);
+
+ /* Choose a tab label, which MUST end with the uuid string, since
+ * we use the tab label to store uuid.
+ */
+ len = strlen (vm->description) + 1 + strlen (vm->uuid) + 1;
+ label2 = g_alloca (len);
+ snprintf (label2, len, "%s %s", vm->description, vm->uuid);
+
+ i = gtk_notebook_append_page (GTK_NOTEBOOK (notebook), child, NULL);
+ gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (notebook), child, label2);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), i);
+
+ DEBUG ("finished creating new tab");
+}
+
+/* Send key to a virtual machine. This callback is called from the
+ * send key menu. If finds vm in focus, and sends specified key to it
+ */
+static void
+send_key_to_vm (GtkWidget *widget, gpointer _keyComboDef)
+{
+ GtkWidget* viewer;
+ struct keyComboDef* key_combo = (struct keyComboDef*) _keyComboDef;
+ int c = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
+
+ if(c < 0){
+ return;
+ }
+
+ viewer = gtk_notebook_get_nth_page(GTK_NOTEBOOK (notebook), c);
+ if(viewer != NULL){
+ DEBUG ("sending keys to vm");
+ vnc_display_send_keys(VNC_DISPLAY(viewer),
+ key_combo->keys,
+ key_combo->nkeys);
+ }
+}
+
+
+/*
+static void viewer_grab(GtkWidget *vnc, GtkWidget *window)
+{
+ int i;
+
+ viewer_set_title(VNC_DISPLAY(vnc), window, TRUE);
+
+ for (i = 0 ; i < LAST_MENU; i++) {
+ gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].grabbed_text);
+ }
+}
+
+static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window)
+{
+ int i;
+
+ viewer_set_title(VNC_DISPLAY(vnc), window, FALSE);
+
+ for (i = 0 ; i < LAST_MENU; i++) {
+ gtk_label_set_text_with_mnemonic(GTK_LABEL(menuItems[i].label), menuItems[i].ungrabbed_text);
+ }
+}
+*/
+
+static void
+viewer_shutdown (GtkWidget *src, void *dummy, GtkWidget *vnc)
+{
+ vnc_display_close (VNC_DISPLAY(vnc));
+
+ /* Just close the notebook tab for now. XXX */
+ gtk_notebook_remove_page (GTK_NOTEBOOK (notebook),
+ gtk_notebook_page_num (GTK_NOTEBOOK (notebook),
+ vnc));
+}
+
+#if 0
+static void
+viewer_quit (GtkWidget *src, GtkWidget *vnc)
+{
+ viewer_shutdown (src, NULL, vnc);
+}
+#endif
+
+static void
+viewer_connected (GtkWidget *vnc)
+{
+ DEBUG ("Connected to server");
+}
+
+static void
+viewer_initialized (GtkWidget *vnc, GtkWidget *data)
+{
+ DEBUG ("Connection initialized");
+}
+
+static void
+viewer_disconnected (GtkWidget *vnc)
+{
+ DEBUG ("Disconnected from server");
+}
+
+static void
+viewer_credential (GtkWidget *vnc, GValueArray *credList)
+{
+ GtkWidget *dialog = NULL;
+ int response;
+ unsigned int i, prompt = 0;
+ const char **data;
+
+ DEBUG ("Got credential request for %d credential(s)",
+ credList->n_values);
+
+ data = g_new0(const char *, credList->n_values);
+
+ for (i = 0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_DISPLAY_CREDENTIAL_USERNAME:
+ case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+ prompt++;
+ break;
+ case VNC_DISPLAY_CREDENTIAL_CLIENTNAME:
+ data[i] = "libvirt";
+ default:
+ break;
+ }
+ }
+
+ if (prompt) {
+ GtkWidget **label, **entry, *box, *vbox;
+ int row;
+ dialog = gtk_dialog_new_with_buttons("Authentication required",
+ NULL,
+ 0,
+ GTK_STOCK_CANCEL,
+ GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK,
+ GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+ box = gtk_table_new(credList->n_values, 2, FALSE);
+ label = g_new(GtkWidget *, prompt);
+ entry = g_new(GtkWidget *, prompt);
+
+ for (i = 0, row =0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_DISPLAY_CREDENTIAL_USERNAME:
+ label[row] = gtk_label_new("Username:");
+ break;
+ case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+ label[row] = gtk_label_new("Password:");
+ break;
+ default:
+ continue;
+ }
+ entry[row] = gtk_entry_new();
+ if (g_value_get_enum(cred) == VNC_DISPLAY_CREDENTIAL_PASSWORD)
+ gtk_entry_set_visibility(GTK_ENTRY(entry[row]), FALSE);
+
+ gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
+ gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, row, row+1, GTK_SHRINK, GTK_SHRINK, 3, 3);
+ row++;
+ }
+
+ vbox = gtk_bin_get_child(GTK_BIN(dialog));
+ gtk_container_add(GTK_CONTAINER(vbox), box);
+
+ gtk_widget_show_all(dialog);
+ response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_hide(GTK_WIDGET(dialog));
+
+ if (response == GTK_RESPONSE_OK) {
+ for (i = 0, row = 0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ switch (g_value_get_enum(cred)) {
+ case VNC_DISPLAY_CREDENTIAL_USERNAME:
+ case VNC_DISPLAY_CREDENTIAL_PASSWORD:
+ data[i] = gtk_entry_get_text(GTK_ENTRY(entry[row]));
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0 ; i < credList->n_values ; i++) {
+ GValue *cred = g_value_array_get_nth(credList, i);
+ if (data[i]) {
+ if (vnc_display_set_credential(VNC_DISPLAY(vnc),
+ g_value_get_enum(cred),
+ data[i])) {
+ DEBUG("Failed to set credential type %d",
+ g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(vnc));
+ }
+ } else {
+ DEBUG("Unsupported credential type %d",
+ g_value_get_enum(cred));
+ vnc_display_close(VNC_DISPLAY(vnc));
+ }
+ }
+
+ g_free(data);
+ if (dialog)
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+}
+
+#if defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS)
+
+static int
+viewer_open_vnc_socket(const char* vnchost, int vncport)
+{
+ int result, socketfd;
+ char port[10];
+ struct addrinfo* vnc_addr;
+
+ sprintf(port, "%d", vncport);
+
+ result = getaddrinfo(vnchost, port, NULL, &vnc_addr);
+ if(result != 0 || vnc_addr == NULL)
+ return -1;
+
+ // just use first found, ignoring rest
+ socketfd = socket(vnc_addr->ai_family,
+ vnc_addr->ai_socktype,
+ vnc_addr->ai_protocol);
+
+ if(connect(socketfd, vnc_addr->ai_addr, vnc_addr->ai_addrlen) <0)
+ socketfd = -1;
+
+ freeaddrinfo(vnc_addr);
+
+ return socketfd;
+}
+
+#endif /* defined(HAVE_SOCKET) && defined(HAVE_CONNECT) && defined(HAVE_HTONS) */
+
+/* Remove all menu items from the Connect menu. */
+static void
+remove_menu_item (GtkWidget *menu_item, gpointer data)
+{
+ gtk_container_remove (GTK_CONTAINER (connectmenu), menu_item);
+}
+
+static void
+clear_connectmenu (void)
+{
+ DEBUG ("clear Connect menu");
+ gtk_container_foreach (GTK_CONTAINER (connectmenu), remove_menu_item, NULL);
+}
+
+/* The WUI thread has changed its state to connected. */
+gboolean
+main_connected (gpointer data)
+{
+ DEBUG ("connected");
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_label_set_text (GTK_LABEL (ca_error), NULL);
+
+ gtk_widget_hide (connection_area);
+ if (!wui_thread_is_logged_in ())
+ gtk_widget_show (login_area);
+ return FALSE;
+}
+
+/* The WUI thread has changed its state to disconnected. */
+gboolean
+main_disconnected (gpointer data)
+{
+ DEBUG ("disconnected");
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_widget_show (connection_area);
+ gtk_widget_hide (login_area);
+
+ clear_connectmenu ();
+ gtk_menu_append (GTK_MENU (connectmenu), no_connections);
+ gtk_widget_show_all (connectmenu);
+
+ return FALSE;
+}
+
+/* The WUI thread has changed its state to logged in. */
+gboolean
+main_logged_in (gpointer data)
+{
+ DEBUG ("logged in");
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_widget_hide (login_area);
+ return FALSE;
+}
+
+/* The WUI thread has changed its state to logged out. */
+gboolean
+main_logged_out (gpointer data)
+{
+ DEBUG ("logged out");
+ ASSERT_IS_MAIN_THREAD ();
+
+ if (wui_thread_is_connected ())
+ gtk_widget_show (login_area);
+ return FALSE;
+}
+
+/* The WUI thread has changed its state to busy. */
+gboolean
+main_busy (gpointer data)
+{
+ GdkWindow *gdk_window;
+
+ DEBUG ("busy");
+ ASSERT_IS_MAIN_THREAD ();
+
+ gdk_window = gtk_widget_get_window (window);
+ if (gdk_window) {
+ gdk_window_set_cursor (gdk_window, busy_cursor);
+ gdk_flush ();
+ }
+
+ return FALSE;
+}
+
+/* The WUI thread has changed its state to idle. */
+gboolean
+main_idle (gpointer data)
+{
+ GdkWindow *gdk_window;
+
+ DEBUG ("idle");
+ ASSERT_IS_MAIN_THREAD ();
+
+ gdk_window = gtk_widget_get_window (window);
+ if (gdk_window) {
+ gdk_window_set_cursor (gdk_window, NULL);
+ gdk_flush ();
+ }
+
+ return FALSE;
+}
+
+/* The WUI thread had a connection error. This function must
+ * free the string.
+ */
+gboolean
+main_connection_error (gpointer _str)
+{
+ char *str = (char *) _str;
+
+ DEBUG ("connection error: %s", str);
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_label_set_text (GTK_LABEL (ca_error), str);
+ g_free (str);
+
+ return FALSE;
+}
+
+/* The WUI thread had a login error. This function must
+ * free the string.
+ */
+gboolean
+main_login_error (gpointer _str)
+{
+ char *str = (char *) _str;
+
+ DEBUG ("login error: %s", str);
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_label_set_text (GTK_LABEL (la_error), str);
+ g_free (str);
+
+ return FALSE;
+}
+
+/* The WUI thread reports a general status error. This function
+ * must free the string.
+ */
+gboolean
+main_status_error (gpointer _str)
+{
+ char *str = (char *) _str;
+
+ DEBUG ("status error: %s", str);
+ ASSERT_IS_MAIN_THREAD ();
+
+ gtk_statusbar_pop (GTK_STATUSBAR (statusbar), statusbar_ctx);
+ gtk_statusbar_push (GTK_STATUSBAR (statusbar), statusbar_ctx, str);
+ g_free (str);
+
+ return FALSE;
+}
+
+gboolean
+main_vmlist_updated (gpointer data)
+{
+ GSList *new_vmlist;
+
+ DEBUG ("vmlist updated");
+ ASSERT_IS_MAIN_THREAD ();
+
+ /* Get the new vmlist. */
+ if (wui_thread_get_vmlist (&new_vmlist)) {
+ /* Free the previous vmlist. This invalidates all the vm pointers
+ * contained in the Connect menu callbacks, but we're going to
+ * delete those callbacks and create news ones in a moment anyway ...
+ */
+ free_vmlist (vmlist);
+
+ vmlist = new_vmlist;
+
+ clear_connectmenu ();
+
+ gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist);
+
+ if (vmlist != NULL) {
+ gtk_menu_append (GTK_MENU (connectmenu), refresh_vmlist_separator);
+ g_slist_foreach (vmlist, add_vm_to_connectmenu, NULL);
+ }
+
+ /* Grrrr Gtk is stupid. */
+ gtk_widget_show_all (connectmenu);
+ }
+
+ return FALSE;
+}
+
+static void
+add_vm_to_connectmenu (gpointer _vm, gpointer data)
+{
+ struct vm *vm = (struct vm *) _vm;
+ GtkWidget *item;
+
+ // TODO only present running vms ?
+ // if(vm->state == "running")
+
+ DEBUG ("adding %s to Connect menu", vm->description);
+
+ item = gtk_menu_item_new_with_label (vm->description);
+ gtk_menu_append (GTK_MENU (connectmenu), item);
+
+ g_signal_connect (G_OBJECT (item), "activate",
+ G_CALLBACK (connect_to_vm), vm);
+}
diff --git a/src/tunnel.c b/src/tunnel.c
new file mode 100644
index 0000000..17b512c
--- /dev/null
+++ b/src/tunnel.c
@@ -0,0 +1,327 @@
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Mohammed Morsi <mmorsi at redhat.com>
+ *
+ * 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.
+ */
+
+/* ovirt-viewer starts listening on network port to
+ * encapsulate vnc packets including the vm's name
+ * so as to be able to be proxied.
+ *
+ * This operation takes place in another thread
+ * which can be started/stopped by calling
+ * start_tunnel / stop_tunnel.
+ *
+ * An additional connection thread is created and maintained
+ * internally for each vm / vnc connection open in ovirt-viewer
+ * establishing a connection w/ the ovirt server.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "internal.h"
+
+/* constants */
+
+// max length of a vm name
+const int VM_NAME_MAX_LEN = 250;
+
+// max length of vnc data
+const int VNC_DATA_MAX_LEN = 800000;
+
+/* Private thread functions */
+static gpointer tunnel_thread(gpointer data);
+static gpointer client_server_thread(gpointer data);
+static gpointer server_client_thread(gpointer data);
+
+/* Other private functions */
+static void close_socket(gpointer _socket, gpointer data);
+static void wait_for_thread(gpointer _thread, gpointer data);
+
+/* tunnel and main threads */
+static GThread *tunnel_gthread = NULL;
+static GThread *main_gthread = NULL;
+
+/* list of communication threads */
+static GSList *communication_threads = NULL;
+
+/* list of sockets */
+static GSList *sockets = NULL;
+
+/* thread termination flag */
+static gboolean run_tunnel = FALSE;
+
+/* internal.h shared constructs */
+int tunnel_port;
+
+/////////////////
+
+/** public implementations **/
+
+/* start tunnel thread */
+void
+start_tunnel(void)
+{
+ GError *error = NULL;
+
+ DEBUG ("starting the tunnel thread");
+
+ assert (tunnel_gthread == NULL);
+
+ run_tunnel = TRUE;
+
+ main_gthread = g_thread_self ();
+
+ tunnel_gthread = g_thread_create (tunnel_thread, NULL, TRUE, &error);
+ if (error) {
+ g_print ("%s\n", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+};
+
+/* stop tunnel thread */
+void
+stop_tunnel(void)
+{
+ if(!run_tunnel)
+ return;
+
+ DEBUG ("stopping the tunnel thread");
+
+ assert (tunnel_gthread != NULL);
+ ASSERT_IS_MAIN_THREAD ();
+
+ run_tunnel = FALSE;
+
+ g_slist_foreach(sockets, close_socket, NULL);
+
+ (void) g_thread_join (tunnel_gthread);
+ tunnel_gthread = NULL;
+};
+
+/////////////////
+
+/** private implementations **/
+
+/* the tunnel thread */
+static gpointer
+tunnel_thread (gpointer _data)
+{
+ struct hostent *dns_serv;
+
+ //char vm_data[VM_NAME_MAX_LEN];
+ int local_server_socketfd, ovirt_server_socket, client_socketfd;
+ unsigned int local_server_len, client_len, ovirt_server_len;
+
+ struct sockaddr_in local_server_address;
+ struct sockaddr_in ovirt_server_address;
+ struct sockaddr_in client_address;
+
+ struct sockaddr_in local_server_address_lookup;
+ unsigned int local_server_address_lookup_len = sizeof(local_server_address_lookup);
+
+ GThread *client_server_gthread = NULL;
+ GThread *server_client_gthread = NULL;
+
+ int sockets_param[2];
+ int * c_socket;
+
+ DEBUG ("tunnel thread starting up");
+
+ // ovirt server address
+ dns_serv = gethostbyname(hostname);
+ if(dns_serv == NULL){
+ DEBUG("ovirt server lookup failed");
+ return NULL;
+ }
+ ovirt_server_address.sin_family = PF_INET;
+ ovirt_server_address.sin_addr.s_addr = ((struct in_addr*)(dns_serv->h_addr))->s_addr; //inet_addr(hostname);
+ ovirt_server_address.sin_port = htons(ovirt_server_vnc_port);
+ ovirt_server_len = sizeof(ovirt_server_address);
+
+ // create local net socket
+ local_server_socketfd = socket(PF_INET, SOCK_STREAM, 0);
+ c_socket = malloc(sizeof(int)); *c_socket = local_server_socketfd;
+ sockets = g_slist_prepend(sockets, c_socket);
+
+ // local server address
+ local_server_address.sin_family = PF_INET;
+ local_server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
+ local_server_address.sin_port = 0;
+ local_server_len = sizeof(local_server_address);
+
+ // increment ports until one is available
+ if(bind(local_server_socketfd, (struct sockaddr*)&local_server_address, local_server_len) < 0){
+ DEBUG("tunnel bind failed");
+ return NULL;
+ }
+
+ getsockname(local_server_socketfd,
+ (struct sockaddr*) &local_server_address_lookup,
+ &local_server_address_lookup_len);
+ tunnel_port = (int)ntohs(local_server_address_lookup.sin_port);
+ DEBUG ("tunnel bound to local port %i", tunnel_port);
+
+ // increase client buffer size?
+ listen(local_server_socketfd, 5);
+
+ while(run_tunnel) {
+ // accept a client connection
+ DEBUG("tunnel accepting");
+ client_len = sizeof(client_address);
+ client_socketfd = accept(local_server_socketfd, (struct sockaddr*)&client_address, &client_len);
+ if(client_socketfd < 0){
+ DEBUG("tunnel accept failed");
+ break;
+ }
+ // TODO check accept return value for err
+ c_socket = malloc(sizeof(int)); *c_socket = client_socketfd;
+ sockets = g_slist_prepend(sockets, c_socket);
+
+ DEBUG ("client connected to tunnel");
+
+ // establish connection w/ ovirt server
+ ovirt_server_socket = socket(PF_INET, SOCK_STREAM, 0);
+ c_socket = malloc(sizeof(int)); *c_socket = ovirt_server_socket;
+ sockets = g_slist_prepend(sockets, c_socket);
+ DEBUG ("connecting to ovirt server %s on %i", hostname, ovirt_server_vnc_port);
+ if(connect(ovirt_server_socket, (struct sockaddr*)&ovirt_server_address, ovirt_server_len) < 0){
+ DEBUG ("could not connect to ovirt server");
+ break;
+ //return NULL;
+ }
+ DEBUG ("connected to ovirt server");
+
+ sockets_param[0] = ovirt_server_socket;
+ sockets_param[1] = client_socketfd;
+
+ // launch thread for client -> server traffic
+ client_server_gthread = g_thread_create (client_server_thread,
+ &sockets_param, TRUE, NULL);
+
+ // launch thread for server -> client traffic
+ server_client_gthread = g_thread_create (server_client_thread,
+ &sockets_param, TRUE, NULL);
+
+ communication_threads = g_slist_prepend(communication_threads, client_server_gthread);
+ communication_threads = g_slist_prepend(communication_threads, server_client_gthread);
+
+ // send target vm for this session
+ //strcpy(vm_data, vm_in_focus->description);
+ DEBUG ("sending vm %s", vm_in_focus->description);
+ write(ovirt_server_socket, vm_in_focus->description, strlen(vm_in_focus->description));
+
+ }
+
+ DEBUG("terminating tunnel thread");
+
+ // wait for connection threads to finish
+ g_slist_foreach(communication_threads, wait_for_thread, NULL);
+
+ DEBUG ("tunnel thread completed");
+ return NULL;
+};
+
+/* the tunnel thread */
+static gpointer
+client_server_thread (gpointer _data){
+ int nbytes;
+ char vnc_data[VNC_DATA_MAX_LEN];
+
+ int ovirt_server_socket = ((int*)_data)[0],
+ client_socket = ((int*)_data)[1];
+
+ DEBUG ("client/server thread starting up");
+
+ while(run_tunnel){
+ VERBOSE( "accepting client data");
+
+ // grab vnc data
+ nbytes = read(client_socket, vnc_data, VNC_DATA_MAX_LEN);
+ if(nbytes <= 0){
+ DEBUG ( "error reading data from client" );
+ break;
+ }
+ VERBOSE ("read %i bytes from client", nbytes);
+
+ // send network_data onto server
+ nbytes = write(ovirt_server_socket, vnc_data, nbytes);
+ if(nbytes <= 0){
+ DEBUG ( "error writing data to server" );
+ break;
+ }
+ VERBOSE ("wrote %i bytes to server", nbytes);
+ }
+
+ DEBUG ("client/server thread completed");
+ return NULL;
+};
+
+/* the server thread */
+static gpointer
+server_client_thread (gpointer _data){
+ char vnc_data[VNC_DATA_MAX_LEN];
+
+ int ovirt_server_socket = ((int*)_data)[0],
+ client_socket = ((int*)_data)[1];
+
+ int nbytes;
+
+ DEBUG ("server/client thread starting up");
+
+ while(run_tunnel){
+ // grab vnc data
+ nbytes = read(ovirt_server_socket, vnc_data, VNC_DATA_MAX_LEN);
+ if(nbytes <= 0){
+ DEBUG ( "error reading data from server" );
+ break;
+ }
+ VERBOSE ("read %i bytes from server", nbytes);
+
+ // send network_data onto client
+ nbytes = write(client_socket, vnc_data, nbytes);
+ if(nbytes <= 0){
+ DEBUG ( "error writing data to client" );
+ break;
+ }
+ VERBOSE ("wrote %i bytes to client", nbytes);
+ }
+
+ DEBUG ("server/client thread completed");
+ return NULL;
+};
+
+static void close_socket(gpointer _socket, gpointer data){
+ shutdown(*(int*) _socket, 2);
+ close(*(int*) _socket);
+ free((int*) _socket);
+};
+
+static void wait_for_thread(gpointer _thread, gpointer data){
+ g_thread_join((GThread*)_thread);
+};
diff --git a/src/wui_thread.c b/src/wui_thread.c
new file mode 100644
index 0000000..8bfa8ca
--- /dev/null
+++ b/src/wui_thread.c
@@ -0,0 +1,1106 @@
+/* ovirt viewer console application
+ * Copyright (C) 2008 Red Hat Inc.
+ * Written by Richard W.M. Jones <rjones at redhat.com>
+ *
+ * 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 <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <libxml/parser.h>
+
+#include <curl/curl.h>
+
+#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 <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;
+}
+
+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;
+}
diff --git a/tunnel.c b/tunnel.c
deleted file mode 100644
index 17b512c..0000000
--- a/tunnel.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/* ovirt viewer console application
- * Copyright (C) 2008 Red Hat Inc.
- * Written by Mohammed Morsi <mmorsi at redhat.com>
- *
- * 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.
- */
-
-/* ovirt-viewer starts listening on network port to
- * encapsulate vnc packets including the vm's name
- * so as to be able to be proxied.
- *
- * This operation takes place in another thread
- * which can be started/stopped by calling
- * start_tunnel / stop_tunnel.
- *
- * An additional connection thread is created and maintained
- * internally for each vm / vnc connection open in ovirt-viewer
- * establishing a connection w/ the ovirt server.
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <assert.h>
-#include <string.h>
-
-#include <glib.h>
-
-#include "internal.h"
-
-/* constants */
-
-// max length of a vm name
-const int VM_NAME_MAX_LEN = 250;
-
-// max length of vnc data
-const int VNC_DATA_MAX_LEN = 800000;
-
-/* Private thread functions */
-static gpointer tunnel_thread(gpointer data);
-static gpointer client_server_thread(gpointer data);
-static gpointer server_client_thread(gpointer data);
-
-/* Other private functions */
-static void close_socket(gpointer _socket, gpointer data);
-static void wait_for_thread(gpointer _thread, gpointer data);
-
-/* tunnel and main threads */
-static GThread *tunnel_gthread = NULL;
-static GThread *main_gthread = NULL;
-
-/* list of communication threads */
-static GSList *communication_threads = NULL;
-
-/* list of sockets */
-static GSList *sockets = NULL;
-
-/* thread termination flag */
-static gboolean run_tunnel = FALSE;
-
-/* internal.h shared constructs */
-int tunnel_port;
-
-/////////////////
-
-/** public implementations **/
-
-/* start tunnel thread */
-void
-start_tunnel(void)
-{
- GError *error = NULL;
-
- DEBUG ("starting the tunnel thread");
-
- assert (tunnel_gthread == NULL);
-
- run_tunnel = TRUE;
-
- main_gthread = g_thread_self ();
-
- tunnel_gthread = g_thread_create (tunnel_thread, NULL, TRUE, &error);
- if (error) {
- g_print ("%s\n", error->message);
- g_error_free (error);
- exit (1);
- }
-};
-
-/* stop tunnel thread */
-void
-stop_tunnel(void)
-{
- if(!run_tunnel)
- return;
-
- DEBUG ("stopping the tunnel thread");
-
- assert (tunnel_gthread != NULL);
- ASSERT_IS_MAIN_THREAD ();
-
- run_tunnel = FALSE;
-
- g_slist_foreach(sockets, close_socket, NULL);
-
- (void) g_thread_join (tunnel_gthread);
- tunnel_gthread = NULL;
-};
-
-/////////////////
-
-/** private implementations **/
-
-/* the tunnel thread */
-static gpointer
-tunnel_thread (gpointer _data)
-{
- struct hostent *dns_serv;
-
- //char vm_data[VM_NAME_MAX_LEN];
- int local_server_socketfd, ovirt_server_socket, client_socketfd;
- unsigned int local_server_len, client_len, ovirt_server_len;
-
- struct sockaddr_in local_server_address;
- struct sockaddr_in ovirt_server_address;
- struct sockaddr_in client_address;
-
- struct sockaddr_in local_server_address_lookup;
- unsigned int local_server_address_lookup_len = sizeof(local_server_address_lookup);
-
- GThread *client_server_gthread = NULL;
- GThread *server_client_gthread = NULL;
-
- int sockets_param[2];
- int * c_socket;
-
- DEBUG ("tunnel thread starting up");
-
- // ovirt server address
- dns_serv = gethostbyname(hostname);
- if(dns_serv == NULL){
- DEBUG("ovirt server lookup failed");
- return NULL;
- }
- ovirt_server_address.sin_family = PF_INET;
- ovirt_server_address.sin_addr.s_addr = ((struct in_addr*)(dns_serv->h_addr))->s_addr; //inet_addr(hostname);
- ovirt_server_address.sin_port = htons(ovirt_server_vnc_port);
- ovirt_server_len = sizeof(ovirt_server_address);
-
- // create local net socket
- local_server_socketfd = socket(PF_INET, SOCK_STREAM, 0);
- c_socket = malloc(sizeof(int)); *c_socket = local_server_socketfd;
- sockets = g_slist_prepend(sockets, c_socket);
-
- // local server address
- local_server_address.sin_family = PF_INET;
- local_server_address.sin_addr.s_addr = inet_addr("127.0.0.1");
- local_server_address.sin_port = 0;
- local_server_len = sizeof(local_server_address);
-
- // increment ports until one is available
- if(bind(local_server_socketfd, (struct sockaddr*)&local_server_address, local_server_len) < 0){
- DEBUG("tunnel bind failed");
- return NULL;
- }
-
- getsockname(local_server_socketfd,
- (struct sockaddr*) &local_server_address_lookup,
- &local_server_address_lookup_len);
- tunnel_port = (int)ntohs(local_server_address_lookup.sin_port);
- DEBUG ("tunnel bound to local port %i", tunnel_port);
-
- // increase client buffer size?
- listen(local_server_socketfd, 5);
-
- while(run_tunnel) {
- // accept a client connection
- DEBUG("tunnel accepting");
- client_len = sizeof(client_address);
- client_socketfd = accept(local_server_socketfd, (struct sockaddr*)&client_address, &client_len);
- if(client_socketfd < 0){
- DEBUG("tunnel accept failed");
- break;
- }
- // TODO check accept return value for err
- c_socket = malloc(sizeof(int)); *c_socket = client_socketfd;
- sockets = g_slist_prepend(sockets, c_socket);
-
- DEBUG ("client connected to tunnel");
-
- // establish connection w/ ovirt server
- ovirt_server_socket = socket(PF_INET, SOCK_STREAM, 0);
- c_socket = malloc(sizeof(int)); *c_socket = ovirt_server_socket;
- sockets = g_slist_prepend(sockets, c_socket);
- DEBUG ("connecting to ovirt server %s on %i", hostname, ovirt_server_vnc_port);
- if(connect(ovirt_server_socket, (struct sockaddr*)&ovirt_server_address, ovirt_server_len) < 0){
- DEBUG ("could not connect to ovirt server");
- break;
- //return NULL;
- }
- DEBUG ("connected to ovirt server");
-
- sockets_param[0] = ovirt_server_socket;
- sockets_param[1] = client_socketfd;
-
- // launch thread for client -> server traffic
- client_server_gthread = g_thread_create (client_server_thread,
- &sockets_param, TRUE, NULL);
-
- // launch thread for server -> client traffic
- server_client_gthread = g_thread_create (server_client_thread,
- &sockets_param, TRUE, NULL);
-
- communication_threads = g_slist_prepend(communication_threads, client_server_gthread);
- communication_threads = g_slist_prepend(communication_threads, server_client_gthread);
-
- // send target vm for this session
- //strcpy(vm_data, vm_in_focus->description);
- DEBUG ("sending vm %s", vm_in_focus->description);
- write(ovirt_server_socket, vm_in_focus->description, strlen(vm_in_focus->description));
-
- }
-
- DEBUG("terminating tunnel thread");
-
- // wait for connection threads to finish
- g_slist_foreach(communication_threads, wait_for_thread, NULL);
-
- DEBUG ("tunnel thread completed");
- return NULL;
-};
-
-/* the tunnel thread */
-static gpointer
-client_server_thread (gpointer _data){
- int nbytes;
- char vnc_data[VNC_DATA_MAX_LEN];
-
- int ovirt_server_socket = ((int*)_data)[0],
- client_socket = ((int*)_data)[1];
-
- DEBUG ("client/server thread starting up");
-
- while(run_tunnel){
- VERBOSE( "accepting client data");
-
- // grab vnc data
- nbytes = read(client_socket, vnc_data, VNC_DATA_MAX_LEN);
- if(nbytes <= 0){
- DEBUG ( "error reading data from client" );
- break;
- }
- VERBOSE ("read %i bytes from client", nbytes);
-
- // send network_data onto server
- nbytes = write(ovirt_server_socket, vnc_data, nbytes);
- if(nbytes <= 0){
- DEBUG ( "error writing data to server" );
- break;
- }
- VERBOSE ("wrote %i bytes to server", nbytes);
- }
-
- DEBUG ("client/server thread completed");
- return NULL;
-};
-
-/* the server thread */
-static gpointer
-server_client_thread (gpointer _data){
- char vnc_data[VNC_DATA_MAX_LEN];
-
- int ovirt_server_socket = ((int*)_data)[0],
- client_socket = ((int*)_data)[1];
-
- int nbytes;
-
- DEBUG ("server/client thread starting up");
-
- while(run_tunnel){
- // grab vnc data
- nbytes = read(ovirt_server_socket, vnc_data, VNC_DATA_MAX_LEN);
- if(nbytes <= 0){
- DEBUG ( "error reading data from server" );
- break;
- }
- VERBOSE ("read %i bytes from server", nbytes);
-
- // send network_data onto client
- nbytes = write(client_socket, vnc_data, nbytes);
- if(nbytes <= 0){
- DEBUG ( "error writing data to client" );
- break;
- }
- VERBOSE ("wrote %i bytes to client", nbytes);
- }
-
- DEBUG ("server/client thread completed");
- return NULL;
-};
-
-static void close_socket(gpointer _socket, gpointer data){
- shutdown(*(int*) _socket, 2);
- close(*(int*) _socket);
- free((int*) _socket);
-};
-
-static void wait_for_thread(gpointer _thread, gpointer data){
- g_thread_join((GThread*)_thread);
-};
diff --git a/wui_thread.c b/wui_thread.c
deleted file mode 100644
index 8bfa8ca..0000000
--- a/wui_thread.c
+++ /dev/null
@@ -1,1106 +0,0 @@
-/* ovirt viewer console application
- * Copyright (C) 2008 Red Hat Inc.
- * Written by Richard W.M. Jones <rjones at redhat.com>
- *
- * 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 <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include <glib.h>
-#include <glib/gprintf.h>
-
-#include <libxml/parser.h>
-
-#include <curl/curl.h>
-
-#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 <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;
-}
-
-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;
-}
--
1.6.0.6
More information about the ovirt-devel
mailing list