#include #include #include #include #include #include #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) { xs_transaction_t 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 (!(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; xs_transaction_t 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 (!(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 : ""); 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 : ""); 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 : "", 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 fcntl(xs_fileno(st->xenstore),F_SETFD,FD_CLOEXEC); 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 : ""); 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; xs_transaction_t 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 (!(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 : ""); 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 : "", 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); }