summaryrefslogtreecommitdiff
path: root/src/propmon.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/propmon.c')
-rw-r--r--src/propmon.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/propmon.c b/src/propmon.c
new file mode 100644
index 0000000..96182db
--- /dev/null
+++ b/src/propmon.c
@@ -0,0 +1,466 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "app.h"
+
+
+static xcb_connection_t *conn;
+static GtkTreeStore *store;
+xcb_window_t root;
+
+static void
+setup_x(const gchar *display)
+{
+ const xcb_setup_t *setup;
+ xcb_screen_iterator_t iter;
+ uint32_t mask, list;
+
+ conn = make_connection(display);
+
+ setup = xcb_get_setup(conn);
+ iter = xcb_setup_roots_iterator(setup);
+ root = iter.data->root;
+
+ atom_init(conn);
+
+ /* Select for root window events */
+ mask = XCB_CW_EVENT_MASK;
+ list = XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(conn, root, mask, &list);
+
+ xcb_flush(conn);
+}
+
+static char *
+get_prop_text(xcb_get_property_reply_t *reply)
+{
+ size_t size;
+ char *data;
+
+ if (!atom_is_string_type(reply->type))
+ return NULL;
+
+ size = (reply->format >> 3) * xcb_get_property_value_length(reply);
+
+ data = malloc(size + 1);
+ if (!data)
+ APP_ERR(NULL, "Out of memory");
+
+ memcpy(data, xcb_get_property_value(reply), size);
+ data[size] = '\0';
+
+ return scrub_text(data);
+}
+
+static gboolean
+find_window(xcb_window_t window, GtkTreeIter *iter)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ xcb_window_t cur;
+
+ if (gtk_tree_model_get_iter_first(tree, iter)) {
+ do {
+ gtk_tree_model_get(tree, iter, COL_WIN, &cur, -1);
+ if (cur == window)
+ return TRUE;
+
+ } while (gtk_tree_model_iter_next(tree, iter));
+ }
+
+ return FALSE;
+}
+
+static void
+update_window_name(xcb_window_t window, const char *text)
+{
+ GtkTreeIter iter;
+ char buf[128];
+
+ if (find_window(window, &iter)) {
+ snprintf(buf, sizeof(buf), "%s (%08x)", text, window);
+ gtk_tree_store_set(store, &iter, COL_NAME, buf, -1);
+ }
+}
+
+static void
+add_item(GtkTreeIter *parent, GtkTreeIter *sibling,
+ xcb_window_t window, xcb_atom_t atom,
+ security_context_t octx, security_context_t dctx,
+ xcb_atom_t type, const char *data)
+{
+ GtkTreeIter iter;
+ char *color;
+
+ color = get_colors(octx, dctx);
+
+ gtk_tree_store_insert_before(store, &iter, parent, sibling);
+ gtk_tree_store_set(store, &iter,
+ COL_ICON, GTK_STOCK_FILE,
+ COL_ATOM, atom,
+ COL_WIN, window,
+ COL_DATA, data,
+ COL_NAME, atom_lookup(conn, atom),
+ COL_TYPE, atom_lookup(conn, type),
+ COL_OCTX, octx,
+ COL_DCTX, dctx,
+ COL_OFG, color,
+ COL_OBG, color + 8,
+ COL_DFG, color + 16,
+ COL_DBG, color + 24,
+ -1);
+
+ free(color);
+}
+
+static void
+remove_items(xcb_window_t window, xcb_atom_t atom)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ GtkTreeIter iter, child;
+ xcb_atom_t iatom;
+ int more;
+
+ if (!find_window(window, &iter))
+ return;
+
+ if (gtk_tree_model_iter_children(tree, &child, &iter)) {
+ do {
+ gtk_tree_model_get(tree, &child, COL_ATOM, &iatom, -1);
+ if (atom == iatom)
+ more = gtk_tree_store_remove(store, &child);
+ else
+ more = gtk_tree_model_iter_next(tree, &child);
+ } while (more);
+ }
+}
+
+static int
+update_item_matches(GtkTreeIter *iter, xcb_atom_t atom, security_context_t ctx)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ xcb_atom_t iatom;
+ security_context_t ictx;
+ int result;
+
+ gtk_tree_model_get(tree, iter, COL_ATOM, &iatom, COL_OCTX, &ictx, -1);
+ result = (atom == iatom) && !strcmp(ctx, ictx);
+ g_free(ictx);
+ return result;
+}
+
+static gboolean
+update_item_next(xcb_atom_t atom, GtkTreeIter *iter, GtkTreeIter **sibling)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ xcb_atom_t iatom;
+ const char *name, *iname;
+
+ gtk_tree_model_get(tree, iter, COL_ATOM, &iatom, -1);
+ name = atom_lookup(conn, atom);
+ iname = atom_lookup(conn, iatom);
+
+ if (strcasecmp(name, iname) < 0) {
+ *sibling = iter;
+ return FALSE;
+ }
+
+ *sibling = NULL;
+ return gtk_tree_model_iter_next(tree, iter);
+}
+
+static void
+update_item(xcb_window_t window, xcb_atom_t atom,
+ security_context_t octx, security_context_t dctx,
+ xcb_atom_t type, const char *data)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ GtkTreeIter iter, child, *sibling = NULL;
+ char *color;
+
+ if (!find_window(window, &iter))
+ return;
+
+ if (gtk_tree_model_iter_children(tree, &child, &iter)) {
+ do {
+ if (update_item_matches(&child, atom, octx)) {
+ color = get_colors(NULL, dctx);
+ gtk_tree_store_set(store, &child,
+ COL_DATA, data,
+ COL_TYPE, atom_lookup(conn, type),
+ COL_DCTX, dctx,
+ COL_DFG, color + 16,
+ COL_DBG, color + 24,
+ -1);
+ free(color);
+ return;
+ }
+ } while (update_item_next(atom, &child, &sibling));
+ }
+
+ add_item(&iter, sibling, window, atom, octx, dctx, type, data);
+}
+
+static gboolean
+update_is_managed(xcb_atom_t atom, xcb_atom_t wildcard)
+{
+ return (wildcard == XCB_NONE || wildcard == atom);
+}
+
+static void
+update(xcb_window_t window, xcb_atom_t atom)
+{
+ xcb_selinux_list_properties_cookie_t cookie;
+ xcb_selinux_list_properties_reply_t *reply;
+ xcb_get_property_cookie_t *cookies;
+ xcb_get_property_reply_t *propinfo;
+ xcb_generic_error_t *error;
+ xcb_selinux_list_item_iterator_t iter;
+ security_context_t octx, dctx;
+ char *text;
+ int i;
+
+ /* remove existing items if updating */
+ if (atom != XCB_NONE)
+ remove_items(window, atom);
+
+ /* list all property instances on the window */
+ cookie = xcb_selinux_list_properties(conn, window);
+ reply = xcb_selinux_list_properties_reply(conn, cookie, &error);
+ if (error) {
+ /* window might have gone away */
+ if (error->error_code == XCB_WINDOW) {
+ free(error);
+ return;
+ }
+ APP_ERR(error, "An error was received during ListProperties.\n");
+ }
+
+ iter = xcb_selinux_list_properties_properties_iterator(reply);
+ cookies = malloc(iter.rem * sizeof(*cookies));
+ if (!cookies)
+ APP_ERR(NULL, "Out of memory\n");
+
+ /* get the property type and text content */
+ i = 0;
+ while (iter.rem) {
+ if (update_is_managed(iter.data->name, atom)) {
+ octx = xcb_selinux_list_item_object_context(iter.data);
+ xcb_selinux_set_property_use_context(conn, strlen(octx) + 1, octx);
+ cookies[i] = xcb_get_property(conn, FALSE, window, iter.data->name,
+ XCB_GET_PROPERTY_TYPE_ANY, 0, 1024);
+ }
+ xcb_selinux_list_item_next(&iter);
+ ++i;
+ }
+
+ /* process the replies */
+ i = 0;
+ iter = xcb_selinux_list_properties_properties_iterator(reply);
+ while (iter.rem) {
+ if (update_is_managed(iter.data->name, atom)) {
+ octx = xcb_selinux_list_item_object_context(iter.data);
+ dctx = xcb_selinux_list_item_data_context(iter.data);
+ propinfo = xcb_get_property_reply(conn, cookies[i], &error);
+ if (error)
+ APP_ERR(error, "An error was received during GetProperty.\n");
+
+ text = get_prop_text(propinfo);
+ update_item(window, iter.data->name, octx, dctx, propinfo->type, text);
+
+ /* special window updates */
+ if (iter.data->name == wm_name)
+ update_window_name(window, text);
+
+ free(text);
+ free(propinfo);
+ }
+ xcb_selinux_list_item_next(&iter);
+ ++i;
+ }
+
+ free(reply);
+ free(cookies);
+ xcb_flush(conn);
+}
+
+static void
+add_window(xcb_window_t window)
+{
+ xcb_selinux_get_window_context_cookie_t cookie;
+ xcb_selinux_get_window_context_reply_t *reply;
+ xcb_generic_error_t *error;
+ security_context_t octx;
+ uint32_t mask, list;
+ GtkTreeIter iter;
+ char *color, buf[24];
+
+ /* Get window context */
+ cookie = xcb_selinux_get_window_context(conn, window);
+ reply = xcb_selinux_get_window_context_reply(conn, cookie, &error);
+ if (error)
+ APP_ERR(error, "An error was received during GetWindowContext.\n");
+
+ octx = xcb_selinux_get_window_context_context(reply);
+
+ /* Add window to list */
+ color = get_colors(octx, NULL);
+ if (window == root)
+ snprintf(buf, sizeof(buf), "root (%08x)", window);
+ else
+ snprintf(buf, sizeof(buf), "unnamed (%08x)", window);
+
+ gtk_tree_store_append(store, &iter, NULL);
+ gtk_tree_store_set(store, &iter,
+ COL_ICON, GTK_STOCK_DIRECTORY,
+ COL_WIN, window,
+ COL_NAME, buf,
+ COL_OCTX, octx,
+ COL_OFG, color,
+ COL_OBG, color + 8,
+ -1);
+
+ /* Select for window events */
+ if (window != root) {
+ mask = XCB_CW_EVENT_MASK;
+ list = XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_PROPERTY_CHANGE;
+ xcb_change_window_attributes(conn, window, mask, &list);
+ }
+
+ update(window, XCB_NONE);
+ free(color);
+ free(reply);
+}
+
+static void
+add_windows(void)
+{
+ xcb_query_tree_cookie_t cookie;
+ xcb_query_tree_reply_t *reply;
+ xcb_generic_error_t *error;
+ xcb_window_t *children;
+ int i;
+
+ cookie = xcb_query_tree(conn, root);
+ reply = xcb_query_tree_reply(conn, cookie, &error);
+ if (error)
+ APP_ERR(error, "An error was received during QueryTree.\n");
+
+ children = xcb_query_tree_children(reply);
+
+ add_window(root);
+ for (i = 0; i < reply->children_len; i++)
+ add_window(children[i]);
+
+ free(reply);
+}
+
+static void
+del_window_helper(xcb_window_t window, GtkTreeIter *iter)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ GtkTreeIter child;
+ xcb_window_t cur;
+ int more;
+
+ do {
+ gtk_tree_model_get(tree, iter, COL_WIN, &cur, -1);
+ if (cur == window) {
+ if (gtk_tree_model_iter_children(tree, &child, iter))
+ del_window_helper(window, &child);
+ more = gtk_tree_store_remove(store, iter);
+ } else
+ more = gtk_tree_model_iter_next(tree, iter);
+ } while (more);
+}
+
+static void
+del_window(xcb_window_t window)
+{
+ GtkTreeModel *tree = GTK_TREE_MODEL(store);
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_first(tree, &iter))
+ del_window_helper(window, &iter);
+}
+
+static void
+handle_property(xcb_property_notify_event_t *event)
+{
+ update(event->window, event->atom);
+}
+
+static void
+handle_create(xcb_create_notify_event_t *event)
+{
+ add_window(event->window);
+}
+
+static void
+handle_destroy(xcb_destroy_notify_event_t *event)
+{
+ del_window(event->window);
+}
+
+static gboolean
+handle_event(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ xcb_generic_event_t *event;
+
+ while ((event = xcb_poll_for_event(conn))) {
+ switch (event->response_type) {
+ case XCB_PROPERTY_NOTIFY:
+ handle_property((xcb_property_notify_event_t *)event);
+ break;
+
+ case XCB_CREATE_NOTIFY:
+ handle_create((xcb_create_notify_event_t *)event);
+ break;
+
+ case XCB_DESTROY_NOTIFY:
+ handle_destroy((xcb_destroy_notify_event_t *)event);
+ break;
+
+ case XCB_MAP_NOTIFY:
+ case XCB_UNMAP_NOTIFY:
+ case XCB_UNMAP_NOTIFY | 0x80:
+ case XCB_CONFIGURE_NOTIFY:
+ case XCB_CONFIGURE_NOTIFY | 0x80:
+ case XCB_REPARENT_NOTIFY:
+ case XCB_MAPPING_NOTIFY:
+ case XCB_CLIENT_MESSAGE:
+ case XCB_CLIENT_MESSAGE | 0x80:
+ /* do nothing */
+ break;
+
+ default:
+ fprintf(stderr, "Monitor: %d event\n", event->response_type);
+ break;
+ }
+ free(event);
+ xcb_flush(conn);
+ }
+
+ return TRUE;
+}
+
+int
+prop_monitor_init(struct thread_data *data)
+{
+ int fd;
+ GIOChannel *channel;
+
+ store = GTK_TREE_STORE(data->store);
+
+ setup_x(data->display);
+
+ fd = xcb_get_file_descriptor(conn);
+ channel = g_io_channel_unix_new(fd);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_add_watch(channel, G_IO_IN|G_IO_ERR|G_IO_HUP, handle_event, NULL);
+
+ add_windows();
+ handle_event(channel, 0, NULL);
+
+ return 0;
+}