aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkraxel <kraxel>2008-09-18 15:51:56 +0000
committerkraxel <kraxel>2008-09-18 15:51:56 +0000
commita2fa1c886c58b89428d7e9a88080bd9e29f20d51 (patch)
treea015e98534a140c0d124824e1d999cd524281037
downloadqemu-gtk-a2fa1c886c58b89428d7e9a88080bd9e29f20d51.tar.gz
Initial revision
-rw-r--r--GNUmakefile65
-rw-r--r--Make.config4
-rw-r--r--README36
-rw-r--r--VERSION1
-rw-r--r--mk/Autoconf.mk167
-rw-r--r--mk/Compile.mk102
-rw-r--r--mk/Maintainer.mk28
-rw-r--r--mk/Variables.mk55
-rw-r--r--monitor.c163
-rwxr-xr-xqemu-gtkbin0 -> 80074 bytes
-rw-r--r--qemu-gtk.c528
-rw-r--r--qemu-gtk.h70
-rw-r--r--tcp.c197
-rw-r--r--tcp.h15
14 files changed, 1431 insertions, 0 deletions
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..87aec2f
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,65 @@
+# config
+-include Make.config
+include mk/Variables.mk
+
+# add our flags + libs
+CFLAGS += -DVERSION='"$(VERSION)"' -DLIB='"$(LIB)"'
+
+# build
+TARGETS := qemu-gtk
+
+# default target
+all: build
+
+#################################################################
+# poor man's autoconf ;-)
+
+include mk/Autoconf.mk
+
+define make-config
+LIB := $(LIB)
+HAVE_GTK := $(call ac_pkg_config,gtk+-x11-2.0)
+HAVE_GTK_VNC := $(call ac_pkg_config,gtk-vnc-1.0)
+HAVE_VTE := $(call ac_pkg_config,vte)
+endef
+
+pkglst := gtk+-x11-2.0 gtk-vnc-1.0 vte
+
+CFLAGS += -Wno-strict-prototypes
+CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst))
+LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst))
+
+# desktop files
+DESKTOP := $(wildcard $(patsubst %,%.desktop,$(TARGETS)))
+
+
+########################################################################
+# rules
+
+ifeq ($(HAVE_GTK)$(HAVE_GTK_VNC)$(HAVE_VTE),yesyesyes)
+build: $(TARGETS)
+else
+build:
+ @echo "build dependencies are missing"
+ @false
+endif
+
+install: build
+ $(INSTALL_BINARY) $(TARGETS) $(bindir)
+ $(INSTALL_DATA) $(DESKTOP) $(appdir)
+
+clean:
+ -rm -f *.o *~ $(depfiles)
+
+realclean distclean: clean
+ -rm -f Make.config
+ -rm -f $(TARGETS) *~ *.bak
+
+#############################################
+
+qemu-gtk: qemu-gtk.o monitor.o tcp.o
+
+include mk/Compile.mk
+include mk/Maintainer.mk
+-include $(depfiles)
+
diff --git a/Make.config b/Make.config
new file mode 100644
index 0000000..c183710
--- /dev/null
+++ b/Make.config
@@ -0,0 +1,4 @@
+LIB := lib64
+HAVE_GTK := yes
+HAVE_GTK_VNC := yes
+HAVE_VTE := yes
diff --git a/README b/README
new file mode 100644
index 0000000..2f3b266
--- /dev/null
+++ b/README
@@ -0,0 +1,36 @@
+
+about qemu-gtk
+==============
+
+This is a simple gtk-based gui for qemu. It is designed to run
+independant from the qemu process, so you can stop and restart the gui
+without disturbing your virtual machines.
+
+qemu-gtk doesn't (yet?) support starting and stopping virtual
+machines. It can only connect to a already running qemu process.
+
+
+using qemu-gtk
+--------------
+
+qemu-gtk connects to the qemu monitor using tcp or unix sockets and to
+the built-in vnc server. Thus you must enable the monitor and vnc
+when starting qemu. The monitor should be in non-blocking server mode.
+
+i.e. you'll start qemu like this:
+
+ "qemu -monitor unix:/tmp/monitor,server,nowait -vnc :1 <more-args>"
+
+then the gui like this:
+
+ "qemu-gtk unix:/tmp/monitor"
+
+qemu-gtk will figure automatically where the vnc display is, using the
+monitor.
+
+
+Have fun,
+ Gerd
+
+--
+Gerd Hoffmann <kraxel@redhat.com>
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..49d5957
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+0.1
diff --git a/mk/Autoconf.mk b/mk/Autoconf.mk
new file mode 100644
index 0000000..27eeb32
--- /dev/null
+++ b/mk/Autoconf.mk
@@ -0,0 +1,167 @@
+#
+# simple autoconf system for GNU make
+#
+# (c) 2002-2006 Gerd Hoffmann <kraxel@suse.de>
+#
+# credits for creating this one go to the autotools people because
+# they managed it to annoy lots of developers and users (including
+# me) with version incompatibilities.
+#
+# This file is public domain. No warranty. If it breaks you keep
+# both pieces.
+#
+########################################################################
+
+# verbose yes/no
+verbose ?= no
+
+# some stuff used by the tests
+ifneq ($(verbose),no)
+ # verbose (for debug)
+ ac_init = echo "checking $(1) ... " >&2; rc=no
+ ac_b_cmd = echo "run: $(1)" >&2; $(1) >/dev/null && rc=yes
+ ac_s_cmd = echo "run: $(1)" >&2; rc=`$(1)`
+ ac_fini = echo "... result is $${rc}" >&2; echo >&2; echo "$${rc}"
+else
+ # normal
+ ac_init = echo -n "checking $(1) ... " >&2; rc=no
+ ac_b_cmd = $(1) >/dev/null 2>&1 && rc=yes
+ ac_s_cmd = rc=`$(1) 2>/dev/null`
+ ac_fini = echo "$${rc}" >&2; echo "$${rc}"
+endif
+
+# some helpers to build cflags and related variables
+ac_def_cflags_1 = $(if $(filter yes,$($(1))),-D$(1))
+ac_lib_cflags = $(foreach lib,$(1),$(call ac_def_cflags_1,HAVE_LIB$(lib)))
+ac_inc_cflags = $(foreach inc,$(1),$(call ac_def_cflags_1,HAVE_$(inc)))
+ac_lib_mkvar_1 = $(if $(filter yes,$(HAVE_LIB$(1))),$($(1)_$(2)))
+ac_lib_mkvar = $(foreach lib,$(1),$(call ac_lib_mkvar_1,$(lib),$(2)))
+
+
+########################################################################
+# the tests ...
+
+# get uname
+ac_uname = $(shell \
+ $(call ac_init,for system);\
+ $(call ac_s_cmd,uname -s | tr 'A-Z' 'a-z');\
+ $(call ac_fini))
+
+ac_uname_arch = $(shell \
+ $(call ac_init,for arch);\
+ $(call ac_s_cmd,uname -m | tr 'A-Z' 'a-z');\
+ $(call ac_fini))
+
+# check for some header file
+# args: header file
+ac_header = $(shell \
+ $(call ac_init,for $(1));\
+ $(call ac_b_cmd,echo '\#include <$(1)>' |\
+ $(CC) $(CFLAGS) -E -);\
+ $(call ac_fini))
+
+# check for some function
+# args: function [, additional libs ]
+ac_func = $(shell \
+ $(call ac_init,for $(1));\
+ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
+ > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
+ __actest __actest.c $(2));\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check for some library
+# args: function, library [, additional libs ]
+ac_lib = $(shell \
+ $(call ac_init,for $(1) in $(2));\
+ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \
+ > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \
+ __actest __actest.c -l$(2) $(3));\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check if some compiler flag works
+# args: compiler flag
+ac_cflag = $(shell \
+ $(call ac_init,if $(CC) supports $(1));\
+ echo 'int main() {return 0;}' > __actest.c;\
+ $(call ac_b_cmd,$(CC) $(CFLAGS) $(1) $(LDFLAGS) -o \
+ __actest __actest.c);\
+ rm -f __actest __actest.c;\
+ $(call ac_fini))
+
+# check for some binary
+# args: binary name
+ac_binary = $(shell \
+ $(call ac_init,for $(1));\
+ $(call ac_s_cmd,which $(1));\
+ bin="$$rc";rc="no";\
+ $(call ac_b_cmd,test -x "$$$$bin");\
+ $(call ac_fini))
+
+# check if lib64 is used
+#ac_lib64 = $(shell \
+# $(call ac_init,for libdir name);\
+# $(call ac_s_cmd,$(CC) -print-search-dirs | grep -q lib64 &&\
+# echo "lib64" || echo "lib");\
+# $(call ac_fini))
+ac_lib64 = $(shell \
+ $(call ac_init,for libdir name);\
+ $(call ac_s_cmd,/sbin/ldconfig -p | grep -q lib64 &&\
+ echo "lib64" || echo "lib");\
+ $(call ac_fini))
+
+# check for x11 ressource dir prefix
+ac_resdir = $(shell \
+ $(call ac_init,for X11 app-defaults prefix);\
+ $(call ac_s_cmd, for dir in \
+ /etc/X11/app-defaults \
+ /usr/X11R6/lib/X11/app-defaults \
+ /usr/share/X11/app-defaults \
+ /usr/lib/X11/app-defaults \
+ ; do test -d "$$dir" || continue;\
+ dirname "$$dir"; break; done);\
+ $(call ac_fini))
+
+# check if package is installed, via pkg-config
+# args: pkg name
+ac_pkg_config = $(shell \
+ $(call ac_init,for $(1) (using pkg-config));\
+ $(call ac_b_cmd, pkg-config $(1));\
+ $(call ac_fini))
+
+# grep some file
+# args: regex, file
+ac_grep = $(shell \
+ $(call ac_init,for $(1) in $(2));\
+ $(call ac_b_cmd, grep -q $(1) $(2));\
+ $(call ac_fini))
+
+
+########################################################################
+# build Make.config
+
+define newline
+
+
+endef
+make-config-q = $(subst $(newline),\n,$(make-config))
+
+ifeq ($(filter config,$(MAKECMDGOALS)),config)
+.PHONY: Make.config
+ LIB := $(call ac_lib64)
+else
+ LIB ?= $(call ac_lib64)
+ LIB := $(LIB)
+endif
+.PHONY: config
+config: Make.config
+ @true
+
+Make.config: $(srcdir)/GNUmakefile
+ @echo -e "$(make-config-q)" > $@
+ @echo
+ @echo "Make.config written, edit if needed"
+ @echo
diff --git a/mk/Compile.mk b/mk/Compile.mk
new file mode 100644
index 0000000..4ab59f4
--- /dev/null
+++ b/mk/Compile.mk
@@ -0,0 +1,102 @@
+#
+# some rules to compile stuff ...
+#
+# (c) 2002-2006 Gerd Hoffmann <kraxel@suse.de>
+#
+# main features:
+# * autodependencies via "cpp -MD"
+# * fancy, non-verbose output
+#
+# This file is public domain. No warranty. If it breaks you keep
+# both pieces.
+#
+########################################################################
+
+# verbose yes/no
+verbose ?= no
+
+# dependency files
+tmpdep = mk/$(subst /,_,$*).tmp
+depfile = mk/$(subst /,_,$*).dep
+depfiles = mk/*.dep
+
+compile_c = $(CC) $(CFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
+compile_c_pic = $(CC) $(CFLAGS) -fPIC -Wp,-MD,$(tmpdep) -c -o $@ $<
+compile_cc = $(CXX) $(CXXFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $<
+fixup_deps = sed -e "s|.*\.o:|$@:|" < $(tmpdep) > $(depfile) && rm -f $(tmpdep)
+cc_makedirs = mkdir -p $(dir $@) $(dir $(depfile))
+
+link_app = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+link_so = $(CC) $(LDFLAGS) -shared -Wl,-soname,$(@F) -o $@ $^ $(LDLIBS)
+ar_lib = rm -f $@ && ar -r $@ $^ && ranlib $@
+
+moc_h = $(MOC) $< -o $@
+msgfmt_po = msgfmt -o $@ $<
+
+# non-verbose output
+ifeq ($(verbose),no)
+ echo_compile_c = echo " CC " $@
+ echo_compile_c_pic = echo " CC " $@
+ echo_compile_cc = echo " CXX " $@
+ echo_link_app = echo " LD " $@
+ echo_link_so = echo " LD " $@
+ echo_ar_lib = echo " AR " $@
+ echo_moc_h = echo " MOC " $@
+ echo_msgfmt_po = echo " MSGFMT " $@
+else
+ echo_compile_c = echo $(compile_c)
+ echo_compile_c = echo $(compile_c_pic)
+ echo_compile_cc = echo $(compile_cc)
+ echo_link_app = echo $(link_app)
+ echo_link_so = echo $(link_so)
+ echo_ar_lib = echo $(ar_lib)
+ echo_moc_h = echo $(moc_h)
+ echo_msgfmt_po = echo $(msgfmt_po)
+endif
+
+%.o: %.c
+ @$(cc_makedirs)
+ @$(echo_compile_c)
+ @$(compile_c)
+ @$(fixup_deps)
+
+%.opic: %.c
+ @$(cc_makedirs)
+ @$(echo_compile_c_pic)
+ @$(compile_c_pic)
+ @$(fixup_deps)
+
+%.o: %.cc
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+%.o: %.cpp
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+
+%: %.o
+ @$(echo_link_app)
+ @$(link_app)
+
+%.so: %.o
+ @$(echo_link_so)
+ @$(link_so)
+
+%.a: %.o
+ @$(echo_ar_lib)
+ @$(ar_lib)
+
+
+%.moc : %.h
+ @$(echo_moc_h)
+ @$(moc_h)
+
+%.mo : %.po
+ @$(echo_msgfmt_po)
+ @$(msgfmt_po)
+
diff --git a/mk/Maintainer.mk b/mk/Maintainer.mk
new file mode 100644
index 0000000..23446ae
--- /dev/null
+++ b/mk/Maintainer.mk
@@ -0,0 +1,28 @@
+# just some maintainer stuff for me ...
+########################################################################
+
+make-sync-dir = $(HOME)/projects/gnu-makefiles
+
+.PHONY: sync
+sync:: distclean
+ test -d $(make-sync-dir)
+ rm -f $(srcdir)/INSTALL $(srcdir)/mk/*.mk
+ cp -v $(make-sync-dir)/INSTALL $(srcdir)/.
+ cp -v $(make-sync-dir)/*.mk $(srcdir)/mk
+ chmod 444 $(srcdir)/INSTALL $(srcdir)/mk/*.mk
+
+
+repository = $(shell cat CVS/Repository)
+release-dir ?= $(HOME)/projects/Releases
+release-pub ?= goldbach@me.in-berlin.de:dl.bytesex.org/releases/$(repository)
+tarball = $(release-dir)/$(repository)-$(VERSION).tar.gz
+
+.PHONY: release
+release:
+ cvs tag $(RELTAG)
+ cvs export -r $(RELTAG) -d "$(repository)-$(VERSION)" "$(repository)"
+ find "$(repository)-$(VERSION)" -name .cvsignore -exec rm -fv "{}" ";"
+ tar -c -z -f "$(tarball)" "$(repository)-$(VERSION)"
+ rm -rf "$(repository)-$(VERSION)"
+ scp $(tarball) $(release-pub)
+
diff --git a/mk/Variables.mk b/mk/Variables.mk
new file mode 100644
index 0000000..99f787c
--- /dev/null
+++ b/mk/Variables.mk
@@ -0,0 +1,55 @@
+# common variables ...
+########################################################################
+
+# directories
+DESTDIR =
+srcdir ?= .
+prefix ?= /usr/local
+bindir = $(DESTDIR)$(prefix)/bin
+sbindir = $(DESTDIR)$(prefix)/sbin
+libdir = $(DESTDIR)$(prefix)/$(LIB)
+shrdir = $(DESTDIR)$(prefix)/share
+mandir = $(shrdir)/man
+locdir = $(shrdir)/locale
+appdir = $(shrdir)/applications
+
+# package + version
+empty :=
+space := $(empty) $(empty)
+ifneq ($(wildcard $(srcdir)/VERSION),)
+ VERSION := $(shell cat $(srcdir)/VERSION)
+else
+ VERSION := 42
+endif
+RELTAG := v$(subst .,_,$(VERSION))
+
+# programs
+CC ?= gcc
+CXX ?= g++
+MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc)
+
+STRIP ?= -s
+INSTALL ?= install
+INSTALL_BINARY := $(INSTALL) $(STRIP)
+INSTALL_SCRIPT := $(INSTALL)
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -d
+
+# cflags
+CFLAGS ?= -g -O2
+CXXFLAGS ?= $(CFLAGS)
+CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \
+ -Wpointer-arith -Wunused
+CXXFLAGS += -Wall -Wpointer-arith -Wunused
+
+# add /usr/local to the search path if something is in there ...
+ifneq ($(wildcard /usr/local/include/*.h),)
+ CFLAGS += -I/usr/local/include
+ LDFLAGS += -L/usr/local/$(LIB)
+endif
+
+# fixup include path for $(srcdir) != "."
+ifneq ($(srcdir),.)
+ CFLAGS += -I. -I$(srcdir)
+endif
+
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 0000000..f8bd9ff
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <gtk/gtk.h>
+#include <vte/vte.h>
+
+#include "qemu-gtk.h"
+#include "tcp.h"
+
+/* ----------------------------------------------------------------- */
+
+static int monitor_parse(struct qemu_window *win, char *buf, int len)
+{
+ char *reply, *prompt, *cmd;
+ int off;
+
+ prompt = strstr(buf, "(qemu) ");
+ if (NULL == prompt)
+ return 0;
+ off = prompt - buf;
+ if (prompt > buf+1 && prompt[-2] == '\r' && prompt[-1] == '\n')
+ prompt -= 2;
+ *prompt = 0;
+
+ if (!win->mrun)
+ goto out;
+ cmd = win->mrun->cmd;
+ reply = strstr(buf, "\r\n");
+ if (!reply)
+ goto out;
+ reply += 2;
+
+#if 0
+ fprintf(stderr, "\"%s\" -> [%s]\n--\n", cmd, reply);
+#endif
+ if (0 == strcmp(cmd, "info version")) {
+ snprintf(win->version, sizeof(win->version), "%s", reply);
+ update_status(win);
+ } else if (0 == strcmp(cmd, "info name")) {
+ snprintf(win->name, sizeof(win->name), "%s", reply);
+ update_status(win);
+ } else if (0 == strcmp(cmd, "info vnc")) {
+ if (1 == sscanf(reply, "VNC server active on: %127[^\r\n]",
+ win->vnc_display) &&
+ strstr(reply, "No client connected"))
+ vnc_connect(win);
+ }
+
+out:
+ return off + 7;
+}
+
+/* ----------------------------------------------------------------- */
+
+static void monitor_next(struct qemu_window *win)
+{
+ char buf[256];
+ int len;
+
+ if (win->mrun)
+ free(win->mrun);
+ win->mrun = win->mqueue;
+ if (!win->mrun)
+ return;
+ win->mqueue = win->mrun->next;
+ len = snprintf(buf, sizeof(buf), "%s\n", win->mrun->cmd);
+ write(win->monitor.handle, buf, len);
+}
+
+void monitor_append(struct qemu_window *win, char *cmd)
+{
+ struct qemu_mq *mq;
+
+ if (win->mqueue) {
+ for (mq = win->mqueue; mq->next; mq = mq->next)
+ ;
+ mq->next = malloc(sizeof(*mq));
+ mq = mq->next;
+ } else {
+ win->mqueue = malloc(sizeof(*mq));
+ mq = win->mqueue;
+ }
+ mq->next = NULL;
+ snprintf(mq->cmd, sizeof(mq->cmd), "%s", cmd);
+
+ if (0 == win->mused)
+ monitor_next(win);
+}
+
+static gboolean monitor_watch(GIOChannel *source, GIOCondition condition,
+ gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ int rc;
+
+ if (win->mused == win->msize) {
+ if (!win->msize)
+ win->msize = 4;
+ win->msize *= 2;
+ win->mbuf = realloc(win->mbuf, win->msize +1);
+ }
+
+ rc = read(win->monitor.handle, win->mbuf + win->mused, win->msize - win->mused);
+ switch(rc) {
+ case -1:
+ if (EINTR == errno)
+ break;
+ perror("monitor: read");
+ goto close;
+ case 0:
+ fprintf(stderr, "monitor: EOF\n");
+ goto close;
+ default:
+ if (win->monitor.vte)
+ vte_terminal_feed(VTE_TERMINAL(win->monitor.vte), win->mbuf + win->mused, rc);
+ win->mused += rc;
+ win->mbuf[win->mused] = 0;
+ rc = monitor_parse(win, win->mbuf, win->mused);
+ if (rc) {
+ if (rc < win->mused)
+ memmove(win->mbuf, win->mbuf + rc, win->mused - rc);
+ win->mused -= rc;
+ }
+ break;
+ }
+
+ if (0 == win->mused)
+ monitor_next(win);
+ return TRUE;
+
+close:
+ if (win->monitor.vte)
+ vte_terminal_feed(VTE_TERMINAL(win->monitor.vte), "\r\n=== CLOSED ===", 16);
+ close(win->monitor.handle);
+ win->monitor.handle = -1;
+// gtk_widget_destroy(win->toplevel);
+ return FALSE;
+}
+
+/* ----------------------------------------------------------------- */
+
+int monitor_connect(struct qemu_window *win, char *dest)
+{
+ int fd;
+
+ fd = conn_init(&win->monitor, "monitor", dest);
+ if (-1 == fd)
+ return -1;
+
+ win->monitor.ch = g_io_channel_unix_new(fd);
+ win->monitor.id = g_io_add_watch(win->monitor.ch, G_IO_IN, monitor_watch, win);
+
+ monitor_append(win, "info version");
+ monitor_append(win, "info name");
+ monitor_append(win, "info vnc");
+ return fd;
+}
diff --git a/qemu-gtk b/qemu-gtk
new file mode 100755
index 0000000..cf40928
--- /dev/null
+++ b/qemu-gtk
Binary files differ
diff --git a/qemu-gtk.c b/qemu-gtk.c
new file mode 100644
index 0000000..164e1c7
--- /dev/null
+++ b/qemu-gtk.c
@@ -0,0 +1,528 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <signal.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <vte/vte.h>
+#include <vncdisplay.h>
+
+#include "qemu-gtk.h"
+#include "tcp.h"
+
+/* ------------------------------------------------------------------ */
+
+static void vte_configure(GtkWidget *vte)
+{
+ vte_terminal_set_scrollback_lines(VTE_TERMINAL(vte), 4096);
+ vte_terminal_set_backspace_binding(VTE_TERMINAL(vte),
+ VTE_ERASE_ASCII_BACKSPACE);
+}
+
+static void tabs_configure(struct qemu_window *win)
+{
+ if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(win->tab)) == 1) {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->tab), 0);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(win->tab), 0);
+ } else {
+ gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->tab), 1);
+ gtk_notebook_set_show_border(GTK_NOTEBOOK(win->tab), 1);
+ }
+}
+
+static void tabs_add(struct qemu_window *win, GtkWidget *child,
+ const char *text, int pos)
+{
+ GtkWidget *label;
+
+ label = gtk_label_new(text);
+ gtk_notebook_insert_page(GTK_NOTEBOOK(win->tab), child, label, pos);
+ gtk_widget_show(child);
+ if (0 == pos)
+ gtk_notebook_set_current_page(GTK_NOTEBOOK(win->tab), 0);
+ tabs_configure(win);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void menu_cb_scale_display(GtkToggleAction *action, gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ gboolean active;
+
+ active = gtk_toggle_action_get_active(action);
+ vnc_display_set_scaling(VNC_DISPLAY(win->vnc), active);
+}
+
+static void menu_cb_close(GtkAction *action, gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ gtk_widget_destroy(win->toplevel);
+}
+
+static void menu_cb_monitor_stop(GtkAction *action, gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ monitor_append(win, "stop");
+}
+
+static void menu_cb_monitor_cont(GtkAction *action, gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ monitor_append(win, "cont");
+}
+
+static void menu_cb_run_gdb(GtkAction *action, gpointer userdata)
+{
+ struct qemu_window *win = userdata;
+ char *argv[] = { "gdb", NULL };
+ char cmd[256];
+ int len;
+
+ if (win->gdb_vte)
+ return;
+ win->gdb_vte = vte_terminal_new();
+ vte_configure(win->gdb_vte);
+ tabs_add(win, win->gdb_vte, "gdb", -1);
+
+ monitor_append(win, "gdbserver 1234");
+ len = snprintf(cmd, sizeof(cmd), "target remote %s:1234\n", win->monitor.hostname);
+
+ win->gdb_pid = vte_terminal_fork_command(VTE_TERMINAL(win->gdb_vte),
+ argv[0], argv, NULL, NULL,
+ FALSE, FALSE, FALSE);
+ vte_terminal_feed_child(VTE_TERMINAL(win->gdb_vte), cmd, len);
+}
+
+void update_status(struct qemu_window *win)
+{
+ char msg[256];
+ int len = 0;
+
+ if (win->vnc_grab) {
+ len = snprintf(msg, sizeof(msg), "Press Ctrl-Alt to release input grab.");
+ goto out;
+ }
+
+ if (win->vnc_state == VNC_INITIALIZED) {
+ len += snprintf(msg+len, sizeof(msg)-len, "VNC display \"%s\", %dx%d",
+ win->vnc_display, win->vnc_width, win->vnc_height);
+ } else {
+ len += snprintf(msg+len, sizeof(msg)-len, "No VNC");
+ }
+
+ if (win->version && strlen(win->version))
+ len += snprintf(msg+len, sizeof(msg)-len, ", qemu %s", win->version);
+ if (win->name && strlen(win->name))
+ len += snprintf(msg+len, sizeof(msg)-len, ", name \"%s\"", win->name);
+
+ len += snprintf(msg+len, sizeof(msg)-len, ".");
+
+out:
+ gtk_label_set_text(GTK_LABEL(win->status), msg);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void vnc_connected(GtkWidget *vncdisplay, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_state = VNC_CONNECTED;
+ update_status(win);
+}
+
+static void vnc_initialized(GtkWidget *vncdisplay, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_state = VNC_INITIALIZED;
+ update_status(win);
+}
+
+static void vnc_disconnected(GtkWidget *vncdisplay, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_state = VNC_DISCONNECTED;
+ update_status(win);
+}
+
+static void vnc_grab(GtkWidget *vncdisplay, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_grab = 1;
+ update_status(win);
+}
+
+static void vnc_ungrab(GtkWidget *vncdisplay, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_grab = 0;
+ update_status(win);
+}
+
+static void vnc_desktop_resize(GtkWidget *vncdisplay, int x, int y, void *data)
+{
+ struct qemu_window *win = data;
+ win->vnc_width = x;
+ win->vnc_height = y;
+ update_status(win);
+}
+
+static void vnc_credential(GtkWidget *vncdisplay,
+ GValueArray *credList,
+ void *data)
+{
+// struct qemu_window *win = data;
+ fprintf(stderr, "%s: FIXME\n", __FUNCTION__);
+}
+
+void vnc_connect(struct qemu_window *win)
+{
+ int nr;
+
+ if (2 == sscanf(win->vnc_display, "%127[^:]:%d", win->vnc_hostname, &nr)) {
+ sprintf(win->vnc_tcpport, "%d", nr + 5900);
+ } else if (1 == sscanf(win->vnc_display, ":%d", &nr)) {
+ sprintf(win->vnc_hostname, "%s", win->monitor.hostname);
+ sprintf(win->vnc_tcpport, "%d", nr + 5900);
+ } else {
+ fprintf(stderr, "parse error: \"%s\"\n", win->vnc_display);
+ return;
+ }
+
+ qemu_vnc_tab(win);
+ vnc_display_open_host(VNC_DISPLAY(win->vnc),
+ win->vnc_hostname, win->vnc_tcpport);
+}
+
+/* ------------------------------------------------------------------ */
+
+int conn_init(struct qemu_conn *conn, char *name, char *dest)
+{
+ struct addrinfo ask;
+ char path[256];
+ char serv[33];
+
+ memset(&ask,0,sizeof(ask));
+ ask.ai_socktype = SOCK_STREAM;
+ ask.ai_family = PF_UNSPEC;
+ tcp_verbose = 1;
+
+ snprintf(conn->name, sizeof(conn->name), "%s", name);
+ strcpy(conn->hostname, "localhost");
+ if (2 == sscanf(dest, "tcp:%64[^:]:%32s", conn->hostname, serv)) {
+ conn->handle = tcp_connect(&ask, NULL, NULL, conn->hostname, serv);
+ } else if (1 == sscanf(dest, "tcp:%32s", serv)) {
+ conn->handle = tcp_connect(&ask, NULL, NULL, conn->hostname, serv);
+ } else if (1 == sscanf(dest, "unix:%255s", path)) {
+ conn->handle = unix_connect(path);
+ } else if (1 == sscanf(dest, "pipe:%255s", path)) {
+ conn->handle = pipe_connect(path);
+ } else {
+ fprintf(stderr, "can't parse \"%s\"\n", dest);
+ conn->handle = -1;
+ }
+ return conn->handle;
+}
+
+static void conn_user_input(VteTerminal *vte, gchar *buf, guint len,
+ gpointer data)
+{
+ struct qemu_conn *conn = data;
+
+ if (conn->handle != -1)
+ write(conn->handle, buf, len);
+}
+
+static gboolean conn_watch(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ struct qemu_conn *conn = data;
+ char buf[256];
+ int rc;
+
+ rc = read(conn->handle, buf, sizeof(buf));
+ switch(rc) {
+ case -1:
+ if (EINTR == errno)
+ break;
+ perror("console: read");
+ goto close;
+ case 0:
+ fprintf(stderr, "console: EOF\n");
+ goto close;
+ default:
+ if (conn->vte)
+ vte_terminal_feed(VTE_TERMINAL(conn->vte), buf, rc);
+ break;
+ }
+ return TRUE;
+
+close:
+ if (conn->vte)
+ vte_terminal_feed(VTE_TERMINAL(conn->vte), "\r\n=== CLOSED ===", 16);
+ close(conn->handle);
+ conn->handle = -1;
+ return FALSE;
+}
+
+static int conn_connect(struct qemu_conn *conn, char *name, char *dest)
+{
+ int fd;
+
+ fd = conn_init(conn, name, dest);
+ if (-1 == fd)
+ return -1;
+
+ conn->ch = g_io_channel_unix_new(fd);
+ conn->id = g_io_add_watch(conn->ch, G_IO_IN, conn_watch, conn);
+ return fd;
+}
+
+/* ------------------------------------------------------------------ */
+
+static const GtkActionEntry entries[] = {
+ {
+ .name = "FileMenu",
+ .label = "_File",
+ },{
+ .name = "ViewMenu",
+ .label = "_View",
+ },{
+ .name = "ActionMenu",
+ .label = "_Actions",
+ },{
+ .name = "Close",
+ .stock_id = GTK_STOCK_CLOSE,
+ .label = "_Close",
+ .accelerator = "<control>Q",
+ .callback = G_CALLBACK(menu_cb_close),
+ },{
+ .name = "MonitorStop",
+ .label = "_Pause VM",
+ .callback = G_CALLBACK(menu_cb_monitor_stop),
+ },{
+ .name = "MonitorCont",
+ .label = "_Unpause VM",
+ .callback = G_CALLBACK(menu_cb_monitor_cont),
+ },{
+ .name = "RunGdb",
+ .label = "_Debug with gdb",
+ .callback = G_CALLBACK(menu_cb_run_gdb),
+ },
+};
+
+static const GtkToggleActionEntry tentries[] = {
+ {
+ .name = "ScaleDisplay",
+ .label = "_Scale Display",
+ .callback = G_CALLBACK(menu_cb_scale_display),
+ }
+};
+
+static char ui_xml[] =
+"<ui>"
+" <menubar name='MainMenu'>"
+" <menu action='FileMenu'>"
+" <menuitem action='Close'/>"
+" </menu>"
+" <menu action='ViewMenu'>"
+" <menuitem action='ScaleDisplay'/>"
+" </menu>"
+" <menu action='ActionMenu'>"
+" <menuitem action='MonitorStop'/>"
+" <menuitem action='MonitorCont'/>"
+" <menuitem action='RunGdb'/>"
+" </menu>"
+" </menubar>"
+#ifdef WITH_TOOLBAR
+" <toolbar action='ToolBar'>"
+" <toolitem action='Close'/>"
+" </toolbar>"
+#endif
+"</ui>";
+
+static void destroy(GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit();
+}
+
+static struct qemu_window *qemu_create_window(void)
+{
+ struct qemu_window *win;
+ GtkWidget *vbox, *menubar, *toolbar, *frame;
+ GtkAccelGroup *accel;
+ GtkActionGroup *ag;
+ GtkUIManager *ui;
+ GError *err;
+
+ win = malloc(sizeof(*win));
+ if (NULL == win)
+ return NULL;
+ memset(win,0,sizeof(*win));
+
+ win->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(win->toplevel), "qemu-gtk");
+ gtk_window_set_default_size(GTK_WINDOW(win->toplevel), 320, 280);
+ g_signal_connect(G_OBJECT(win->toplevel), "destroy",
+ G_CALLBACK(destroy), win);
+
+ /* menu + toolbar */
+ ui = gtk_ui_manager_new();
+ ag = gtk_action_group_new("MenuActions");
+ gtk_action_group_add_actions(ag, entries, G_N_ELEMENTS(entries), win);
+ gtk_action_group_add_toggle_actions(ag, tentries,
+ G_N_ELEMENTS(tentries), win);
+ gtk_ui_manager_insert_action_group(ui, ag, 0);
+ accel = gtk_ui_manager_get_accel_group(ui);
+ gtk_window_add_accel_group(GTK_WINDOW(win->toplevel), accel);
+
+ err = NULL;
+ if (!gtk_ui_manager_add_ui_from_string(ui, ui_xml, -1, &err)) {
+ g_message("building menus failed: %s", err->message);
+ g_error_free(err);
+ exit(1);
+ }
+
+ /* main area */
+ win->tab = gtk_notebook_new();
+
+
+ /* status line */
+ win->status = gtk_label_new("status line");
+ gtk_misc_set_alignment(GTK_MISC(win->status), 0, 0.5);
+ gtk_misc_set_padding(GTK_MISC(win->status), 3, 1);
+ update_status(win);
+
+ /* Make a vbox and put stuff in */
+ vbox = gtk_vbox_new(FALSE, 1);
+ gtk_container_set_border_width(GTK_CONTAINER(vbox), 1);
+ gtk_container_add(GTK_CONTAINER(win->toplevel), vbox);
+ menubar = gtk_ui_manager_get_widget(ui, "/MainMenu");
+ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
+ toolbar = gtk_ui_manager_get_widget(ui, "/ToolBar");
+ if (toolbar)
+ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), win->tab, TRUE, TRUE, 0);
+
+ frame = gtk_frame_new(NULL);
+ gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(frame), win->status);
+
+ return win;
+}
+
+void qemu_vnc_tab(struct qemu_window *win)
+{
+ if (win->vnc)
+ return;
+ win->vnc = vnc_display_new();
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-connected",
+ GTK_SIGNAL_FUNC(vnc_connected), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-initialized",
+ GTK_SIGNAL_FUNC(vnc_initialized), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-disconnected",
+ GTK_SIGNAL_FUNC(vnc_disconnected), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-pointer-grab",
+ GTK_SIGNAL_FUNC(vnc_grab), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-pointer-ungrab",
+ GTK_SIGNAL_FUNC(vnc_ungrab), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-auth-credential",
+ GTK_SIGNAL_FUNC(vnc_credential), win);
+ gtk_signal_connect(GTK_OBJECT(win->vnc), "vnc-desktop-resize",
+ GTK_SIGNAL_FUNC(vnc_desktop_resize), win);
+ tabs_add(win, win->vnc, "vnc display", 0);
+}
+
+void qemu_conn_tab(struct qemu_window *win, struct qemu_conn *conn, int pos)
+{
+ if (conn->vte)
+ return;
+ conn->vte = vte_terminal_new();
+ vte_configure(conn->vte);
+ g_signal_connect(conn->vte, "commit",
+ G_CALLBACK(conn_user_input), conn);
+ tabs_add(win, conn->vte, conn->name, pos);
+}
+
+/* ------------------------------------------------------------------ */
+
+static void usage(FILE *fp)
+{
+ fprintf(fp,
+ "This is a simple qemu gui\n"
+ "\n"
+ "usage: qemu-gtk [ options ] monitor\n"
+ "options:\n"
+ " -h Print this text.\n"
+ " -d Raise debug level.\n"
+ " -m Enable monitor logging and access.\n"
+ " -c <dev> Show serial console.\n"
+ "\n"
+ "-- \n"
+ "(c) 2008 Gerd Hoffmann <kraxel@redhat.com>\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct qemu_window *win;
+ char *console_tab = NULL;
+ int monitor_tab = 0;
+ int debug = 0;
+ int c;
+
+ gtk_init(&argc, &argv);
+ for (;;) {
+ if (-1 == (c = getopt(argc, argv, "hdmc:")))
+ break;
+ switch (c) {
+ case 'd':
+ debug++;
+ break;
+ case 'm':
+ monitor_tab++;
+ break;
+ case 'c':
+ console_tab = optarg;
+ break;
+ case 'h':
+ usage(stdout);
+ exit(0);
+ default:
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ if (optind == argc) {
+ usage(stderr);
+ exit(1);
+ }
+
+ /* main window */
+ win = qemu_create_window();
+ if (-1 == monitor_connect(win, argv[optind])) {
+ exit(1);
+ }
+
+ /* tabs */
+ if (monitor_tab)
+ qemu_conn_tab(win, &win->monitor, -1);
+ if (console_tab) {
+ if (-1 != conn_connect(&win->console, "console", console_tab))
+ qemu_conn_tab(win, &win->console, 0);
+ }
+
+ /* main loop */
+ gtk_widget_show_all(win->toplevel);
+ gtk_main();
+ if (win->gdb_pid)
+ kill(win->gdb_pid, SIGTERM);
+ exit(0);
+}
diff --git a/qemu-gtk.h b/qemu-gtk.h
new file mode 100644
index 0000000..1db432b
--- /dev/null
+++ b/qemu-gtk.h
@@ -0,0 +1,70 @@
+struct qemu_window;
+
+struct qemu_mq {
+ struct qemu_mq *next;
+ char cmd[64];
+};
+
+enum vnc_state {
+ VNC_NONE = 0,
+ VNC_CONNECTED,
+ VNC_INITIALIZED,
+ VNC_DISCONNECTED,
+};
+
+struct qemu_conn {
+ int handle;
+ GIOChannel *ch;
+ guint id;
+ GtkWidget *vte;
+ struct qemu_window *win;
+ char name[32];
+ char hostname[128];
+};
+
+struct qemu_window {
+ /* widgets */
+ GtkWidget *toplevel, *status;
+ GtkWidget *tab, *vnc;
+
+ /* vnc (gfx) */
+ char vnc_display[128];
+ char vnc_hostname[128];
+ char vnc_tcpport[16];
+ enum vnc_state vnc_state;
+ int vnc_grab;
+ int vnc_width;
+ int vnc_height;
+
+ /* console (text) */
+ struct qemu_conn console;
+
+ /* monitor */
+ struct qemu_conn monitor;
+ char *mbuf;
+ int msize;
+ int mused;
+ struct qemu_mq *mqueue;
+ struct qemu_mq *mrun;
+
+ /* gdb */
+ GtkWidget *gdb_vte;
+ pid_t gdb_pid;
+
+ /* vm info */
+ char version[32];
+ char name[128];
+};
+
+/* qemu-gtk.c */
+void update_status(struct qemu_window *win);
+void vnc_connect(struct qemu_window *win);
+int conn_init(struct qemu_conn *conn, char *name, char *dest);
+void qemu_vnc_tab(struct qemu_window *win);
+void qemu_conn_tab(struct qemu_window *win, struct qemu_conn *conn, int pos);
+
+/* monitor.c */
+int monitor_connect(struct qemu_window *win, char *dest);
+void monitor_append(struct qemu_window *win, char *cmd);
+
+
diff --git a/tcp.c b/tcp.c
new file mode 100644
index 0000000..60a824f
--- /dev/null
+++ b/tcp.c
@@ -0,0 +1,197 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "tcp.h"
+
+int tcp_verbose;
+
+/* ------------------------------------------------------------------ */
+
+static char *strfamily(int family)
+{
+ switch (family) {
+ case PF_INET6: return "ipv6";
+ case PF_INET: return "ipv4";
+ case PF_UNIX: return "unix";
+ }
+ return "????";
+}
+
+int tcp_connect(struct addrinfo *ai,
+ char *addr, char *port,
+ char *host, char *serv)
+{
+ struct addrinfo *res,*e;
+ struct addrinfo *lres, ask;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ char uhost[INET6_ADDRSTRLEN+1];
+ char userv[33];
+ int sock,rc,opt=1;
+
+ /* lookup peer */
+ ai->ai_flags = AI_CANONNAME;
+ if (0 != (rc = getaddrinfo(host, serv, ai, &res))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo (peer): %s\n", gai_strerror(rc));
+ return -1;
+ }
+ for (e = res; e != NULL; e = e->ai_next) {
+ if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uhost,INET6_ADDRSTRLEN,userv,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ if (tcp_verbose)
+ fprintf(stderr,"getnameinfo (peer): oops\n");
+ continue;
+ }
+ if (-1 == (sock = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ if (tcp_verbose)
+ fprintf(stderr,"socket (%s): %s\n",
+ strfamily(e->ai_family),strerror(errno));
+ continue;
+ }
+ setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ if (NULL != addr || NULL != port) {
+ /* bind local port */
+ memset(&ask,0,sizeof(ask));
+ ask.ai_flags = AI_PASSIVE;
+ ask.ai_family = e->ai_family;
+ ask.ai_socktype = e->ai_socktype;
+ if (0 != (rc = getaddrinfo(addr, port, &ask, &lres))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo (local): %s\n",
+ gai_strerror(rc));
+ continue;
+ }
+ if (0 != getnameinfo((struct sockaddr*)lres->ai_addr,
+ lres->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV)) {
+ if (tcp_verbose)
+ fprintf(stderr,"getnameinfo (local): oops\n");
+ continue;
+ }
+ if (-1 == bind(sock, lres->ai_addr, lres->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s [%s] %s bind: %s\n",
+ strfamily(lres->ai_family),uaddr,uport,
+ strerror(errno));
+ continue;
+ }
+ }
+ /* connect to peer */
+ if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s %s [%s] %s connect: %s\n",
+ strfamily(e->ai_family),e->ai_canonname,uhost,userv,
+ strerror(errno));
+ close(sock);
+ continue;
+ }
+ if (tcp_verbose)
+ fprintf(stderr,"%s %s [%s] %s open\n",
+ strfamily(e->ai_family),e->ai_canonname,uhost,userv);
+ fcntl(sock,F_SETFL,O_NONBLOCK);
+ return sock;
+ }
+ return -1;
+}
+
+int tcp_listen(struct addrinfo *ai, char *addr, char *port)
+{
+ struct addrinfo *res,*e;
+ char uaddr[INET6_ADDRSTRLEN+1];
+ char uport[33];
+ int slisten,rc,opt=1;
+
+ /* lookup */
+ ai->ai_flags = AI_PASSIVE;
+ if (0 != (rc = getaddrinfo(addr, port, ai, &res))) {
+ if (tcp_verbose)
+ fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(rc));
+ exit(1);
+ }
+
+ /* create socket + bind */
+ for (e = res; e != NULL; e = e->ai_next) {
+ getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
+ uaddr,INET6_ADDRSTRLEN,uport,32,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (-1 == (slisten = socket(e->ai_family, e->ai_socktype,
+ e->ai_protocol))) {
+ if (tcp_verbose)
+ fprintf(stderr,"socket (%s): %s\n",
+ strfamily(e->ai_family),strerror(errno));
+ continue;
+ }
+ opt = 1;
+ setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
+ if (-1 == bind(slisten, e->ai_addr, e->ai_addrlen)) {
+ if (tcp_verbose)
+ fprintf(stderr,"%s [%s] %s bind: %s\n",
+ strfamily(e->ai_family),uaddr,uport,
+ strerror(errno));
+ continue;
+ }
+ listen(slisten,1);
+ break;
+ }
+ if (NULL == e)
+ return -1;
+
+ /* wait for a incoming connection */
+ if (tcp_verbose)
+ fprintf(stderr,"listen on %s [%s] %s ...\n",
+ strfamily(e->ai_family),uaddr,uport);
+ fcntl(slisten,F_SETFL,O_NONBLOCK);
+ return slisten;
+}
+
+int unix_connect(char *path)
+{
+ struct sockaddr_un un;
+ int sock;
+
+ if (-1 == (sock = socket(PF_UNIX, SOCK_STREAM, 0))) {
+ perror("socket");
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ strncpy(un.sun_path, path, sizeof(un.sun_path));
+ if (-1 == connect(sock, (struct sockaddr*) &un, sizeof(un))) {
+ fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+
+ return sock;
+}
+
+int pipe_connect(char *path)
+{
+ struct stat st;
+ int fd;
+
+ fd = open(path, O_RDWR);
+ if (fd == -1) {
+ mkfifo(path, 0666);
+ fd = open(path, O_RDWR);
+ }
+ if (fd == -1) {
+ fprintf(stderr, "open %s: %s\n", path, strerror(errno));
+ return -1;
+ }
+ fstat(fd, &st);
+ if (!S_ISFIFO(st.st_mode)) {
+ fprintf(stderr, "not a pipe: %s\n", path);
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
diff --git a/tcp.h b/tcp.h
new file mode 100644
index 0000000..963ab83
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,15 @@
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+extern int tcp_verbose;
+
+int tcp_connect(struct addrinfo *ai,
+ char *addr, char *port,
+ char *host, char *serv);
+
+int tcp_listen(struct addrinfo *ai, char *addr, char *port);
+
+int unix_connect(char *path);
+int pipe_connect(char *path);