#include #include #include #include #include #include #include #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_TERMINAL ] = { DOM, G_TYPE_STRING, "console/tty" }, [ XEN_DOMS_COL_I_VNCPORT ] = { DOM, G_TYPE_INT, "console/vnc-port" }, [ 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, "vcpu_avail" }, [ XEN_DOMS_COL_I_MAXCPUS ] = { VM, G_TYPE_INT, "vcpus" }, }; /* ------------------------------------------------------------------------- */ 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; xs_transaction_t 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 (!(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 : "", xs_value ? xs_value : ""); 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) { xs_transaction_t 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 fcntl(xs_fileno(xd->xenstore),F_SETFD,FD_CLOEXEC); 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 (!(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) { xs_transaction_t 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 (!(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; xs_transaction_t 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 (!(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); }