diff options
-rw-r--r-- | GNUmakefile | 67 | ||||
-rw-r--r-- | INSTALL | 59 | ||||
-rw-r--r-- | VERSION | 1 | ||||
-rw-r--r-- | list.h | 169 | ||||
-rw-r--r-- | mk/Autoconf.mk | 145 | ||||
-rw-r--r-- | mk/Compile.mk | 88 | ||||
-rw-r--r-- | mk/Maintainer.mk | 12 | ||||
-rw-r--r-- | mk/Variables.mk | 46 | ||||
-rw-r--r-- | xd_store.c | 700 | ||||
-rw-r--r-- | xd_store.h | 44 | ||||
-rw-r--r-- | xd_view.c | 252 | ||||
-rw-r--r-- | xenlog.c | 55 | ||||
-rw-r--r-- | xenviews.h | 11 | ||||
-rw-r--r-- | xenwatch.c | 27 | ||||
-rw-r--r-- | xs_store.c | 693 | ||||
-rw-r--r-- | xs_store.h | 37 | ||||
-rw-r--r-- | xs_view.c | 187 |
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) + @@ -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> @@ -0,0 +1 @@ +0.1 @@ -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; +} |