xenwatch-0.5.4/0000775000076400007640000000000011122440013012675 5ustar kraxelkraxelxenwatch-0.5.4/apps.h0000664000076400007640000000112210524114404014014 0ustar kraxelkraxel#define array_size(x) (sizeof(x)/sizeof(*x)) #define XENCONSOLE "/usr/" LIB "/xen/bin/xenconsole" enum desktop_type { DESKTOP_ANY = 0, DESKTOP_KDE, DESKTOP_GNOME, DESKTOP_OTHER, }; extern enum desktop_type desktop_type; extern char app_error[]; void detect_desktop(void); int have_application(char *name); int run_application_va(int do_wait, const char *app, char **argv); int run_application(int do_wait, const char *app, ...); int run_cmdline(int do_wait, char *line); int run_cmdline_replace(int do_wait, char *str, ...); int open_vnc_session(char *host, int tcpport); xenwatch-0.5.4/mdns.c0000664000076400007640000007715610613650246014041 0ustar kraxelkraxel#include #include #include #include #include #include #include #include /* FIXME: find more portable one */ #include #include #ifdef HAVE_AVAHI # include # include # include # include # include # include #endif #include "list.h" #include "apps.h" #include "mdns.h" #ifdef HAVE_AVAHI static int debug = 0; /* ---------------------------------------------------------------------- */ /* build URLs */ struct protocols { char *type; char *proto; int defport; }; static const struct protocols protocols[] = { { "_ftp._tcp", "ftp", 21 }, { "_ssh._tcp", "ssh", 22 }, { "_http._tcp", "http", 80 }, { "_rfb._tcp", "vnc", -1 }, }; /* ---------------------------------------------------------------------- */ /* start apps */ struct actions { enum desktop_type desktop; char *type; char *tryapp; char *cmdline; int needurl:1; }; static const struct actions default_actions[] = { { .desktop = DESKTOP_ANY, .needurl = 1, .tryapp = "xdg-open", .cmdline = "xdg-open %u", },{ .desktop = DESKTOP_KDE, .needurl = 1, .tryapp = "kfmclient", .cmdline = "kfmclient exec %u", },{ .desktop = DESKTOP_GNOME, .needurl = 1, .tryapp = "gnome-open", .cmdline = "gnome-open %u", },{ .desktop = DESKTOP_ANY, .type = "_ssh._tcp", .cmdline = "xterm -title \"%n\" -e ssh -p %p \"%h\"", },{ .desktop = DESKTOP_ANY, .type = "_rfb._tcp", .tryapp = "vncviewer", .cmdline = "vncviewer %h::%p", } }; /* ---------------------------------------------------------------------- */ enum { /* browse */ ST_COL_NAME = 0, ST_COL_TYPE, ST_COL_DOMAIN, ST_COL_INTERFACE, ST_COL_PROTOCOL, /* resolve */ ST_COL_HOSTNAME, ST_COL_ADDR, ST_COL_PORT, ST_COL_PATH, /* other */ ST_COL_URL, ST_COL_XEN_DOM_ID, ST_COL_XEN_VM_UUID, ST_NUM_COLS }; static int str_sort[] = { ST_COL_NAME, ST_COL_TYPE, ST_COL_DOMAIN, ST_COL_INTERFACE, ST_COL_PROTOCOL, ST_COL_HOSTNAME, }; struct mdns_browser { char *service; char *domain; AvahiServiceBrowser *sb; struct list_head next; }; struct mdns_window { GtkListStore *store; GtkWidget *toplevel, *view, *status; GtkActionGroup *ag; int standalone; mdns_callback callback; const AvahiPoll *poll_api; AvahiGLibPoll *glib_poll; AvahiClient *client; struct list_head browser; }; /* ---------------------------------------------------------------------- */ static const char *revents[] = { [ AVAHI_RESOLVER_FOUND ] = "FOUND", [ AVAHI_RESOLVER_FAILURE ] = "FAILURE", }; static const char *bevents[] = { [ AVAHI_BROWSER_NEW ] = "NEW", [ AVAHI_BROWSER_REMOVE ] = "REMOVE", [ AVAHI_BROWSER_CACHE_EXHAUSTED ] = "CACHE_EXHAUSTED", [ AVAHI_BROWSER_ALL_FOR_NOW ] = "ALL_FOR_NOW", [ AVAHI_BROWSER_FAILURE ] = "FAILURE", }; /* ---------------------------------------------------------------------- */ static int find_entry(struct mdns_window *mdns, GtkTreeIter *iter, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); gboolean valid; char *name, *type, *domain, *proto, *nif; for (valid = gtk_tree_model_get_iter_first(model, iter); valid; valid = gtk_tree_model_iter_next(model, iter)) { gtk_tree_model_get(model, iter, ST_COL_NAME, &name, ST_COL_TYPE, &type, ST_COL_DOMAIN, &domain, ST_COL_PROTOCOL, &proto, ST_COL_INTERFACE, &nif, -1); if (0 == strcmp(name, sname) && 0 == strcmp(type, stype) && 0 == strcmp(domain, sdomain) && 0 == strcmp(proto, sproto) && 0 == strcmp(nif, snif)) return 0; } return -1; } static void get_entry(struct mdns_window *mdns, GtkTreeIter *iter, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { if (0 == find_entry(mdns, iter, sname, stype, sdomain, sproto, snif)) return; gtk_list_store_append(mdns->store, iter); gtk_list_store_set(mdns->store, iter, ST_COL_NAME, sname, ST_COL_TYPE, stype, ST_COL_DOMAIN, sdomain, ST_COL_PROTOCOL, sproto, ST_COL_INTERFACE, snif, -1); if (debug) fprintf(stderr, "add: %s: %s\n",stype, sname); } static void del_entry(struct mdns_window *mdns, const char *sname, const char *stype, const char *sdomain, const char *sproto, const char *snif) { GtkTreeIter iter; if (0 != find_entry(mdns, &iter, sname, stype, sdomain, sproto, snif)) return; gtk_list_store_remove(mdns->store, &iter); if (debug) fprintf(stderr, "del: %s: %s\n", stype, sname); } static void del_entries(struct mdns_window *mdns) { GtkTreeModel *model = GTK_TREE_MODEL(mdns->store); GtkTreeIter iter; while (gtk_tree_model_get_iter_first(model, &iter)) gtk_list_store_remove(mdns->store, &iter); } /* ---------------------------------------------------------------------- */ static void ifname(char *dst, int len, int nif) { #ifdef SIOCGIFNAME struct ifreq ifr; int fd; fd = socket(PF_INET, SOCK_STREAM, 0); if (fd < 0) goto fallback; ifr.ifr_ifindex = nif; if (ioctl (fd, SIOCGIFNAME, &ifr) < 0) { close (fd); goto fallback; } snprintf(dst, len, "%s", ifr.ifr_name); close (fd); return; fallback: #endif if (-1 == nif) { snprintf(dst, len, "wide"); return; } snprintf(dst, len, "if-%d", nif); } static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void* userdata) { struct mdns_window *mdns = userdata; char a[AVAHI_ADDRESS_STR_MAX], p[32], url[256]; unsigned char *txtstr, *path = NULL; char *proto = NULL; AvahiStringList *txtlist; GtkTreeIter iter; char nif[32]; int defport = 0, i; switch (event) { case AVAHI_RESOLVER_FOUND: ifname(nif, sizeof(nif), interface); if (0 != find_entry(mdns, &iter, name, type, domain, avahi_proto_to_string(protocol), nif)) break; avahi_address_snprint(a, sizeof(a), address); snprintf(p, sizeof(p), "%d", port); gtk_list_store_set(mdns->store, &iter, ST_COL_HOSTNAME, host_name, ST_COL_ADDR, a, ST_COL_PORT, p, -1); /* xen stuff */ txtlist = avahi_string_list_find(txt, "dom-id"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { gtk_list_store_set(mdns->store, &iter, ST_COL_XEN_DOM_ID, txtstr+7, -1); } } txtlist = avahi_string_list_find(txt, "vm-uuid"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { gtk_list_store_set(mdns->store, &iter, ST_COL_XEN_VM_UUID, txtstr+8, -1); } } /* path */ txtlist = avahi_string_list_find(txt, "path"); if (txtlist) { txtstr = avahi_string_list_get_text(txtlist); if (txtstr) { path = txtstr+5; gtk_list_store_set(mdns->store, &iter, ST_COL_PATH, path, -1); } } /* url */ for (i = 0; i < array_size(protocols); i++) { if (0 != strcmp(protocols[i].type, type)) continue; proto = protocols[i].proto; defport = protocols[i].defport; break; } if (!proto) break; if (!path) path = (unsigned char*)""; if (defport != port) snprintf(url, sizeof(url), "%s://%s:%d%s", proto, a /* host_name */, port, path); else snprintf(url, sizeof(url), "%s://%s%s", proto, a /* host_name */, path); gtk_list_store_set(mdns->store, &iter, ST_COL_URL, url, -1); break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, revents[event], event); break; } avahi_service_resolver_free(r); } static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AvahiLookupResultFlags flags, void* userdata) { struct mdns_window *mdns = userdata; GtkTreeIter iter; char nif[32]; ifname(nif, sizeof(nif), interface); switch (event) { case AVAHI_BROWSER_NEW: get_entry(mdns, &iter, name, type, domain, avahi_proto_to_string(protocol), nif); avahi_service_resolver_new(mdns->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, mdns); break; case AVAHI_BROWSER_REMOVE: del_entry(mdns, name, type, domain, avahi_proto_to_string(protocol), nif); break; default: if (debug) fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void client_callback(AvahiClient *client, AvahiClientState state, void *userdata) { // struct mdns_window *mdns = userdata; switch (state) { case AVAHI_CLIENT_FAILURE: fprintf(stderr, "%s: error: connection lost\n", __FUNCTION__); break; default: if (debug) fprintf(stderr, "%s: state %d\n", __FUNCTION__, state); break; } } /* ---------------------------------------------------------------------- */ static void service_type_browser_callback(AvahiServiceTypeBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *type, const char *domain, AvahiLookupResultFlags flags, void *userdata) { char nif[32]; switch (event) { case AVAHI_BROWSER_NEW: ifname(nif, sizeof(nif), interface); fprintf(stderr, " service: %s, %s, %s: %s\n", nif, avahi_proto_to_string(protocol), domain, type); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_FAILURE: avahi_service_type_browser_free(b); break; case AVAHI_BROWSER_CACHE_EXHAUSTED: /* don't log */ break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void domain_browser_callback(AvahiDomainBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *domain, AvahiLookupResultFlags flags, void *userdata) { struct mdns_window *mdns = userdata; char nif[32]; switch (event) { case AVAHI_BROWSER_NEW: ifname(nif, sizeof(nif), interface); fprintf(stderr, " domain : %s, %s, %s\n", nif, avahi_proto_to_string(protocol), domain); avahi_service_type_browser_new(mdns->client, interface, protocol, domain, 0, service_type_browser_callback, mdns); break; case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_FAILURE: avahi_domain_browser_free(b); break; case AVAHI_BROWSER_CACHE_EXHAUSTED: /* don't log */ break; default: fprintf(stderr, "%s: %s (#%d)\n", __FUNCTION__, bevents[event], event); break; } } static void dump_stuff(struct mdns_window *mdns) { const char *domain; /* playground ... */ domain = avahi_client_get_domain_name(mdns->client); fprintf(stderr, "default domain is \"%s\"\n", domain); avahi_service_type_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, domain, 0, service_type_browser_callback, mdns); avahi_domain_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, domain_browser_callback, mdns); } /* ---------------------------------------------------------------------- */ static void mdns_fini(struct mdns_window *mdns) { if (mdns->client) { if (debug) fprintf(stderr, "%s\n", __FUNCTION__); avahi_client_free(mdns->client); mdns->client = NULL; } if (mdns->glib_poll) { avahi_glib_poll_free(mdns->glib_poll); mdns->glib_poll = NULL; } } static int mdns_init(struct mdns_window *mdns) { int error; if (mdns->client) return 0; if (debug) fprintf(stderr, "%s\n", __FUNCTION__); /* Create the GLIB Adaptor */ avahi_set_allocator(avahi_glib_allocator()); mdns->glib_poll = avahi_glib_poll_new(NULL, G_PRIORITY_DEFAULT); mdns->poll_api = avahi_glib_poll_get(mdns->glib_poll); mdns->client = avahi_client_new(mdns->poll_api, AVAHI_CLIENT_NO_FAIL, client_callback, mdns, &error); if (mdns->client == NULL) goto fail; INIT_LIST_HEAD(&mdns->browser); if (0) dump_stuff(mdns); return 0; fail: mdns_fini(mdns); return -1; } int mdns_browse(struct mdns_window *mdns, int replace, const char *service, const char *domain) { struct list_head *item, *tmp; struct mdns_browser *br; char label[256]; int pos; if (NULL == domain) domain = avahi_client_get_domain_name(mdns->client); if (replace) { /* zap old browsers and entries */ list_for_each_safe(item, tmp, &mdns->browser) { br = list_entry(item, struct mdns_browser, next); avahi_service_browser_free(br->sb); list_del(&br->next); free(br->domain); free(br->service); free(br); } del_entries(mdns); gtk_label_set_text(GTK_LABEL(mdns->status), "idle"); } /* allocate new one */ br = malloc(sizeof(*br)); memset(br,0,sizeof(*br)); br->sb = avahi_service_browser_new(mdns->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, service, domain, 0, browse_callback, mdns); if (NULL == br->sb) { fprintf(stderr, "%s: failed to create service browser: %s\n", __FUNCTION__, avahi_strerror(avahi_client_errno(mdns->client))); free(br); return -1; } br->service = strdup(service); br->domain = strdup(domain); list_add_tail(&br->next,&mdns->browser); /* update status line */ pos = 0; list_for_each_safe(item, tmp, &mdns->browser) { br = list_entry(item, struct mdns_browser, next); pos += snprintf(label+pos, sizeof(label)-pos, "%s%s", pos ? ", " : "", br->service); } gtk_label_set_text(GTK_LABEL(mdns->status), label); return 0; } /* ---------------------------------------------------------------------- */ static void menu_cb_view_default(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_DEFAULT); } static void menu_cb_view_url(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_URL); } static void menu_cb_view_xen(GtkToggleAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; mdns_view(mdns, MDNS_VIEW_XEN); } static void menu_cb_view_col(GtkToggleAction *action, gpointer userdata, int id) { struct mdns_window *mdns = userdata; GtkTreeViewColumn *col; gboolean active; active = gtk_toggle_action_get_active(action); col = gtk_tree_view_get_column(GTK_TREE_VIEW(mdns->view), id); gtk_tree_view_column_set_visible(col, active); } static void menu_cb_view_col_type(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_TYPE); } static void menu_cb_view_col_domain(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_DOMAIN); } static void menu_cb_view_col_interface(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_INTERFACE); } static void menu_cb_view_col_protocol(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PROTOCOL); } static void menu_cb_view_col_hostname(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_HOSTNAME); } static void menu_cb_view_col_addr(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_ADDR); } static void menu_cb_view_col_port(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PORT); } static void menu_cb_view_col_path(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_PATH); } static void menu_cb_view_col_url(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_URL); } static void menu_cb_view_col_xen_dom_id(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_XEN_DOM_ID); } static void menu_cb_view_col_xen_vm_uuid(GtkToggleAction *action, gpointer userdata) { menu_cb_view_col(action, userdata, ST_COL_XEN_VM_UUID); } static void menu_cb_close(GtkAction *action, gpointer userdata) { struct mdns_window *mdns = userdata; gtk_widget_destroy(mdns->toplevel); } static void destroy(GtkWidget *widget, gpointer data) { struct mdns_window *mdns = data; mdns_fini(mdns); g_object_unref(mdns->store); if (mdns->standalone) gtk_main_quit(); free(mdns); } /* ---------------------------------------------------------------------- */ static int run_actions(const struct actions *actions, int nactions, char *name, char *type, char *host, char *port, char *url) { int i; for (i = 0; i < nactions; i++) { if (actions[i].desktop != DESKTOP_ANY && actions[i].desktop != desktop_type) continue; if (actions[i].type && 0 != strcmp(actions[i].type, type)) continue; if (actions[i].tryapp && !have_application(actions[i].tryapp)) continue; if (actions[i].needurl && NULL == url) continue; run_cmdline_replace(0, actions[i].cmdline, "%n", name, "%t", type, "%h", host, "%p", port, "%u", url, NULL); return 0; } return -1; } static void row_activate(GtkTreeView* treeview, GtkTreePath *path, GtkTreeViewColumn* col, gpointer data) { struct mdns_window *mdns = data; GtkTreeModel* model; GtkTreeIter iter; char *name, *type, *host, *port, *url; int rc; model = gtk_tree_view_get_model(treeview); if (!gtk_tree_model_get_iter(model, &iter, path)) return; gtk_tree_model_get(model, &iter, ST_COL_NAME, &name, ST_COL_TYPE, &type, ST_COL_HOSTNAME, &host, ST_COL_PORT, &port, ST_COL_URL, &url, -1); if (mdns->callback) { mdns->callback(mdns, name, type, host, atoi(port), url); } else { rc = run_actions(default_actions, array_size(default_actions), name, type, host, port, url); /* FIXME: error message if failed */ } } /* ---------------------------------------------------------------------- */ static const GtkActionEntry entries[] = { { .name = "FileMenu", .label = "_File", },{ .name = "ViewMenu", .label = "_View", },{ .name = "Close", .stock_id = GTK_STOCK_CLOSE, .label = "_Close", .accelerator = "Q", .callback = G_CALLBACK(menu_cb_close), },{ .name = "ViewDefault", .label = "_Default", .callback = G_CALLBACK(menu_cb_view_default), },{ .name = "ViewURL", .label = "_URL", .callback = G_CALLBACK(menu_cb_view_url), },{ .name = "ViewXen", .label = "_Xen", .callback = G_CALLBACK(menu_cb_view_xen), }, }; static const GtkToggleActionEntry tentries[] = { { .name = "ColType", .label = "Type", .callback = G_CALLBACK(menu_cb_view_col_type), .is_active = 1, },{ .name = "ColDomain", .label = "Domain", .callback = G_CALLBACK(menu_cb_view_col_domain), .is_active = 1, },{ .name = "ColInterface", .label = "Interface", .callback = G_CALLBACK(menu_cb_view_col_interface), .is_active = 1, },{ .name = "ColProtocol", .label = "Protocol", .callback = G_CALLBACK(menu_cb_view_col_protocol), .is_active = 1, },{ .name = "ColHostname", .label = "Hostname", .callback = G_CALLBACK(menu_cb_view_col_hostname), .is_active = 1, },{ .name = "ColAddress", .label = "Address", .callback = G_CALLBACK(menu_cb_view_col_addr), .is_active = 1, },{ .name = "ColPort", .label = "Port", .callback = G_CALLBACK(menu_cb_view_col_port), .is_active = 1, },{ .name = "ColPath", .label = "Path", .callback = G_CALLBACK(menu_cb_view_col_path), .is_active = 1, },{ .name = "ColURL", .label = "URL", .callback = G_CALLBACK(menu_cb_view_col_url), .is_active = 1, },{ .name = "ColXenDomID", .label = "Xen dom-id", .callback = G_CALLBACK(menu_cb_view_col_xen_dom_id), .is_active = 1, },{ .name = "ColXenVmUUID", .label = "Xen vm-uuid", .callback = G_CALLBACK(menu_cb_view_col_xen_vm_uuid), .is_active = 1, } }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #ifdef WITH_TOOLBAR " " " " " " #endif ""; /* ------------------------------------------------------------------ */ static gint gtk_sort_iter_compare_str(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) { gint sortcol = GPOINTER_TO_INT(userdata); char *aa,*bb; gtk_tree_model_get(model, a, sortcol, &aa, -1); gtk_tree_model_get(model, b, sortcol, &bb, -1); if (NULL == aa && NULL == bb) return 0; if (NULL == aa) return 1; if (NULL == bb) return -1; return strcmp(aa,bb); } static GtkWidget *mdns_create_view(struct mdns_window *mdns) { GtkCellRenderer *renderer; GtkTreeSortable *sortable; GtkWidget *view; GtkTreeViewColumn *col; int i; view = gtk_tree_view_new(); gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(mdns->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", ST_COL_NAME, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Type", renderer, "text", ST_COL_TYPE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Domain", renderer, "text", ST_COL_DOMAIN, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "If", renderer, "text", ST_COL_INTERFACE, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Proto", renderer, "text", ST_COL_PROTOCOL, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Hostname", renderer, "text", ST_COL_HOSTNAME, 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, "Address", renderer, "text", ST_COL_ADDR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Port", renderer, "text", ST_COL_PORT, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "Path", renderer, "text", ST_COL_PATH, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "URL", renderer, "text", ST_COL_URL, 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", ST_COL_XEN_DOM_ID, 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, "VM", renderer, "text", ST_COL_XEN_VM_UUID, NULL); /* fill remaining space */ renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, "", renderer, NULL); /* sort bits */ sortable = GTK_TREE_SORTABLE(mdns->store); for (i = 0; i < sizeof(str_sort)/sizeof(str_sort[0]); i++) { gtk_tree_sortable_set_sort_func(sortable, str_sort[i], gtk_sort_iter_compare_str, GINT_TO_POINTER(str_sort[i]), NULL); col = gtk_tree_view_get_column(GTK_TREE_VIEW(view), str_sort[i]); gtk_tree_view_column_set_sort_column_id(col, str_sort[i]); } gtk_tree_sortable_set_sort_column_id(sortable, ST_COL_NAME, GTK_SORT_ASCENDING); return view; } static void set_default_visible_cols(struct mdns_window *mdns, enum mdns_view view) { GtkToggleAction *ta; ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColType")); gtk_toggle_action_set_active(ta, FALSE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColDomain")); gtk_toggle_action_set_active(ta, FALSE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColInterface")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColProtocol")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColHostname")); gtk_toggle_action_set_active(ta, TRUE); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColAddress")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColPort")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColPath")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_DEFAULT); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColURL")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_URL); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColXenDomID")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_XEN); ta = GTK_TOGGLE_ACTION(gtk_action_group_get_action(mdns->ag, "ColXenVmUUID")); gtk_toggle_action_set_active(ta, view == MDNS_VIEW_XEN); } struct mdns_window *mdns_create_window(int standalone, enum mdns_view view, mdns_callback callback) { struct mdns_window *mdns; GtkWidget *vbox, *menubar, *toolbar, *scroll, *frame; GtkAccelGroup *accel; GtkUIManager *ui; GError *err; mdns = malloc(sizeof(*mdns)); memset(mdns,0,sizeof(*mdns)); if (-1 == mdns_init(mdns)) { free(mdns); return NULL; } mdns->standalone = standalone; mdns->callback = callback; mdns->toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(mdns->toplevel), "mdns"); gtk_widget_set_size_request(GTK_WIDGET(mdns->toplevel), 480, 320); g_signal_connect(G_OBJECT(mdns->toplevel), "destroy", G_CALLBACK(destroy), mdns); /* menu + toolbar */ ui = gtk_ui_manager_new(); mdns->ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(mdns->ag, entries, G_N_ELEMENTS(entries), mdns); gtk_action_group_add_toggle_actions(mdns->ag, tentries, G_N_ELEMENTS(tentries), mdns); gtk_ui_manager_insert_action_group(ui, mdns->ag, 0); accel = gtk_ui_manager_get_accel_group(ui); gtk_window_add_accel_group(GTK_WINDOW(mdns->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 */ mdns->store = gtk_list_store_new(ST_NUM_COLS, G_TYPE_STRING, // ST_COL_NAME G_TYPE_STRING, // ST_COL_TYPE G_TYPE_STRING, // ST_COL_DOMAIN G_TYPE_STRING, // ST_COL_INTERFACE G_TYPE_STRING, // ST_COL_PROTOCOL G_TYPE_STRING, // ST_COL_HOSTNAME G_TYPE_STRING, // ST_COL_ADDR G_TYPE_STRING, // ST_COL_PORT G_TYPE_STRING, // ST_COL_PATH G_TYPE_STRING, // ST_COL_URL G_TYPE_STRING, // ST_COL_XEN_DOM_ID G_TYPE_STRING, // ST_COL_XEN_VM_UUID NULL); mdns->view = mdns_create_view(mdns); g_signal_connect(mdns->view, "row-activated", G_CALLBACK(row_activate), mdns); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); /* other widgets */ mdns->status = gtk_label_new("status line"); gtk_misc_set_alignment(GTK_MISC(mdns->status), 0, 0.5); gtk_misc_set_padding(GTK_MISC(mdns->status), 3, 1); /* 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(mdns->toplevel), vbox); menubar = gtk_ui_manager_get_widget(ui, "/MainMenu"); gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); toolbar = gtk_ui_manager_get_widget(ui, "/ToolBar"); if (toolbar) gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(scroll), mdns->view); frame = gtk_frame_new(NULL); gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), mdns->status); set_default_visible_cols(mdns, view); return mdns; } void mdns_show_window(struct mdns_window *mdns) { gtk_widget_show_all(mdns->toplevel); } void mdns_destroy_window(struct mdns_window *mdns) { gtk_widget_destroy(mdns->toplevel); } int mdns_view(struct mdns_window *mdns, enum mdns_view view) { set_default_visible_cols(mdns, view); return 0; } /* ---------------------------------------------------------------------- */ #else /* ! HAVE_AVAHI */ struct mdns_window *mdns_create_window(int standalone, enum mdns_view view, mdns_callback callback) { fprintf(stderr,"Compiled without mDNS support, sorry.\n"); return NULL; } void mdns_show_window(struct mdns_window *mdns) {} void mdns_destroy_window(struct mdns_window *mdns) {} int mdns_browse(struct mdns_window *mdns, int replace, const char *service, const char *domain) { return -1; } int mdns_view(struct mdns_window *mdns, enum mdns_view view) { return -1; } #endif xenwatch-0.5.4/mdns.h0000664000076400007640000000115210562316561014027 0ustar kraxelkraxelstruct mdns_window; enum mdns_view { MDNS_VIEW_DEFAULT = 0, MDNS_VIEW_URL, MDNS_VIEW_XEN, }; typedef void (*mdns_callback)(struct mdns_window *mdns, char *name, char *type, char *host, int port, char *url); struct mdns_window *mdns_create_window(int standalone, enum mdns_view view, mdns_callback callback); void mdns_show_window(struct mdns_window *mdns); void mdns_destroy_window(struct mdns_window *mdns); int mdns_view(struct mdns_window *mdns, enum mdns_view view); int mdns_browse(struct mdns_window *mdns, int replace, const char *service, const char *domain); xenwatch-0.5.4/VERSION0000664000076400007640000000000611122440100013736 0ustar kraxelkraxel0.5.4 xenwatch-0.5.4/xs_store.h0000664000076400007640000000204710346013136014730 0ustar kraxelkraxel#ifndef _xenstore_h_included_ #define _xenstore_h_included_ #include /* 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_ */ xenwatch-0.5.4/kbdmap.sh0000664000076400007640000000055610520122143014477 0ustar kraxelkraxel#!/bin/sh xmodmap -pke | while read line; do set -- $line sep="" shift keycode="$1" shift shift if test "$1" = ""; then continue fi printf " [ %3s ] = { " "$keycode" while test "$1" != ""; do case "$1" in NoSymbol | XF86_*) keysym="0" ;; *) keysym="XK_$1" ;; esac echo -n "${sep}${keysym}" sep=", " shift done echo " }," done xenwatch-0.5.4/mdns-publish-xendom.c0000664000076400007640000001216710665532134016766 0ustar kraxelkraxel#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mdns-publish.h" /* --------------------------------------------------------------------- */ static struct xs_handle *xenstore; static int debug = 0; static char *appname = "mdns-publish-xendom"; static struct utsname uts; static char *service = "_xendom._tcp"; static int port = 9; static char vm_uuid[256]; static char dom_id[256]; static struct mdns_pub *mdns; static struct mdns_pub_entry *entry; /* --------------------------------------------------------------------- */ static int xen_init(void) { struct stat st; if (-1 == stat("/proc/xen", &st)) { mdns_log_printf(mdns, LOG_ERR, "/proc/xen not found, running on bare metal?\n"); return -1; } xenstore = xs_domain_open(); if (NULL == xenstore) { mdns_log_printf(mdns, LOG_ERR, "can't connect to xenstore (%s)\n", xs_domain_dev()); return -1; } return 0; } static int xen_get_info(void) { xs_transaction_t xst; char *xs_value; if (!(xst = xs_transaction_start(xenstore))) { mdns_log_printf(mdns, LOG_ERR, "can't start xenstore transaction\n"); goto out; } xs_value = xs_read(xenstore, xst, "vm", NULL); xs_transaction_end(xenstore, xst, 0); if (!xs_value) { mdns_log_printf(mdns, LOG_ERR, "can't read 'vm' value from xenstore\n"); goto out; } snprintf(vm_uuid, sizeof(vm_uuid), "vm-uuid=%s", xs_value+4); if (!(xst = xs_transaction_start(xenstore))) { mdns_log_printf(mdns, LOG_ERR, "can't start xenstore transaction\n"); goto out; } xs_value = xs_read(xenstore, xst, "domid", NULL); xs_transaction_end(xenstore, xst, 0); if (!xs_value) { mdns_log_printf(mdns, LOG_ERR, "can't read 'domid' value from xenstore\n"); goto out; } snprintf(dom_id, sizeof(dom_id), "dom-id=%s", xs_value); return 0; out: return -1; } static int xen_watch_add(char *path) { int ret = 0; /* Hmm, not working ... */ if (!xs_watch(xenstore, path, "token")) { mdns_log_printf(mdns, LOG_ERR, "%s: xs_watch for \"%s\" failed\n", __FUNCTION__, path); ret = -1; } return ret; } static int xen_publish(void) { static int count = 1; char buf[128]; if (debug) snprintf(buf, sizeof(buf), "Xen domain %s (%d)", uts.nodename, count++); else snprintf(buf, sizeof(buf), "Xen domain %s", uts.nodename); if (entry) mdns_pub_del(entry); entry = mdns_pub_add(mdns, buf, service, port, vm_uuid, dom_id, NULL); return 0; } static int xen_watch_data(void) { char **vec = NULL; unsigned int count; vec = xs_read_watch(xenstore, &count); mdns_log_printf(mdns, LOG_DEBUG, "%s: \"%s\"\n", __FUNCTION__, vec[XS_WATCH_PATH]); xen_get_info(); xen_publish(); if (vec) free(vec); return 0; } /* --------------------------------------------------------------------- */ static void usage(FILE *fp) { fprintf(fp, "This little daemon publishes xen domain info,\n" "via mDNS (using avahi), as service '_xendom._tcp'.\n" "\n" "usage: %s [options]\n" "options:\n" " -h print this text\n" " -d enable debug mode\n" "\n" "-- \n" "(c) 2006 Gerd Hoffmann \n", appname); } static int wait_fd(int fd, int secs) { struct timeval tv; fd_set rd; int rc; FD_ZERO(&rd); FD_SET(fd,&rd); tv.tv_sec = secs; tv.tv_usec = 0; rc = select(fd+1, &rd, NULL, NULL, &tv); if (-1 == rc) { if (EINTR == errno) return 0; } return rc; } int main(int argc, char*argv[]) { int ret = 1; int c; /* parse options */ for (;;) { if (-1 == (c = getopt(argc, argv, "hd"))) break; switch (c) { case 'd': debug = 1; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } /* figure name */ uname(&uts); mdns_pub_appname = appname; if (!debug) mdns_daemonize(); mdns = mdns_pub_init(debug); if (NULL == mdns) goto fail; mdns_sigsetup(mdns); /* figure domain info */ if (0 != xen_init()) goto fail; xen_get_info(); xen_watch_add("vm"); xen_watch_add("domid"); /* enter main loop */ ret = 0; mdns_pub_start(mdns); xen_publish(); for (;;) { if (mdns_pub_appquit) break; switch (wait_fd(xs_fileno(xenstore),1)) { case -1: mdns_log_printf(mdns, LOG_ERR, "select: %s\n", strerror(errno)); mdns_pub_appquit = 1; case 0: /* timeout */ break; default: /* data to read */ xen_watch_data(); } } mdns_pub_del_all(mdns); mdns_pub_stop(mdns); fail: mdns_log_printf(mdns, ret ? LOG_ERR : LOG_INFO, "exiting (%d)%s%s\n", ret, mdns_pub_termsig ? ", on signal " : "", mdns_pub_termsig ? strsignal(mdns_pub_termsig) : ""); mdns_pub_fini(mdns); return ret; } xenwatch-0.5.4/vnc-client.desktop0000664000076400007640000000020610666015663016355 0ustar kraxelkraxel[Desktop Entry] Encoding=UTF-8 Name=vnc-client Exec=vnc-client Terminal=false Type=Application Categories=GTK;System;Network;Utility; xenwatch-0.5.4/x11.c0000664000076400007640000001270610665532134013501 0ustar kraxelkraxel/* * some X11 ximage / pixmaps rotines * * (c) 2006 Gerd Hoffmann * */ #include #include #include #include #include #include #include #include #include "x11.h" /* ------------------------------------------------------------------------ */ XVisualInfo *x11_info; int have_shmem = 0; int x11_red_bits = 0; int x11_red_shift = 0; int x11_green_bits = 0; int x11_green_shift = 0; int x11_blue_bits = 0; int x11_blue_shift = 0; static void x11_find_bits(unsigned long red_mask, unsigned long green_mask, unsigned long blue_mask) { int i; unsigned long mask; for (i = 0; i < 24; i++) { mask = (1 << i); if (red_mask & mask) x11_red_bits++; else if (!x11_red_bits) x11_red_shift++; if (green_mask & mask) x11_green_bits++; else if (!x11_green_bits) x11_green_shift++; if (blue_mask & mask) x11_blue_bits++; else if (!x11_blue_bits) x11_blue_shift++; } #if 0 printf("color: bits shift\n"); printf("red : %04i %05i\n", x11_red_bits, x11_red_shift); printf("green: %04i %05i\n", x11_green_bits, x11_green_shift); printf("blue : %04i %05i\n", x11_blue_bits, x11_blue_shift); #endif } int x11_color_init(Display *dpy) { Screen *scr; XVisualInfo template; int found; scr = DefaultScreenOfDisplay(dpy); /* Ask for visual type */ template.screen = XDefaultScreen(dpy); template.visualid = XVisualIDFromVisual(DefaultVisualOfScreen(scr)); x11_info = XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask, &template, &found); if (XShmQueryExtension(dpy)) have_shmem = 1; if (x11_info->class != TrueColor) { fprintf(stderr, "sorry, can't handle visual\n"); return -1; } x11_find_bits(x11_info->red_mask, x11_info->green_mask, x11_info->blue_mask); return 0; } /* ------------------------------------------------------------------------ */ static int mitshm_bang = 0; static int x11_error_dev_null(Display *dpy, XErrorEvent *event) { mitshm_bang = 1; return 0; } XImage* x11_create_ximage(Display *dpy, int width, int height, void **shm) { XImage *ximage = NULL; char *ximage_data; XShmSegmentInfo *shminfo = NULL; void *old_handler; Screen *scr = DefaultScreenOfDisplay(dpy); if (have_shmem) { old_handler = XSetErrorHandler(x11_error_dev_null); (*shm) = shminfo = malloc(sizeof(XShmSegmentInfo)); memset(shminfo, 0, sizeof(XShmSegmentInfo)); ximage = XShmCreateImage(dpy, DefaultVisualOfScreen(scr), DefaultDepthOfScreen(scr), ZPixmap, NULL, shminfo, width, height); if (ximage) { shminfo->shmid = shmget(IPC_PRIVATE, ximage->bytes_per_line * ximage->height, IPC_CREAT | 0777); if (-1 == shminfo->shmid) { fprintf(stderr,"shmget(%dMB): %s\n", ximage->bytes_per_line * ximage->height / 1024 / 1024, strerror(errno)); goto oom; } shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); if ((void *) -1 == shminfo->shmaddr) { perror("shmat"); goto oom; } ximage->data = shminfo->shmaddr; shminfo->readOnly = False; XShmAttach(dpy, shminfo); XSync(dpy, False); shmctl(shminfo->shmid, IPC_RMID, 0); if (mitshm_bang) { have_shmem = 0; shmdt(shminfo->shmaddr); free(shminfo); shminfo = *shm = NULL; XDestroyImage(ximage); ximage = NULL; } } else { have_shmem = 0; free(shminfo); shminfo = *shm = NULL; } XSetErrorHandler(old_handler); } if (ximage == NULL) { (*shm) = NULL; if (NULL == (ximage_data = malloc(width * height * 4))) { fprintf(stderr,"Oops: out of memory\n"); goto oom; } ximage = XCreateImage(dpy, DefaultVisualOfScreen(scr), DefaultDepthOfScreen(scr), ZPixmap, 0, ximage_data, width, height, 8, 0); } memset(ximage->data, 0, ximage->bytes_per_line * ximage->height); return ximage; oom: if (shminfo) { if (shminfo->shmid && shminfo->shmid != -1) shmctl(shminfo->shmid, IPC_RMID, 0); free(shminfo); } if (ximage) XDestroyImage(ximage); return NULL; } void x11_destroy_ximage(Display *dpy, XImage *ximage, void *shm) { XShmSegmentInfo *shminfo = shm; if (shminfo) { XShmDetach(dpy, shminfo); XDestroyImage(ximage); shmdt(shminfo->shmaddr); free(shminfo); } else XDestroyImage(ximage); } Pixmap x11_create_pixmap(Display *dpy, unsigned char *byte_data, int width, int height) { Pixmap pixmap; XImage *ximage; XGCValues values; GC gc; unsigned long *long_data = (unsigned long *)byte_data; int x, y; void *shm; Screen *scr = DefaultScreenOfDisplay(dpy); pixmap = XCreatePixmap(dpy, RootWindowOfScreen(scr), width, height, DefaultDepthOfScreen(scr)); gc = XCreateGC(dpy, pixmap, 0, &values); if (NULL == (ximage = x11_create_ximage(dpy, width, height, &shm))) { XFreePixmap(dpy, pixmap); XFreeGC(dpy, gc); return 0; } for (y = 0; y < height; y++) for (x = 0; x < width; x++) XPutPixel(ximage, x, y, *(long_data++)); XPUTIMAGE(dpy, pixmap, gc, ximage, 0, 0, 0, 0, width, height); x11_destroy_ximage(dpy, ximage, shm); XFreeGC(dpy, gc); return pixmap; } xenwatch-0.5.4/xs_tools.c0000664000076400007640000000274510666770005014746 0ustar kraxelkraxel#ifdef HAVE_XENSTORE #include #include #include #include "xs_tools.h" /* ------------------------------------------------------------- */ struct xs_handle *xenstore_open(int daemon, int domain, int readonly_ok, int verbose) { struct xs_handle *xenstore = NULL; if (NULL == xenstore && daemon && readonly_ok) { xenstore = xs_daemon_open_readonly(); if (NULL == xenstore) fprintf(stderr,"can't connect to %s\n",xs_daemon_socket_ro()); else if (verbose) printf("connected to %s\n", xs_daemon_socket_ro()); } if (NULL == xenstore && daemon) { xenstore = xs_daemon_open(); if (NULL == xenstore) fprintf(stderr,"can't connect to %s\n",xs_daemon_socket()); else if (verbose) printf("connected to %s\n", xs_daemon_socket()); } if (NULL == xenstore && domain) { xenstore = xs_domain_open(); if (NULL == xenstore) fprintf(stderr, "can't connect to %s\n", xs_domain_dev()); else if (verbose) printf("connected to %s\n", xs_domain_dev()); } return xenstore; } int xenstore_read(struct xs_handle *xenstore, char *path, char *dst, size_t size) { xs_transaction_t xst; char *xs_value = NULL; xst = xs_transaction_start(xenstore); if (!xst) return -1; xs_value = xs_read(xenstore, xst, path, NULL); xs_transaction_end(xenstore, xst, 0); if (NULL == xs_value) return -2; snprintf(dst, size, "%s", xs_value); free(xs_value); return 0; } #endif /* HAVE_XENSTORE */ xenwatch-0.5.4/xd_store.c0000664000076400007640000004442410520106476014715 0ustar kraxelkraxel#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); } xenwatch-0.5.4/linux-uskbd.h0000664000076400007640000000665410536036023015340 0ustar kraxelkraxel [ 9 ] = { XK_Escape }, [ 10 ] = { XK_1, XK_exclam }, [ 11 ] = { XK_2, XK_at }, [ 12 ] = { XK_3, XK_numbersign }, [ 13 ] = { XK_4, XK_dollar }, [ 14 ] = { XK_5, XK_percent }, [ 15 ] = { XK_6, XK_asciicircum }, [ 16 ] = { XK_7, XK_ampersand }, [ 17 ] = { XK_8, XK_asterisk }, [ 18 ] = { XK_9, XK_parenleft }, [ 19 ] = { XK_0, XK_parenright }, [ 20 ] = { XK_minus, XK_underscore }, [ 21 ] = { XK_equal, XK_plus }, [ 22 ] = { XK_BackSpace, XK_Terminate_Server }, [ 23 ] = { XK_Tab, XK_ISO_Left_Tab }, [ 24 ] = { XK_q, XK_Q }, [ 25 ] = { XK_w, XK_W }, [ 26 ] = { XK_e, XK_E }, [ 27 ] = { XK_r, XK_R }, [ 28 ] = { XK_t, XK_T }, [ 29 ] = { XK_y, XK_Y }, [ 30 ] = { XK_u, XK_U }, [ 31 ] = { XK_i, XK_I }, [ 32 ] = { XK_o, XK_O }, [ 33 ] = { XK_p, XK_P }, [ 34 ] = { XK_bracketleft, XK_braceleft }, [ 35 ] = { XK_bracketright, XK_braceright }, [ 36 ] = { XK_Return }, [ 37 ] = { XK_Control_L }, [ 38 ] = { XK_a, XK_A }, [ 39 ] = { XK_s, XK_S }, [ 40 ] = { XK_d, XK_D }, [ 41 ] = { XK_f, XK_F }, [ 42 ] = { XK_g, XK_G }, [ 43 ] = { XK_h, XK_H }, [ 44 ] = { XK_j, XK_J }, [ 45 ] = { XK_k, XK_K }, [ 46 ] = { XK_l, XK_L }, [ 47 ] = { XK_semicolon, XK_colon }, [ 48 ] = { XK_apostrophe, XK_quotedbl }, [ 49 ] = { XK_grave, XK_asciitilde }, [ 50 ] = { XK_Shift_L }, [ 51 ] = { XK_backslash, XK_bar }, [ 52 ] = { XK_z, XK_Z }, [ 53 ] = { XK_x, XK_X }, [ 54 ] = { XK_c, XK_C }, [ 55 ] = { XK_v, XK_V }, [ 56 ] = { XK_b, XK_B }, [ 57 ] = { XK_n, XK_N }, [ 58 ] = { XK_m, XK_M }, [ 59 ] = { XK_comma, XK_less }, [ 60 ] = { XK_period, XK_greater }, [ 61 ] = { XK_slash, XK_question }, [ 62 ] = { XK_Shift_R }, [ 63 ] = { XK_KP_Multiply, 0 }, [ 64 ] = { XK_Alt_L, XK_Meta_L }, [ 65 ] = { XK_space }, [ 66 ] = { XK_Caps_Lock }, [ 67 ] = { XK_F1, 0 }, [ 68 ] = { XK_F2, 0 }, [ 69 ] = { XK_F3, 0 }, [ 70 ] = { XK_F4, 0 }, [ 71 ] = { XK_F5, 0 }, [ 72 ] = { XK_F6, 0 }, [ 73 ] = { XK_F7, 0 }, [ 74 ] = { XK_F8, 0 }, [ 75 ] = { XK_F9, 0 }, [ 76 ] = { XK_F10, 0 }, [ 77 ] = { XK_Num_Lock, XK_Pointer_EnableKeys }, [ 78 ] = { XK_Scroll_Lock }, [ 79 ] = { XK_KP_Home, XK_KP_7 }, [ 80 ] = { XK_KP_Up, XK_KP_8 }, [ 81 ] = { XK_KP_Prior, XK_KP_9 }, [ 82 ] = { XK_KP_Subtract, 0 }, [ 83 ] = { XK_KP_Left, XK_KP_4 }, [ 84 ] = { XK_KP_Begin, XK_KP_5 }, [ 85 ] = { XK_KP_Right, XK_KP_6 }, [ 86 ] = { XK_KP_Add, 0 }, [ 87 ] = { XK_KP_End, XK_KP_1 }, [ 88 ] = { XK_KP_Down, XK_KP_2 }, [ 89 ] = { XK_KP_Next, XK_KP_3 }, [ 90 ] = { XK_KP_Insert, XK_KP_0 }, [ 91 ] = { XK_KP_Delete, XK_KP_Decimal }, [ 93 ] = { XK_Mode_switch }, [ 94 ] = { XK_VoidSymbol }, /* Hmm, 102th key on intl kbd */ [ 95 ] = { XK_F11, 0 }, [ 96 ] = { XK_F12, 0 }, [ 97 ] = { XK_Home }, [ 98 ] = { XK_Up }, [ 99 ] = { XK_Prior }, [ 100 ] = { XK_Left }, [ 102 ] = { XK_Right }, [ 103 ] = { XK_End }, [ 104 ] = { XK_Down }, [ 105 ] = { XK_Next }, [ 106 ] = { XK_Insert }, [ 107 ] = { XK_Delete }, [ 108 ] = { XK_KP_Enter }, [ 109 ] = { XK_Control_R, XK_Multi_key }, [ 110 ] = { XK_Pause, XK_Break }, [ 111 ] = { XK_Print, XK_Sys_Req }, [ 112 ] = { XK_KP_Divide, 0 }, [ 113 ] = { XK_Alt_R, XK_Meta_R }, [ 115 ] = { XK_Super_L }, [ 116 ] = { XK_Super_R, XK_Multi_key }, [ 117 ] = { XK_Menu }, [ 124 ] = { XK_ISO_Level3_Shift }, [ 125 ] = { 0, XK_Alt_L }, [ 126 ] = { XK_KP_Equal }, [ 127 ] = { 0, XK_Super_L }, [ 128 ] = { 0, XK_Hyper_L }, [ 156 ] = { 0, XK_Meta_L }, xenwatch-0.5.4/vnc-old.c0000664000076400007640000011305410662272731014431 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "x11.h" #include "vnc.h" #ifdef HAVE_VNCCLIENT #include static int debug_libvnc; /* ------------------------------------------------------------------ */ struct pos { int x; int y; }; struct rect { int w; int h; }; struct vnc_window { /* vnc connection */ char display[128]; rfbClient *client; GIOChannel *ch; guint id, connected; /* gtk */ GtkAccelGroup *ac; GtkActionGroup *ag; GtkUIManager *ui; GtkWidget *win; GtkWidget *draw; GtkWidget *line, *res, *mbutton, *popup; GdkCursor *on,*off; int filter_installed; int input_grabbed; /* x11 */ XImage *ximage; void *shm; GC gc; Display *dpy; unsigned char keydown[32]; /* opengl */ int have_gl; GLuint tex; int tex_max; unsigned char *tex_data; unsigned int dirty_y1, dirty_y2; /* window / vnc display config */ struct rect window; struct pos vncoff; struct rect vncdpy; struct rect texture; int updates, redraw; /* config */ int fullscreen; int viewonly; int standalone; int showpointer; int gl_allways; int gl_fullscreen; int uskbd; int debug; }; /* ------------------------------------------------------------------ */ /* data tables */ rfbKeySym linux_uskbd[][2] = { #include "linux-uskbd.h" }; static int linux_uskbd_size = sizeof(linux_uskbd)/sizeof(linux_uskbd[0]); /* ------------------------------------------------------------------ */ /* prototypes */ static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, gpointer data); static void vnc_window_conf(struct vnc_window *vnc); static void vnc_window_texts(struct vnc_window *vnc); /* ------------------------------------------------------------------ */ /* opengl bits */ static int gl_error; static int gl_attrib[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, None }; static int catch_gl_error(Display * dpy, XErrorEvent * event) { fprintf(stderr,"WARNING: Your OpenGL setup is broken.\n"); gl_error++; return 0; } static int gl_init(struct vnc_window *vnc) { Window win = gdk_x11_drawable_get_xid(vnc->draw->window); Screen *scr = DefaultScreenOfDisplay(vnc->dpy); void *old_handler; XVisualInfo *visinfo; GLXContext ctx; if (vnc->debug) fprintf(stderr, "gl: init [window=0x%lx]\n", win); if (!win) return -1; visinfo = glXChooseVisual(vnc->dpy, XScreenNumberOfScreen(scr), gl_attrib); if (!visinfo) { if (vnc->debug) fprintf(stderr,"gl: can't get visual (rgb,db)\n"); return -1; } ctx = glXCreateContext(vnc->dpy, visinfo, NULL, True); if (!ctx) { if (vnc->debug) fprintf(stderr,"gl: can't create context\n"); return -1; } /* there is no point in using OpenGL for image scaling if it * isn't hardware accelerated ... */ if (vnc->debug) fprintf(stderr, "gl: DRI=%s\n", glXIsDirect(vnc->dpy, ctx) ? "Yes" : "No"); if (!glXIsDirect(vnc->dpy, ctx)) return -1; old_handler = XSetErrorHandler(catch_gl_error); glXMakeCurrent(vnc->dpy, win, ctx); XSync(vnc->dpy, False); XSetErrorHandler(old_handler); if (gl_error) return -1; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &vnc->tex_max); if (vnc->debug) fprintf(stderr, "gl: texture max size: %d\n", vnc->tex_max); return 0; } static void gl_resize_window(struct vnc_window *vnc) { if (!vnc->tex) return; glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glViewport(0, 0, vnc->window.w, vnc->window.h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, vnc->window.w, 0.0, vnc->window.h, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); vnc->redraw++; } static void gl_blit(struct vnc_window *vnc, int y1, int y2) { Window win = gdk_x11_drawable_get_xid(vnc->draw->window); float x,y; unsigned int ww = vnc->window.w, wh = vnc->window.h; int wx = 0, wy = 0; if (!vnc->tex) return; if (y1 > vnc->vncdpy.h) y1 = vnc->vncdpy.h; if (y2 > vnc->vncdpy.h) y2 = vnc->vncdpy.h; glBindTexture(GL_TEXTURE_2D, vnc->tex); glTexSubImage2D(GL_TEXTURE_2D, 0, 0,y1, vnc->vncdpy.w, y2 - y1, GL_BGRA_EXT /* GL_RGB */, GL_UNSIGNED_BYTE, vnc->tex_data + y1 * 4 * vnc->vncdpy.w); x = (float)vnc->vncdpy.w / vnc->texture.w; y = (float)vnc->vncdpy.h / vnc->texture.h; glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glBegin(GL_QUADS); glTexCoord2f(0,y); glVertex3f(wx, wy, 0); glTexCoord2f(0,0); glVertex3f(wx, wy+wh, 0); glTexCoord2f(x,0); glVertex3f(wx+ww, wy+wh, 0); glTexCoord2f(x,y); glVertex3f(wx+ww, wy, 0); glEnd(); glXSwapBuffers(vnc->dpy, win); glDisable(GL_TEXTURE_2D); } static void gl_update_vnc(struct vnc_window *vnc, int x, int y, int w, int h) { if (vnc->dirty_y1 > y) vnc->dirty_y1 = y; if (vnc->dirty_y2 < y+h) vnc->dirty_y2 = y+h; vnc->updates++; } static void gl_update_win(struct vnc_window *vnc, int x, int y, int w, int h) { vnc->dirty_y1 = 0; vnc->dirty_y2 = vnc->vncdpy.h; vnc->updates++; } static void gl_flush(struct vnc_window *vnc) { if (vnc->redraw) { vnc->dirty_y1 = 0; vnc->dirty_y2 = vnc->vncdpy.h; } gl_blit(vnc, vnc->dirty_y1, vnc->dirty_y2); vnc->dirty_y1 = 99999; vnc->dirty_y2 = 0; vnc->updates = 0; vnc->redraw = 0; } /* ------------------------------------------------------------------ */ /* x11 draw bits */ static void x11_update_win(struct vnc_window *vnc, int x, int y, int w, int h) { Window win = gdk_x11_drawable_get_xid(vnc->draw->window); if (!vnc->gc) { XGCValues values; XColor color, dummy; Colormap cmap; cmap = gdk_x11_colormap_get_xcolormap(gtk_widget_get_colormap(vnc->win)); XAllocNamedColor(vnc->dpy, cmap, "#404040", &color, &dummy); values.function = GXcopy; values.foreground = color.pixel; vnc->gc = XCreateGC(vnc->dpy, win, GCForeground|GCFunction, &values); } if (x < vnc->vncoff.x) vnc->redraw++; if (y < vnc->vncoff.y) vnc->redraw++; if (x+w > vnc->vncoff.x + vnc->vncdpy.w) vnc->redraw++; if (y+h > vnc->vncoff.y + vnc->vncdpy.h) vnc->redraw++; if (vnc->redraw) { XFillRectangle(vnc->dpy, win, vnc->gc, 0,0, vnc->window.w, vnc->window.h); XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, 0,0, vnc->vncoff.x, vnc->vncoff.y, vnc->vncdpy.w, vnc->vncdpy.h); vnc->redraw = 0; } else { XPUTIMAGE(vnc->dpy, win, vnc->gc, vnc->ximage, x-vnc->vncoff.x, y-vnc->vncoff.y, x,y, w,h); } } static void x11_update_vnc(struct vnc_window *vnc, int x, int y, int w, int h) { x11_update_win(vnc, x + vnc->vncoff.x, y + vnc->vncoff.y, w, h); } static void x11_resize_window(struct vnc_window *vnc) { vnc->vncoff.x = (vnc->window.w - vnc->vncdpy.w) / 2; vnc->vncoff.y = (vnc->window.h - vnc->vncdpy.h) / 2; } static void x11_flush(struct vnc_window *vnc) { if (vnc->redraw) x11_update_win(vnc, 0, 0, vnc->vncdpy.w, vnc->vncdpy.w); vnc->updates = 0; vnc->redraw = 0; } /* ------------------------------------------------------------------ */ /* x11/gl wrappers */ /* vnc display coordinates */ static void dpy_update_vnc(struct vnc_window *vnc, int x, int y, int w, int h) { if (vnc->debug) fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, vnc->tex ? " GL" : "", vnc->ximage ? " X11" : "", w, h, x, y); if (vnc->ximage) x11_update_vnc(vnc, x, y, w, h); if (vnc->tex) gl_update_vnc(vnc, x, y, w, h); } /* app window coordinates */ static void dpy_update_win(struct vnc_window *vnc, int x, int y, int w, int h) { if (vnc->debug) fprintf(stderr, "%s: mode%s%s, %dx%d+%d+%d\n", __FUNCTION__, vnc->tex ? " GL" : "", vnc->ximage ? " X11" : "", w, h, x, y); if (vnc->ximage) x11_update_win(vnc, x, y, w, h); if (vnc->tex) gl_update_win(vnc, x, y, w, h); } static void dpy_redraw(struct vnc_window *vnc) { vnc->redraw++; } static void dpy_flush(struct vnc_window *vnc, const char *caller) { if (vnc->debug) fprintf(stderr, "%s: from %s, mode%s%s, updates %d, redraw %d\n", __FUNCTION__, caller, vnc->tex ? " GL" : "", vnc->ximage ? " X11" : "", vnc->updates, vnc->redraw); if (!vnc->updates && !vnc->redraw) return; if (vnc->ximage) x11_flush(vnc); if (vnc->tex) gl_flush(vnc); } static int dpy_gl_check(struct vnc_window *vnc) { int using_gl = 0; if (vnc->have_gl && vnc->vncdpy.w < vnc->tex_max && vnc->vncdpy.h < vnc->tex_max) { if (vnc->gl_allways) using_gl = 1; if (vnc->gl_fullscreen && vnc->fullscreen) using_gl = 1; } return using_gl; } static void dpy_setup(struct vnc_window *vnc, int width, int height, int using_gl) { /* cleanup */ if (vnc->ximage) { x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); vnc->ximage = NULL; } if (vnc->tex) { /* FIXME: release texture */ vnc->tex = 0; vnc->vncdpy.w = 0; vnc->vncdpy.h = 0; free(vnc->tex_data); vnc->tex_data = NULL; } vnc->vncdpy.w = width; vnc->vncdpy.h = height; if (!using_gl) { /* init X11 */ vnc->ximage = x11_create_ximage(vnc->dpy, vnc->vncdpy.w, vnc->vncdpy.h, &vnc->shm); if (NULL == vnc->ximage) { fprintf(stderr, "Oops: creating ximage failed\n"); return; } vnc->client->width = vnc->ximage->bytes_per_line / (vnc->ximage->bits_per_pixel / 8); vnc->client->frameBuffer = (void*)vnc->ximage->data; vnc->client->format.bitsPerPixel = vnc->ximage->bits_per_pixel; vnc->client->format.redShift = x11_red_shift; vnc->client->format.greenShift = x11_green_shift; vnc->client->format.blueShift = x11_blue_shift; vnc->client->format.redMax = (1 << x11_red_bits) - 1; vnc->client->format.greenMax = (1 << x11_green_bits) - 1; vnc->client->format.blueMax = (1 << x11_blue_bits) - 1; } else { /* init OpenGL */ void *dummy; int i; /* figure texture size (power of two) */ for (i = 0; vnc->vncdpy.w >= (1 << i); i++) ; vnc->texture.w = (1 << i); for (i = 0; vnc->vncdpy.h >= (1 << i); i++) ; vnc->texture.h = (1 << i); if (vnc->debug) fprintf(stderr,"%s: client %dx%d, tex %dx%d\n", __FUNCTION__, vnc->vncdpy.w, vnc->vncdpy.h, vnc->texture.w, vnc->texture.h); /* create texture */ glGenTextures(1, &vnc->tex); glBindTexture(GL_TEXTURE_2D, vnc->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); dummy = malloc(vnc->texture.w * vnc->texture.h * 4); memset(dummy, 128, vnc->texture.w * vnc->texture.h * 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, vnc->texture.w, vnc->texture.h, 0, GL_RGB, GL_UNSIGNED_BYTE, dummy); free(dummy); /* image buffer */ vnc->tex_data = malloc(vnc->vncdpy.w * vnc->vncdpy.h * 4); vnc->client->frameBuffer = vnc->tex_data; vnc->client->format.bitsPerPixel = 32; vnc->client->format.redShift = 16; vnc->client->format.greenShift = 8; vnc->client->format.blueShift = 0; vnc->client->format.redMax = 255; vnc->client->format.greenMax = 255; vnc->client->format.blueMax = 255; } if (vnc->debug) fprintf(stderr, "%s: SetFormatAndEncodings: %s\n", __FUNCTION__, using_gl ? "GL" : "X11" ); SetFormatAndEncodings(vnc->client); SendFramebufferUpdateRequest(vnc->client, 0, 0, vnc->vncdpy.w, vnc->vncdpy.h, False); vnc_window_conf(vnc); } static void dpy_resize_window(struct vnc_window *vnc) { int using_gl; vnc->vncoff.x = 0; vnc->vncoff.y = 0; using_gl = dpy_gl_check(vnc); if ((vnc->tex && !using_gl) || (vnc->ximage && using_gl)) { /* switching display mode */ dpy_setup(vnc, vnc->client->width, vnc->client->height, using_gl); } else { if (vnc->ximage) x11_resize_window(vnc); if (vnc->tex) gl_resize_window(vnc); dpy_redraw(vnc); } vnc_window_texts(vnc); } /* ------------------------------------------------------------------ */ /* helper functions */ static void XAddInput(Display *dpy, Window win, long mask) { XWindowAttributes attr; XGetWindowAttributes(dpy, win, &attr); XSelectInput(dpy, win, attr.your_event_mask | mask); } static GdkCursor* empty_cursor(void) { static char bits[32]; GdkCursor *cursor; GdkPixmap *pixmap; GdkColor fg = { 0, 0, 0, 0 }; GdkColor bg = { 0, 0, 0, 0 }; pixmap = gdk_bitmap_create_from_data(NULL, bits, 16, 16); cursor = gdk_cursor_new_from_pixmap(pixmap, pixmap, &fg, &bg, 0, 0); gdk_pixmap_unref(pixmap); return cursor; } static void vnc_window_texts(struct vnc_window *vnc) { char textline[256]; int pos; if (vnc->client && vnc->client->desktopName && strlen(vnc->client->desktopName)) { gtk_window_set_title(GTK_WINDOW(vnc->win), vnc->client->desktopName); } else { snprintf(textline, sizeof(textline), "connecting to %s ...", vnc->display); gtk_window_set_title(GTK_WINDOW(vnc->win), textline); } if (vnc->input_grabbed) { gtk_label_set_text(GTK_LABEL(vnc->line), "Press Ctrl-Alt to release input grab."); } else if (vnc->client) { snprintf(textline, sizeof(textline), "VNC: \"%s\" at %s", vnc->client->desktopName ?: "", vnc->display); gtk_label_set_text(GTK_LABEL(vnc->line), textline); } if (vnc->client) { pos = 0; pos += snprintf(textline+pos, sizeof(textline)-pos, "%dx%d", vnc->vncdpy.w, vnc->vncdpy.h); if (vnc->vncdpy.w != vnc->window.w || vnc->vncdpy.h != vnc->window.h) pos += snprintf(textline+pos, sizeof(textline)-pos, " @ %dx%d", vnc->window.w, vnc->window.h); pos += snprintf(textline+pos, sizeof(textline)-pos, " (%s)", vnc->ximage ? "X11" : "GL"); gtk_label_set_text(GTK_LABEL(vnc->res), textline); } } static void vnc_window_conf(struct vnc_window *vnc) { if (!vnc->draw) return; if (vnc->client) gtk_widget_set_size_request(vnc->draw, vnc->vncdpy.w, vnc->vncdpy.h); if (vnc->draw->window) { gdk_window_set_cursor(vnc->draw->window, vnc->showpointer ? vnc->on : vnc->off); /* FIXME */ XAddInput(vnc->dpy, gdk_x11_drawable_get_xid(vnc->draw->window), KeymapStateMask); } dpy_resize_window(vnc); } static void vnc_release(struct vnc_window *vnc) { int sock = -1; if (NULL == vnc) return; if (vnc->connected && vnc->client) { /* * library bugs? * - calling rfbClientCleanup() unconnected segfaults * - rfbClientCleanup() doesn't close the socket */ sock = vnc->client->sock; rfbClientCleanup(vnc->client); vnc->client = NULL; } if (vnc->filter_installed) gdk_window_remove_filter(vnc->draw->window, event_filter, vnc); if (vnc->id) g_source_destroy(g_main_context_find_source_by_id (g_main_context_default(), vnc->id)); if (vnc->ximage) x11_destroy_ximage(vnc->dpy, vnc->ximage, vnc->shm); if (vnc->gc) XFreeGC(vnc->dpy, vnc->gc); if (-1 != sock) close(sock); free(vnc); } static void grab_input(struct vnc_window *vnc, guint32 time) { if (vnc->viewonly) return; if (vnc->input_grabbed) return; gdk_pointer_grab(vnc->draw->window, FALSE, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK, vnc->draw->window, NULL, time); gdk_keyboard_grab(vnc->draw->window, FALSE, time); vnc->input_grabbed = 1; vnc_window_texts(vnc); } static void ungrab_input(struct vnc_window *vnc, guint32 time) { if (!vnc->input_grabbed) return; gdk_pointer_ungrab(time); gdk_keyboard_ungrab(time); vnc->input_grabbed = 0; vnc_window_texts(vnc); } /* ------------------------------------------------------------------ */ /* libvncclient callbacks */ static rfbBool vnc_resize(rfbClient* client) { struct vnc_window *vnc = rfbClientGetClientData(client, vnc_open); if (vnc->debug) fprintf(stderr, "%s: %dx%d\n", __FUNCTION__, client->width, client->height); if (vnc->vncdpy.w == client->width && vnc->vncdpy.h == client->height) { if (vnc->debug) fprintf(stderr, "%s: no size change, early exit\n", __FUNCTION__); return TRUE; } dpy_setup(vnc, client->width, client->height, dpy_gl_check(vnc)); return TRUE; } static void vnc_update(rfbClient* cl, int x, int y, int w, int h) { struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); if (!GTK_WIDGET_DRAWABLE(vnc->draw)) return; dpy_update_vnc(vnc, x,y, w,h); } #ifdef HAVE_VNC_TEXT static void vnc_textchat(rfbClient* cl, int value, char *text) { switch(value) { case rfbTextChatOpen: fprintf(stderr,"%s: Open\n", __FUNCTION__); break; case rfbTextChatClose: case rfbTextChatFinished: fprintf(stderr,"%s: Close/Finished\n", __FUNCTION__); break; default: fprintf(stderr,"%s: \"%s\"\n", __FUNCTION__, text); break; } } #endif static char *vnc_passwd(rfbClient* cl) { struct vnc_window *vnc = rfbClientGetClientData(cl, vnc_open); GtkWidget *dialog, *label, *entry; char *passwd = NULL; const char *txt; char message[256]; if (vnc->debug) fprintf(stderr,"%s: called\n", __FUNCTION__); /* Create the widgets */ dialog = gtk_dialog_new_with_buttons("Password needed", vnc->win ? GTK_WINDOW(vnc->win) : NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); snprintf(message, sizeof(message), "Please enter vnc screen password for \"%s\".", vnc->client->desktopName ? : "unknown"); label = gtk_label_new(message); entry = gtk_entry_new(); gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 10); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); /* show and wait for response */ gtk_widget_show_all(dialog); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case GTK_RESPONSE_ACCEPT: txt = gtk_entry_get_text(GTK_ENTRY(entry)); passwd = strdup(txt); if (vnc->debug) fprintf(stderr,"%s: OK: \"%s\"\n", __FUNCTION__, passwd); break; default: if (vnc->debug) fprintf(stderr,"%s: canceled\n", __FUNCTION__); passwd = strdup(""); break; } gtk_widget_destroy(dialog); return passwd; } static void vnc_log(const char *format, ...) { va_list args; if (!debug_libvnc) return; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } /* ------------------------------------------------------------------ */ /* glib/gtk callbacks */ static gboolean vnc_data_cb(GIOChannel *source, GIOCondition condition, gpointer data) { struct vnc_window *vnc = data; if (!HandleRFBServerMessage(vnc->client)) { /* server closed connection */ g_source_destroy(g_main_context_find_source_by_id (g_main_context_default(), vnc->id)); vnc->id = 0; gtk_widget_destroy(vnc->win); } dpy_flush(vnc, __FUNCTION__); return TRUE; } static void destroy_cb(GtkWidget *widget, gpointer data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr,"%s: called\n", __FUNCTION__); if (vnc->standalone) gtk_main_quit(); vnc_release(vnc); } static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct vnc_window *vnc = data; dpy_update_win(vnc, event->area.x, event->area.y, event->area.width, event->area.height); if (0 == event->count) dpy_flush(vnc, __FUNCTION__); return TRUE; } static gboolean configure_cb(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr,"%s: %dx%d\n", __FUNCTION__, event->width, event->height); vnc->window.w = event->width; vnc->window.h = event->height; dpy_resize_window(vnc); dpy_flush(vnc, __FUNCTION__); return TRUE; } static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, gpointer data) { struct vnc_window *vnc = data; GtkWidget *item; if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { vnc->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; if (vnc->debug) fprintf(stderr, "%s: fullscreen %s\n", __FUNCTION__, vnc->fullscreen ? "on" : "off"); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/FullScreen"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->fullscreen); } return TRUE; } static void send_mouse(struct vnc_window *vnc, int x, int y, int x11state, int x11press, int x11release) { static const struct { int rfbmask; int x11mask; int x11nr; } buttons[] = { { .rfbmask = rfbButton1Mask, .x11mask = Button1Mask, .x11nr = Button1, },{ .rfbmask = rfbButton2Mask, .x11mask = Button2Mask, .x11nr = Button2, },{ .rfbmask = rfbButton3Mask, .x11mask = Button3Mask, .x11nr = Button3, },{ .rfbmask = rfbButton4Mask, .x11mask = Button4Mask, .x11nr = Button4, },{ .rfbmask = rfbButton5Mask, .x11mask = Button5Mask, .x11nr = Button5, } }; int i, rfbstate = 0; if (vnc->viewonly) return; for (i = 0; i < sizeof(buttons)/sizeof(buttons[0]); i++) { if (x11state & buttons[i].x11mask) rfbstate |= buttons[i].rfbmask; if (x11press == buttons[i].x11nr) rfbstate |= buttons[i].rfbmask; if (x11release == buttons[i].x11nr) rfbstate &= ~buttons[i].rfbmask; } /* fixup mouse coordinates */ x -= vnc->vncoff.x; y -= vnc->vncoff.y; if (vnc->tex) { x = x * vnc->window.w / vnc->vncdpy.w; y = y * vnc->window.h / vnc->vncdpy.h; } if (x < 0) x = 0; if (y < 0) y = 0; if (x >= vnc->vncdpy.w) x = vnc->vncdpy.w -1; if (y >= vnc->vncdpy.h) y = vnc->vncdpy.h -1; if (vnc->debug) fprintf(stderr,"%s: +%d+%d x11state 0x%x rfbstate 0x%x\n", __FUNCTION__, x, y, x11state, rfbstate); SendPointerEvent(vnc->client, x, y, rfbstate); } static gboolean button_cb(GtkWidget *widget, GdkEventButton *event, gpointer data) { struct vnc_window *vnc = data; switch (event->type) { case GDK_BUTTON_PRESS: send_mouse(vnc, event->x, event->y, event->state, event->button, 0); grab_input(vnc, event->time); break; case GDK_BUTTON_RELEASE: send_mouse(vnc, event->x, event->y, event->state, 0, event->button); break; default: /* keep gcc happy */ break; } return TRUE; } static gboolean motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data) { struct vnc_window *vnc = data; send_mouse(vnc, event->x, event->y, event->state, 0, 0); return TRUE; } static void key_local(struct vnc_window *vnc, GdkEventKey *event) { int keydown; if (vnc->debug) fprintf(stderr,"%s[%d]: called: keysym %d\n", __FUNCTION__, event->type, event->keyval); keydown = (8 == event->type); if (!vnc->viewonly) SendKeyEvent(vnc->client, event->keyval, keydown ? TRUE : FALSE); } static void key_uskbd(struct vnc_window *vnc, GdkEventKey *event) { rfbKeySym keysym = 0; int shift,keydown; keydown = (8 == event->type); if (event->hardware_keycode < 256) { int by = event->hardware_keycode / 8; int bi = event->hardware_keycode % 8; if (keydown) vnc->keydown[by] |= (1 << bi); else vnc->keydown[by] &= ~(1 << bi); } shift = (event->state & GDK_SHIFT_MASK) ? 1 : 0; if (event->hardware_keycode < linux_uskbd_size) { keysym = linux_uskbd[event->hardware_keycode][shift]; if (0 == keysym) keysym = linux_uskbd[event->hardware_keycode][0]; } if (vnc->debug || 0 == keysym) fprintf(stderr,"%s[%d]: called: keycode %d => keysym %d\n", __FUNCTION__, event->type, event->hardware_keycode, keysym); if (keysym && !vnc->viewonly) SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); } static gboolean key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct vnc_window *vnc = data; int mask = GDK_CONTROL_MASK | GDK_MOD1_MASK; if (mask == (event->state & mask)) ungrab_input(vnc, event->time); if (vnc->uskbd) key_uskbd(vnc, event); else key_local(vnc, event); return TRUE; } static void menu_btn(GtkWidget *widget, gpointer data) { struct vnc_window *vnc = data; gtk_menu_popup(GTK_MENU(vnc->popup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); } static GdkFilterReturn event_filter(GdkXEvent *gdkxevent, GdkEvent *gtkevent, gpointer data) { struct vnc_window *vnc = data; XEvent *xevent = gdkxevent; int by, bi, keydown, keycode; rfbKeySym keysym = 0; switch (xevent->type) { case KeymapNotify: if (!vnc->uskbd) return GDK_FILTER_REMOVE; if (!GTK_WIDGET_HAS_FOCUS(vnc->draw)) return GDK_FILTER_REMOVE; for (by = 0; by < 32; by++) { if (vnc->keydown[by] == xevent->xkeymap.key_vector[by]) continue; for (bi = 0; bi < 8; bi++) { if ((vnc->keydown[by] & (1 << bi)) == (xevent->xkeymap.key_vector[by] & (1 << bi))) continue; keydown = xevent->xkeymap.key_vector[by] & (1 << bi); keycode = by * 8 + bi; keysym = linux_uskbd[keycode][0]; if (!keysym) continue; if (vnc->debug) fprintf(stderr,"%s: KeymapNotify: %-7s %3d\n", __FUNCTION__, keydown ? "press" : "release", keycode); if (!vnc->viewonly) SendKeyEvent(vnc->client, keysym, keydown ? TRUE : FALSE); } } memcpy(vnc->keydown, xevent->xkeymap.key_vector, 32); return GDK_FILTER_REMOVE; default: return GDK_FILTER_CONTINUE; } } /* ------------------------------------------------------------------ */ static void menu_cb_full_screen(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; vnc->fullscreen = gtk_toggle_action_get_active(action); if (vnc->fullscreen) gtk_window_fullscreen(GTK_WINDOW(vnc->win)); else gtk_window_unfullscreen(GTK_WINDOW(vnc->win)); } static void menu_cb_us_keyboard(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; vnc->uskbd = gtk_toggle_action_get_active(action); } static void menu_cb_show_pointer(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; vnc->showpointer = gtk_toggle_action_get_active(action); if (vnc->draw->window) gdk_window_set_cursor(vnc->draw->window, vnc->showpointer ? vnc->on : vnc->off); } static void menu_cb_view_only(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; vnc->viewonly = gtk_toggle_action_get_active(action); } static void menu_cb_gl(GtkRadioAction *action, GtkRadioAction *current, gpointer user_data) { struct vnc_window *vnc = user_data; int value = gtk_radio_action_get_current_value(action); switch (value) { case 1: /* OFF */ vnc->gl_fullscreen = 0; vnc->gl_allways = 0; break; case 2: /* fullscreen ON */ vnc->gl_fullscreen = 1; vnc->gl_allways = 0; break; case 3: /* allways ON */ vnc->gl_fullscreen = 1; vnc->gl_allways = 1; break; } dpy_resize_window(vnc); } static void menu_cb_about(GtkMenuItem *item, void *user_data) { static char *comments = "simple vnc client"; static char *copyright = "(c) 2005-2007 Gerd Hoffmann"; static char *authors[] = { "Gerd Hoffmann ", NULL }; struct vnc_window *vnc = user_data; gtk_show_about_dialog(GTK_WINDOW(vnc->win), "authors", authors, "comments", comments, "copyright", copyright, "logo-icon-name", GTK_STOCK_ABOUT, "version", VERSION, NULL); } static void menu_cb_quit(GtkMenuItem *item, void *user_data) { struct vnc_window *vnc = user_data; gtk_widget_destroy(vnc->win); } /* ------------------------------------------------------------------ */ static const GtkActionEntry entries[] = { { /* popup menu */ .name = "ConfMenu", .label = "Config", },{ /* menu items */ .name = "About", .stock_id = GTK_STOCK_ABOUT, .label = "_About ...", .callback = G_CALLBACK(menu_cb_about), },{ .name = "Close", .stock_id = GTK_STOCK_QUIT, .label = "_Close", // .accelerator = "Q", .tooltip = "Quit the job", .callback = G_CALLBACK(menu_cb_quit), } }; static const GtkToggleActionEntry tentries[] = { { .name = "FullScreen", .label = "_Fullscreen", .accelerator = "F11", .callback = G_CALLBACK(menu_cb_full_screen), },{ .name = "USKbd", .label = "US _Keyboard", .callback = G_CALLBACK(menu_cb_us_keyboard), },{ .name = "ShowPointer", .label = "Show _Pointer", .callback = G_CALLBACK(menu_cb_show_pointer), },{ .name = "ViewOnly", .label = "_View only", .callback = G_CALLBACK(menu_cb_view_only), } }; static const GtkRadioActionEntry rentries[] = { { .name = "GL_OFF", .label = "_Disable OpenGL scaling", .value = 1, },{ .name = "GL_FSonly", .label = "OpenGL scaling for fullscreen", .value = 2, },{ .name = "GL_ON", .label = "Allways scale using _OpenGL", .value = 3, } }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ------------------------------------------------------------------ */ /* public API functions */ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) { GtkWidget *vbox, *hbox, *frame, *item; GtkAction *action; GError *err; char *argv[] = { "vnc-client", NULL, NULL }; int argc = sizeof(argv)/sizeof(argv[0]) -1; struct vnc_window *vnc; int rc; vnc = malloc(sizeof(*vnc)); if (NULL == vnc) goto err; memset(vnc,0,sizeof(*vnc)); vnc->standalone = (flags & VNC_FLAG_STANDALONE); vnc->showpointer = (flags & VNC_FLAG_SHOW_MOUSE); vnc->uskbd = (flags & VNC_FLAG_US_KBD); vnc->viewonly = (flags & VNC_FLAG_VIEW_ONLY); vnc->gl_allways = (flags & VNC_FLAG_GL_ALLWAYS); vnc->gl_fullscreen = (flags & VNC_FLAG_GL_FULLSCR); vnc->debug = debug_level; debug_libvnc = debug_level; snprintf(vnc->display, sizeof(vnc->display), "%s:%d", hostname, tcpport - 5900); vnc->dirty_y1 = 99999; vnc->dirty_y2 = 0; /* x11 */ vnc->dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); if (NULL == x11_info) if (0 != x11_color_init(vnc->dpy)) goto err; /* gtk toplevel */ vnc->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(vnc->win), "destroy", G_CALLBACK(destroy_cb), vnc); vnc->on = gdk_cursor_new(GDK_LEFT_PTR); vnc->off = empty_cursor(); /* gtk drawing area */ vnc->draw = gtk_drawing_area_new(); GTK_WIDGET_SET_FLAGS(vnc->draw, GTK_CAN_FOCUS); gtk_widget_add_events(vnc->draw, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_EXPOSURE_MASK); gtk_widget_set_app_paintable(vnc->draw, TRUE); gtk_widget_set_double_buffered(vnc->draw, FALSE); g_signal_connect(G_OBJECT(vnc->draw), "expose-event", G_CALLBACK(expose_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "button-press-event", G_CALLBACK(button_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "button-release-event", G_CALLBACK(button_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "motion-notify-event", G_CALLBACK(motion_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "key-press-event", G_CALLBACK(key_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "key-release-event", G_CALLBACK(key_cb), vnc); g_signal_connect(G_OBJECT(vnc->draw), "configure-event", G_CALLBACK(configure_cb), vnc); g_signal_connect(G_OBJECT(vnc->win), "window-state-event", G_CALLBACK(window_state_cb), vnc); gdk_window_add_filter(NULL, event_filter, vnc); vnc->filter_installed = 1; /* popup menu */ vnc->ui = gtk_ui_manager_new(); vnc->ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(vnc->ag, entries, G_N_ELEMENTS(entries), vnc); gtk_action_group_add_toggle_actions(vnc->ag, tentries, G_N_ELEMENTS(tentries), vnc); gtk_action_group_add_radio_actions(vnc->ag, rentries, G_N_ELEMENTS(rentries), 0, G_CALLBACK(menu_cb_gl), vnc); gtk_ui_manager_insert_action_group(vnc->ui, vnc->ag, 0); vnc->ac = gtk_ui_manager_get_accel_group(vnc->ui); gtk_window_add_accel_group(GTK_WINDOW(vnc->win), vnc->ac); err = NULL; if (!gtk_ui_manager_add_ui_from_string(vnc->ui, ui_xml, -1, &err)) { g_message("building menus failed: %s", err->message); g_error_free(err); exit(1); } vnc->popup = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu"); gtk_menu_set_title(GTK_MENU(vnc->popup), "Config"); /* popup menu: initial state */ item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/USKbd"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->uskbd); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ShowPointer"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->showpointer); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ViewOnly"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->viewonly); action = gtk_ui_manager_get_action(vnc->ui, "/ConfMenu/GL_ON"); if (vnc->gl_allways) gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 3); else if (vnc->gl_fullscreen) gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 2); else gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), 1); /* labels for the status line */ vnc->line = gtk_label_new("status line"); vnc->res = gtk_label_new("vnc screen resolution"); vnc->mbutton = gtk_button_new_with_label("config"); g_signal_connect(G_OBJECT(vnc->mbutton), "clicked", G_CALLBACK(menu_btn), vnc); GTK_WIDGET_UNSET_FLAGS(vnc->mbutton, GTK_CAN_FOCUS); /* packing */ vbox = gtk_vbox_new(FALSE, 0); hbox = gtk_hbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(vnc->win), vbox); gtk_box_pack_start(GTK_BOX(vbox), vnc->draw, TRUE, TRUE, 0); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vnc->line); gtk_misc_set_alignment(GTK_MISC(vnc->line), 0, 0.5); gtk_misc_set_padding(GTK_MISC(vnc->line), 3, 1); frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vnc->res); gtk_misc_set_padding(GTK_MISC(vnc->res), 3, 1); gtk_box_pack_start(GTK_BOX(hbox), vnc->mbutton, FALSE, TRUE, 0); /* show window */ gtk_widget_show_all(vnc->win); vnc_window_conf(vnc); if (flags & VNC_FLAG_FULLSCREEN) gtk_window_fullscreen(GTK_WINDOW(vnc->win)); /* opengl */ if (0 == gl_init(vnc)) vnc->have_gl = 1; /* rfb client */ argv[1] = vnc->display; fprintf(stderr, "%s: connecting to %s\n", __FUNCTION__, vnc->display); if (8 == x11_red_bits) vnc->client = rfbGetClient(8,3,4); else vnc->client = rfbGetClient(5,3,2); if (NULL == vnc->client) goto err; rfbClientSetClientData(vnc->client, vnc_open, vnc); vnc->client->MallocFrameBuffer = vnc_resize; vnc->client->GotFrameBufferUpdate = vnc_update; vnc->client->GetPassword = vnc_passwd; #ifdef HAVE_VNC_TEXT vnc->client->canHandleNewFBSize = TRUE; /* was added before textchat */ vnc->client->HandleTextChat = vnc_textchat; #endif rfbClientLog = vnc_log; rc = rfbInitClient(vnc->client, &argc, argv); if (0 == rc) goto err; vnc->ch = g_io_channel_unix_new(vnc->client->sock); vnc->id = g_io_add_watch(vnc->ch, G_IO_IN, vnc_data_cb, vnc); vnc->connected = 1; return vnc->win; err: vnc_release(vnc); return NULL; } #else /* HAVE_VNCCLIENT */ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) { fprintf(stderr, "compiled without VNC support, sorry\n"); return NULL; } #endif /* HAVE_VNCCLIENT */ xenwatch-0.5.4/xenwatch.desktop0000664000076400007640000000022410666015663016134 0ustar kraxelkraxel[Desktop Entry] Encoding=UTF-8 Name=xenwatch Comment=watch xen domains Exec=xenstore Terminal=false Type=Application Categories=GTK;System;Utility; xenwatch-0.5.4/xenlog.desktop0000664000076400007640000000022610666015663015611 0ustar kraxelkraxel[Desktop Entry] Encoding=UTF-8 Name=xenlog Comment=xenstore activity logger Exec=xenlog Terminal=true Type=Application Categories=GTK;System;Utility; xenwatch-0.5.4/tcp.h0000664000076400007640000000036410350571765013664 0ustar kraxelkraxel#include #include #include extern int tcp_verbose; int tcp_connect(struct addrinfo *ai, char *addr, char *port, char *host, char *serv); int tcp_listen(struct addrinfo *ai, char *addr, char *port); xenwatch-0.5.4/xs_tools.h0000664000076400007640000000026410661015474014742 0ustar kraxelkraxelstruct xs_handle * xenstore_open(int daemon, int domain, int readonly_ok, int verbose); int xenstore_read(struct xs_handle *xenstore, char *path, char *dst, size_t size); xenwatch-0.5.4/xenstore-gtk.c0000664000076400007640000000076311122437756015525 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include "xenviews.h" /* ------------------------------------------------------------------ */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); gtk_init(&argc, &argv); xenstore_create_window(); gtk_widget_show_all(xs_toplevel); gtk_main(); fprintf(stderr,"bye...\n"); exit(0); } xenwatch-0.5.4/x11.h0000664000076400007640000000157510517674357013522 0ustar kraxelkraxel#include #include #include #include extern XVisualInfo *x11_info; extern int have_shmem; extern int x11_red_bits; extern int x11_red_shift; extern int x11_green_bits; extern int x11_green_shift; extern int x11_blue_bits; extern int x11_blue_shift; int x11_color_init(Display *dpy); XImage *x11_create_ximage(Display *dpy, int width, int height, void **shm); void x11_destroy_ximage(Display *dpy, XImage * ximage, void *shm); Pixmap x11_create_pixmap(Display *dpy, unsigned char *data, int width, int height); #define XPUTIMAGE(dpy,dr,gc,xi,a,b,c,d,w,h) \ if (have_shmem) \ XShmPutImage(dpy,dr,gc,xi,a,b,c,d,w,h,True); \ else \ XPutImage(dpy,dr,gc,xi,a,b,c,d,w,h) xenwatch-0.5.4/list.h0000664000076400007640000001045410346013137014037 0ustar kraxelkraxel#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 */ xenwatch-0.5.4/Testbuild.sh0000775000076400007640000000033210666015663015216 0ustar kraxelkraxel#!/bin/sh set -ex make distclean make make clean for option in HAVE_GTK HAVE_GTK_VNC HAVE_AVAHI HAVE_XENSTORE HAVE_LIBVIRT; do make $option=no make $option=no -n install make $option=no clean done make distclean xenwatch-0.5.4/xenlog.c0000664000076400007640000000201110661015474014347 0ustar kraxelkraxel#include #include #include #include #include "xs_tools.h" /* ------------------------------------------------------------- */ int main(int argc, char *argv[]) { struct xs_handle *xenstore = NULL; char **vec; char value[256]; unsigned int count, rc; xenstore = xenstore_open(1,1,1,1); if (NULL == xenstore) { fprintf(stderr, "can't access xenstore, exiting\n"); 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 '/': rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); switch (rc) { case -1: strcpy(value, ""); break; case -2: strcpy(value, ""); break; } break; default: strcpy(value, ""); break; } printf("%-64s : \"%s\"\n", vec[XS_WATCH_PATH], value); fflush(stdout); free(vec); } } xenwatch-0.5.4/xs_store.c0000664000076400007640000004460110366200066014727 0ustar kraxelkraxel#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); } xenwatch-0.5.4/COPYING0000664000076400007640000004307610452665067013771 0ustar kraxelkraxel GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. xenwatch-0.5.4/mdns-publish.h0000664000076400007640000000143110562316561015473 0ustar kraxelkraxelstruct mdns_pub; struct mdns_pub_entry; extern char *mdns_pub_appname; extern int mdns_pub_termsig; extern int mdns_pub_appquit; /* initialization and cleanup */ struct mdns_pub *mdns_pub_init(int debug); int mdns_pub_start(struct mdns_pub *mdns); int mdns_pub_stop(struct mdns_pub *mdns); void mdns_pub_fini(struct mdns_pub *mdns); /* add and remove services */ struct mdns_pub_entry *mdns_pub_add(struct mdns_pub *mdns, char *name, char *service, int port, ...); void mdns_pub_del(struct mdns_pub_entry *entry); void mdns_pub_del_all(struct mdns_pub *mdns); /* misc helper functions */ int __attribute__ ((format (printf, 3, 0))) mdns_log_printf(struct mdns_pub *mdns, int priority, char *fmt, ...); int mdns_daemonize(void); void mdns_sigsetup(struct mdns_pub *mdns); xenwatch-0.5.4/apps.c0000664000076400007640000001407510524114757014035 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include "list.h" #include "apps.h" /* ------------------------------------------------------------------ */ enum desktop_type desktop_type = DESKTOP_OTHER; char app_error[256]; /* ------------------------------------------------------------------ */ static int debug = 0; struct have_app { char *name; int present; struct list_head next; }; static LIST_HEAD(apps); /* ------------------------------------------------------------------ */ static struct have_app *find_app_entry(char *name) { struct list_head *item; struct have_app *app; list_for_each(item, &apps) { app = list_entry(item, struct have_app, next); if (0 == strcmp(app->name, name)) return app; } app = malloc(sizeof(*app)); memset(app,0,sizeof(*app)); app->name = strdup(name); app->present = -1; list_add_tail(&app->next, &apps); return app; } int have_application(char *name) { struct have_app *app; char *path, *elem, *binary; struct stat st; int rc; app = find_app_entry(name); if (-1 != app->present) goto done; app->present = 0; if (strchr(name,'/')) { /* path specified ... */ if (-1 == stat(name, &st)) goto done; if (!S_ISREG(st.st_mode)) goto done; if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) goto done; app->present = 1; goto done; } /* walk $PATH */ path = getenv("PATH"); if (NULL == path) goto done; path = strdup(path); for (elem = strtok(path, ":"); NULL != elem; elem = strtok(NULL, ":")) { binary = malloc(strlen(elem)+strlen(name)+2); sprintf(binary, "%s/%s", elem, name); rc = stat(binary, &st); free(binary); if (-1 == rc) continue; if (!S_ISREG(st.st_mode)) continue; if (!(st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) continue; app->present = 1; break; } free(path); done: return app->present; } /* ------------------------------------------------------------------ */ int run_application_va(int do_wait, const char *app, char **argv) { int status, rc; pid_t pid; while (waitpid(-1, &status, WNOHANG) > 0) /* collect zombies */; if (0 == (pid = fork())) { /* child */ execvp(app, argv); fprintf(stderr,"%s: execvp(%s): %s\n", __FUNCTION__, app, strerror(errno)); exit(1); } if (!do_wait) return 0; rc = waitpid(pid, &status, 0); if (rc != pid) { /* Huh? */ fprintf(stderr,"%s: waidpid(%d): %s\n", __FUNCTION__, pid, strerror(errno)); exit(1); } if (!WIFEXITED(status)) return -1; return WEXITSTATUS(status); } int run_application(int do_wait, const char *app, ...) { va_list args; char *argv[64]; int i; if (debug) fprintf(stderr,"%s: %s |", __FUNCTION__, app); va_start(args, app); for (i = 0; i < array_size(argv); i++) { argv[i] = va_arg(args, char*); if (NULL == argv[i]) break; if (debug) fprintf(stderr,"%s \"%s\"", (0 == i) ? "" : ",", argv[i]); } va_end(args); if (debug) fprintf(stderr,"\n"); if (array_size(argv) == i) { fprintf(stderr,"%s: oops: argv too small\n", __FUNCTION__); return -1; } return run_application_va(do_wait, app, argv); } int run_cmdline(int do_wait, char *line) { char *argbuf, *token, *h; char *argv[64]; int rc = -1, argc = 0; h = argbuf = strdup(line); for (;;) { while (' ' == *h || '\t' == *h) h++; if ('\0' == *h) break; if ('"' == *h) { /* quoted string */ h++; token = h; while ('\0' != *h && '"' != *h) h++; } else { /* normal string */ token = h; while ('\0' != *h && ' ' != *h && '\t' != *h) h++; } if ('\0' != *h) { *h = 0; h++; } argv[argc++] = token; if (argc == array_size(argv)-1) { fprintf(stderr,"%s: oops: argv too small\n", __FUNCTION__); goto out; } } if (!argc) goto out; argv[argc++] = NULL; rc = run_application_va(do_wait, argv[0], argv); out: free(argbuf); return rc; } int run_cmdline_replace(int do_wait, char *str, ...) { va_list args; char *tag, *val; char *src, *dst, *pos; int start, end, cont, rc = 0; va_start(args, str); src = strdup(str); for (;;) { tag = va_arg(args, char*); if (NULL == tag) break; val = va_arg(args, char*); if (NULL == val) break; for (cont = 0;;) { if (NULL == (pos = strstr(src + cont, tag))) break; start = pos - src; end = start + strlen(tag); cont = start + strlen(val); dst = malloc(strlen(src) + strlen(val)); strncpy(dst, src, start); strcpy(dst + start, val); strcpy(dst + cont, src + end); free(src); src = dst; } } va_end(args); if (1 || debug) fprintf(stderr,"run: %s\n", src); rc = run_cmdline(do_wait, src); free(src); return rc; } /* ------------------------------------------------------------------ */ int open_vnc_session(char *host, int tcpport) { char display[64]; char javaurl[64]; char vncurl[64]; snprintf(display, sizeof(display), "%s::%d", host, tcpport); snprintf(javaurl, sizeof(javaurl), "http://%s:%d/", host, tcpport - 100); snprintf(vncurl, sizeof(vncurl), "vnc://%s:%d/", host, tcpport); /* --- try client apps --- */ if (have_application("vncviewer")) return run_application(0, "vncviewer", "vncviewer", "-xrm", "vncviewer*passwordDialog: true", display, NULL); if (have_application("krdc")) /* KDE remote desktop client */ return run_application(0, "krdc", "krdc", display, NULL); /* --- try web browser (java applet client) --- */ if (DESKTOP_KDE == desktop_type && have_application("kfmclient")) /* open new konqueror window */ return run_application(0, "kfmclient", "kfmclient", "openURL", javaurl, NULL); if (have_application("firefox")) return run_application(0, "firefox", "firefox", javaurl, NULL); snprintf(app_error, sizeof(app_error), "no vnc client found, please install one\n"); return -1; } xenwatch-0.5.4/xs_view.c0000664000076400007640000001356110661015474014553 0ustar kraxelkraxel#include #include #include #include #include #include #include #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_expand_subtree(void) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreeIter iter; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if (!gtk_tree_selection_get_selected(sel, &model, &iter)) return; gtk_tree_view_expand_row(GTK_TREE_VIEW(view), gtk_tree_model_get_path(model, &iter), true); } static void menu_cb_expand_all(void) { gtk_tree_view_expand_all(GTK_TREE_VIEW(view)); } static void menu_cb_about(void) { static char *comments = "xenstore browser"; static char *copyright = "(c) 2005-2007 Gerd Hoffmann"; static char *authors[] = { "Gerd Hoffmann ", NULL }; gtk_show_about_dialog(GTK_WINDOW(xs_toplevel), "authors", authors, "comments", comments, "copyright", copyright, "logo-icon-name", GTK_STOCK_ABOUT, "version", VERSION, NULL); } static void destroy(GtkWidget *widget, gpointer data) { g_object_unref(store); xs_toplevel = NULL; gtk_main_quit(); } /* ------------------------------------------------------------------ */ static const GtkActionEntry entries[] = { { .name = "FileMenu", .label = "_File", },{ .name = "Quit", .stock_id = GTK_STOCK_QUIT, .label = "_Quit", .callback = menu_cb_close, },{ .name = "ViewMenu", .label = "_View", },{ .name = "ExpandSubTree", .label = "_Expand subtree", .accelerator = "E", .callback = menu_cb_expand_subtree, },{ .name = "ExpandAll", .label = "Expand _all", .accelerator = "A", .callback = menu_cb_expand_all, },{ .name = "HelpMenu", .label = "_Help", },{ .name = "About", .stock_id = GTK_STOCK_ABOUT, .label = "_About ...", .callback = menu_cb_about, }, }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ------------------------------------------------------------------ */ 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, *frame; 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), 480, 320); 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_label_new("status line"); gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5); gtk_misc_set_padding(GTK_MISC(status), 3, 1); /* 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); frame = gtk_frame_new(NULL); gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), status); return; } xenwatch-0.5.4/xenwatch.c0000664000076400007640000000144010415503600014667 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include "xenviews.h" #include "apps.h" /* ------------------------------------------------------------------ */ static Display *dpy; /* ------------------------------------------------------------------ */ int main(int argc, char *argv[]) { setlocale(LC_ALL,""); gtk_init(&argc, &argv); dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC); detect_desktop(); xen_doms_create_window(); gtk_widget_show_all(xd_toplevel); gtk_main(); fprintf(stderr,"bye...\n"); exit(0); } xenwatch-0.5.4/vnc.h0000664000076400007640000000055110662306226013654 0ustar kraxelkraxel#define VNC_FLAG_STANDALONE (1 << 1) #define VNC_FLAG_DISCONNECT_CLOSE (1 << 2) #define VNC_FLAG_FULLSCREEN (1 << 3) #define VNC_FLAG_SHOW_MOUSE (1 << 4) #define VNC_FLAG_GRAB_MOUSE (1 << 5) #define VNC_FLAG_GRAB_KEYBOARD (1 << 6) GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level); xenwatch-0.5.4/xenstore.desktop0000664000076400007640000000022310666015663016161 0ustar kraxelkraxel[Desktop Entry] Encoding=UTF-8 Name=xenstore Comment=xenstore browser Exec=xenstore Terminal=false Type=Application Categories=GTK;System;Utility; xenwatch-0.5.4/vnc-client.c0000664000076400007640000000350410665532134015126 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include "vnc.h" /* ------------------------------------------------------------------ */ static void usage(FILE *fp) { fprintf(fp, "This is a simple vnc client\n" "\n" "usage: vnc-client [options] display\n" "options:\n" " -h Print this text.\n" " -d Enable debug output.\n" " -p Show mouse pointer.\n" " -f Start in fullscreen mode.\n" " -c Close on disconnect.\n" "\n" "-- \n" "(c) 2006 Gerd Hoffmann \n"); } int main(int argc, char *argv[]) { unsigned long vnc_flags = VNC_FLAG_STANDALONE; char hostname[65]; int displayno, port; int debug = 0; int c; gtk_init(&argc, &argv); for (;;) { if (-1 == (c = getopt(argc, argv, "hdpfc"))) break; switch (c) { case 'd': debug++; break; case 'p': vnc_flags |= VNC_FLAG_SHOW_MOUSE; break; case 'f': vnc_flags |= VNC_FLAG_FULLSCREEN; break; case 'c': vnc_flags |= VNC_FLAG_DISCONNECT_CLOSE; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } if (optind < argc) { if (2 == sscanf(argv[optind], "%64[^:]:%d", hostname, &displayno)) { port = displayno + 5900; goto connect; } if (2 != sscanf(argv[optind], "%64[^:]::%d", hostname, &port)) { goto connect; } } strcpy(hostname, ""); connect: if (NULL == vnc_open(strlen(hostname) ? hostname : NULL, port, vnc_flags, debug)) exit(1); gtk_main(); fprintf(stderr,"bye...\n"); exit(0); } xenwatch-0.5.4/mk/0000775000076400007640000000000011122440013013304 5ustar kraxelkraxelxenwatch-0.5.4/mk/Autoconf.mk0000664000076400007640000001073411122437756015444 0ustar kraxelkraxel# # simple autoconf system for GNU make # # (c) 2002-2006 Gerd Hoffmann # # credits for creating this one go to the autotools people because # they managed it to annoy lots of developers and users (including # me) with version incompatibilities. # # This file is public domain. No warranty. If it breaks you keep # both pieces. # ######################################################################## # verbose yes/no verbose ?= no # some stuff used by the tests ifneq ($(verbose),no) # verbose (for debug) ac_init = echo "checking $(1) ... " >&2; rc=no ac_b_cmd = echo "run: $(1)" >&2; $(1) >/dev/null && rc=yes ac_s_cmd = echo "run: $(1)" >&2; rc=`$(1)` ac_fini = echo "... result is $${rc}" >&2; echo >&2; echo "$${rc}" else # normal ac_init = echo -n "checking $(1) ... " >&2; rc=no ac_b_cmd = $(1) >/dev/null 2>&1 && rc=yes ac_s_cmd = rc=`$(1) 2>/dev/null` ac_fini = echo "$${rc}" >&2; echo "$${rc}" endif # some helpers to build cflags and related variables ac_def_cflags_1 = $(if $(filter yes,$($(1))),-D$(1)) ac_lib_cflags = $(foreach lib,$(1),$(call ac_def_cflags_1,HAVE_LIB$(lib))) ac_inc_cflags = $(foreach inc,$(1),$(call ac_def_cflags_1,HAVE_$(inc))) ac_lib_mkvar_1 = $(if $(filter yes,$(HAVE_LIB$(1))),$($(1)_$(2))) ac_lib_mkvar = $(foreach lib,$(1),$(call ac_lib_mkvar_1,$(lib),$(2))) ######################################################################## # the tests ... # get uname ac_uname = $(shell \ $(call ac_init,for system);\ $(call ac_s_cmd,uname -s | tr 'A-Z' 'a-z');\ $(call ac_fini)) ac_uname_arch = $(shell \ $(call ac_init,for arch);\ $(call ac_s_cmd,uname -m | tr 'A-Z' 'a-z');\ $(call ac_fini)) # check for some header file # args: header file ac_header = $(shell \ $(call ac_init,for $(1));\ $(call ac_b_cmd,echo '\#include <$(1)>' |\ $(CC) $(CFLAGS) -E -);\ $(call ac_fini)) # check for some function # args: function [, additional libs ] ac_func = $(shell \ $(call ac_init,for $(1));\ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \ > __actest.c;\ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \ __actest __actest.c $(2));\ rm -f __actest __actest.c;\ $(call ac_fini)) # check for some library # args: function, library [, additional libs ] ac_lib = $(shell \ $(call ac_init,for $(1) in $(2));\ echo 'void $(1)(void); int main(void) {$(1)();return 0;}' \ > __actest.c;\ $(call ac_b_cmd,$(CC) $(CFLAGS) $(LDFLAGS) -o \ __actest __actest.c -l$(2) $(3));\ rm -f __actest __actest.c;\ $(call ac_fini)) # check if some compiler flag works # args: compiler flag ac_cflag = $(shell \ $(call ac_init,if $(CC) supports $(1));\ echo 'int main() {return 0;}' > __actest.c;\ $(call ac_b_cmd,$(CC) $(CFLAGS) $(1) $(LDFLAGS) -o \ __actest __actest.c);\ rm -f __actest __actest.c;\ $(call ac_fini)) # check for some binary # args: binary name ac_binary = $(shell \ $(call ac_init,for $(1));\ $(call ac_s_cmd,which $(1));\ bin="$$rc";rc="no";\ $(call ac_b_cmd,test -x "$$$$bin");\ $(call ac_fini)) # check if lib64 is used #ac_lib64 = $(shell \ # $(call ac_init,for libdir name);\ # $(call ac_s_cmd,$(CC) -print-search-dirs | grep -q lib64 &&\ # echo "lib64" || echo "lib");\ # $(call ac_fini)) ac_lib64 = $(shell \ $(call ac_init,for libdir name);\ $(call ac_s_cmd,/sbin/ldconfig -p | grep -q lib64 &&\ echo "lib64" || echo "lib");\ $(call ac_fini)) # check for x11 ressource dir prefix ac_resdir = $(shell \ $(call ac_init,for X11 app-defaults prefix);\ $(call ac_s_cmd, for dir in \ /etc/X11/app-defaults \ /usr/X11R6/lib/X11/app-defaults \ /usr/share/X11/app-defaults \ /usr/lib/X11/app-defaults \ ; do test -d "$$dir" || continue;\ dirname "$$dir"; break; done);\ $(call ac_fini)) # check if package is installed, via pkg-config # args: pkg name ac_pkg_config = $(shell \ $(call ac_init,for $(1) (using pkg-config));\ $(call ac_b_cmd, pkg-config $(1));\ $(call ac_fini)) # grep some file # args: regex, file ac_grep = $(shell \ $(call ac_init,for $(1) in $(2));\ $(call ac_b_cmd, grep -q $(1) $(2));\ $(call ac_fini)) ######################################################################## # build Make.config define newline endef make-config-q = $(subst $(newline),\n,$(make-config)) ifeq ($(filter config,$(MAKECMDGOALS)),config) .PHONY: Make.config LIB := $(call ac_lib64) else LIB ?= $(call ac_lib64) LIB := $(LIB) endif .PHONY: config config: Make.config @true Make.config: $(srcdir)/GNUmakefile @echo -e "$(make-config-q)" > $@ @echo @echo "Make.config written, edit if needed" @echo xenwatch-0.5.4/mk/Compile.mk0000664000076400007640000000422511122437756015254 0ustar kraxelkraxel# # some rules to compile stuff ... # # (c) 2002-2006 Gerd Hoffmann # # main features: # * autodependencies via "cpp -MD" # * fancy, non-verbose output # # This file is public domain. No warranty. If it breaks you keep # both pieces. # ######################################################################## # verbose yes/no verbose ?= no # dependency files tmpdep = mk/$(subst /,_,$*).tmp depfile = mk/$(subst /,_,$*).dep depfiles = mk/*.dep compile_c = $(CC) $(CFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $< compile_c_pic = $(CC) $(CFLAGS) -fPIC -Wp,-MD,$(tmpdep) -c -o $@ $< compile_cc = $(CXX) $(CXXFLAGS) -Wp,-MD,$(tmpdep) -c -o $@ $< fixup_deps = sed -e "s|.*\.o:|$@:|" < $(tmpdep) > $(depfile) && rm -f $(tmpdep) cc_makedirs = mkdir -p $(dir $@) $(dir $(depfile)) link_app = $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS) link_so = $(CC) $(LDFLAGS) -shared -Wl,-soname,$(@F) -o $@ $^ $(LDLIBS) ar_lib = rm -f $@ && ar -r $@ $^ && ranlib $@ moc_h = $(MOC) $< -o $@ msgfmt_po = msgfmt -o $@ $< # non-verbose output ifeq ($(verbose),no) echo_compile_c = echo " CC " $@ echo_compile_c_pic = echo " CC " $@ echo_compile_cc = echo " CXX " $@ echo_link_app = echo " LD " $@ echo_link_so = echo " LD " $@ echo_ar_lib = echo " AR " $@ echo_moc_h = echo " MOC " $@ echo_msgfmt_po = echo " MSGFMT " $@ else echo_compile_c = echo $(compile_c) echo_compile_c = echo $(compile_c_pic) echo_compile_cc = echo $(compile_cc) echo_link_app = echo $(link_app) echo_link_so = echo $(link_so) echo_ar_lib = echo $(ar_lib) echo_moc_h = echo $(moc_h) echo_msgfmt_po = echo $(msgfmt_po) endif %.o: %.c @$(cc_makedirs) @$(echo_compile_c) @$(compile_c) @$(fixup_deps) %.opic: %.c @$(cc_makedirs) @$(echo_compile_c_pic) @$(compile_c_pic) @$(fixup_deps) %.o: %.cc @$(cc_makedirs) @$(echo_compile_cc) @$(compile_cc) @$(fixup_deps) %.o: %.cpp @$(cc_makedirs) @$(echo_compile_cc) @$(compile_cc) @$(fixup_deps) %: %.o @$(echo_link_app) @$(link_app) %.so: %.o @$(echo_link_so) @$(link_so) %.a: %.o @$(echo_ar_lib) @$(ar_lib) %.moc : %.h @$(echo_moc_h) @$(moc_h) %.mo : %.po @$(echo_msgfmt_po) @$(msgfmt_po) xenwatch-0.5.4/mk/Maintainer.mk0000664000076400007640000000163210661066035015745 0ustar kraxelkraxel# just some maintainer stuff for me ... ######################################################################## make-sync-dir = $(HOME)/projects/gnu-makefiles .PHONY: sync sync:: distclean test -d $(make-sync-dir) rm -f $(srcdir)/INSTALL $(srcdir)/mk/*.mk cp -v $(make-sync-dir)/INSTALL $(srcdir)/. cp -v $(make-sync-dir)/*.mk $(srcdir)/mk chmod 444 $(srcdir)/INSTALL $(srcdir)/mk/*.mk repository = $(shell cat CVS/Repository) release-dir ?= $(HOME)/projects/Releases release-pub ?= goldbach@me.in-berlin.de:dl.bytesex.org/releases/$(repository) tarball = $(release-dir)/$(repository)-$(VERSION).tar.gz .PHONY: release release: cvs tag $(RELTAG) cvs export -r $(RELTAG) -d "$(repository)-$(VERSION)" "$(repository)" find "$(repository)-$(VERSION)" -name .cvsignore -exec rm -fv "{}" ";" tar -c -z -f "$(tarball)" "$(repository)-$(VERSION)" rm -rf "$(repository)-$(VERSION)" scp $(tarball) $(release-pub) xenwatch-0.5.4/mk/Variables.mk0000664000076400007640000000243410712636247015574 0ustar kraxelkraxel# common variables ... ######################################################################## # directories DESTDIR = srcdir ?= . prefix ?= /usr/local bindir = $(DESTDIR)$(prefix)/bin sbindir = $(DESTDIR)$(prefix)/sbin libdir = $(DESTDIR)$(prefix)/$(LIB) shrdir = $(DESTDIR)$(prefix)/share mandir = $(shrdir)/man locdir = $(shrdir)/locale appdir = $(shrdir)/applications # package + version empty := space := $(empty) $(empty) ifneq ($(wildcard $(srcdir)/VERSION),) VERSION := $(shell cat $(srcdir)/VERSION) else VERSION := 42 endif RELTAG := v$(subst .,_,$(VERSION)) # programs CC ?= gcc CXX ?= g++ MOC ?= $(if $(QTDIR),$(QTDIR)/bin/moc,moc) STRIP ?= -s INSTALL ?= install INSTALL_BINARY := $(INSTALL) $(STRIP) INSTALL_SCRIPT := $(INSTALL) INSTALL_DATA := $(INSTALL) -m 644 INSTALL_DIR := $(INSTALL) -d # cflags CFLAGS ?= -g -O2 CXXFLAGS ?= $(CFLAGS) CFLAGS += -Wall -Wmissing-prototypes -Wstrict-prototypes \ -Wpointer-arith -Wunused CXXFLAGS += -Wall -Wpointer-arith -Wunused # add /usr/local to the search path if something is in there ... ifneq ($(wildcard /usr/local/include/*.h),) CFLAGS += -I/usr/local/include LDFLAGS += -L/usr/local/$(LIB) endif # fixup include path for $(srcdir) != "." ifneq ($(srcdir),.) CFLAGS += -I. -I$(srcdir) endif xenwatch-0.5.4/vnc.c0000664000076400007640000004617710703423064013661 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include "x11.h" #include "vnc.h" #ifdef HAVE_GTK_VNC #include /* ------------------------------------------------------------------ */ enum vnc_state { CONN_NONE = 0, CONN_CONNECTING, CONN_CONNECTED, CONN_DISCONNECTED, }; struct vnc_window { /* gtk */ GtkAccelGroup *ac; GtkActionGroup *ag; GtkUIManager *ui; GtkWidget *win; GtkWidget *vnc; GtkWidget *line, *res, *mbutton, *popup; /* connection */ char display[100]; char hostname[80]; char tcpport[16]; /* state */ int input_grabbed; int width, height; enum vnc_state conn_state; /* config */ int standalone; int disconn_close; int fullscreen; int showpointer; int grab_pointer; int grab_keyboard; int debug; }; /* ------------------------------------------------------------------ */ /* helper functions */ static void vnc_window_texts(struct vnc_window *vnc) { const char *name = vnc_display_get_name(VNC_DISPLAY(vnc->vnc)); char ti[256]; char st[256]; char si[16] = "none"; switch (vnc->conn_state) { case CONN_NONE: snprintf(ti, sizeof(ti), "%s", g_get_application_name()); snprintf(st, sizeof(st), "VNC: idle"); break; case CONN_CONNECTING: snprintf(ti, sizeof(ti), "connecting (%s)", g_get_application_name()); snprintf(st, sizeof(st), "VNC: connecting to %s ...", vnc->display); break; case CONN_CONNECTED: snprintf(ti, sizeof(ti), "%s (%s)", name, g_get_application_name()); snprintf(st, sizeof(st), "VNC: \"%s\" at %s", name ?: "", vnc->display); snprintf(si, sizeof(si), "%dx%d", vnc->width, vnc->height); break; case CONN_DISCONNECTED: snprintf(ti, sizeof(ti), "%s", g_get_application_name()); snprintf(st, sizeof(st), "VNC: disconnected from %s.", vnc->display); break; } if (vnc->input_grabbed) snprintf(st, sizeof(st), "Press Ctrl-Alt to release input grab."); gtk_window_set_title(GTK_WINDOW(vnc->win), ti); gtk_label_set_text(GTK_LABEL(vnc->line), st); gtk_label_set_text(GTK_LABEL(vnc->res), si); } static void vnc_release(struct vnc_window *vnc) { if (NULL == vnc) return; free(vnc); } static void vnc_connect_to(struct vnc_window *vnc, char *hostname, int tcpport) { snprintf(vnc->display, sizeof(vnc->display), "%s:%d", hostname, tcpport - 5900); snprintf(vnc->hostname, sizeof(vnc->hostname),"%s", hostname); snprintf(vnc->tcpport, sizeof(vnc->tcpport),"%d", tcpport); vnc->conn_state = CONN_CONNECTING; vnc_window_texts(vnc); vnc_display_open_host(VNC_DISPLAY(vnc->vnc), vnc->hostname, vnc->tcpport); } static int user_getstring(GtkWidget *window, char *title, char *message, char *dest, int dlen, int hide) { GtkWidget *dialog, *label, *entry; const char *txt; int retval; /* Create the widgets */ dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); label = gtk_label_new(message); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), dest); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); if (hide) gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), entry); gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 10); #if 0 /* FIXME: doesn't work ... */ gtk_container_set_border_width(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), 10); #endif /* show and wait for response */ gtk_widget_show_all(dialog); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case GTK_RESPONSE_ACCEPT: txt = gtk_entry_get_text(GTK_ENTRY(entry)); snprintf(dest, dlen, "%s", txt); retval = 0; break; default: retval = -1; break; } gtk_widget_destroy(dialog); return retval; } /* ------------------------------------------------------------------ */ /* vnc widget callbacks */ static void vnc_connected(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); } static void vnc_initialized(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); vnc->conn_state = CONN_CONNECTED; vnc_window_texts(vnc); } static void vnc_disconnected(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); vnc->conn_state = CONN_DISCONNECTED; vnc_window_texts(vnc); if (vnc->disconn_close) gtk_widget_destroy(vnc->win); } static void vnc_grab(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); vnc->input_grabbed = 1; vnc_window_texts(vnc); } static void vnc_ungrab(GtkWidget *vncdisplay, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); vnc->input_grabbed = 0; vnc_window_texts(vnc); } static void vnc_desktop_resize(GtkWidget *vncdisplay, int x, int y, void *data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr, "%s (%dx%d)\n", __FUNCTION__, x, y); vnc->width = x; vnc->height = y; vnc_window_texts(vnc); } static void vnc_credential(GtkWidget *vncdisplay, GValueArray *credList, void *data) { struct vnc_window *vnc = data; char *val, msg[127], str[128]; int i, rc; for (i = 0 ; i < credList->n_values ; i++) { GValue *cred = g_value_array_get_nth(credList, i); switch (g_value_get_enum(cred)) { case VNC_DISPLAY_CREDENTIAL_USERNAME: snprintf(msg, sizeof(msg), "Username for %s ?", vnc->display); rc = user_getstring(vnc->win, "Authentication", msg, str, sizeof(str), 0); if (0 != rc) return; val = str; break; case VNC_DISPLAY_CREDENTIAL_PASSWORD: snprintf(msg, sizeof(msg), "Password for %s ?", vnc->display); rc = user_getstring(vnc->win, "Authentication", msg, str, sizeof(str), 1); if (0 != rc) return; val = str; break; case VNC_DISPLAY_CREDENTIAL_CLIENTNAME: val = "vnc"; break; default: fprintf(stderr, "can't handle credential type %d\n", g_value_get_enum(cred)); return; } vnc_display_set_credential(VNC_DISPLAY(vnc->vnc), g_value_get_enum(cred), val); } } /* ------------------------------------------------------------------ */ /* glib/gtk callbacks */ static void destroy_cb(GtkWidget *widget, gpointer data) { struct vnc_window *vnc = data; if (vnc->debug) fprintf(stderr,"%s: called\n", __FUNCTION__); if (vnc->standalone) gtk_main_quit(); vnc_release(vnc); } static gboolean window_state_cb(GtkWidget *widget, GdkEventWindowState *event, gpointer data) { struct vnc_window *vnc = data; GtkWidget *item; if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) { vnc->fullscreen = event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN; if (vnc->debug) fprintf(stderr, "%s: fullscreen %s\n", __FUNCTION__, vnc->fullscreen ? "on" : "off"); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/FullScreen"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->fullscreen); } return TRUE; } static void menu_btn(GtkWidget *widget, gpointer data) { struct vnc_window *vnc = data; gtk_menu_popup(GTK_MENU(vnc->popup), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); } /* ------------------------------------------------------------------ */ static void menu_cb_full_screen(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; gboolean state = gtk_toggle_action_get_active(action); vnc->fullscreen = state; if (vnc->debug) fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off"); if (vnc->fullscreen) gtk_window_fullscreen(GTK_WINDOW(vnc->win)); else gtk_window_unfullscreen(GTK_WINDOW(vnc->win)); } static void menu_cb_show_pointer(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; gboolean state = gtk_toggle_action_get_active(action); if (vnc->debug) fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off"); vnc->showpointer = state; vnc_display_set_pointer_local(VNC_DISPLAY(vnc->vnc), state); } static void menu_cb_grab_pointer(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; gboolean state = gtk_toggle_action_get_active(action); if (vnc->debug) fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off"); vnc->grab_pointer = state; vnc_display_set_pointer_grab(VNC_DISPLAY(vnc->vnc), state); } static void menu_cb_grab_keyboard(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; gboolean state = gtk_toggle_action_get_active(action); if (vnc->debug) fprintf(stderr, "%s: %s\n", __FUNCTION__, state ? "on" : "off"); vnc->grab_keyboard = state; vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc->vnc), state); } static void menu_cb_connect(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; char str[128], hostname[65]; int rc, displayno, port; if (vnc->conn_state == CONN_CONNECTED) return; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); snprintf(str, sizeof(str), "%s", vnc->display); rc = user_getstring(vnc->win, "Connecting", "Connect to vnc display ?", str, sizeof(str), 0); if (0 != rc) return; if (2 == sscanf(str, "%64[^:]:%d", hostname, &displayno)) { port = displayno + 5900; goto connect; } if (2 == sscanf(str, "%64[^:]::%d", hostname, &port)) goto connect; return; connect: vnc_connect_to(vnc, hostname, port); } static void menu_cb_reconnect(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; if (vnc->conn_state != CONN_DISCONNECTED) return; if (vnc->debug) fprintf(stderr, "%s: %s %s\n", __FUNCTION__, vnc->hostname, vnc->tcpport); vnc->conn_state = CONN_CONNECTING; vnc_window_texts(vnc); vnc_display_open_host(VNC_DISPLAY(vnc->vnc), vnc->hostname, vnc->tcpport); } static void menu_cb_disconnect(GtkToggleAction *action, gpointer user_data) { struct vnc_window *vnc = user_data; if (vnc->conn_state != CONN_CONNECTED) return; if (vnc->debug) fprintf(stderr, "%s\n", __FUNCTION__); vnc_display_close(VNC_DISPLAY(vnc->vnc)); } static void menu_cb_about(GtkMenuItem *item, void *user_data) { static char *comments = "simple vnc client"; static char *copyright = "(c) 2005-2007 Gerd Hoffmann"; static char *authors[] = { "Gerd Hoffmann ", NULL }; struct vnc_window *vnc = user_data; gtk_show_about_dialog(GTK_WINDOW(vnc->win), "authors", authors, "comments", comments, "copyright", copyright, "logo-icon-name", GTK_STOCK_ABOUT, "version", VERSION, NULL); } static void menu_cb_quit(GtkMenuItem *item, void *user_data) { struct vnc_window *vnc = user_data; gtk_widget_destroy(vnc->win); } /* ------------------------------------------------------------------ */ static const GtkActionEntry entries[] = { { /* popup menu */ .name = "ConfMenu", .label = "Config", },{ /* menu items */ .name = "Connect", .stock_id = GTK_STOCK_CONNECT, .label = "Connect ...", .callback = G_CALLBACK(menu_cb_connect), },{ .name = "Reconnect", .label = "Reconnect", .callback = G_CALLBACK(menu_cb_reconnect), },{ .name = "Disconnect", .stock_id = GTK_STOCK_DISCONNECT, .label = "Disconnect", .callback = G_CALLBACK(menu_cb_disconnect), },{ .name = "About", .stock_id = GTK_STOCK_ABOUT, .label = "_About ...", .callback = G_CALLBACK(menu_cb_about), },{ .name = "Close", .stock_id = GTK_STOCK_QUIT, .label = "_Close", .tooltip = "Quit the job", .callback = G_CALLBACK(menu_cb_quit), } }; static const GtkToggleActionEntry tentries[] = { { .name = "FullScreen", .label = "_Fullscreen", .accelerator = "F11", .callback = G_CALLBACK(menu_cb_full_screen), },{ .name = "ShowPointer", .label = "Show _Pointer", .callback = G_CALLBACK(menu_cb_show_pointer), },{ .name = "GrabPointer", .label = "Grab Pointer", .callback = G_CALLBACK(menu_cb_grab_pointer), },{ .name = "GrabKeyboard", .label = "Grab Keyboard", .callback = G_CALLBACK(menu_cb_grab_keyboard), } }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ------------------------------------------------------------------ */ /* public API functions */ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) { GtkWidget *vbox, *hbox, *frame, *item; GtkWidget *ebox, *align; GdkColor bg; GError *err; struct vnc_window *vnc; vnc = malloc(sizeof(*vnc)); if (NULL == vnc) goto err; memset(vnc,0,sizeof(*vnc)); vnc->standalone = (flags & VNC_FLAG_STANDALONE); vnc->showpointer = (flags & VNC_FLAG_SHOW_MOUSE); vnc->grab_pointer = (flags & VNC_FLAG_GRAB_MOUSE); vnc->grab_keyboard = (flags & VNC_FLAG_GRAB_KEYBOARD); vnc->disconn_close = (flags & VNC_FLAG_DISCONNECT_CLOSE); vnc->debug = debug_level; /* gtk toplevel */ vnc->win = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(vnc->win), "destroy", G_CALLBACK(destroy_cb), vnc); g_signal_connect(G_OBJECT(vnc->win), "window-state-event", G_CALLBACK(window_state_cb), vnc); gtk_window_set_default_size(GTK_WINDOW(vnc->win), 320, 200); /* vnc display widget */ vnc->vnc = vnc_display_new(); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-connected", GTK_SIGNAL_FUNC(vnc_connected), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-initialized", GTK_SIGNAL_FUNC(vnc_initialized), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-disconnected", GTK_SIGNAL_FUNC(vnc_disconnected), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-pointer-grab", GTK_SIGNAL_FUNC(vnc_grab), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-pointer-ungrab", GTK_SIGNAL_FUNC(vnc_ungrab), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-auth-credential", GTK_SIGNAL_FUNC(vnc_credential), vnc); gtk_signal_connect(GTK_OBJECT(vnc->vnc), "vnc-desktop-resize", GTK_SIGNAL_FUNC(vnc_desktop_resize), vnc); ebox = gtk_event_box_new(); gtk_event_box_set_visible_window(GTK_EVENT_BOX(ebox), TRUE); gdk_color_parse("darkgray", &bg); gtk_widget_modify_bg(ebox, GTK_STATE_NORMAL, &bg); align = gtk_alignment_new(0.5, 0.5, 0,0); /* popup menu */ vnc->ui = gtk_ui_manager_new(); vnc->ag = gtk_action_group_new("MenuActions"); gtk_action_group_add_actions(vnc->ag, entries, G_N_ELEMENTS(entries), vnc); gtk_action_group_add_toggle_actions(vnc->ag, tentries, G_N_ELEMENTS(tentries), vnc); gtk_ui_manager_insert_action_group(vnc->ui, vnc->ag, 0); vnc->ac = gtk_ui_manager_get_accel_group(vnc->ui); gtk_window_add_accel_group(GTK_WINDOW(vnc->win), vnc->ac); err = NULL; if (!gtk_ui_manager_add_ui_from_string(vnc->ui, ui_xml, -1, &err)) { g_message("building menus failed: %s", err->message); g_error_free(err); exit(1); } vnc->popup = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu"); gtk_menu_set_title(GTK_MENU(vnc->popup), "Menu"); /* popup menu: initial state */ item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/ShowPointer"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->showpointer); vnc_display_set_pointer_local(VNC_DISPLAY(vnc->vnc), vnc->showpointer); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/GrabPointer"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->grab_pointer); vnc_display_set_pointer_grab(VNC_DISPLAY(vnc->vnc), vnc->grab_pointer); item = gtk_ui_manager_get_widget(vnc->ui, "/ConfMenu/GrabKeyboard"); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), vnc->grab_keyboard); vnc_display_set_keyboard_grab(VNC_DISPLAY(vnc->vnc), vnc->grab_keyboard); /* labels for the status line */ vnc->line = gtk_label_new("status line"); vnc->res = gtk_label_new("vnc screen resolution"); vnc->mbutton = gtk_button_new_with_label("menu"); g_signal_connect(G_OBJECT(vnc->mbutton), "clicked", G_CALLBACK(menu_btn), vnc); GTK_WIDGET_UNSET_FLAGS(vnc->mbutton, GTK_CAN_FOCUS); /* packing */ vbox = gtk_vbox_new(FALSE, 0); hbox = gtk_hbox_new(FALSE, 1); gtk_container_add(GTK_CONTAINER(vnc->win), vbox); gtk_container_add(GTK_CONTAINER(ebox), align); gtk_container_add(GTK_CONTAINER(align), vnc->vnc); gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, TRUE, 0); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, TRUE, 0); frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vnc->line); gtk_misc_set_alignment(GTK_MISC(vnc->line), 0, 0.5); gtk_misc_set_padding(GTK_MISC(vnc->line), 3, 1); frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), vnc->res); gtk_misc_set_padding(GTK_MISC(vnc->res), 3, 1); gtk_box_pack_start(GTK_BOX(hbox), vnc->mbutton, FALSE, TRUE, 0); /* show window */ gtk_widget_show_all(vnc->win); vnc_window_texts(vnc); if (flags & VNC_FLAG_FULLSCREEN) gtk_window_fullscreen(GTK_WINDOW(vnc->win)); /* connect */ if (hostname) vnc_connect_to(vnc, hostname, tcpport); return vnc->win; err: vnc_release(vnc); return NULL; } #else /* HAVE_GTK_VNC */ GtkWidget *vnc_open(char *hostname, int tcpport, unsigned long flags, int debug_level) { fprintf(stderr, "compiled without VNC support, sorry\n"); return NULL; } #endif /* HAVE_GTK_VNC */ xenwatch-0.5.4/mdns-publish-vnc.c0000664000076400007640000001275410661015474016263 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "xs_tools.h" #include "mdns-publish.h" /* ------------------------------------------------------------- */ struct dom { int domid; char name[128]; int destroyed; char label[256]; int vnc_port; int published; struct mdns_pub_entry *entry; struct list_head next; }; static LIST_HEAD(doms); static struct utsname uts; /* ------------------------------------------------------------- */ static struct dom *find_dom(int domid) { struct dom *dom; struct list_head *item; list_for_each(item, &doms) { dom = list_entry(item, struct dom, next); if (dom->domid == domid) return dom; } return NULL; } static struct dom *get_dom(int domid) { struct dom *dom; dom = find_dom(domid); if (!dom) { dom = malloc(sizeof(*dom)); memset(dom,0,sizeof(*dom)); dom->domid = domid; list_add_tail(&dom->next, &doms); } return dom; } static void try_attach_domain(struct mdns_pub *mdns, struct dom *dom, int boot) { if (dom->published) return; if (!strlen(dom->name)) return; if (!dom->vnc_port) return; snprintf(dom->label, sizeof(dom->label), "Xen guest %s (%s, #%d)", dom->name, uts.nodename, dom->domid); mdns_log_printf(mdns, LOG_INFO, "pub: %s (%d)\n", dom->name, dom->domid); dom->entry = mdns_pub_add(mdns, dom->label, "_rfb._tcp", dom->vnc_port, NULL); dom->published = 1; } static void try_release_domain(struct mdns_pub *mdns, struct dom *dom) { if (!dom->destroyed) return; if (dom->published) { mdns_log_printf(mdns, LOG_INFO, "del: %s (%d)\n", dom->name, dom->domid); mdns_pub_del(dom->entry); } list_del(&dom->next); free(dom); } /* ------------------------------------------------------------- */ static void usage(FILE *fp) { fprintf(fp, "usage: [fixme]\n"); } int main(int argc, char *argv[]) { struct mdns_pub *mdns = NULL; struct xs_handle *xenstore = NULL; xs_transaction_t xst; char **vec = NULL; int domid, c; int debug = 0; char path[128], value[128]; unsigned int count, i, rc; struct dom *dom; fd_set set; for (;;) { if (-1 == (c = getopt(argc, argv, "hd"))) break; switch (c) { case 'd': debug++; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } mdns_pub_appname = "mdns-publish-vnc"; uname(&uts); if (!debug) mdns_daemonize(); /* connect to xenstore */ xenstore = xenstore_open(1,1,1,1); if (NULL == xenstore) { mdns_log_printf(mdns, LOG_ERR, "can't access xenstore, exiting\n"); exit(1); } xs_watch(xenstore, "/local/domain", "token"); mdns = mdns_pub_init(debug); if (NULL == mdns) exit(1); mdns_sigsetup(mdns); mdns_pub_start(mdns); /* look for running domains */ if (!(xst = xs_transaction_start(xenstore))) { mdns_log_printf(mdns, LOG_ERR, "Oops, can't start xenstore transaction\n"); exit(1); } vec = xs_directory(xenstore, xst, "/local/domain", &count); xs_transaction_end(xenstore, xst, 0); fprintf(stderr,"looking for existing domains\n"); for (i = 0; i < count; i++) { domid = atoi(vec[i]); dom = get_dom(domid); snprintf(path, sizeof(path), "/local/domain/%d/name", domid); xenstore_read(xenstore, path, dom->name, sizeof(dom->name)); snprintf(path, sizeof(path), "/local/domain/%d/console/vnc-port", domid); if (0 == xenstore_read(xenstore, path, value, sizeof(value))) dom->vnc_port = atoi(value); try_attach_domain(mdns, dom, 1); } /* main loop */ fprintf(stderr,"ok, watching out for changes now\n"); for (;;) { if (mdns_pub_appquit) break; FD_ZERO(&set); FD_SET(xs_fileno(xenstore), &set); switch (select(xs_fileno(xenstore)+1, &set, NULL, NULL, NULL)) { case -1: if (EINTR == errno) continue; /* termsig check */ perror("select"); break; case 0: fprintf(stderr,"Huh, select() timeout?\n"); mdns_pub_appquit++; break; default: break; } if (vec) free(vec); vec = xs_read_watch(xenstore, &count); if (NULL == vec) { fprintf(stderr,"xs_read_watch() failed\n"); exit(1); } if (2 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d/%64s", &domid, path)) { if (1 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d", &domid)) continue; strcpy(path, ""); } dom = get_dom(domid); if (0 == strcmp(path,"")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) dom->destroyed = 1; } else if (0 == strcmp(path, "name")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) continue; strcpy(dom->name, value); mdns_log_printf(mdns, LOG_INFO, "new: %s (%d)\n", dom->name, dom->domid); } else if (0 == strcmp(path, "console/vnc-port")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) continue; dom->vnc_port = atoi(value); } else { continue; } try_attach_domain(mdns, dom, 0); try_release_domain(mdns, dom); } mdns_pub_del_all(mdns); mdns_pub_stop(mdns); mdns_pub_fini(mdns); return 0; } xenwatch-0.5.4/INSTALL0000664000076400007640000000335110665532134013751 0ustar kraxelkraxel 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 Hoffmann xenwatch-0.5.4/GNUmakefile0000664000076400007640000000633311122437756015000 0ustar kraxelkraxel# config -include Make.config include mk/Variables.mk # add our flags + libs CFLAGS += -DVERSION='"$(VERSION)"' -DLIB='"$(LIB)"' # build TARGETS := xenlog xenscreen xenwatch xenstore-gtk \ mdns-browser mdns-publish-xendom mdns-publish-vnc \ vnc-client BUILD_XENSTORE := xenlog xenwatch xenstore-gtk \ mdns-publish-xendom mdns-publish-vnc BUILD_GTK := xenwatch xenstore-gtk mdns-browser vnc-client BUILD_AVAHI := mdns-browser mdns-publish-xendom mdns-publish-vnc BUILD_GTK_VNC := vnc-client NEEDS_XENSTORE := xenlog xenscreen xenwatch xenstore-gtk mdns-publish-xendom mdns-publish-vnc NEEDS_LIBVIRT := xenscreen NEEDS_GTK := xenwatch xenstore-gtk mdns-browser vnc-client NEEDS_AVAHI := mdns-browser mdns-publish-xendom mdns-publish-vnc NEEDS_GTK_VNC := xenwatch mdns-browser vnc-client # default target all: build ################################################################# # poor man's autoconf ;-) include mk/Autoconf.mk define make-config LIB := $(LIB) HAVE_GTK := $(call ac_pkg_config,gtk+-x11-2.0) HAVE_AVAHI := $(call ac_pkg_config,avahi-glib) HAVE_LIBVIRT := $(call ac_pkg_config,libvirt) HAVE_GTK_VNC := $(call ac_pkg_config,gtk-vnc-1.0) HAVE_XENSTORE := $(call ac_lib,xs_daemon_open,xenstore) endef # gtk ifeq ($(HAVE_GTK),yes) $(NEEDS_GTK) : CFLAGS += -Wno-strict-prototypes $(NEEDS_GTK) : pkglst += gtk+-x11-2.0 else TARGETS := $(filter-out $(BUILD_GTK), $(TARGETS)) endif # gtk-vnc ifeq ($(HAVE_GTK_VNC),yes) $(NEEDS_GTK_VNC) : pkglst += gtk-vnc-1.0 else TARGETS := $(filter-out $(BUILD_GTK_VNC), $(TARGETS)) endif # avahi ifeq ($(HAVE_AVAHI),yes) $(NEEDS_AVAHI) : pkglst += avahi-client avahi-glib else TARGETS := $(filter-out $(BUILD_AVAHI), $(TARGETS)) endif # xenstore ifeq ($(HAVE_XENSTORE),yes) $(NEEDS_XENSTORE) : LDLIBS += -lxenstore else TARGETS := $(filter-out $(BUILD_XENSTORE), $(TARGETS)) endif # libvirt ifeq ($(HAVE_LIBVIRT),yes) $(NEEDS_LIBVIRT) : pkglst += libvirt libxml-2.0 endif # compile flags CFLAGS += $(call ac_inc_cflags,XENSTORE LIBVIRT AVAHI GTK_VNC) CFLAGS += $(shell test "$(pkglst)" != "" && pkg-config --cflags $(pkglst)) LDLIBS += $(shell test "$(pkglst)" != "" && pkg-config --libs $(pkglst)) # desktop files DESKTOP := $(wildcard $(patsubst %,%.desktop,$(TARGETS))) ######################################################################## # rules build: $(TARGETS) xenscreenrc install: build $(INSTALL_DIR) -d $(DESTDIR)/etc/xen $(bindir) $(appdir) $(INSTALL_DATA) xenscreenrc $(DESTDIR)/etc/xen $(INSTALL_BINARY) $(TARGETS) $(bindir) $(INSTALL_DATA) $(DESKTOP) $(appdir) clean: -rm -f *.o *~ $(depfiles) realclean distclean: clean -rm -f Make.config -rm -f $(TARGETS) *~ xpm/*~ *.bak ############################################# xenlog: xenlog.o xs_tools.o xenscreen: xenscreen.o xs_tools.o apps.o xenwatch: xenwatch.o xd_view.o xd_store.o apps.o apps-x11.o tcp.o vnc.o x11.o xenstore-gtk: xenstore-gtk.o xs_view.o xs_store.o mdns-browser: mdns-browser.o mdns.o vnc.o x11.o apps.o apps-x11.o mdns-publish-xendom: mdns-publish-xendom.o mdns-publish.o mdns-publish-vnc: mdns-publish-vnc.o mdns-publish.o xs_tools.o vnc-client: vnc-client.o vnc.o x11.o xenscreenrc: xenscreen ./xenscreen -b > $@ include mk/Compile.mk include mk/Maintainer.mk -include $(depfiles) xenwatch-0.5.4/xd_view.c0000664000076400007640000004103211122437756014532 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xd_store.h" #include "xenviews.h" #include "tcp.h" #include "mdns.h" #include "apps.h" #include "vnc.h" #define array_size(x) (sizeof(x)/sizeof(*x)) /* ------------------------------------------------------------------ */ GtkWidget *xd_toplevel; static GtkWidget *status; static XenDoms *store; static GtkWidget *view; static int debug = 0; /* ------------------------------------------------------------------ */ static char *gtk_msg_type_name[] = { [ GTK_MESSAGE_INFO ] = "INFO", [ GTK_MESSAGE_WARNING ] = "WARNING", [ GTK_MESSAGE_QUESTION ] = "QUESTION", [ GTK_MESSAGE_ERROR ] = "ERROR", }; static int __attribute__ ((format (printf, 2, 0))) gtk_message(GtkMessageType type, char *fmt, ...) { va_list args; GtkWidget *dialog; char msgbuf[1024]; int rc; va_start(args, fmt); rc = vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); va_end(args); if (debug) fprintf(stderr, "%s: %s", gtk_msg_type_name[type], msgbuf); dialog = gtk_message_dialog_new(GTK_WINDOW(xd_toplevel), GTK_DIALOG_DESTROY_WITH_PARENT, type, GTK_BUTTONS_CLOSE, "%s", msgbuf); g_signal_connect_swapped(dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_widget_show_all(dialog); return rc; } static gboolean get_domain(gint *id, char **name, char **os, char **tty, gint *vncport) { GtkTreeSelection *sel; GtkTreeModel *model; GtkTreeIter iter; sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view)); if (!gtk_tree_selection_get_selected(sel, &model, &iter)) { gtk_message(GTK_MESSAGE_ERROR, "No domain selected\n"); return false; } gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, XEN_DOMS_COL_I_ID, id, XEN_DOMS_COL_S_NAME, name, XEN_DOMS_COL_S_OSTYPE, os, XEN_DOMS_COL_S_TERMINAL, tty, XEN_DOMS_COL_I_VNCPORT, vncport, -1); if (0 == *id) { gtk_message(GTK_MESSAGE_ERROR, "You can't do that for Domain-0\n"); return false; } return true; } /* ------------------------------------------------------------------ */ static void open_xenconsole(gint id, char *name, char *tty) { char title[64], ids[8]; snprintf(title, sizeof(title), "xen console: %s (%d)", name, id); snprintf(ids, sizeof(ids), "%d", id); run_application(0, "xterm", "xterm", "-name", "console_xterm", "-title", title, "-e", XENCONSOLE, ids, NULL); } static void open_tty(gint id, char *name, char *tty) { int rc; /* sanity checks */ if (0 != access(tty, R_OK)) { gtk_message(GTK_MESSAGE_ERROR, "no access to tty %s\n", tty); return; } rc = run_application(1, "fuser", "fuser", "-s", tty, NULL); if (0 == rc) { gtk_message(GTK_MESSAGE_ERROR, "tty %s already in use\n", tty); return; } /* open terminal */ if (have_application(XENCONSOLE)) { open_xenconsole(id, name, tty); } else { gtk_message(GTK_MESSAGE_ERROR, "need xen-tools, please install\n"); } } static void open_vnc(gint id, char *hostname, gint tcpport) { #ifdef HAVE_GTK_VNC if (1) { vnc_open(hostname, tcpport, VNC_FLAG_SHOW_MOUSE | VNC_FLAG_DISCONNECT_CLOSE, 0); return; } #endif if (-1 == open_vnc_session(hostname, tcpport)) gtk_message(GTK_MESSAGE_ERROR, app_error); } /* ------------------------------------------------------------------ */ static int xmlrpc_request(char *method, ...) { struct addrinfo ask; char *host = "localhost"; char *serv = "8005"; struct sockaddr_un unix_xend = { .sun_family = PF_UNIX, .sun_path = "/var/run/xend/xmlrpc.sock", }; va_list args; char head[256]; char body[1024]; char reply[1024]; int sock,lhead, lbody, lreply, rc, eof; char *name, *tty, *ostype, *arg; gint id = -1, vncport; struct timeval tv; fd_set rd; if (!get_domain(&id, &name, &ostype, &tty, &vncport)) return -1; /* try tcp first */ memset(&ask,0,sizeof(ask)); ask.ai_socktype = SOCK_STREAM; ask.ai_family = PF_UNSPEC; sock = tcp_connect(&ask, NULL, NULL, host, serv); if (0 == sock) goto connected; /* failing that unix sockets */ sock = socket(PF_UNIX, SOCK_STREAM, 0); if (-1 == connect(sock, (struct sockaddr*)&unix_xend, sizeof(unix_xend))) { gtk_message(GTK_MESSAGE_ERROR, "can't connect to xend\n"); return -1; } connected: /* req start + method name */ lbody = snprintf(body, sizeof(body), "\n" "\n" " %s\n" " \n", method); /* domain */ lbody += snprintf(body + lbody, sizeof(body) - lbody, " \n" " %d\n" " \n", id); /* maybe more args */ va_start(args, method); for (;;) { arg = va_arg(args, char*); if (NULL == arg) break; lbody += snprintf(body + lbody, sizeof(body) - lbody, " \n" " %s\n" " \n", arg); } va_end(args); /* finish off */ lbody += snprintf(body + lbody, sizeof(body) - lbody, " \n" "\n"); if (debug) write(2, body, lbody); /* http header */ lhead = snprintf(head, sizeof(head), "POST /RPC2 HTTP/1.0\r\n" "Host: %s\r\n" "User-Agent: xenwatch\r\n" "Content-Type: text/xml\r\n" "Content-Length: %d\r\n" "\r\n", host, lbody); write(sock, head, lhead); write(sock, body, lbody); for (lreply = 0, eof = 0; !eof;) { FD_ZERO(&rd); FD_SET(sock, &rd); tv.tv_sec = 3; tv.tv_usec = 0; if (1 == select(sock+1, &rd, NULL, NULL, &tv)) { rc = read(sock, reply + lreply, sizeof(reply) - lreply -1); switch (rc) { case -1: perror("read"); /* fall through */ case 0: eof = 1; break; default: lreply += rc; reply[lreply] = 0; break; } } } close(sock); if (!lreply) { gtk_message(GTK_MESSAGE_ERROR, "Huh, no xend reply?\n"); return -1; } if (NULL != strstr(reply, "")) { gtk_message(GTK_MESSAGE_ERROR, "XMLRPC call failed:\n\n%s", reply); return -1; } if (debug) write(2, reply, lreply); return 0; } /* ------------------------------------------------------------------ */ static void menu_cb_quit(void) { gtk_widget_destroy(xd_toplevel); } static void menu_cb_open_vnc(void) { char *name, *tty, *ostype; gint id = -1, vncport, tcpport = -1; if (!get_domain(&id, &name, &ostype, &tty, &vncport)) return; if (debug) fprintf(stderr, "%s: %d\n", __FUNCTION__, id); if (vncport) /* xen 3.0.3+ */ tcpport = vncport; else if (0 == strcmp(ostype, "hvm")) { /* xen 3.0.2 */ tcpport = id + 5900; } if (-1 != tcpport) open_vnc(id, "localhost", tcpport); else gtk_message(GTK_MESSAGE_ERROR, "Domain has no graphical display.\n"); } static void menu_cb_open_console(void) { char *name, *tty, *ostype; gint id = -1, vncport; if (!get_domain(&id, &name, &ostype, &tty, &vncport)) return; if (debug) fprintf(stderr, "%s: %d\n", __FUNCTION__, id); open_tty(id, name, tty); } static void menu_cb_domain_pause(void) { xmlrpc_request("xend.domain.pause", NULL); } static void menu_cb_domain_unpause(void) { xmlrpc_request("xend.domain.unpause", NULL); } static void menu_cb_domain_shutdown(void) { xmlrpc_request("xend.domain.shutdown", "poweroff", NULL); } static void menu_cb_domain_reboot(void) { xmlrpc_request("xend.domain.shutdown", "reboot", NULL); } static void menu_cb_domain_destroy(void) { xmlrpc_request("xend.domain.destroy", NULL); } static void menu_cb_about(void) { static char *comments = "xen domain monitor"; static char *copyright = "(c) 2005-2006 Gerd Hoffmann"; static char *authors[] = { "Gerd Hoffmann ", NULL }; gtk_show_about_dialog(GTK_WINDOW(xd_toplevel), "authors", authors, "comments", comments, "copyright", copyright, "logo-icon-name", GTK_STOCK_ABOUT, "version", VERSION, NULL); } static void destroy(void) { g_object_unref(store); xd_toplevel = NULL; gtk_main_quit(); } /* ------------------------------------------------------------------ */ static const GtkActionEntry entries[] = { { /* menus */ .name = "FileMenu", .label = "_File", },{ .name = "ConnectMenu", .label = "_Connect", },{ .name = "DomainMenu", .label = "_Domain", },{ .name = "HelpMenu", .label = "_Help", },{ /* menu items */ .name = "Quit", .stock_id = GTK_STOCK_QUIT, .label = "_Quit", .accelerator = "Q", .tooltip = "Quit the job", .callback = menu_cb_quit, },{ .name = "About", .stock_id = GTK_STOCK_ABOUT, .label = "_About ...", .callback = menu_cb_about, },{ .name = "OpenVNC", .label = "_VNC", .accelerator = "V", .tooltip = "Open VNC viewer (hvm domains only)", .callback = menu_cb_open_vnc, },{ .name = "OpenConsole", .label = "_Console", .accelerator = "C", .tooltip = "Open xterm with console", .callback = menu_cb_open_console, },{ .name = "DomainPause", .stock_id = GTK_STOCK_MEDIA_PAUSE, .label = "_Pause", .tooltip = "Pause domain", .callback = menu_cb_domain_pause, },{ .name = "DomainUnpause", .stock_id = GTK_STOCK_MEDIA_PLAY, .label = "_Unpause", .tooltip = "Unpause domain", .callback = menu_cb_domain_unpause, },{ .name = "DomainReboot", .stock_id = GTK_STOCK_REFRESH, .label = "_Reboot", .accelerator = "R", .tooltip = "Reboot domain", .callback = menu_cb_domain_reboot, },{ .name = "DomainShutdown", .stock_id = GTK_STOCK_STOP, .label = "_Shutdown", .tooltip = "Graceful shutdown of the domain", .callback = menu_cb_domain_shutdown, },{ .name = "DomainDestroy", .stock_id = GTK_STOCK_DELETE, .label = "Destroy", .tooltip = "Radically kill off domain", .callback = menu_cb_domain_destroy, }, }; static char ui_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " #if 0 " " " " " " #endif " " " " " " ""; /* ------------------------------------------------------------------ */ 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); if (debug) fprintf(stderr, "%s: %d\n", __FUNCTION__, id); /* TODO: something useful ;) */ } 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); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, _("tty"), renderer, "text", XEN_DOMS_COL_S_TERMINAL, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW(view), -1, _("vnc"), renderer, "text", XEN_DOMS_COL_I_VNCPORT, 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, *frame; 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), 480, 320); 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_label_new("status line"); gtk_misc_set_alignment(GTK_MISC(status), 0, 0.5); gtk_misc_set_padding(GTK_MISC(status), 3, 1); /* 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); frame = gtk_frame_new(NULL); gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, TRUE, 0); gtk_container_add(GTK_CONTAINER(frame), status); return; } xenwatch-0.5.4/mdns-publish.c0000664000076400007640000002413210562316561015471 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "mdns-publish.h" /* --------------------------------------------------------------------- */ struct mdns_pub { int have_tty; int have_syslog; int debug; AvahiThreadedPoll *thread_poll; AvahiClient *client; struct list_head entries; }; struct mdns_pub_entry { struct list_head next; char *name; char *service; int port; char *txt[4]; struct mdns_pub *mdns; AvahiEntryGroup *group; }; char *mdns_pub_appname; int mdns_pub_termsig; int mdns_pub_appquit; /* ------------------------------------------------------------------ */ static char *group_state_name[] = { [ AVAHI_ENTRY_GROUP_UNCOMMITED ] = "uncommited", [ AVAHI_ENTRY_GROUP_REGISTERING ] = "registering", [ AVAHI_ENTRY_GROUP_ESTABLISHED ] = "established", [ AVAHI_ENTRY_GROUP_COLLISION ] = "collision", [ AVAHI_ENTRY_GROUP_FAILURE ] = "failure", }; static char *client_state_name[] = { [ AVAHI_CLIENT_S_REGISTERING ] = "server registering", [ AVAHI_CLIENT_S_RUNNING ] = "server running", [ AVAHI_CLIENT_S_COLLISION ] = "server collision", [ AVAHI_CLIENT_FAILURE ] = "failure", [ AVAHI_CLIENT_CONNECTING ] = "connecting", }; static void update_services(AvahiClient *c, struct mdns_pub *mdns); static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userdata) { struct mdns_pub_entry *entry = userdata; char *n; mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s: state %d [%s]\n", __FUNCTION__, entry->name, state, group_state_name[state]); switch (state) { case AVAHI_ENTRY_GROUP_COLLISION: n = avahi_alternative_service_name(entry->name); mdns_log_printf(entry->mdns, LOG_NOTICE, "service name collision, renaming '%s' to '%s'\n", entry->name, n); avahi_free(entry->name); entry->name = n; avahi_entry_group_reset(entry->group); update_services(avahi_entry_group_get_client(g), entry->mdns); break; case AVAHI_ENTRY_GROUP_FAILURE: mdns_pub_appquit++; break; default: break; } } static void update_services(AvahiClient *c, struct mdns_pub *mdns) { struct list_head *item; struct mdns_pub_entry *entry; AvahiEntryGroupState state; int ret; if (AVAHI_CLIENT_S_RUNNING != avahi_client_get_state(c)) return; list_for_each(item, &mdns->entries) { entry = list_entry(item, struct mdns_pub_entry, next); /* If this is the first time we're called, let's create a new entry group */ if (!entry->group) { entry->group = avahi_entry_group_new(c, entry_group_callback, entry); if (!entry->group) { mdns_log_printf(mdns, LOG_ERR, "avahi_entry_group_new() failed: %s\n", avahi_strerror(avahi_client_errno(c))); goto fail; } } /* something to do ? */ state = avahi_entry_group_get_state(entry->group); mdns_log_printf(mdns, LOG_DEBUG, "%s: %s: %d [%s]\n", __FUNCTION__, entry->name, state, group_state_name[state]); if (AVAHI_ENTRY_GROUP_UNCOMMITED != state) continue; /* Add the service */ ret = avahi_entry_group_add_service(entry->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, entry->name, entry->service, NULL, NULL, entry->port, entry->txt[0], entry->txt[1], entry->txt[2], entry->txt[3], NULL); if (ret < 0) { mdns_log_printf(mdns, LOG_ERR, "failed to add '%s' service: %s\n", entry->name, avahi_strerror(ret)); goto fail; } /* Tell the server to register the service */ ret = avahi_entry_group_commit(entry->group); if (ret < 0) { mdns_log_printf(mdns, LOG_ERR, "failed to commit entry_group: %s\n", avahi_strerror(ret)); goto fail; } } return; fail: mdns_pub_appquit++; } static void reset_services(struct mdns_pub *mdns, int free_groups) { struct list_head *item; struct mdns_pub_entry *entry; list_for_each(item, &mdns->entries) { entry = list_entry(item, struct mdns_pub_entry, next); avahi_entry_group_reset(entry->group); if (!free_groups) continue; avahi_entry_group_free(entry->group); entry->group = NULL; } } static void client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { struct mdns_pub *mdns = userdata; int error; mdns_log_printf(mdns, LOG_DEBUG, "%s: state %d [%s]\n", __FUNCTION__, state, client_state_name[state]); switch (state) { case AVAHI_CLIENT_CONNECTING: mdns_log_printf(mdns, LOG_NOTICE, "avahi daemon not running (yet), I'll keep trying ...\n"); break; case AVAHI_CLIENT_S_RUNNING: update_services(c, mdns); break; case AVAHI_CLIENT_S_COLLISION: reset_services(mdns, 0); break; case AVAHI_CLIENT_FAILURE: switch (avahi_client_errno(c)) { case AVAHI_ERR_DISCONNECTED: reset_services(mdns, 1); avahi_client_free(c); mdns_log_printf(mdns, LOG_NOTICE, "disconnected from avahi daemon, reconnecting ...\n"); mdns->client = avahi_client_new(avahi_threaded_poll_get(mdns->thread_poll), AVAHI_CLIENT_NO_FAIL, client_callback, mdns, &error); if (!mdns->client) { mdns_log_printf(mdns, LOG_ERR, "failed to create client: %s\n", avahi_strerror(error)); goto fail; } break; default: mdns_log_printf(mdns, LOG_ERR, "client failure: %s\n", avahi_strerror(avahi_client_errno(c))); goto fail; break; } break; default: break; } return; fail: mdns_pub_appquit++; } /* ------------------------------------------------------------------ */ struct mdns_pub *mdns_pub_init(int debug) { struct mdns_pub *mdns; int error; mdns = avahi_malloc(sizeof(*mdns)); if (NULL == mdns) { fprintf(stderr, "%s: out of memory\n", mdns_pub_appname); goto fail; } memset(mdns, 0, sizeof(*mdns)); INIT_LIST_HEAD(&mdns->entries); mdns->debug = debug; mdns->have_tty = isatty(2); openlog(mdns_pub_appname, 0, LOG_LOCAL0); mdns->have_syslog = 1; mdns->thread_poll = avahi_threaded_poll_new(); if (!mdns->thread_poll) { mdns_log_printf(mdns, LOG_ERR, "failed to create simple poll object\n"); goto fail; } mdns->client = avahi_client_new(avahi_threaded_poll_get(mdns->thread_poll), AVAHI_CLIENT_NO_FAIL, client_callback, mdns, &error); if (!mdns->client) { mdns_log_printf(mdns, LOG_ERR, "failed to create client: %s\n", avahi_strerror(error)); goto fail; } return mdns; fail: mdns_pub_fini(mdns); return NULL; } int mdns_pub_start(struct mdns_pub *mdns) { return avahi_threaded_poll_stop(mdns->thread_poll); } int mdns_pub_stop(struct mdns_pub *mdns) { return avahi_threaded_poll_stop(mdns->thread_poll); } void mdns_pub_fini(struct mdns_pub *mdns) { if (!mdns) return; mdns_pub_del_all(mdns); if (mdns->client) avahi_client_free(mdns->client); if (mdns->thread_poll) avahi_threaded_poll_free(mdns->thread_poll); avahi_free(mdns); } /* --------------------------------------------------------------------- */ struct mdns_pub_entry *mdns_pub_add(struct mdns_pub *mdns, char *name, char *service, int port, ...) { struct mdns_pub_entry *entry; va_list args; char *txt; int i; entry = avahi_malloc(sizeof(*entry)); if (NULL == entry) return NULL; memset(entry, 0, sizeof(*entry)); entry->name = avahi_strdup(name); entry->service = service; entry->port = port; entry->mdns = mdns; va_start(args, port); for (i = 0; i < sizeof(entry->txt)/sizeof(entry->txt[0]); i++) { txt = va_arg(args,char*); if (NULL == txt) break; entry->txt[i] = txt; } va_end(args); list_add_tail(&entry->next, &mdns->entries); if (mdns->client) update_services(mdns->client, mdns); mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s\n", __FUNCTION__, entry->name); return entry; } void mdns_pub_del(struct mdns_pub_entry *entry) { mdns_log_printf(entry->mdns, LOG_DEBUG, "%s: %s\n", __FUNCTION__, entry->name); if (entry->group) { avahi_entry_group_reset(entry->group); avahi_entry_group_free(entry->group); entry->group = NULL; } avahi_free(entry->name); list_del(&entry->next); avahi_free(entry); } void mdns_pub_del_all(struct mdns_pub *mdns) { struct mdns_pub_entry *entry; while (!list_empty(&mdns->entries)) { entry = list_entry(mdns->entries.next, struct mdns_pub_entry, next); mdns_pub_del(entry); } } /* --------------------------------------------------------------------- */ int mdns_log_printf(struct mdns_pub *mdns, int priority, char *fmt, ...) { va_list args; char msgbuf[1024]; int rc; va_start(args, fmt); rc = vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); va_end(args); if (!mdns || mdns->have_tty) fprintf(stderr, "%s: %s", mdns_pub_appname, msgbuf); if (mdns && mdns->have_syslog) syslog(priority, "%s", msgbuf); return rc; } int mdns_daemonize(void) { switch (fork()) { case -1: mdns_log_printf(NULL, LOG_ERR, "fork: %s", strerror(errno)); return -1; case 0: /* child */ close(0); close(1); close(2); setsid(); open("/dev/null", O_RDWR); dup(0); dup(0); return 0; default: /* parent */ exit(0); } } static void catchsig(int signal) { mdns_pub_termsig = signal; mdns_pub_appquit = 1; } void mdns_sigsetup(struct mdns_pub *mdns) { struct sigaction act,old; memset(&act,0,sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = SIG_IGN; sigaction(SIGPIPE,&act,&old); act.sa_handler = catchsig; sigaction(SIGHUP,&act,&old); sigaction(SIGUSR1,&act,&old); sigaction(SIGTERM,&act,&old); if (mdns->debug) sigaction(SIGINT,&act,&old); } xenwatch-0.5.4/README0000664000076400007640000000304110666020427013572 0ustar kraxelkraxelThis is a bunch of utilities for xen hosts. Some useful are for admins, some for developers, some are just experiental and not very useful at all ... xenwatch First application which also gave the name to the whole package. gtk application, lists running domains with some properties, allows to connect to text console or vnc display. Uses xenstore, thus does see active domains only. xenscreen Attaches xen domain consoles to screen windows. Great tool for text mode hackers and screen users. This one can run either using xenstore or using libvirt. xenstore gtk xenstore browser. If you don't know what xenstore is you don't need it. xenlog Log xenstore activity to stdout. vnc-client xenwatch builtin vnc client as standalone application. mdns-publish-xendom Publish xen uuid and domid via multicast dns. Runs inside your xen guests aka domUs. Doesn't notice save/restore and migration, due to xenstore watches not working inside guests, thus it isn't terribly useful ... mdns-publish-vnc Publish vnc ports of xen guests via multicast dns. Unconditionally, even when listening on 127.0.0.1 only (default) and thus you can't connect from outside anyway ... mdns-browser Simple multicast dns service browser. Depending on the libraries installed at build time some utilities might not be built or build with incomplete features set. You can make /var/run/xenstored/socket_ro world-readable, then the utilities using xenstore will work for non-root users too. enjoy, Gerd -- Gerd Hoffmann xenwatch-0.5.4/xd_store.h0000664000076400007640000000240410520106476014712 0ustar kraxelkraxel#ifndef _xen_doms_h_included_ #define _xen_doms_h_included_ #include /* 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, XEN_DOMS_COL_S_TERMINAL, /* 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, XEN_DOMS_COL_I_VNCPORT, /* that's it */ XEN_DOMS_N_COLUMNS, }; XenDoms *xen_doms_new(void); #endif /* _xen_doms_h_included_ */ xenwatch-0.5.4/mdns-browser.c0000664000076400007640000000547010665532134015512 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include #include #include "mdns.h" #include "apps.h" #include "vnc.h" /* ------------------------------------------------------------------ */ static Display *dpy; static struct mdns_window *mdns; static char *service = "_ssh._tcp"; static char *domain = NULL; static enum mdns_view view = MDNS_VIEW_DEFAULT; static mdns_callback callback = NULL; /* ------------------------------------------------------------------ */ static void usage(FILE *fp) { fprintf(fp, "This is a mDNS browser\n" "\n" "usage: mdns-browser [options]\n" "options:\n" " -h print this text\n" " -s specify service [%s]\n" " -d specify domain\n" " -u url mode, displays a different set of default\n" " columns, useful for _http._tcp & friends.\n" " -x xen mode, yet another default column set, this\n" " also sets service to _xendom._tcp\n" " -r vnc mode, sets service to _rfb._tcp\n" " -w sets service to _workstation._tcp\n" "\n" "-- \n" "(c) 2006 Gerd Hoffmann \n", service); } static void vnc_callback(struct mdns_window *mdns, char *name, char *type, char *host, int port, char *url) { vnc_open(host, port, VNC_FLAG_SHOW_MOUSE, 0); } int main(int argc, char *argv[]) { int c; gtk_init(&argc, &argv); for (;;) { if (-1 == (c = getopt(argc, argv, "huxrws:d:"))) break; switch (c) { case 's': service = optarg; break; case 'd': domain = optarg; break; case 'u': view = MDNS_VIEW_URL; break; case 'x': service = "_xendom._tcp"; view = MDNS_VIEW_XEN; break; case 'r': service = "_rfb._tcp"; break; case 'w': service = "_workstation._tcp"; break; case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); fcntl(ConnectionNumber(dpy),F_SETFD,FD_CLOEXEC); detect_desktop(); #ifdef HAVE_GTK_VNC if (0 == strcmp(service, "_rfb._tcp")) callback = vnc_callback; #endif mdns = mdns_create_window(1, view, callback); if (NULL == mdns) { fprintf(stderr,"Oops: mDNS did't initialize ok\n"); exit(1); } mdns_show_window(mdns); mdns_browse(mdns, 1, service, domain); if (0) mdns_browse(mdns, 0, "_workstation._tcp", NULL); gtk_main(); fprintf(stderr,"bye...\n"); exit(0); } xenwatch-0.5.4/xenviews.h0000664000076400007640000000034010346013137014725 0ustar kraxelkraxel/* 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); xenwatch-0.5.4/apps-x11.c0000664000076400007640000000422710452203337014433 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #include "apps.h" /* ------------------------------------------------------------------ */ #if 0 /* check_wm_capability(dpy, root, _NET_SUPPORTED, _NET_WM_whatever); */ static int check_wm_capability(Display *dpy, Window root, Atom list, Atom wanted) { Atom type; int format; unsigned int i; unsigned long nitems, bytesafter; unsigned char *args; unsigned long *ldata; char *name; int retval = -1; if (Success != XGetWindowProperty (dpy, root, list, 0, (65536 / sizeof(long)), False, AnyPropertyType, &type, &format, &nitems, &bytesafter, &args)) return -1; if (type != XA_ATOM) return -1; ldata = (unsigned long*)args; for (i = 0; i < nitems; i++) { if (ldata[i] == wanted) retval = 0; if (debug) { name = XGetAtomName(dpy,ldata[i]); fprintf(stderr,"wm cap: %s\n",name); XFree(name); } } XFree(ldata); return retval; } #endif static int check_atom_present(Display *dpy, Window root, Atom check) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *args; if (Success != XGetWindowProperty (dpy, root, check, 0, (65536 / sizeof(long)), False, AnyPropertyType, &type, &format, &nitems, &bytesafter, &args)) return -1; if (NULL == args) return -1; return 0; } #define INIT_ATOM(dpy,atom) atom = XInternAtom(dpy,#atom,False) static Atom KWIN_RUNNING; static Atom _METACITY_SENTINEL; void detect_desktop(void) { Display *dpy = gdk_x11_display_get_xdisplay(gdk_display_get_default()); Window root = DefaultRootWindow(dpy); INIT_ATOM(dpy, KWIN_RUNNING); INIT_ATOM(dpy, _METACITY_SENTINEL); if (0 == check_atom_present(dpy, root, KWIN_RUNNING)) { fprintf(stderr,"Desktop: KDE\n"); desktop_type = DESKTOP_KDE; } if (0 == check_atom_present(dpy, root, _METACITY_SENTINEL)) { fprintf(stderr,"Desktop: Gnome\n"); desktop_type = DESKTOP_GNOME; } } xenwatch-0.5.4/tcp.c0000664000076400007640000001005310350571765013653 0ustar kraxelkraxel#include #include #include #include #include #include #include "tcp.h" int tcp_verbose; /* ------------------------------------------------------------------ */ static char *strfamily(int family) { switch (family) { case PF_INET6: return "ipv6"; case PF_INET: return "ipv4"; case PF_UNIX: return "unix"; } return "????"; } int tcp_connect(struct addrinfo *ai, char *addr, char *port, char *host, char *serv) { struct addrinfo *res,*e; struct addrinfo *lres, ask; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; char uhost[INET6_ADDRSTRLEN+1]; char userv[33]; int sock,rc,opt=1; /* lookup peer */ ai->ai_flags = AI_CANONNAME; if (0 != (rc = getaddrinfo(host, serv, ai, &res))) { if (tcp_verbose) fprintf(stderr,"getaddrinfo (peer): %s\n", gai_strerror(rc)); return -1; } for (e = res; e != NULL; e = e->ai_next) { if (0 != getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, uhost,INET6_ADDRSTRLEN,userv,32, NI_NUMERICHOST | NI_NUMERICSERV)) { if (tcp_verbose) fprintf(stderr,"getnameinfo (peer): oops\n"); continue; } if (-1 == (sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol))) { if (tcp_verbose) fprintf(stderr,"socket (%s): %s\n", strfamily(e->ai_family),strerror(errno)); continue; } setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); if (NULL != addr || NULL != port) { /* bind local port */ memset(&ask,0,sizeof(ask)); ask.ai_flags = AI_PASSIVE; ask.ai_family = e->ai_family; ask.ai_socktype = e->ai_socktype; if (0 != (rc = getaddrinfo(addr, port, &ask, &lres))) { if (tcp_verbose) fprintf(stderr,"getaddrinfo (local): %s\n", gai_strerror(rc)); continue; } if (0 != getnameinfo((struct sockaddr*)lres->ai_addr, lres->ai_addrlen, uaddr,INET6_ADDRSTRLEN,uport,32, NI_NUMERICHOST | NI_NUMERICSERV)) { if (tcp_verbose) fprintf(stderr,"getnameinfo (local): oops\n"); continue; } if (-1 == bind(sock, lres->ai_addr, lres->ai_addrlen)) { if (tcp_verbose) fprintf(stderr,"%s [%s] %s bind: %s\n", strfamily(lres->ai_family),uaddr,uport, strerror(errno)); continue; } } /* connect to peer */ if (-1 == connect(sock,e->ai_addr,e->ai_addrlen)) { if (tcp_verbose) fprintf(stderr,"%s %s [%s] %s connect: %s\n", strfamily(e->ai_family),e->ai_canonname,uhost,userv, strerror(errno)); close(sock); continue; } if (tcp_verbose) fprintf(stderr,"%s %s [%s] %s open\n", strfamily(e->ai_family),e->ai_canonname,uhost,userv); fcntl(sock,F_SETFL,O_NONBLOCK); return sock; } return -1; } int tcp_listen(struct addrinfo *ai, char *addr, char *port) { struct addrinfo *res,*e; char uaddr[INET6_ADDRSTRLEN+1]; char uport[33]; int slisten,rc,opt=1; /* lookup */ ai->ai_flags = AI_PASSIVE; if (0 != (rc = getaddrinfo(addr, port, ai, &res))) { if (tcp_verbose) fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(rc)); exit(1); } /* create socket + bind */ for (e = res; e != NULL; e = e->ai_next) { getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, uaddr,INET6_ADDRSTRLEN,uport,32, NI_NUMERICHOST | NI_NUMERICSERV); if (-1 == (slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol))) { if (tcp_verbose) fprintf(stderr,"socket (%s): %s\n", strfamily(e->ai_family),strerror(errno)); continue; } opt = 1; setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt)); if (-1 == bind(slisten, e->ai_addr, e->ai_addrlen)) { if (tcp_verbose) fprintf(stderr,"%s [%s] %s bind: %s\n", strfamily(e->ai_family),uaddr,uport, strerror(errno)); continue; } listen(slisten,1); break; } if (NULL == e) return -1; /* wait for a incoming connection */ if (tcp_verbose) fprintf(stderr,"listen on %s [%s] %s ...\n", strfamily(e->ai_family),uaddr,uport); fcntl(slisten,F_SETFL,O_NONBLOCK); return slisten; } xenwatch-0.5.4/xenscreen.c0000664000076400007640000004002311122437756015056 0ustar kraxelkraxel#include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBVIRT # include # include # include # include #endif #ifdef HAVE_XENSTORE # include #endif #include "list.h" #include "xs_tools.h" #include "apps.h" /* ------------------------------------------------------------- */ #define BUFSIZE 64 #define PRE_STATE "%16s: " struct dom { int domid; char name[BUFSIZE]; char tty[BUFSIZE]; int connected; int destroyed; int watched; struct list_head next; }; static LIST_HEAD(doms); static int domcnt; static char *screen_rc = "/etc/xen/xenscreenrc"; static char *screen_unlink = NULL; static char *screen_session = "xencon"; static char *screen_title = "mon"; static int screen_detached; static int screen_logging; static int screen_shell; static char builtin_screen_rc[] = "multiuser on\n" "\n" "# status line\n" "sorendition =s wb\n" "hardstatus lastline \"%{=b bw} xen |%{-} %-w%{= yb} %50>%n* %t %{-}%+w%<\"\n" "\n" "# logging\n" "logfile /var/log/xen/console.%t\n" "logfile flush 1\n" "logtstamp on\n" "\n" "# disable killing windows\n" "bind k\n" "bind K\n" "bind ^K\n" "\n" "# misc other useful settings\n" "shell /bin/bash\n" "defscrollback 5000\n" "compacthist on\n" "defutf8 on\n" "termcapinfo xterm hs@\n" "\n" ; /* ------------------------------------------------------------- */ static struct dom *find_dom(int domid) { struct dom *dom; struct list_head *item; list_for_each(item, &doms) { dom = list_entry(item, struct dom, next); if (dom->domid == domid) return dom; } return NULL; } static struct dom *get_dom(int domid) { struct dom *dom; dom = find_dom(domid); if (!dom) { dom = malloc(sizeof(*dom)); memset(dom,0,sizeof(*dom)); dom->domid = domid; list_add_tail(&dom->next, &doms); } return dom; } /* ------------------------------------------------------------- */ static int termsig; static void catchsig(int sig) { termsig = sig; } /* ------------------------------------------------------------- */ static int screen_command(char *arg0, ...) { va_list args; char *argv[64]; int i = 0; argv[i++] = "screen"; argv[i++] = "-X"; argv[i++] = "-S"; argv[i++] = screen_session; argv[i++] = arg0; va_start(args, arg0); while (i < array_size(argv)) { argv[i] = va_arg(args, char*); if (NULL == argv[i]) break; i++; } va_end(args); return run_application_va(1, "screen", argv); } static int screen_attach(char *window) { return run_application(1, "screen", "screen", "-S", screen_session, "-r", "-x", "-p", window ? window : "=", NULL); } static void try_attach_domain(struct dom *dom, int boot) { int rc; if (dom->connected) return; if (!strlen(dom->name)) return; if (!strlen(dom->tty)) return; fprintf(stderr, PRE_STATE "%s (%d) @ %s\n", "connecting", dom->name, dom->domid, dom->tty); if (0 != access(dom->tty, R_OK)) { fprintf(stderr, " error: no access to tty %s\n", dom->tty); return; } #if 0 /* known-racy, but better than nothing ... */ rc = run_application(1, "fuser", "fuser", "-s", dom->tty, NULL); if (0 == rc) { fprintf(stderr," error: tty %s already in use\n", dom->tty); return; } #endif if (screen_logging) rc = screen_command("screen", "-L", "-t", dom->name, dom->tty, NULL); else rc = screen_command("screen", "-t", dom->name, dom->tty, NULL); #if 0 /* * Hmm, not exactly the most elegant way to do this, has * some ugly glitches too. * * Switches back from the new window to the previous one. * Better would be to not switch in the first place, * seems screen can't do that though :-( */ if (!boot) rc = screen_command("other", NULL); #endif dom->connected = 1; domcnt++; } static void try_release_domain(struct dom *dom) { if (!dom->destroyed) return; fprintf(stderr, PRE_STATE "%s (%d)%s\n", "disappeared", dom->name, dom->domid, dom->connected ? " [conn]" : ""); if (dom->connected) domcnt--; list_del(&dom->next); free(dom); } static void builtin_screen_setup(void) { char *rc = strdup("/tmp/xenscreen-XXXXXX"); int fd; fd = mkstemp(rc); if (-1 == fd) { fprintf(stderr,"mkstmp(%s): %s\n", rc, strerror(errno)); return; } write(fd, builtin_screen_rc, sizeof(builtin_screen_rc)); close(fd); fprintf(stderr, "Config file \"%s\" doesn't exist, using builtin config (%s).\n", screen_rc, rc); screen_rc = rc; screen_unlink = rc; return; } /* ------------------------------------------------------------- */ /* libvirt bits */ #ifdef HAVE_LIBVIRT static int libvirt_xml_xpath_str(const char *doc, char *xpath, char *dest, int len) { xmlDocPtr xml = NULL; xmlXPathObjectPtr obj = NULL; xmlXPathContextPtr ctxt = NULL; int ret = -1; xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, XML_PARSE_NOENT | XML_PARSE_NONET | XML_PARSE_NOWARNING); if (!xml) goto cleanup; ctxt = xmlXPathNewContext(xml); if (!ctxt) goto cleanup; obj = xmlXPathEval(BAD_CAST xpath, ctxt); if ((obj == NULL) || (obj->type != XPATH_STRING) || (obj->stringval == NULL) || (obj->stringval[0] == 0)) { goto cleanup; } snprintf(dest, len, "%s", obj->stringval); ret = 0; cleanup: if (obj) xmlXPathFreeObject(obj); if (ctxt) xmlXPathFreeContext(ctxt); if (xml) xmlFreeDoc(xml); return ret; } static void libvirt_scan(virConnectPtr conn, int boot) { int i, count, *ids; virDomainPtr vdom; struct dom *dom; const char *name, *xml; struct list_head *item; list_for_each(item, &doms) { dom = list_entry(item, struct dom, next); dom->destroyed = 1; } count = virConnectNumOfDomains(conn) + 4; ids = malloc(count * sizeof(int)); count = virConnectListDomains(conn, ids, count); for (i = 0; i < count; i++) { dom = find_dom(ids[i]); if (dom) { /* have it */ dom->destroyed = 0; if (dom->connected) continue; /* try again a few times in case we have no tty, * it may show up a little later ... */ if (dom->watched > 8) continue; dom->watched++; } /* new one */ vdom = virDomainLookupByID(conn, ids[i]); name = virDomainGetName(vdom); xml = virDomainGetXMLDesc(vdom, 0); if (!name || !xml) continue; // fprintf(stderr, "\n-- xmldesc --\n%s\n--\n", xml); dom = get_dom(ids[i]); snprintf(dom->name, sizeof(dom->name), "%s", name); libvirt_xml_xpath_str(xml, "string(/domain/devices/console/@tty)", dom->tty, sizeof(dom->tty)); fprintf(stderr, "[ libvirt poll debug: %s (%d): tty=\"%s\" ]\n", dom->name, dom->watched, dom->tty); try_attach_domain(dom, boot); } free(ids); list_for_each(item, &doms) { dom = list_entry(item, struct dom, next); if (dom->destroyed) try_release_domain(dom); } } #endif /* ------------------------------------------------------------- */ /* xenstore bits */ #ifdef HAVE_XENSTORE static void xenstore_scan(struct xs_handle *xenstore) { xs_transaction_t xst; char **vec = NULL; int domid; unsigned int count, i; char path[BUFSIZE]; struct dom *dom; /* look for running domains */ if (!(xst = xs_transaction_start(xenstore))) { fprintf(stderr,"Oops, can't start xenstore transaction\n"); exit(1); } vec = xs_directory(xenstore, xst, "/local/domain", &count); xs_transaction_end(xenstore, xst, 0); for (i = 0; i < count; i++) { domid = atoi(vec[i]); dom = get_dom(domid); snprintf(path, sizeof(path), "/local/domain/%d/name", domid); xenstore_read(xenstore, path, dom->name, sizeof(dom->name)); snprintf(path, sizeof(path), "/local/domain/%d/console/tty", domid); xenstore_read(xenstore, path, dom->tty, sizeof(dom->tty)); try_attach_domain(dom, 1); } if (vec) free(vec); } static void xenstore_update(struct xs_handle *xenstore) { char **vec = NULL; int domid; unsigned int count, rc; char path[BUFSIZE], value[BUFSIZE]; struct dom *dom; vec = xs_read_watch(xenstore, &count); if (NULL == vec) { fprintf(stderr,"xs_read_watch() failed\n"); exit(1); } if (2 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d/%64s", &domid, path)) { if (1 != sscanf(vec[XS_WATCH_PATH], "/local/domain/%d", &domid)) goto cleanup; strcpy(path, ""); } dom = get_dom(domid); if (0 == strcmp(path,"")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) dom->destroyed = 1; } else if (0 == strcmp(path, "console/tty")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) goto cleanup; strcpy(dom->tty, value); } else if (0 == strcmp(path, "name")) { rc = xenstore_read(xenstore, vec[XS_WATCH_PATH], value, sizeof(value)); if (0 != rc) goto cleanup; strcpy(dom->name, value); fprintf(stderr, PRE_STATE "%s (%d)\n", "new domain", dom->name, dom->domid); } else { goto cleanup; } try_attach_domain(dom, 0); try_release_domain(dom); cleanup: if (vec) free(vec); } #endif /* ------------------------------------------------------------- */ static void usage(FILE *fp) { fprintf(fp, "I'm managing xen consoles using screen.\n" "\n" "usage: xenscreen [options]\n" "options:\n" " -h print this text\n" " -b print default screen config file\n" " -z open a screen window with a shell\n" #ifdef HAVE_LIBVIRT " -v uri libvirt connect uri\n" #endif "\n" " -L enable console output logging\n" " -c screenrc screen config file [%s]\n" " -S session screen session name [%s]\n" " -p window preselect screen window\n" "\n" "-- \n" "(c) 2006,07 Gerd Hoffmann \n", screen_rc, screen_session); } int main(int argc, char *argv[]) { struct sigaction act,old; struct utsname uts; int maxfd; fd_set set; char *window = NULL; int nac, c; unsigned int rc, i; time_t last_ctrl_c = 0; char **nav; struct timeval tv; #ifdef HAVE_LIBVIRT char *vir_url = getenv("VIRSH_DEFAULT_CONNECT_URI"); virConnectPtr vir_conn = NULL; #else void *vir_conn = NULL; #endif #ifdef HAVE_XENSTORE struct xs_handle *xenstore = NULL; #else void *xenstore = NULL; #endif for (;;) { if (-1 == (c = getopt(argc, argv, "hdbLzc:S:u:p:v:"))) break; switch (c) { /* screen-like behaviour */ case 'c': screen_rc = optarg; break; case 'S': screen_session = optarg; break; case 'p': window = optarg; break; case 'L': screen_logging = 1; break; case 'd': screen_detached = 1; break; /* other options */ case 'u': screen_unlink = optarg; break; case 'z': screen_shell = 1; break; case 'b': printf("%s", builtin_screen_rc); exit(0); #ifdef HAVE_LIBVIRT case 'v': vir_url = optarg; break; #endif case 'h': usage(stdout); exit(0); default: usage(stderr); exit(1); } } if (!have_application("screen")) { fprintf(stderr, "screen not found in $PATH (not installed?), exiting.\n"); exit(1); } memset(&uts, 0, sizeof(uts)); if (0 == uname(&uts)) { char *h; if (NULL != (h = strstr(uts.nodename, "."))) *h = 0; screen_title = malloc(strlen(uts.nodename) +4); sprintf(screen_title, "[%s]", uts.nodename); } if (NULL == getenv("STY") || NULL == strstr(getenv("STY"),screen_session)) { /* not running inside screen */ if (!screen_detached) { /* try to attach */ rc = screen_attach(window); if (0 == rc) exit(0); } else { /* This is a nop: just check if screen is running */ rc = screen_command("select", ".", NULL); if (0 == rc) { fprintf(stderr,"Screen session \"%s\" already active, exiting.\n", screen_session); exit(0); } } /* failing that, start a new screen session ... */ fprintf(stderr,"Starting new screen session \"%s\".\n", screen_session); if (0 != access(screen_rc, R_OK)) builtin_screen_setup(); nav = malloc(sizeof(char*) * (argc + 16)); nac = 0; nav[nac++] = "screen"; nav[nac++] = "-d"; nav[nac++] = "-m"; nav[nac++] = "-S"; nav[nac++] = screen_session; nav[nac++] = "-c"; nav[nac++] = screen_rc; nav[nac++] = "-t"; nav[nac++] = screen_title; for (i = 0; argv[i] != NULL;) nav[nac++] = argv[i++]; if (screen_unlink) { nav[nac++] = "-u"; nav[nac++] = screen_unlink; } nav[nac++] = NULL; rc = run_application_va(1, "screen", nav); /* ... and attach if asked for */ if (0 == rc && !screen_detached) { sleep(1); /* quick & dirty race work around */ rc = screen_attach(window); } exit(rc); } /* setup signal handler */ memset(&act,0,sizeof(act)); sigemptyset(&act.sa_mask); act.sa_handler = catchsig; sigaction(SIGTERM,&act,&old); sigaction(SIGINT,&act,&old); fprintf(stderr, "###\n" "### Managing Xen consoles using screen.\n" "### This is the monitor process, at %s.\n" "###\n" "\n", uts.nodename); if (screen_shell) screen_command("screen", "-t", "[shell]", "/bin/bash", NULL); #ifdef HAVE_LIBVIRT if (vir_url) { fprintf(stderr, "trying libvirt (%s) ...\n", vir_url); vir_conn = virConnectOpenReadOnly(vir_url); if (vir_conn) fprintf(stderr, "using libvirt\n"); else fprintf(stderr, "libvirt: can't connect\n"); } #endif #ifdef HAVE_XENSTORE if (!vir_conn) { /* connect to xenstore */ fprintf(stderr, "trying xenstore ...\n"); xenstore = xenstore_open(1,1,1,1); if (xenstore) { fprintf(stderr, "using xenstore\n"); xs_watch(xenstore, "/local/domain", "token"); } else fprintf(stderr, "xenstore: can't connect\n"); } #endif if (!vir_conn && !xenstore) { fprintf(stderr, "Failed to establish VM management connection.\n"); fprintf(stderr, "Exiting in 10 seconds ...\n"); sleep(10); /* give the user the chance to see the error */ exit(1); } fprintf(stderr,"looking for existing domains\n"); #ifdef HAVE_LIBVIRT if (vir_conn) libvirt_scan(vir_conn, 1); #endif #ifdef HAVE_XENSTORE if (xenstore) xenstore_scan(xenstore); #endif /* main loop */ fprintf(stderr,"ok, watching out for changes now\n"); for (;;) { if (termsig) { if (!domcnt) break; if (time(NULL) - last_ctrl_c < 3) break; fprintf(stderr, "\n" "Got ^C - still %d domain(s) active - not quitting.\n" "\n" "You should better use detach instead (^A d).\n" "Or kill all windows (^A \\) if you don't want\n" "keep screen hanging around.\n" "\n" "Hit ^C within 3 secs again to quit nevertheless.\n" "\n", domcnt); last_ctrl_c = time(NULL); termsig = 0; } FD_ZERO(&set); maxfd = 0; #ifdef HAVE_XENSTORE if (xenstore) { int fd = xs_fileno(xenstore); FD_SET(fd, &set); if (maxfd < fd) maxfd = fd; tv.tv_sec = 0; tv.tv_usec = 0; } #endif #ifdef HAVE_LIBVIRT if (vir_conn) { /* FIXME: polling once per second */ tv.tv_sec = 1; tv.tv_usec = 0; } #endif switch (select(maxfd+1, &set, NULL, NULL, tv.tv_sec ? &tv : NULL)) { case -1: if (EINTR == errno) continue; /* termsig check */ perror("select"); break; case 0: #ifdef HAVE_LIBVIRT if (vir_conn) libvirt_scan(vir_conn, 0); #endif break; default: break; } #ifdef HAVE_XENSTORE if (xenstore && FD_ISSET(xs_fileno(xenstore), &set)) xenstore_update(xenstore); #endif } if (screen_unlink) unlink(screen_unlink); return 0; }