aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile67
-rw-r--r--INSTALL59
-rw-r--r--VERSION1
-rw-r--r--list.h169
-rw-r--r--mk/Autoconf.mk145
-rw-r--r--mk/Compile.mk88
-rw-r--r--mk/Maintainer.mk12
-rw-r--r--mk/Variables.mk46
-rw-r--r--xd_store.c700
-rw-r--r--xd_store.h44
-rw-r--r--xd_view.c252
-rw-r--r--xenlog.c55
-rw-r--r--xenviews.h11
-rw-r--r--xenwatch.c27
-rw-r--r--xs_store.c693
-rw-r--r--xs_store.h37
-rw-r--r--xs_view.c187
17 files changed, 2593 insertions, 0 deletions
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..3ed3abe
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,67 @@
+# config
+-include Make.config
+include mk/Variables.mk
+
+# add our flags + libs
+CFLAGS += -DVERSION='"$(VERSION)"'
+
+# build
+TARGETS := xenlog
+GTK_TARGETS := xenwatch
+
+# 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)
+endef
+
+# gtk stuff
+ifeq ($(HAVE_GTK),yes)
+ $(GTK_TARGETS) : CFLAGS += $(shell pkg-config --cflags gtk+-x11-2.0) -Wno-strict-prototypes
+ $(GTK_TARGETS) : LDLIBS += $(shell pkg-config --libs gtk+-x11-2.0)
+ TARGETS += $(GTK_TARGETS)
+endif
+
+# xenstore
+ifeq ($(XENSRC),)
+ LDLIBS += -lxenstore
+else
+ CFLAGS += -I $(XENSRC)/dist/install/usr/include
+ LDLIBS += -I $(XENSRC)/dist/install/usr/$(LIB) -lxenstore
+endif
+
+
+########################################################################
+# rules
+
+build: $(TARGETS)
+
+install: build
+ install -d $(bindir)
+ install -s $(TARGETS) $(bindir)
+
+clean:
+ -rm -f *.o $(depfiles)
+
+realclean distclean: clean
+ -rm -f Make.config
+ -rm -f $(TARGETS) *~ xpm/*~ *.bak
+
+#############################################
+
+xenwatch: xenwatch.o xs_view.o xs_store.o xd_view.o xd_store.o
+
+xenlog: xenlog.o
+
+include mk/Compile.mk
+include mk/Maintainer.mk
+-include $(depfiles)
+
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..0d1ba36
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,59 @@
+
+howto compile and install this package
+======================================
+
+
+really short install instructions
+---------------------------------
+
+ $ make
+ $ su -c "make install"
+
+
+
+the more detailed version
+-------------------------
+
+Make sure you use GNU make. The file name "GNUmakefile" isn't a joke,
+this package really requires GNU make.
+
+As first step make will do some config checks on your system and write
+the results to Make.config. If you want to have a look at Make.config
+before the actual build starts you can run this step separately using
+"make config".
+
+The Makefiles use the usual GNU-ish Makefile conventions for variable
+names and default values, i.e. prefix=/usr/local, ...
+
+The values for some frequently adapted variables are initialized from
+the enviroment. Thus you can change the defaults simply by setting
+environment variables:
+
+ $ prefix="/usr"
+ $ CFLAGS="-O3 -mcpu=i686"
+ $ export prefix CFLAGS
+
+Almost any variable can be overridden on the make command line. It is
+often used this way to install into some buildroot for packaging ...
+
+ $ su -c "make DESTDIR=/tmp/buildroot install"
+
+... but it works for most other variables equally well. There are
+some exceptions through, it usually does _not_ work for CFLAGS for
+example.
+
+Try "make verbose=yes" if you want to see the complete command lines
+executed by make instead of the short messages (for trouble shooting,
+because you like this way, for whatever reason ...). This also makes
+the config checks performed by "make config" more verbose.
+
+If you don't trust my Makefiles you can run "make -n install" to see
+what "make install" would do on your system. It will produce
+human-readable output (unlike automake ...).
+
+Have fun,
+
+ Gerd
+
+--
+Gerd Knorr <kraxel@bytesex.org>
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/list.h b/list.h
new file mode 100644
index 0000000..6072f68
--- /dev/null
+++ b/list.h
@@ -0,0 +1,169 @@
+#ifndef _LIST_H
+#define _LIST_H 1
+
+/*
+ * Simple doubly linked list implementation.
+ * -- shameless stolen from the linux kernel sources
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+ struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a item entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * item,
+ struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = item;
+ item->next = next;
+ item->prev = prev;
+ prev->next = item;
+}
+
+/**
+ * list_add - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a item entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *item, struct list_head *head)
+{
+ __list_add(item, head, head->next);
+}
+
+/**
+ * list_add_tail - add a item entry
+ * @item: item entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a item entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *item, struct list_head *head)
+{
+ __list_add(item, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+ struct list_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+ return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the item list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+ struct list_head *first = list->next;
+
+ if (first != list) {
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+ }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr: the &struct list_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each - iterate over a list
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos: the &struct list_head to use as a loop counter.
+ * @n: another &struct list_head to use as temporary storage
+ * @head: the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev - iterate over a list in reverse order
+ * @pos: the &struct list_head to use as a loop counter.
+ * @head: the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+ for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+#endif /* _LIST_H */
diff --git a/mk/Autoconf.mk b/mk/Autoconf.mk
new file mode 100644
index 0000000..51053c9
--- /dev/null
+++ b/mk/Autoconf.mk
@@ -0,0 +1,145 @@
+#
+# simple autoconf system for GNU make
+#
+# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org>
+#
+# 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))
+
+# 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))
+
+# check for x11 ressource dir prefix
+ac_resdir = $(shell \
+ $(call ac_init,for X11 app-defaults prefix);\
+ $(call ac_s_cmd, test -d /etc/X11/app-defaults &&\
+ echo "/etc/X11" || echo "/usr/X11R6/lib/X11");\
+ $(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))
+
+
+########################################################################
+# 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..49dddbf
--- /dev/null
+++ b/mk/Compile.mk
@@ -0,0 +1,88 @@
+#
+# some rules to compile stuff ...
+#
+# (c) 2002-2004 Gerd Knorr <kraxel@bytesex.org>
+#
+# 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_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_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_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)
+
+%.o: %.cc
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+%.o: %.cpp
+ @$(cc_makedirs)
+ @$(echo_compile_cc)
+ @$(compile_cc)
+ @$(fixup_deps)
+
+
+%.so: %.o
+ @$(echo_link_so)
+ @$(link_so)
+
+%: %.o
+ @$(echo_link_app)
+ @$(link_app)
+
+%.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..62f02d6
--- /dev/null
+++ b/mk/Maintainer.mk
@@ -0,0 +1,12 @@
+# 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
diff --git a/mk/Variables.mk b/mk/Variables.mk
new file mode 100644
index 0000000..930f824
--- /dev/null
+++ b/mk/Variables.mk
@@ -0,0 +1,46 @@
+# common variables ...
+########################################################################
+
+# directories
+DESTDIR =
+srcdir ?= .
+prefix ?= /usr/local
+bindir = $(DESTDIR)$(prefix)/bin
+mandir = $(DESTDIR)$(prefix)/share/man
+locdir = $(DESTDIR)$(prefix)/share/locale
+
+# package + version
+empty :=
+space := $(empty) $(empty)
+ifneq ($(wildcard $(srcdir)/VERSION),)
+ VERSION := $(shell cat $(srcdir)/VERSION)
+else
+ VERSION := 42
+endif
+
+# programs
+CC ?= gcc
+CXX ?= g++
+MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc)
+INSTALL ?= install
+INSTALL_BINARY := $(INSTALL) -s
+INSTALL_SCRIPT := $(INSTALL)
+INSTALL_DATA := $(INSTALL) -m 644
+INSTALL_DIR := $(INSTALL) -d
+
+# cflags
+CFLAGS ?= -g -O2
+CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \
+ -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/xd_store.c b/xd_store.c
new file mode 100644
index 0000000..a4f6c37
--- /dev/null
+++ b/xd_store.c
@@ -0,0 +1,700 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <xs.h>
+#include "xd_store.h"
+#include "list.h"
+
+/* boring declarations of local functions */
+
+static void xen_doms_init (XenDoms *pkg_tree);
+static void xen_doms_class_init (XenDomsClass *klass);
+static void xen_doms_tree_model_init (GtkTreeModelIface *iface);
+static void xen_doms_finalize (GObject *object);
+static GtkTreeModelFlags xen_doms_get_flags (GtkTreeModel *tree_model);
+static gint xen_doms_get_n_columns (GtkTreeModel *tree_model);
+static GType xen_doms_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean xen_doms_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *xen_doms_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void xen_doms_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean xen_doms_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean xen_doms_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean xen_doms_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint xen_doms_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean xen_doms_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean xen_doms_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+static GObjectClass *parent_class = NULL;
+
+/* ------------------------------------------------------------------------- */
+
+#define XS_DOM_WATCH "/local/domain"
+#define XS_VM_WATCH "/vm"
+
+static int trace = 0;
+
+/* domains */
+struct xd_node {
+ char *xs_dom_path;
+ char *xs_vm_path;
+ char *id;
+ int updated;
+ struct list_head next;
+};
+
+struct _XenDoms {
+ GObject parent; /* this MUST be the first member */
+ struct xs_handle *xenstore;
+ GIOChannel *ch;
+ guint id;
+
+ struct list_head nodes;
+ int count;
+};
+
+static struct xs_data {
+ enum { DOM, VM } mode;
+ gint type;
+ char *name;
+} xs_fields[] = {
+ [ XEN_DOMS_COL_S_NAME ] = { DOM, G_TYPE_STRING, "name" },
+ [ XEN_DOMS_COL_S_UUID ] = { VM, G_TYPE_STRING, "uuid" },
+ [ XEN_DOMS_COL_S_OSTYPE ] = { VM, G_TYPE_STRING, "image/ostype" },
+ [ XEN_DOMS_COL_I_MEM ] = { VM, G_TYPE_INT, "memory" },
+ [ XEN_DOMS_COL_I_MAXMEM ] = { VM, G_TYPE_INT, "maxmem" },
+ [ XEN_DOMS_COL_I_CPUS ] = { VM, G_TYPE_INT, "vcpus" },
+ [ XEN_DOMS_COL_I_MAXCPUS ] = { VM, G_TYPE_INT, "vcpu_avail" },
+};
+
+/* ------------------------------------------------------------------------- */
+
+GType
+xen_doms_get_type (void)
+{
+ static GType xen_doms_type = 0;
+ static const GTypeInfo xen_doms_info = {
+ sizeof (XenDomsClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) xen_doms_class_init,
+ NULL, /* class finalize */
+ NULL, /* class_data */
+ sizeof (XenDoms),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) xen_doms_init
+ };
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) xen_doms_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ if (xen_doms_type)
+ return xen_doms_type;
+
+ xen_doms_type = g_type_register_static (G_TYPE_OBJECT, "XenDoms",
+ &xen_doms_info, (GTypeFlags)0);
+ g_type_add_interface_static (xen_doms_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+ return xen_doms_type;
+}
+
+static void
+xen_doms_class_init (XenDomsClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
+ object_class = (GObjectClass*) klass;
+
+ object_class->finalize = xen_doms_finalize;
+}
+
+static void
+xen_doms_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = xen_doms_get_flags;
+ iface->get_n_columns = xen_doms_get_n_columns;
+ iface->get_column_type = xen_doms_get_column_type;
+ iface->get_iter = xen_doms_get_iter;
+ iface->get_path = xen_doms_get_path;
+ iface->get_value = xen_doms_get_value;
+ iface->iter_next = xen_doms_iter_next;
+ iface->iter_children = xen_doms_iter_children;
+ iface->iter_has_child = xen_doms_iter_has_child;
+ iface->iter_n_children = xen_doms_iter_n_children;
+ iface->iter_nth_child = xen_doms_iter_nth_child;
+ iface->iter_parent = xen_doms_iter_parent;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static struct xd_node* dom_new(XenDoms *xd, char *path)
+{
+ struct xd_node *node;
+
+ node = malloc(sizeof(struct xd_node));
+ memset(node,0,sizeof(struct xd_node));
+ node->xs_dom_path = strdup(path);
+ node->id = strrchr(node->xs_dom_path, '/') + 1;
+ list_add_tail(&node->next, &xd->nodes);
+ xd->count++;
+ if (trace)
+ fprintf(stderr,"%s: %s\n", __FUNCTION__, node->xs_dom_path);
+ return node;
+}
+
+static void dom_remove(XenDoms *xd, struct xd_node *node)
+{
+ if (trace)
+ fprintf(stderr,"%s: %s\n", __FUNCTION__, node->xs_dom_path);
+ list_del(&node->next);
+ xd->count--;
+ free(node->xs_dom_path);
+ if (node->xs_vm_path)
+ free(node->xs_vm_path);
+ free(node);
+}
+
+static struct xd_node* dom_find(XenDoms *xd, char *path)
+{
+ struct xd_node *node;
+ struct list_head *item;
+ int len;
+
+ list_for_each(item, &xd->nodes) {
+ node = list_entry(item, struct xd_node, next);
+ len = strlen(node->xs_dom_path);
+ if (0 != strncmp(path, node->xs_dom_path, len))
+ continue;
+ if (0 == path[len])
+ return node;
+ if ('/' == path[len])
+ return node;
+ }
+ list_for_each(item, &xd->nodes) {
+ node = list_entry(item, struct xd_node, next);
+ if (NULL == node->xs_vm_path)
+ continue;
+ len = strlen(node->xs_vm_path);
+ if (0 != strncmp(path, node->xs_vm_path, len))
+ continue;
+ if (0 == path[len])
+ return node;
+ if ('/' == path[len])
+ return node;
+ }
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+#if 0
+static char *make_kb(char *in)
+{
+ char *out;
+ int value;
+
+ if (NULL == in)
+ return NULL;
+
+ out = malloc(strlen(in) + 16);
+ value = atoi(in);
+ free(in);
+
+ if (value > 9 * 1024 * 1024 )
+ sprintf(out,"%d GB", value / (1024*1024));
+ else if (value > 9 * 1024)
+ sprintf(out,"%d MB", value / 1024);
+ else
+ sprintf(out,"%d kB", value);
+ return out;
+}
+#endif
+
+static GtkTreePath*
+do_get_path(XenDoms *xd, struct xd_node *find)
+{
+ GtkTreePath *path;
+ struct xd_node *node;
+ struct list_head *item;
+ int i = 0;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, find->xs_dom_path);
+
+ path = gtk_tree_path_new();
+ list_for_each(item, &xd->nodes) {
+ node = list_entry(item, struct xd_node, next);
+ if (node == find) {
+ gtk_tree_path_append_index(path, i);
+ return path;
+ }
+ i++;
+ }
+ return NULL;
+}
+
+static void watch_handle_one(XenDoms *xd)
+{
+ char **vec = NULL;
+ unsigned int count;
+ struct xs_transaction_handle *xst;
+ struct xd_node *node;
+ char *xs_value = NULL;
+ char xs_path[256], id[32];
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+
+ vec = xs_read_watch(xd->xenstore, &count);
+ if ('/' != vec[XS_WATCH_PATH][0]) {
+ if (trace)
+ fprintf(stderr,"%s: ignore: no path %d \"%s\"\n", __FUNCTION__,
+ count, vec[XS_WATCH_PATH]);
+ goto out;
+ }
+
+ if (NULL == (xst = xs_transaction_start(xd->xenstore)))
+ goto out;
+ xs_value = xs_read(xd->xenstore, xst, vec[XS_WATCH_PATH], NULL);
+ xs_transaction_end(xd->xenstore, xst, 0);
+ node = dom_find(xd, vec[XS_WATCH_PATH]);
+
+ if (trace > 1)
+ fprintf(stderr,"%s: path \"%s\" node \"%s\" value \"%s\"\n",
+ __FUNCTION__, vec[XS_WATCH_PATH],
+ node ? node->xs_dom_path : "<null>",
+ xs_value ? xs_value : "<del>");
+
+ if (NULL == node) {
+ if (NULL != xs_value) {
+ /* new node */
+ if (1 != sscanf(vec[XS_WATCH_PATH], XS_DOM_WATCH "/%31[0-9]", id)) {
+ if (trace > 1)
+ fprintf(stderr,"%s: can't parse \"%s\"\n",
+ __FUNCTION__, vec[XS_WATCH_PATH]);
+ goto out;
+ }
+ snprintf(xs_path, sizeof(xs_path), "%s/%s", XS_DOM_WATCH, id);
+ node = dom_new(xd, xs_path);
+ path = do_get_path(xd, node);
+ memset(&iter, 0, sizeof(iter));
+ iter.user_data = node;
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(xd), path, &iter);
+ } else {
+ /* Hmm, something unknown deleted ... */
+ goto out;
+ }
+ } else {
+ if (NULL != xs_value ||
+ 0 != strcmp(vec[XS_WATCH_PATH], node->xs_dom_path)) {
+ /* update (also deleted sub-node) */
+ path = do_get_path(xd, node);
+ memset(&iter, 0, sizeof(iter));
+ iter.user_data = node;
+ node->updated++;
+ } else {
+ /* node deleted */
+ path = do_get_path(xd, node);
+ dom_remove(xd, node);
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(xd), path);
+ }
+ }
+
+ out:
+ if (path)
+ gtk_tree_path_free(path);
+ if (xs_value)
+ free(xs_value);
+ if (vec)
+ free(vec);
+}
+
+static int watch_post_updates(XenDoms *xd)
+{
+ struct xd_node *node;
+ struct list_head *item;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+ int rows = 0;
+
+ list_for_each(item, &xd->nodes) {
+ node= list_entry(item, struct xd_node, next);
+ if (0 == node->updated)
+ continue;
+ path = do_get_path(xd, node);
+ memset(&iter, 0, sizeof(iter));
+ iter.user_data = node;
+ gtk_tree_model_row_changed(GTK_TREE_MODEL(xd), path, &iter);
+ gtk_tree_path_free(path);
+ node->updated = 0;
+ rows++;
+ }
+ return rows;
+}
+
+static gboolean watch_trigger(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ XenDoms *xd = data;
+ int fd = xs_fileno(xd->xenstore);
+ struct timeval enter, now, timeout;
+ fd_set set;
+ int rc, done = 0, count=0, rows, usecs;
+
+ gettimeofday(&enter, NULL);
+ for (;!done;) {
+ watch_handle_one(xd);
+ count++;
+ FD_ZERO(&set);
+ FD_SET(fd,&set);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000; /* 0.01 sec */
+ rc = select(fd+1, &set, NULL, NULL, &timeout);
+ switch (rc) {
+ case -1:
+ perror("select");
+ exit(1);
+ case 0:
+ done = 1;
+ break;
+ default:
+ /* more data, continue */
+ break;
+ }
+ gettimeofday(&now, NULL);
+ usecs = (now.tv_sec - enter.tv_sec) * 1000000;
+ usecs += now.tv_usec - enter.tv_usec;
+ if (usecs > 100000)
+ done = 1; /* 0.1 sec */
+ }
+
+ rows = watch_post_updates(xd);
+ if (trace)
+ fprintf(stderr,"%s: %d update(s), %d row(s)\n",
+ __FUNCTION__, count, rows);
+ return TRUE;
+}
+
+static void
+xen_doms_init(XenDoms *xd)
+{
+ struct xs_transaction_handle *xst;
+ unsigned int i, num;
+ char **list;
+ char path[256];
+
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ INIT_LIST_HEAD(&xd->nodes);
+
+#if 1
+ xd->xenstore = xs_daemon_open_readonly();
+ if (NULL == xd->xenstore) {
+ fprintf(stderr,"%s: can't connect to %s\n", __FUNCTION__,
+ xs_daemon_socket_ro());
+ return;
+ }
+#else
+ xd->xenstore = xs_domain_open();
+ if (NULL == xd->xenstore) {
+ fprintf(stderr,"%s: can't connect to %s\n", __FUNCTION__,
+ xs_domain_dev());
+ return;
+ }
+#endif
+
+ xs_watch(xd->xenstore, XS_DOM_WATCH, "token");
+ xs_watch(xd->xenstore, XS_VM_WATCH, "token");
+ xd->ch = g_io_channel_unix_new(xs_fileno(xd->xenstore));
+ xd->id = g_io_add_watch(xd->ch, G_IO_IN, watch_trigger, xd);
+
+ if (NULL == (xst = xs_transaction_start(xd->xenstore)))
+ return;
+ list = xs_directory(xd->xenstore, xst, XS_DOM_WATCH , &num);
+ xs_transaction_end(xd->xenstore, xst, 0);
+
+ for (i = 0; i < num; i++) {
+ snprintf(path, sizeof(path), "%s/%s", XS_DOM_WATCH, list[i]);
+ dom_new(xd, path);
+ }
+ free(list);
+}
+
+static void
+xen_doms_finalize(GObject *object)
+{
+ XenDoms *xd = XEN_DOMS(object);
+
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ if (xd->id)
+ g_source_destroy(g_main_context_find_source_by_id
+ (g_main_context_default(), xd->id));
+ /* TODO: free list */
+ (*parent_class->finalize)(object);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static GtkTreeModelFlags
+xen_doms_get_flags(GtkTreeModel *tree_model)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+
+ if (trace > 2)
+ fprintf(stderr,"%s: trace\n", __FUNCTION__);
+ g_return_val_if_fail(IS_XEN_DOMS(xd), (GtkTreeModelFlags)0);
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint
+xen_doms_get_n_columns(GtkTreeModel *tree_model)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+
+ g_return_val_if_fail(IS_XEN_DOMS(xd), (GtkTreeModelFlags)0);
+ return XEN_DOMS_N_COLUMNS;
+}
+
+static GType
+xen_doms_get_column_type(GtkTreeModel *tree_model,
+ gint index)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ enum xen_doms_cols column = index;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: <= %d\n", __FUNCTION__, index);
+ g_return_val_if_fail(IS_XEN_DOMS(xd), (GtkTreeModelFlags)0);
+ switch(column) {
+ case XEN_DOMS_COL_I_ID:
+ return G_TYPE_INT;
+ default:
+ return xs_fields[column].type;
+ }
+ return G_TYPE_INVALID;
+}
+
+static gboolean
+xen_doms_get_iter(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ struct xd_node *node = NULL;
+ struct list_head *item;
+ gint *indices, i;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: trace\n", __FUNCTION__);
+ g_assert(IS_XEN_DOMS(xd));
+ g_assert(path!=NULL);
+ g_assert(1==gtk_tree_path_get_depth(path));
+ if (list_empty(&xd->nodes))
+ return FALSE;
+
+ indices = gtk_tree_path_get_indices(path);
+ i = 0;
+ list_for_each(item, &xd->nodes) {
+ node = list_entry(item, struct xd_node, next);
+ if (i == indices[0])
+ break;
+ i++;
+ }
+ if (i != indices[0])
+ return FALSE;
+
+ g_assert(NULL != node);
+ memset(iter,0,sizeof(*iter));
+ iter->user_data = node;
+ return TRUE;
+}
+
+static GtkTreePath*
+xen_doms_get_path(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ struct xd_node *find = iter->user_data;
+
+ return do_get_path(xd, find);
+}
+
+static int
+xen_doms_xs_path(XenDoms *xd, char *path, int len,
+ struct xs_data *field, struct xd_node *node)
+{
+ struct xs_transaction_handle *xst;
+
+ switch (field->mode) {
+ case DOM:
+ snprintf(path, len, "%s/%s", node->xs_dom_path, field->name);
+ break;
+ case VM:
+ if (NULL == node->xs_vm_path) {
+ snprintf(path, len, "%s/%s", node->xs_dom_path, "vm");
+ if (NULL == (xst = xs_transaction_start(xd->xenstore)))
+ return -1;
+ node->xs_vm_path = xs_read(xd->xenstore, xst, path, NULL);
+ xs_transaction_end(xd->xenstore, xst, 0);
+ }
+ snprintf(path, len, "%s/%s", node->xs_vm_path, field->name);
+ }
+
+ /* default: relative to xs_path */
+ return 0;
+}
+
+static void
+xen_doms_get_value(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint index,
+ GValue *value)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ enum xen_doms_cols column = index;
+ struct xs_transaction_handle *xst;
+ struct xd_node *node = iter->user_data;
+ char *xs_value;
+ char path[256];
+
+ if (trace > 2)
+ fprintf(stderr,"%s: \"%s\" %d\n", __FUNCTION__, node->xs_dom_path, index);
+ switch (column) {
+ case XEN_DOMS_COL_I_ID:
+ g_value_init(value, G_TYPE_INT);
+ g_value_set_int(value, atoi(node->id));
+ break;
+ case XEN_DOMS_N_COLUMNS:
+ break;
+ default:
+ if (0 != xen_doms_xs_path(xd, path, sizeof(path), xs_fields+column, node))
+ break;
+ if (NULL == (xst = xs_transaction_start(xd->xenstore)))
+ break;
+ xs_value = xs_read(xd->xenstore, xst, path, NULL);
+ xs_transaction_end(xd->xenstore, xst, 0);
+ g_value_init(value, xs_fields[column].type);
+ switch (xs_fields[column].type) {
+ case G_TYPE_STRING:
+ g_value_set_string(value, xs_value ? xs_value : "-");
+ break;
+ case G_TYPE_INT:
+ g_value_set_int(value, xs_value ? atoi(xs_value) : 0);
+ break;
+ }
+ free(xs_value);
+ break;
+ }
+}
+
+static gboolean
+xen_doms_iter_next(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ struct xd_node *node = iter->user_data;
+ struct xd_node *next;
+
+ if (node->next.next == &xd->nodes)
+ return FALSE;
+
+ next = list_entry(node->next.next, struct xd_node, next);
+ iter->user_data = next;
+ return TRUE;
+}
+
+
+static gboolean
+xen_doms_iter_has_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ return xen_doms_iter_n_children(tree_model, iter) ? TRUE : FALSE;
+}
+
+static gboolean
+xen_doms_iter_parent(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ return FALSE; /* I'm a list, not a tree */
+}
+
+static gint
+xen_doms_iter_n_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+
+ if (NULL == iter)
+ return xd->count;
+ return 0;
+}
+
+static gboolean
+xen_doms_iter_nth_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ XenDoms *xd = XEN_DOMS(tree_model);
+ struct xd_node *node = NULL;
+ struct list_head *item;
+ int i;
+
+ if (NULL != parent)
+ return FALSE; /* I'm a list */
+
+ i = 0;
+ list_for_each(item, &xd->nodes) {
+ node = list_entry(item, struct xd_node, next);
+ if (i == n)
+ break;
+ n++;
+ }
+ if (i != n)
+ return FALSE;
+
+ g_assert(NULL != node);
+ memset(iter,0,sizeof(*iter));
+ iter->user_data = node;
+ return TRUE;
+}
+
+static gboolean
+xen_doms_iter_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ return xen_doms_iter_nth_child(tree_model, iter, parent, 0);
+}
+
+/* ------------------------------------------------------------------------- */
+
+XenDoms*
+xen_doms_new(void)
+{
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ return g_object_new(XEN_DOMS_TYPE, NULL);
+}
diff --git a/xd_store.h b/xd_store.h
new file mode 100644
index 0000000..d2dcea2
--- /dev/null
+++ b/xd_store.h
@@ -0,0 +1,44 @@
+#ifndef _xen_doms_h_included_
+#define _xen_doms_h_included_
+
+#include <gtk/gtk.h>
+
+/* Some boilerplate GObject defines */
+
+#define XEN_DOMS_TYPE (xen_doms_get_type ())
+#define XEN_DOMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XEN_DOMS_TYPE, XenDoms))
+#define XEN_DOMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XEN_DOMS_TYPE, XenDomsClass))
+#define IS_XEN_DOMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XEN_DOMS_TYPE))
+#define IS_XEN_DOMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XEN_DOMS_TYPE))
+#define XEN_DOMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XEN_DOMS_TYPE, XenDomsClass))
+
+typedef struct _XenDoms XenDoms;
+typedef struct _XenDomsClass XenDomsClass;
+
+struct _XenDomsClass {
+ GObjectClass parent_class;
+};
+GType xen_doms_get_type (void);
+
+/* here is our stuff ... */
+
+enum xen_doms_cols {
+ /* strings */
+ XEN_DOMS_COL_S_NAME,
+ XEN_DOMS_COL_S_UUID,
+ XEN_DOMS_COL_S_OSTYPE,
+
+ /* integers */
+ XEN_DOMS_COL_I_ID,
+ XEN_DOMS_COL_I_MEM,
+ XEN_DOMS_COL_I_MAXMEM,
+ XEN_DOMS_COL_I_CPUS,
+ XEN_DOMS_COL_I_MAXCPUS,
+
+ /* that's it */
+ XEN_DOMS_N_COLUMNS,
+};
+
+XenDoms *xen_doms_new(void);
+
+#endif /* _xen_doms_h_included_ */
diff --git a/xd_view.c b/xd_view.c
new file mode 100644
index 0000000..f535c88
--- /dev/null
+++ b/xd_view.c
@@ -0,0 +1,252 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+#include <xs.h>
+
+#include "xd_store.h"
+#include "xenviews.h"
+
+/* ------------------------------------------------------------------ */
+
+GtkWidget *xd_toplevel;
+
+static GtkWidget *status;
+static XenDoms *store;
+static GtkWidget *view;
+
+/* ------------------------------------------------------------------ */
+
+static void menu_cb_quit(void)
+{
+ gtk_widget_destroy(xd_toplevel);
+}
+
+static void menu_cb_xenstore(void)
+{
+ if (NULL == xs_toplevel)
+ xenstore_create_window();
+ gtk_widget_show_all(xs_toplevel);
+}
+
+static void menu_cb_about(void)
+{
+ /* TODO */
+}
+
+static void destroy(void)
+{
+ g_object_unref(store);
+ xd_toplevel = NULL;
+
+ gtk_main_quit();
+}
+
+/* ------------------------------------------------------------------ */
+
+static const GtkActionEntry entries[] = {
+ {
+ .name = "FileMenu",
+ .label = "_File",
+ },{
+ .name = "Quit",
+ .stock_id = GTK_STOCK_QUIT,
+ .label = "_Quit",
+ .accelerator = "<control>Q",
+ .tooltip = "Quit the job",
+ .callback = menu_cb_quit,
+ },{
+
+ .name = "ViewMenu",
+ .label = "_View",
+ },{
+ .name = "Xenstore",
+ .label = "_Xenstore browser",
+ .accelerator = "<control>X",
+ .callback = menu_cb_xenstore,
+ },{
+
+ .name = "HelpMenu",
+ .label = "_Help",
+ },{
+ .name = "About",
+ .label = "_About ...",
+ .callback = menu_cb_about,
+ },
+};
+
+static char ui_xml[] =
+"<ui>"
+" <menubar name='MainMenu'>"
+" <menu action='FileMenu'>"
+" <menuitem action='Quit'/>"
+" </menu>"
+" <menu action='ViewMenu'>"
+" <menuitem action='Xenstore'/>"
+" </menu>"
+" <menu action='HelpMenu'>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+" <toolbar action='ToolBar'>"
+" <toolitem action='Quit'/>"
+" </toolbar>"
+"</ui>";
+
+/* ------------------------------------------------------------------ */
+
+static void activate(GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ gpointer user_data)
+{
+ GtkTreeIter iter;
+ gint id;
+
+ if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path))
+ return;
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
+ XEN_DOMS_COL_I_ID, &id,
+ -1);
+ fprintf(stderr, "%s: %d\n", __FUNCTION__, id);
+}
+
+static GtkWidget *xen_doms_create_view(XenDoms *store)
+{
+ GtkCellRenderer *renderer;
+ GtkWidget *view;
+
+ view = gtk_tree_view_new();
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view),
+ GTK_TREE_MODEL(store));
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
+ GTK_SELECTION_SINGLE);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("name"), renderer,
+ "text", XEN_DOMS_COL_S_NAME,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "xalign", 1.0,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("id"), renderer,
+ "text", XEN_DOMS_COL_I_ID,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "xalign", 1.0,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("mem"), renderer,
+ "text", XEN_DOMS_COL_I_MEM,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "xalign", 1.0,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("max"), renderer,
+ "text", XEN_DOMS_COL_I_MAXMEM,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "xalign", 1.0,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("cpus"), renderer,
+ "text", XEN_DOMS_COL_I_CPUS,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "xalign", 1.0,
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("max"), renderer,
+ "text", XEN_DOMS_COL_I_MAXCPUS,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("os"), renderer,
+ "text", XEN_DOMS_COL_S_OSTYPE,
+ NULL);
+
+
+ /* fill remaining space */
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _(""), renderer,
+ NULL);
+
+ return view;
+}
+
+void xen_doms_create_window(void)
+{
+ GtkWidget *vbox, *menubar, *toolbar, *scroll;
+ GtkAccelGroup *accel;
+ GtkActionGroup *ag;
+ GtkUIManager *ui;
+ GError *err;
+
+ xd_toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(xd_toplevel), _("xendoms"));
+ gtk_widget_set_size_request(GTK_WIDGET(xd_toplevel), 400, 300);
+ g_signal_connect(G_OBJECT(xd_toplevel), "destroy",
+ G_CALLBACK(destroy), NULL);
+
+ /* menu + toolbar */
+ ui = gtk_ui_manager_new();
+ ag = gtk_action_group_new("MenuActions");
+ gtk_action_group_add_actions(ag, entries, G_N_ELEMENTS(entries), xd_toplevel);
+ gtk_ui_manager_insert_action_group(ui, ag, 0);
+ accel = gtk_ui_manager_get_accel_group(ui);
+ gtk_window_add_accel_group(GTK_WINDOW(xd_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);
+ }
+
+ /* list */
+ store = xen_doms_new();
+ view = xen_doms_create_view(store);
+ g_signal_connect(view, "row-activated", G_CALLBACK(activate), NULL);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+
+ /* other widgets */
+ status = gtk_widget_new(GTK_TYPE_LABEL,
+ "label", "status line",
+ "xalign", 0.0,
+ NULL);
+
+ /* 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(xd_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");
+ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(scroll), view);
+ gtk_box_pack_end(GTK_BOX(vbox), status, FALSE, TRUE, 0);
+
+ return;
+}
diff --git a/xenlog.c b/xenlog.c
new file mode 100644
index 0000000..d5ab4d0
--- /dev/null
+++ b/xenlog.c
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <xs.h>
+
+/* ------------------------------------------------------------- */
+
+int main(int argc, char *argv[])
+{
+ struct xs_handle *xenstore;
+ struct xs_transaction_handle *xst;
+ char *xs_value = NULL, **vec;
+ char *value;
+ unsigned int count;
+
+ xenstore = xs_daemon_open_readonly();
+ if (NULL == xenstore) {
+ fprintf(stderr,"can't connect to %s\n",xs_daemon_socket_ro());
+ exit(1);
+ }
+ xs_watch(xenstore, "/", "token");
+
+ for (;;) {
+ vec = xs_read_watch(xenstore, &count);
+ if (NULL == vec) {
+ fprintf(stderr,"xs_read_watch() failed\n");
+ exit(1);
+ }
+ switch (vec[XS_WATCH_PATH][0]) {
+ case '/':
+ xst = xs_transaction_start(xenstore);
+ if (NULL != xst) {
+ xs_value = xs_read(xenstore, xst, vec[XS_WATCH_PATH], NULL);
+ xs_transaction_end(xenstore, xst, 0);
+ if (NULL == xs_value)
+ value = "<deleted>";
+ else
+ value = xs_value;
+ } else {
+ value = "<error>";
+ }
+ break;
+ default:
+ value = "<null>";
+ break;
+ }
+ fprintf(stderr,"%-64s : \"%s\"\n", vec[XS_WATCH_PATH], value);
+
+ if (xs_value) {
+ free(xs_value);
+ xs_value = NULL;
+ }
+ free(vec);
+ }
+}
diff --git a/xenviews.h b/xenviews.h
new file mode 100644
index 0000000..f309352
--- /dev/null
+++ b/xenviews.h
@@ -0,0 +1,11 @@
+/* i18n */
+#define _(string) (string)
+#define noop(string) (string)
+
+/* variables */
+GtkWidget *xs_toplevel;
+GtkWidget *xd_toplevel;
+
+/* prototypes */
+void xenstore_create_window(void);
+void xen_doms_create_window(void);
diff --git a/xenwatch.c b/xenwatch.c
new file mode 100644
index 0000000..2a13a65
--- /dev/null
+++ b/xenwatch.c
@@ -0,0 +1,27 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <locale.h>
+
+#include <gtk/gtk.h>
+
+#include "xenviews.h"
+
+/* ------------------------------------------------------------------ */
+
+int
+main(int argc, char *argv[])
+{
+ setlocale(LC_ALL,"");
+ gtk_init(&argc, &argv);
+
+ xen_doms_create_window();
+ gtk_widget_show_all(xd_toplevel);
+
+ gtk_main();
+
+ fprintf(stderr,"bye...\n");
+ exit(0);
+}
diff --git a/xs_store.c b/xs_store.c
new file mode 100644
index 0000000..dbf40c4
--- /dev/null
+++ b/xs_store.c
@@ -0,0 +1,693 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <xs.h>
+#include "xs_store.h"
+#include "list.h"
+
+/* boring declarations of local functions */
+
+static void xenstore_init (XenStore *pkg_tree);
+static void xenstore_class_init (XenStoreClass *klass);
+static void xenstore_tree_model_init (GtkTreeModelIface *iface);
+static void xenstore_finalize (GObject *object);
+static GtkTreeModelFlags xenstore_get_flags (GtkTreeModel *tree_model);
+static gint xenstore_get_n_columns (GtkTreeModel *tree_model);
+static GType xenstore_get_column_type (GtkTreeModel *tree_model,
+ gint index);
+static gboolean xenstore_get_iter (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path);
+static GtkTreePath *xenstore_get_path (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static void xenstore_get_value (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint column,
+ GValue *value);
+static gboolean xenstore_iter_next (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean xenstore_iter_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent);
+static gboolean xenstore_iter_has_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gint xenstore_iter_n_children (GtkTreeModel *tree_model,
+ GtkTreeIter *iter);
+static gboolean xenstore_iter_nth_child (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n);
+static gboolean xenstore_iter_parent (GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child);
+
+static GObjectClass *parent_class = NULL;
+
+/* ------------------------------------------------------------------------- */
+
+static int trace = 0;
+
+struct xs_node {
+ char *xs_path;
+ char *xs_name;
+ struct xs_node *parent;
+ int nchildren;
+ struct list_head children;
+ struct list_head siblings;
+};
+
+struct _XenStore {
+ GObject parent; /* this MUST be the first member */
+ struct xs_handle *xenstore;
+ GIOChannel *ch;
+ guint id;
+ struct xs_node *root;
+};
+
+/* ------------------------------------------------------------------------- */
+
+GType
+xenstore_get_type (void)
+{
+ static GType xenstore_type = 0;
+ static const GTypeInfo xenstore_info = {
+ sizeof (XenStoreClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) xenstore_class_init,
+ NULL, /* class finalize */
+ NULL, /* class_data */
+ sizeof (XenStore),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) xenstore_init
+ };
+ static const GInterfaceInfo tree_model_info = {
+ (GInterfaceInitFunc) xenstore_tree_model_init,
+ NULL,
+ NULL
+ };
+
+ if (xenstore_type)
+ return xenstore_type;
+
+ xenstore_type = g_type_register_static (G_TYPE_OBJECT, "XenStore",
+ &xenstore_info, (GTypeFlags)0);
+ g_type_add_interface_static (xenstore_type, GTK_TYPE_TREE_MODEL, &tree_model_info);
+ return xenstore_type;
+}
+
+static void
+xenstore_class_init (XenStoreClass *klass)
+{
+ GObjectClass *object_class;
+
+ parent_class = (GObjectClass*) g_type_class_peek_parent (klass);
+ object_class = (GObjectClass*) klass;
+
+ object_class->finalize = xenstore_finalize;
+}
+
+static void
+xenstore_tree_model_init (GtkTreeModelIface *iface)
+{
+ iface->get_flags = xenstore_get_flags;
+ iface->get_n_columns = xenstore_get_n_columns;
+ iface->get_column_type = xenstore_get_column_type;
+ iface->get_iter = xenstore_get_iter;
+ iface->get_path = xenstore_get_path;
+ iface->get_value = xenstore_get_value;
+ iface->iter_next = xenstore_iter_next;
+ iface->iter_children = xenstore_iter_children;
+ iface->iter_has_child = xenstore_iter_has_child;
+ iface->iter_n_children = xenstore_iter_n_children;
+ iface->iter_nth_child = xenstore_iter_nth_child;
+ iface->iter_parent = xenstore_iter_parent;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static struct xs_node* node_new(char *path)
+{
+ struct xs_node *node;
+ char *name;
+
+ name = strrchr(path,'/') +1;
+ if (0 == name[0])
+ name = "root";
+
+ if (trace)
+ fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, path);
+ node = malloc(sizeof(struct xs_node));
+ memset(node,0,sizeof(struct xs_node));
+ node->xs_path = strdup(path);
+ node->xs_name = strdup(name);
+ node->nchildren = -1;
+ INIT_LIST_HEAD(&node->children);
+ INIT_LIST_HEAD(&node->siblings);
+ return node;
+}
+
+static void node_remove(struct xs_node *node)
+{
+ struct xs_node *child;
+ struct list_head *item, *safe;
+
+ list_for_each_safe(item, safe, &node->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ node_remove(child);
+ }
+
+ if (trace)
+ fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, node->xs_path);
+ list_del(&node->siblings);
+ free(node->xs_name);
+ free(node->xs_path);
+ free(node);
+}
+
+static struct xs_node* node_add_child(XenStore *st, struct xs_node *node, char *name)
+{
+ struct xs_node *child;
+ char *xs_path;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: parent \"%s\" name \"%s\"\n",
+ __FUNCTION__, node->xs_path, name);
+ xs_path = malloc(strlen(node->xs_path) + strlen(name) + 2);
+ if (0 == strcmp(node->xs_path, "/"))
+ sprintf(xs_path, "/%s", name);
+ else
+ sprintf(xs_path, "%s/%s", node->xs_path, name);
+ child = node_new(xs_path);
+ child->parent = node;
+ list_add_tail(&child->siblings, &node->children);
+ node->nchildren++;
+ return child;
+}
+
+static void node_add_children(XenStore *st, struct xs_node *node)
+{
+ struct xs_transaction_handle *xst;
+ struct xs_node *child;
+ unsigned int i,num;
+ char **list;
+
+ g_assert(-1 == node->nchildren);
+ node->nchildren = 0;
+
+ if (NULL == st->xenstore)
+ return;
+ if (NULL == (xst = xs_transaction_start(st->xenstore))) {
+ fprintf(stderr,"Oops, can't start transaction\n");
+ return;
+ }
+ list = xs_directory(st->xenstore, xst, node->xs_path, &num);
+ xs_transaction_end(st->xenstore, xst, 0);
+
+ for (i = 0; i < num; i++)
+ child = node_add_child(st, node, list[i]);
+ free(list);
+}
+
+static struct xs_node* node_find_path(char *path, struct xs_node *node)
+{
+ struct xs_node *child;
+ struct list_head *item;
+ int len;
+
+ if (0 == strcmp(path, node->xs_path))
+ return node;
+ list_for_each(item, &node->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ len = strlen(child->xs_path);
+ if (0 != strncmp(path, child->xs_path, len))
+ continue;
+ if (0 == path[len])
+ return child;
+ if ('/' == path[len])
+ return node_find_path(path, child);
+ }
+ return NULL;
+}
+
+static struct xs_node* node_find_name(char *name, struct xs_node *node)
+{
+ struct xs_node *child;
+ struct list_head *item;
+
+ list_for_each(item, &node->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ if (0 == strcmp(name, child->xs_name))
+ return child;
+ }
+ return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static GtkTreePath*
+do_get_path(XenStore *st, struct xs_node *find)
+{
+ GtkTreePath *path;
+ struct xs_node *node, *child;
+ struct list_head *item;
+ int level,i,len;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, find->xs_path);
+
+ node = st->root;
+ path = gtk_tree_path_new();
+ for (level = 0;; level++) {
+ i = 0;
+ list_for_each(item, &node->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ len = strlen(child->xs_path);
+ if (0 != strncmp(find->xs_path, child->xs_path, len)) {
+ i++;
+ continue;
+ }
+ if (0 == find->xs_path[len]) {
+ if (trace > 2)
+ fprintf(stderr,"%s: %d %d %*s \"%s\" [final]\n", __FUNCTION__,
+ level, i, level*3, "", child->xs_name);
+ gtk_tree_path_append_index(path, i);
+ return path;
+ }
+ if ('/' == find->xs_path[len]) {
+ if (trace > 2)
+ fprintf(stderr,"%s: %d %d %*s \"%s\"\n", __FUNCTION__,
+ level, i, level*3, "", child->xs_name);
+ gtk_tree_path_append_index(path, i);
+ goto next_level;
+ }
+ i++;
+ }
+ return NULL;
+ next_level:
+ node = child;
+ }
+ return NULL;
+}
+
+static gboolean watch_trigger(GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ XenStore *st = data;
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ char **vec = NULL, *h, *name = NULL;
+ struct xs_transaction_handle *xst;
+ unsigned int count;
+ struct xs_node *node;
+ char *xs_value = NULL;
+ int i;
+
+ vec = xs_read_watch(st->xenstore, &count);
+
+ if ('/' != vec[XS_WATCH_PATH][0]) {
+ if (trace)
+ fprintf(stderr,"%s: ignore: no path %d \"%s\"\n", __FUNCTION__,
+ count, vec[XS_WATCH_PATH]);
+ goto out;
+ }
+ if (0 == strcmp(vec[XS_WATCH_PATH], "/")) {
+ if (trace > 1)
+ fprintf(stderr,"%s: ignore: invisible root\n", __FUNCTION__);
+ goto out;
+ }
+
+ if (NULL == (xst = xs_transaction_start(st->xenstore)))
+ goto out;
+ xs_value = xs_read(st->xenstore, xst, vec[XS_WATCH_PATH], NULL);
+ xs_transaction_end(st->xenstore, xst, 0);
+
+ if (trace)
+ fprintf(stderr,"%s: node: %d \"%s\" \"%s\"\n", __FUNCTION__,
+ count, vec[XS_WATCH_PATH], xs_value ? xs_value : "<deleted>");
+ node = node_find_path(vec[XS_WATCH_PATH], st->root);
+ if (NULL != node) {
+ fprintf(stderr,"%s: node: %p \"%s\"\n", __FUNCTION__,
+ node, node ? node->xs_path : "<null>");
+ path = do_get_path(st, node);
+ if (xs_value) {
+ memset(&iter, 0, sizeof(iter));
+ iter.user_data = node;
+ gtk_tree_model_row_changed(GTK_TREE_MODEL(st), path, &iter);
+ } else {
+ node_remove(node);
+ gtk_tree_model_row_deleted(GTK_TREE_MODEL(st), path);
+ }
+ goto out;
+ }
+ if (!xs_value)
+ /* ignore unknown deletes */
+ goto out;
+
+ i = 0;
+ while (NULL == node) {
+ if (name)
+ free(name);
+ h = strrchr(vec[XS_WATCH_PATH], '/');
+ name = strdup(h+1);
+ if (h == vec[XS_WATCH_PATH])
+ h++;
+ *h = 0;
+ if (trace > 1)
+ fprintf(stderr,"%s: parent %d: \"%s\" -> \"%s\"\n", __FUNCTION__,
+ i++, vec[XS_WATCH_PATH], name);
+ node = node_find_path(vec[XS_WATCH_PATH], st->root);
+ }
+ if (trace > 1)
+ fprintf(stderr,"%s: parent ok: \"%s\" %p -> \"%s\"\n", __FUNCTION__,
+ node ? node->xs_path : "<null>", node, name);
+ if (-1 == node->nchildren) {
+ node_add_children(st, node);
+ node = node_find_name(name, node);
+ g_assert(NULL != node);
+ } else {
+ node = node_add_child(st, node, name);
+ }
+
+ path = do_get_path(st, node);
+ memset(&iter, 0, sizeof(iter));
+ iter.user_data = node;
+ gtk_tree_model_row_inserted(GTK_TREE_MODEL(st), path, &iter);
+
+ out:
+ if (path)
+ gtk_tree_path_free(path);
+ if (xs_value)
+ free(xs_value);
+ if (name)
+ free(name);
+ if (vec)
+ free(vec);
+ return TRUE;
+}
+
+static void
+xenstore_init(XenStore *st)
+{
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ st->root = node_new("/");
+
+#if 1
+ st->xenstore = xs_daemon_open_readonly();
+ if (NULL == st->xenstore) {
+ fprintf(stderr,"%s: can't connect to %s\n", __FUNCTION__,
+ xs_daemon_socket_ro());
+ return;
+ }
+#else
+ st->xenstore = xs_domain_open();
+ if (NULL == st->xenstore) {
+ fprintf(stderr,"%s: can't connect to %s\n", __FUNCTION__,
+ xs_domain_dev());
+ return;
+ }
+#endif
+
+ xs_watch(st->xenstore, "/", "token");
+ st->ch = g_io_channel_unix_new(xs_fileno(st->xenstore));
+ st->id = g_io_add_watch(st->ch, G_IO_IN, watch_trigger, st);
+}
+
+static void
+xenstore_finalize(GObject *object)
+{
+ XenStore *st = XENSTORE(object);
+
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ if (st->id)
+ g_source_destroy(g_main_context_find_source_by_id
+ (g_main_context_default(), st->id));
+ node_remove(st->root);
+ (*parent_class->finalize)(object);
+}
+
+/* ------------------------------------------------------------------------- */
+
+static GtkTreeModelFlags
+xenstore_get_flags(GtkTreeModel *tree_model)
+{
+ XenStore *st = XENSTORE(tree_model);
+
+ if (trace > 2)
+ fprintf(stderr,"%s: trace\n", __FUNCTION__);
+ g_return_val_if_fail(IS_XENSTORE(st), (GtkTreeModelFlags)0);
+ return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static gint
+xenstore_get_n_columns(GtkTreeModel *tree_model)
+{
+ XenStore *st = XENSTORE(tree_model);
+
+ g_return_val_if_fail(IS_XENSTORE(st), (GtkTreeModelFlags)0);
+ return XENSTORE_N_COLUMNS;
+}
+
+static GType
+xenstore_get_column_type(GtkTreeModel *tree_model,
+ gint index)
+{
+ XenStore *st = XENSTORE(tree_model);
+ enum xenstore_cols column = index;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: <= %d\n", __FUNCTION__, index);
+ g_return_val_if_fail(IS_XENSTORE(st), (GtkTreeModelFlags)0);
+ switch(column) {
+ case XENSTORE_COL_NAME:
+ case XENSTORE_COL_VALUE:
+ case XENSTORE_COL_PATH:
+ return G_TYPE_STRING;
+ case XENSTORE_N_COLUMNS:
+ break;
+ }
+ return G_TYPE_INVALID;
+}
+
+static gboolean
+xenstore_get_iter(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreePath *path)
+{
+ XenStore *st = XENSTORE(tree_model);
+ struct xs_node *parent, *child = NULL;
+ struct list_head *item;
+ gint *indices, i, j;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: trace\n", __FUNCTION__);
+ g_assert(IS_XENSTORE(st));
+ g_assert(path!=NULL);
+
+ parent = st->root;
+ indices = gtk_tree_path_get_indices(path);
+ for (i = 0; i < gtk_tree_path_get_depth(path); i++) {
+ if (-1 == parent->nchildren)
+ node_add_children(st, parent);
+
+ j = 0;
+ list_for_each(item, &parent->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ if (j == indices[i])
+ break;
+ j++;
+ }
+ if (j != indices[i])
+ return FALSE;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: %d:%d %*s\"%s\"\n", __FUNCTION__,
+ i, indices[i], 3*i, "",
+ child ? child->xs_name : "<null>");
+ parent = child;
+ }
+
+ if (NULL == child)
+ return FALSE;
+ memset(iter,0,sizeof(*iter));
+ iter->user_data = child;
+ return TRUE;
+}
+
+static GtkTreePath*
+xenstore_get_path(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenStore *st = XENSTORE(tree_model);
+ struct xs_node *find = iter->user_data;
+
+ return do_get_path(st, find);
+}
+
+static void
+xenstore_get_value(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gint index,
+ GValue *value)
+{
+ XenStore *st = XENSTORE(tree_model);
+ enum xenstore_cols column = index;
+ struct xs_transaction_handle *xst;
+ struct xs_node *node = iter->user_data;
+ char *xs_value;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: \"%s\" %d\n", __FUNCTION__, node->xs_path, index);
+ switch (column) {
+ case XENSTORE_COL_NAME:
+ g_value_init(value, G_TYPE_STRING);
+ g_value_set_string(value, node->xs_name);
+ break;
+ case XENSTORE_COL_PATH:
+ g_value_init(value, G_TYPE_STRING);
+ g_value_set_string(value, node->xs_path);
+ break;
+ case XENSTORE_COL_VALUE:
+ if (NULL == (xst = xs_transaction_start(st->xenstore)))
+ break;
+ xs_value = xs_read(st->xenstore, xst, node->xs_path, NULL);
+ g_value_init(value, G_TYPE_STRING);
+ g_value_set_string(value, xs_value);
+ free(xs_value);
+ xs_transaction_end(st->xenstore, xst, 0);
+ break;
+ case XENSTORE_N_COLUMNS:
+ break;
+ }
+}
+
+static gboolean
+xenstore_iter_next(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenStore *st = XENSTORE(tree_model);
+ struct xs_node *node = iter->user_data;
+ struct xs_node *next;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: <= \"%s\"\n", __FUNCTION__, node->xs_path);
+ if (-1 == node->nchildren)
+ node_add_children(st, node);
+
+ if (node->siblings.next == &node->parent->children) {
+ if (trace > 2)
+ fprintf(stderr,"%s: EOF\n", __FUNCTION__);
+ return FALSE;
+ }
+
+ next = list_entry(node->siblings.next, struct xs_node, siblings);
+ if (trace > 2)
+ fprintf(stderr,"%s: => \"%s\"\n", __FUNCTION__, next->xs_path);
+ iter->user_data = next;
+ return TRUE;
+}
+
+
+static gboolean
+xenstore_iter_has_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ return xenstore_iter_n_children(tree_model, iter) ? TRUE : FALSE;
+}
+
+static gboolean
+xenstore_iter_parent(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *child)
+{
+ struct xs_node *node = child->user_data;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: <= \"%s\"\n", __FUNCTION__, node->xs_path);
+ if (NULL == node->parent)
+ return FALSE;
+
+ if (trace > 2)
+ fprintf(stderr,"%s: => \"%s\"\n", __FUNCTION__,
+ node->parent ? node->parent->xs_path : "<null>");
+ memset(iter,0,sizeof(*iter));
+ iter->user_data = node->parent;
+ return TRUE;
+}
+
+static gint
+xenstore_iter_n_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter)
+{
+ XenStore *st = XENSTORE(tree_model);
+ struct xs_node *node = iter->user_data;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: <= \"%s\"\n", __FUNCTION__, node->xs_path);
+ if (-1 == node->nchildren)
+ node_add_children(st, node);
+ if (trace > 2)
+ fprintf(stderr,"%s: => %d\n", __FUNCTION__, node->nchildren);
+ return node->nchildren;
+}
+
+static gboolean
+xenstore_iter_nth_child(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent,
+ gint n)
+{
+ XenStore *st = XENSTORE(tree_model);
+ struct xs_node *node = parent ? parent->user_data : NULL;
+ struct xs_node *child = NULL;
+ struct list_head *item;
+ int i;
+
+ if (trace > 1)
+ fprintf(stderr,"%s: <= \"%s\" %d\n", __FUNCTION__,
+ node ? node->xs_path : "<null>", n);
+ if (NULL == node)
+ node = st->root;
+ if (-1 == node->nchildren)
+ node_add_children(st, node);
+ if (0 == node->nchildren)
+ return FALSE;
+
+ i = 0;
+ list_for_each(item, &node->children) {
+ child = list_entry(item, struct xs_node, siblings);
+ if (i == n)
+ break;
+ n++;
+ }
+ if (i != n)
+ return FALSE;
+
+ g_assert(NULL != child);
+ memset(iter,0,sizeof(*iter));
+ iter->user_data = child;
+ return TRUE;
+}
+
+static gboolean
+xenstore_iter_children(GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ GtkTreeIter *parent)
+{
+ return xenstore_iter_nth_child(tree_model, iter, parent, 0);
+}
+
+/* ------------------------------------------------------------------------- */
+
+XenStore*
+xenstore_new(void)
+{
+ if (trace)
+ fprintf(stderr,"%s\n", __FUNCTION__);
+ return g_object_new(XENSTORE_TYPE, NULL);
+}
+
diff --git a/xs_store.h b/xs_store.h
new file mode 100644
index 0000000..a96c3a0
--- /dev/null
+++ b/xs_store.h
@@ -0,0 +1,37 @@
+#ifndef _xenstore_h_included_
+#define _xenstore_h_included_
+
+#include <gtk/gtk.h>
+
+/* Some boilerplate GObject defines */
+
+#define XENSTORE_TYPE (xenstore_get_type ())
+#define XENSTORE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), XENSTORE_TYPE, XenStore))
+#define XENSTORE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), XENSTORE_TYPE, XenStoreClass))
+#define IS_XENSTORE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), XENSTORE_TYPE))
+#define IS_XENSTORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), XENSTORE_TYPE))
+#define XENSTORE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), XENSTORE_TYPE, XenStoreClass))
+
+typedef struct _XenStore XenStore;
+typedef struct _XenStoreClass XenStoreClass;
+
+struct _XenStoreClass {
+ GObjectClass parent_class;
+};
+GType xenstore_get_type (void);
+
+/* here is our stuff ... */
+
+enum xenstore_cols {
+ /* strings */
+ XENSTORE_COL_NAME,
+ XENSTORE_COL_VALUE,
+
+ XENSTORE_COL_PATH,
+
+ XENSTORE_N_COLUMNS,
+};
+
+XenStore *xenstore_new(void);
+
+#endif /* _xenstore_h_included_ */
diff --git a/xs_view.c b/xs_view.c
new file mode 100644
index 0000000..7e0e97a
--- /dev/null
+++ b/xs_view.c
@@ -0,0 +1,187 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <gtk/gtk.h>
+#include <xs.h>
+
+#include "xs_store.h"
+#include "xenviews.h"
+
+/* ------------------------------------------------------------------ */
+
+GtkWidget *xs_toplevel;
+
+static GtkWidget *status;
+static XenStore *store;
+static GtkWidget *view;
+
+/* ------------------------------------------------------------------ */
+
+static void menu_cb_close(void)
+{
+ gtk_widget_destroy(xs_toplevel);
+}
+
+static void menu_cb_about(void)
+{
+ /* TODO */
+}
+
+static void menu_cb_expand(void)
+{
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(view));
+}
+
+static void destroy(GtkWidget *widget,
+ gpointer data)
+{
+ g_object_unref(store);
+ xs_toplevel = NULL;
+}
+
+/* ------------------------------------------------------------------ */
+
+static const GtkActionEntry entries[] = {
+ {
+ .name = "FileMenu",
+ .label = "_File",
+ },{
+ .name = "Close",
+ .stock_id = GTK_STOCK_QUIT,
+ .label = "_Close",
+ .accelerator = "<control>Q",
+ .callback = menu_cb_close,
+ },{
+
+ .name = "ActionMenu",
+ .label = "_Action",
+ },{
+ .name = "ExpandAll",
+ .label = "_Expand all",
+ .accelerator = "<control>E",
+ .callback = menu_cb_expand,
+ },{
+
+ .name = "HelpMenu",
+ .label = "_Help",
+ },{
+ .name = "About",
+ .label = "_About ...",
+ .callback = menu_cb_about,
+ },
+};
+
+static char ui_xml[] =
+"<ui>"
+" <menubar name='MainMenu'>"
+" <menu action='FileMenu'>"
+" <menuitem action='Close'/>"
+" </menu>"
+" <menu action='ActionMenu'>"
+" <menuitem action='ExpandAll'/>"
+" </menu>"
+" <menu action='HelpMenu'>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+" <toolbar action='ToolBar'>"
+" <toolitem action='Close'/>"
+" </toolbar>"
+"</ui>";
+
+/* ------------------------------------------------------------------ */
+
+static GtkWidget *xenstore_create_view(XenStore *store)
+{
+ GtkCellRenderer *renderer;
+ GtkWidget *view;
+
+ view = gtk_tree_view_new();
+ gtk_tree_view_set_model(GTK_TREE_VIEW(view),
+ GTK_TREE_MODEL(store));
+ gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
+ GTK_SELECTION_SINGLE);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "font", "monospace",
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("Name"), renderer,
+ "text", XENSTORE_COL_NAME,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ g_object_set(renderer,
+ "font", "monospace",
+ NULL);
+ gtk_tree_view_insert_column_with_attributes
+ (GTK_TREE_VIEW(view), -1, _("Value"), renderer,
+ "text", XENSTORE_COL_VALUE,
+ NULL);
+
+ return view;
+}
+
+void xenstore_create_window(void)
+{
+ GtkWidget *vbox, *menubar, *toolbar, *scroll;
+ GtkAccelGroup *accel;
+ GtkActionGroup *ag;
+ GtkUIManager *ui;
+ GError *err;
+
+ xs_toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(xs_toplevel), _("xenstore"));
+ gtk_widget_set_size_request(GTK_WIDGET(xs_toplevel), 400, 300);
+ g_signal_connect(G_OBJECT(xs_toplevel), "destroy",
+ G_CALLBACK(destroy), NULL);
+
+ /* menu + toolbar */
+ ui = gtk_ui_manager_new();
+ ag = gtk_action_group_new("MenuActions");
+ gtk_action_group_add_actions(ag, entries, G_N_ELEMENTS(entries), xs_toplevel);
+ gtk_ui_manager_insert_action_group(ui, ag, 0);
+ accel = gtk_ui_manager_get_accel_group(ui);
+ gtk_window_add_accel_group(GTK_WINDOW(xs_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);
+ }
+
+ /* list */
+ store = xenstore_new();
+ view = xenstore_create_view(store);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+
+ /* other widgets */
+ status = gtk_widget_new(GTK_TYPE_LABEL,
+ "label", "status line",
+ "xalign", 0.0,
+ NULL);
+
+ /* 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(xs_toplevel), vbox);
+ menubar = gtk_ui_manager_get_widget(ui, "/MainMenu");
+ gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
+ if (0) {
+ toolbar = gtk_ui_manager_get_widget(ui, "/ToolBar");
+ gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
+ }
+ gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(scroll), view);
+ gtk_box_pack_end(GTK_BOX(vbox), status, FALSE, TRUE, 0);
+
+ return;
+}