From 07c89c3f5ef84aaee6faded8b78a07c87ba25d7e Mon Sep 17 00:00:00 2001 From: Daniel P. Berrange Date: Jul 20 2007 19:26:08 +0000 Subject: Initial commit --- diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..9dc7ba3 --- /dev/null +++ b/.hgignore @@ -0,0 +1,8 @@ +.*~$ +^build/ +^aclocal\.m4$ +^autom4te\.cache/ +Makefile\.in$ +^config\.guess$ +^config\.sub$ +^configure$ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/AUTHORS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/ChangeLog diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..0632465 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = src + +EXTRA_DIST = @PACKAGE@.spec + +DISTCLEAN_FILES = @PACKAGE@.spec diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/NEWS diff --git a/README b/README new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/README diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000..b37cf34 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,88 @@ +dnl +dnl Taken from gnome-common/macros2/gnome-compiler-flags.m4 +dnl +dnl We've added: +dnl -Wextra -Wshadow -Wcast-align -Wwrite-strings -Waggregate-return -Wstrict-prototypes -Winline -Wredundant-decls +dnl We've removed +dnl CFLAGS="$realsave_CFLAGS" +dnl to avoid clobbering user-specified CFLAGS +dnl +AC_DEFUN([VIRT_VIEWER_COMPILE_WARNINGS],[ + dnl ****************************** + dnl More compiler warnings + dnl ****************************** + + AC_ARG_ENABLE(compile-warnings, + AC_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@], + [Turn on compiler warnings]),, + [enable_compile_warnings="m4_default([$1],[maximum])"]) + + warnCFLAGS= + + try_compiler_flags="-Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -fasynchronous-unwind-tables" + + case "$enable_compile_warnings" in + no) + ;; + minimum) + try_compiler_flags="$try_compiler_flags -Wall" + ;; + yes) + try_compiler_flags="$try_compiler_flags -Wall -Wmissing-prototypes -std=c99" + ;; + maximum|error) + try_compiler_flags="$try_compiler_flags -Wall -Wmissing-prototypes -std=c99 -Wnested-externs -Wpointer-arith" + try_compiler_flags="$try_compiler_flags -Wextra -Wshadow -Wcast-align -Wwrite-strings -Waggregate-return" + try_compiler_flags="$try_compiler_flags -Wstrict-prototypes -Winline -Wredundant-decls -Wno-sign-compare" + if test "$enable_compile_warnings" = "error" ; then + try_compiler_flags="$try_compiler_flags -Werror" + fi + ;; + *) + AC_MSG_ERROR(Unknown argument '$enable_compile_warnings' to --enable-compile-warnings) + ;; + esac + + compiler_flags= + for option in $try_compiler_flags; do + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $option" + AC_MSG_CHECKING([whether gcc understands $option]) + AC_TRY_COMPILE([], [], + has_option=yes, + has_option=no,) + CFLAGS="$SAVE_CFLAGS" + AC_MSG_RESULT($has_option) + if test $has_option = yes; then + compiler_flags="$compiler_flags $option" + fi + unset has_option + unset SAVE_CFLAGS + done + unset option + unset try_compiler_flags + + AC_ARG_ENABLE(iso-c, + AC_HELP_STRING([--enable-iso-c], + [Try to warn if code is not ISO C ]),, + [enable_iso_c=no]) + + AC_MSG_CHECKING(what language compliance flags to pass to the C compiler) + complCFLAGS= + if test "x$enable_iso_c" != "xno"; then + if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-ansi[\ \ ]*) ;; + *) complCFLAGS="$complCFLAGS -ansi" ;; + esac + case " $CFLAGS " in + *[\ \ ]-pedantic[\ \ ]*) ;; + *) complCFLAGS="$complCFLAGS -pedantic" ;; + esac + fi + fi + AC_MSG_RESULT($complCFLAGS) + + WARN_CFLAGS="$compiler_flags $complCFLAGS" + AC_SUBST(WARN_CFLAGS) +]) diff --git a/autobuild.sh b/autobuild.sh new file mode 100755 index 0000000..6b753fe --- /dev/null +++ b/autobuild.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +set -e +set -v + +# Make things clean. +test -f Makefile && make -k distclean || : + +rm -rf build +mkdir build +cd build + +../autogen.sh --prefix=$AUTOBUILD_INSTALL_ROOT --enable-fatal-warnings + +make +make install + +rm -f *.tar.gz +make dist + +if [ -f /usr/bin/rpmbuild ]; then + if [ -n "$AUTOBUILD_COUNTER" ]; then + EXTRA_RELEASE=".auto$AUTOBUILD_COUNTER" + else + NOW=`date +"%s"` + EXTRA_RELEASE=".$USER$NOW" + fi + rpmbuild --nodeps --define "extra_release $EXTRA_RELEASE" -ta --clean *.tar.gz +fi diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..3ae971a --- /dev/null +++ b/autogen.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +set -e +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +THEDIR=`pwd` +cd $srcdir + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile virt-viewer." + echo "Download the appropriate package for your distribution," + echo "or see http://www.gnu.org/software/autoconf" + DIE=1 +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + DIE=1 + echo "You must have automake installed to compile virt-viewer." + echo "Download the appropriate package for your distribution," + echo "or see http://www.gnu.org/software/automake" +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "I am going to run ./configure with --enable-warnings - if you " + echo "wish to pass any extra arguments to it, please specify them on " + echo "the $0 command line." +fi + +aclocal +automake --add-missing +autoconf + +cd $THEDIR + +$srcdir/configure --enable-warnings "$@" && { + echo + echo "Now type 'make' to compile gtk-vnc." +} diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..882747c --- /dev/null +++ b/configure.ac @@ -0,0 +1,15 @@ +AC_INIT(virt-viewer.spec.in) +AM_INIT_AUTOMAKE(virt-viewer, 0.0.1) + +AC_PROG_CC + +VIRT_VIEWER_COMPILE_WARNINGS(maximum) + +PKG_CHECK_MODULES(LIBXML2, libxml-2.0 >= 2.6.0) +PKG_CHECK_MODULES(LIBVIRT, libvirt >= 0.2.0) +PKG_CHECK_MODULES(GTK2, gtk+-2.0 >= 2.2.0) +PKG_CHECK_MODULES(GTKVNC, gtk-vnc-1.0 >= 0.0.1) + +AC_OUTPUT(Makefile + src/Makefile + virt-viewer.spec) diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ecf194d --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,6 @@ + +bin_PROGRAMS = virt-viewer + +virt_viewer_SOURCES = main.c +virt_viewer_LDADD = @GTKVNC_LIBS@ @GTK2_LIBS@ @LIBXML2_LIBS@ @LIBVIRT_LIBS@ +virt_viewer_CFLAGS = @GTKVNC_CFLAGS@ @GTK2_CFLAGS@ @LIBXML2_CFLAGS@ @LIBVIRT_CFLAGS@ @WARN_CFLAGS@ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..cbc6577 --- /dev/null +++ b/src/main.c @@ -0,0 +1,587 @@ +/* + * Virt Viewer: A virtual machine console viewer + * + * Copyright (C) 2007 Red Hat, + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DEBUG 1 +#ifdef DEBUG +#define DEBUG_LOG(s, ...) fprintf(stderr, (s), ## __VA_ARGS__) +#else +#define DEBUG_LOG(s, ...) do {} while (0) +#endif + +static int verbose = 0; +#define MAX_KEY_COMBO 3 +struct keyComboDef { + guint keys[MAX_KEY_COMBO]; + guint nkeys; + const char *label; +}; + +static const 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"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F1 }, 3, "Ctrl+Alt+F_1"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F2 }, 3, "Ctrl+Alt+F_2"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F3 }, 3, "Ctrl+Alt+F_3"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F4 }, 3, "Ctrl+Alt+F_4"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F5 }, 3, "Ctrl+Alt+F_5"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F6 }, 3, "Ctrl+Alt+F_6"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F7 }, 3, "Ctrl+Alt+F_7"}, + { { GDK_Control_L, GDK_Alt_L, GDK_F8 }, 3, "Ctrl+Alt+F_8"}, + { { GDK_Print }, 1, "_PrintScreen"}, +}; + + +static void viewer_set_title(VncDisplay *vnc, GtkWidget *window, gboolean grabbed) +{ + const char *name; + char title[1024]; + const char *subtitle; + + if (grabbed) + subtitle = "(Press Ctrl+Alt to release pointer) "; + else + subtitle = ""; + + name = vnc_display_get_name(VNC_DISPLAY(vnc)); + snprintf(title, sizeof(title), "%s%s - Virt Viewer", + subtitle, name); + + gtk_window_set_title(GTK_WINDOW(window), title); +} + +static void viewer_grab(GtkWidget *vnc, GtkWidget *window) +{ + viewer_set_title(VNC_DISPLAY(vnc), window, TRUE); +} + +static void viewer_ungrab(GtkWidget *vnc, GtkWidget *window) +{ + viewer_set_title(VNC_DISPLAY(vnc), window, FALSE); +} + +static void viewer_shutdown(GtkWidget *src G_GNUC_UNUSED, GtkWidget *vnc) +{ + vnc_display_close(VNC_DISPLAY(vnc)); + gtk_main_quit(); +} + +static void viewer_connected(GtkWidget *vnc G_GNUC_UNUSED) +{ + DEBUG_LOG("Connected to server\n"); +} + +static void viewer_initialized(GtkWidget *vnc, GtkWidget *window) +{ + DEBUG_LOG("Connection initialized\n"); + gtk_widget_show_all(window); + viewer_set_title(VNC_DISPLAY(vnc), window, FALSE); +} + +static void viewer_disconnected(GtkWidget *vnc G_GNUC_UNUSED) +{ + DEBUG_LOG("Disconnected from server\n"); + gtk_main_quit(); +} + +static void viewer_send_key(GtkWidget *menu, GtkWidget *vnc) +{ + int i; + GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu)); + const char *text = gtk_label_get_label(GTK_LABEL(label)); + + for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) { + if (!strcmp(text, keyCombos[i].label)) { + DEBUG_LOG("Sending key combo %s\n", gtk_label_get_text(GTK_LABEL(label))); + vnc_display_send_keys(VNC_DISPLAY(vnc), + keyCombos[i].keys, + keyCombos[i].nkeys); + return; + } + } + DEBUG_LOG("Failed to find key combo %s\n", gtk_label_get_text(GTK_LABEL(label))); +} + +#if 0 +static void viewer_save_screenshot(GtkWidget *vnc, const char *file) +{ + GdkPixbuf *pix = vnc_display_get_pixbuf(VNC_DISPLAY(vnc)); + gdk_pixbuf_save(pix, file, "png", NULL, + "tEXt::Generator App", PACKAGE, NULL); + gdk_pixbuf_unref(pix); +} + +static void viewer_screenshot(GtkWidget *menu, GtkWidget *vnc) +{ + GtkWidget *dialog; + + dialog = gtk_file_chooser_dialog_new ("Save screenshot", + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE); + + //gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), default_folder_for_saving); + //gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), "Screenshot"); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + viewer_save_screenshot(vnc, filename); + g_free (filename); + } + + gtk_widget_destroy (dialog); +} +#endif + +static void viewer_credential(GtkWidget *vnc, GValueArray *credList) +{ + GtkWidget *dialog, **label, **entry, *box, *vbox; + int response; + unsigned int i; + + DEBUG_LOG("Got credential request for %d credential(s)\n", credList->n_values); + + 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 *, credList->n_values); + entry = g_new(GtkWidget *, credList->n_values); + + for (i = 0 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + int credType = g_value_get_enum(cred); + switch (credType) { + case VNC_DISPLAY_CREDENTIAL_USERNAME: + label[i] = gtk_label_new("Username:"); + break; + default: + label[i] = gtk_label_new("Password:"); + break; + } + entry[i] = gtk_entry_new(); + + gtk_table_attach(GTK_TABLE(box), label[i], 0, 1, i, i+1, GTK_SHRINK, GTK_SHRINK, 3, 3); + gtk_table_attach(GTK_TABLE(box), entry[i], 1, 2, i, i+1, GTK_SHRINK, GTK_SHRINK, 3, 3); + } + + + 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 ; i < credList->n_values ; i++) { + GValue *cred = g_value_array_get_nth(credList, i); + int credType = g_value_get_enum(cred); + const char *data = gtk_entry_get_text(GTK_ENTRY(entry[i])); + vnc_display_set_credential(VNC_DISPLAY(vnc), credType, data); + } + } else { + DEBUG_LOG("Aborting connection\n"); + vnc_display_close(VNC_DISPLAY(vnc)); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); +} + +static GtkWidget *viewer_build_file_menu(VncDisplay *vnc) +{ + GtkWidget *file; + GtkWidget *filemenu; + GtkWidget *quit; + + file = gtk_menu_item_new_with_mnemonic("_File"); + + filemenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu); + + quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); + gtk_menu_append(GTK_MENU(filemenu), quit); + g_signal_connect(quit, "activate", GTK_SIGNAL_FUNC(viewer_shutdown), vnc); + + return file; +} + +static GtkWidget *viewer_build_sendkey_menu(VncDisplay *vnc) +{ + GtkWidget *sendkey; + GtkWidget *sendkeymenu; + int i; + + sendkey = gtk_menu_item_new_with_mnemonic("_Send Key"); + + sendkeymenu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(sendkey), sendkeymenu); + + for (i = 0 ; i < (sizeof(keyCombos)/sizeof(keyCombos[0])) ; i++) { + GtkWidget *key; + + key = gtk_menu_item_new_with_mnemonic(keyCombos[i].label); + gtk_menu_append(GTK_MENU(sendkeymenu), key); + g_signal_connect(key, "activate", GTK_SIGNAL_FUNC(viewer_send_key), vnc); + } + + return sendkey; +} + +static GtkWidget *viewer_build_help_menu(void) +{ + GtkWidget *help; + GtkWidget *helpmenu; + GtkWidget *about; + + help = gtk_menu_item_new_with_mnemonic("_Help"); + + 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(viewer_shutdown), vnc); + + return help; +} + +static GtkWidget *viewer_build_menu(VncDisplay *vnc) +{ + GtkWidget *menubar; + GtkWidget *file; + GtkWidget *sendkey; + GtkWidget *help; + + menubar = gtk_menu_bar_new(); + + file = viewer_build_file_menu(vnc); + sendkey = viewer_build_sendkey_menu(vnc); + help = viewer_build_help_menu(); + + gtk_menu_bar_append(GTK_MENU_BAR(menubar), file); + gtk_menu_bar_append(GTK_MENU_BAR(menubar), sendkey); + gtk_menu_bar_append(GTK_MENU_BAR(menubar), help); + + + return menubar; +} + +static GtkWidget *viewer_build_window(VncDisplay *vnc) +{ + GtkWidget *window; + GtkWidget *menubar; + GtkWidget *layout; + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + layout = gtk_vbox_new(FALSE, 3); + menubar = viewer_build_menu(vnc); + + gtk_container_add(GTK_CONTAINER(window), layout); + gtk_container_add(GTK_CONTAINER(layout), menubar); + gtk_container_add(GTK_CONTAINER(layout), GTK_WIDGET(vnc)); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + + gtk_signal_connect(GTK_OBJECT(vnc), "vnc-pointer-grab", + GTK_SIGNAL_FUNC(viewer_grab), window); + gtk_signal_connect(GTK_OBJECT(vnc), "vnc-pointer-ungrab", + GTK_SIGNAL_FUNC(viewer_ungrab), window); + + gtk_signal_connect(GTK_OBJECT(window), "delete-event", + GTK_SIGNAL_FUNC(viewer_shutdown), vnc); + + gtk_signal_connect(GTK_OBJECT(vnc), "vnc-connected", + GTK_SIGNAL_FUNC(viewer_connected), NULL); + gtk_signal_connect(GTK_OBJECT(vnc), "vnc-initialized", + GTK_SIGNAL_FUNC(viewer_initialized), window); + gtk_signal_connect(GTK_OBJECT(vnc), "vnc-disconnected", + GTK_SIGNAL_FUNC(viewer_disconnected), NULL); + + g_signal_connect(GTK_OBJECT(vnc), "vnc-auth-credential", + GTK_SIGNAL_FUNC(viewer_credential), NULL); + + return window; +} + + +static void viewer_version(FILE *out) +{ + fprintf(out, "%s version %s\n", PACKAGE, VERSION); +} + +static void viewer_help(FILE *out, const char *app) +{ + fprintf(out, "\n"); + fprintf(out, "syntax: %s [OPTIONS] DOMAIN-NAME|ID|UUID\n", app); + fprintf(out, "\n"); + viewer_version(out); + fprintf(out, "\n"); + fprintf(out, "Options:"); + fprintf(out, " -h, --help display command line help\n"); + fprintf(out, " -v, --verbose display verbose information\n"); + fprintf(out, " -V, --version display verion informaton\n"); + fprintf(out, " -c URI, --connect URI connect to hypervisor URI\n"); + fprintf(out, " -w, --wait wait for domain to start\n"); + fprintf(out, "\n"); +} + +static int viewer_parse_uuid(const char *name, unsigned char *uuid) +{ + int i; + + const char *cur = name; + for (i = 0;i < 16;) { + uuid[i] = 0; + if (*cur == 0) + return -1; + if ((*cur == '-') || (*cur == ' ')) { + cur++; + continue; + } + if ((*cur >= '0') && (*cur <= '9')) + uuid[i] = *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + uuid[i] = *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + uuid[i] = *cur - 'A' + 10; + else + return -1; + uuid[i] *= 16; + cur++; + if (*cur == 0) + return -1; + if ((*cur >= '0') && (*cur <= '9')) + uuid[i] += *cur - '0'; + else if ((*cur >= 'a') && (*cur <= 'f')) + uuid[i] += *cur - 'a' + 10; + else if ((*cur >= 'A') && (*cur <= 'F')) + uuid[i] += *cur - 'A' + 10; + else + return -1; + i++; + cur++; + } + + return 0; +} + + +static virDomainPtr viewer_lookup_domain(virConnectPtr conn, const char *name) +{ + char *end; + int id = strtol(name, &end, 10); + virDomainPtr dom = NULL; + unsigned char uuid[16]; + + if (id >= 0 && end && !*end) { + dom = virDomainLookupByID(conn, id); + } + if (!dom && viewer_parse_uuid(name, uuid) == 0) { + dom = virDomainLookupByUUID(conn, uuid); + } + if (!dom) { + dom = virDomainLookupByName(conn, name); + } + return dom; +} + +static int viewer_extract_vnc_graphics(virDomainPtr dom, char **host, char **port) +{ + char *xmldesc = virDomainGetXMLDesc(dom, 0); + xmlDocPtr xml = NULL; + xmlParserCtxtPtr pctxt = NULL; + xmlXPathContextPtr ctxt = NULL; + xmlXPathObjectPtr obj = NULL; + int ret = -1; + + pctxt = xmlNewParserCtxt(); + if (!pctxt || !pctxt->sax) + goto error; + + xml = xmlCtxtReadDoc(pctxt, (const xmlChar *)xmldesc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + free(xmldesc); + if (!xml) + goto error; + + ctxt = xmlXPathNewContext(xml); + if (!ctxt) + goto error; + + obj = xmlXPathEval((const xmlChar *)"string(/domain/devices/graphics[@type='vnc']/@port)", ctxt); + if (!obj || obj->type != XPATH_STRING || !obj->stringval || !obj->stringval[0]) + goto error; + if (!strcmp((const char*)obj->stringval, "-1")) + goto missing; + + *port = strdup((const char*)obj->stringval); + xmlXPathFreeObject(obj); + obj = NULL; + /* + obj = xmlXPathEval((const xmlChar *)"string(/domain/devices/graphics[@type='vnc']/@listen)", ctxt); + if (!obj || obj->type != XPATH_STRING || !obj->stringval || !obj->stringval[0]) + */ + *host = NULL; + + missing: + ret = 0; + + error: + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + if (pctxt) + xmlFreeParserCtxt(pctxt); + return ret; +} + +int main(int argc, char **argv) +{ + GtkWidget *window; + GtkWidget *vnc; + char *uri = NULL; + int opt_ind; + const char *sopts = "hVc:"; + static const struct option lopts[] = { + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { "verbose", 0, 0, 'v' }, + { "connect", 1, 0, 'c' }, + { "wait", 0, 0, 'w' }, + { 0, 0, 0, 0 } + }; + int ch; + int waitvnc = 0; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + char *host = NULL; + char *port = NULL; + + while ((ch = getopt_long(argc, argv, sopts, lopts, &opt_ind)) != -1) { + switch (ch) { + case 'h': + viewer_help(stdout, argv[0]); + return 0; + case 'V': + viewer_version(stdout); + return 0; + case 'v': + verbose = 1; + break; + case 'c': + uri = strdup(optarg); + break; + case 'w': + waitvnc = 1; + break; + case '?': + viewer_help(stderr, argv[0]); + return 1; + } + } + + + if (argc != (optind+1)) { + viewer_help(stderr, argv[0]); + return 1; + } + + gtk_init(&argc, &argv); + + conn = virConnectOpenReadOnly(uri); + if (!conn) { + fprintf(stderr, "unable to connect to libvirt %s\n", + uri ? uri : "xen"); + return 2; + } + + do { + dom = viewer_lookup_domain(conn, argv[optind]); + if (!dom && !waitvnc) { + fprintf(stderr, "unable to lookup domain %s\n", argv[optind]); + return 3; + } + usleep(500*1000); + } while (!dom); + + do { + viewer_extract_vnc_graphics(dom, &host, &port); + if (!port && !waitvnc) { + fprintf(stderr, "unable to find vnc graphics for %s\n", argv[optind]); + return 4; + } + usleep(300*1000); + } while (!port); + + if (!host) + host = strdup("localhost"); + + vnc = vnc_display_new(); + window = viewer_build_window(VNC_DISPLAY(vnc)); + gtk_widget_realize(vnc); + + vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc), TRUE); + vnc_display_set_pointer_grab(VNC_DISPLAY(vnc), TRUE); + + vnc_display_open_host(VNC_DISPLAY(vnc), host, port); + + gtk_main(); + + return 0; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/virt-viewer.spec.in b/virt-viewer.spec.in new file mode 100644 index 0000000..45414c4 --- /dev/null +++ b/virt-viewer.spec.in @@ -0,0 +1,53 @@ +# -*- rpm-spec -*- + +# This macro is used for the continuous automated builds. It just +# allows an extra fragment based on the timestamp to be appended +# to the release. This distinguishes automated builds, from formal +# Fedora RPM builds +%define _extra_release %{?dist:%{dist}}%{!?dist:%{?extra_release:%{extra_release}}} + +Name: @PACKAGE@ +Version: @VERSION@ +Release: 1%{_extra_release} +Summary: Virtual Machine Viewer + +Group: Applications/Emulators +License: GPL +URL: http://virt-manager.org/ +Source0: http://virt-manager.org/download/sources/%{name}/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +ExclusiveArch: %{ix86} x86_64 ia64 + +BuildRequires: gtk2-devel +BuildRequires: gtk-vnc-devel + +%description +Virtual Machine Viewer provides a graphical console client for connecting +to virtual machines. It uses the GTK-VNC widget to provide the display, +and libvirt for looking up VNC server details. + +%prep +%setup -q + +%build +%configure +make %{?_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT +make install DESTDIR=$RPM_BUILD_ROOT + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root,-) +%doc README COPYING AUTHORS ChangeLog NEWS +%{_bindir}/%{name} + +%changelog +* Fri Jul 20 2007 Daniel P. Berrange - 0.0.1-1 +- First release +