xhotplug

Run arbirary commands in response to newly connected X11 displays
git clone git://git.danielmoch.com/xhotplug.git
Log | Files | Refs | README | LICENSE

xhotplug.c (3485B)


      1 /* See LICENSE file for copyright and license details. */
      2 
      3 #include <errno.h>
      4 #include <libgen.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <sys/stat.h>
      9 #include <sys/syslimits.h>
     10 #include <unistd.h>
     11 #include <xcb/xcb.h>
     12 #include <xcb/randr.h>
     13 
     14 static char *argv0;
     15 
     16 enum {
     17 	ERR_NONE,
     18 	ERR_UNKNOWN_OPTION,
     19 	ERR_NUM_ARGS,
     20 	ERR_NOT_EXECUTABLE,
     21 	ERR_PLEDGE
     22 };
     23 
     24 void
     25 usage(const int e_val) {
     26 	printf("usage: %s [-hv] script\n", argv0);
     27 	exit(e_val);
     28 }
     29 
     30 void
     31 xhotplug(const char *cmd) {
     32 	xcb_connection_t* conn;
     33 	xcb_screen_t* screen;
     34 	xcb_window_t window;
     35 	xcb_generic_event_t* evt;
     36 	xcb_randr_screen_change_notify_event_t* randr_evt;
     37 	xcb_timestamp_t last_time;
     38 	int monitor_connected = 1;
     39 
     40 	conn = xcb_connect(NULL, NULL);
     41 
     42 	screen = xcb_setup_roots_iterator(xcb_get_setup(conn)).data;
     43 	window = screen->root;
     44 	xcb_randr_select_input(conn, window, XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
     45 	xcb_flush(conn);
     46 
     47 	while ((evt = xcb_wait_for_event(conn)) != NULL) {
     48 		if (evt->response_type & XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE) {
     49 			randr_evt = (xcb_randr_screen_change_notify_event_t*) evt;
     50 			if (last_time != randr_evt->timestamp) {
     51 				last_time = randr_evt->timestamp;
     52 				monitor_connected = !monitor_connected;
     53 				if (monitor_connected) {
     54 					system(cmd);
     55 				} else {
     56 					system(cmd);
     57 				}
     58 			}
     59 		}
     60 		free(evt);
     61 	}
     62 	xcb_disconnect(conn);
     63 }
     64 
     65 int
     66 can_execute(const char *path) {
     67 	uid_t uid = getuid();
     68 	gid_t gid = getgid();
     69 	gid_t gidset[NGROUPS_MAX];
     70 	struct stat sb;
     71 
     72 	if (stat(path, &sb) != 0) {
     73 		fprintf(stderr, "%s: ERROR -- Unable to stat file %s\n", argv0, path);
     74 		fprintf(stderr, "%s: errno -- %d\n", argv0, errno);
     75 		return 0;
     76 	}
     77 	if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
     78 		fprintf(stderr, "%s: script is writeable by others\n", argv0);
     79 		return 0;
     80 	}
     81 	if (sb.st_uid != uid && sb.st_uid != 0) {
     82 		fprintf(stderr, "%s: script not owned by user or root\n", argv0);
     83 		return 0;
     84 	}
     85 	if (sb.st_uid == uid && (sb.st_mode & S_IXUSR) == 0) {
     86 		fprintf(stderr, "%s: script is not executable\n", argv0);
     87 		return 0;
     88 	}
     89 	if (sb.st_uid == 0) {
     90 		for (int i=0; i<getgroups(NGROUPS_MAX, gidset); i++) {
     91 			if (gidset[i] == gid) {
     92 				if ((sb.st_mode & S_IXGRP) == 0) {
     93 					fprintf(stderr, "%s: script is not executable\n", argv0);
     94 					return 0;
     95 				}
     96 				else {
     97 					return 1;
     98 				}
     99 			}
    100 		}
    101 	}
    102 	return 1;
    103 }
    104 
    105 int
    106 main(int argc, char **argv) {
    107 	char ch, script[PATH_MAX], sbuf[PATH_MAX];
    108 	ssize_t sbuf_len;
    109 	int pval;
    110 
    111 	argv0 = basename(argv[0]);
    112 
    113 	while ((ch = getopt(argc, argv, "vh")) != -1) {
    114 		switch (ch) {
    115 		case 'v':
    116 			printf("%s %s\n", argv0, VERSION);
    117 			exit(ERR_NONE);
    118 			break;
    119 		case 'h':
    120 			usage(ERR_NONE);
    121 			break;
    122 		default:
    123 			usage(ERR_UNKNOWN_OPTION);
    124 		}
    125 	}
    126 	argc -= optind;
    127 	argv += optind;
    128 
    129 	if ( argc < 1 || argc > 1) {
    130 		printf("%s: invalid number of arguments\n", argv0);
    131 		usage(ERR_NUM_ARGS);
    132 	}
    133 
    134 	sbuf_len = strnlen(argv[0], PATH_MAX);
    135 	strncpy(script, argv[0], PATH_MAX - 1);
    136 	script[PATH_MAX - 1] = '\0';
    137 	while ((sbuf_len = readlink(script, sbuf, PATH_MAX)) != -1) {
    138 		sbuf[sbuf_len] = '\0';
    139 		strncpy(script, sbuf, PATH_MAX - 1);
    140 		script[PATH_MAX - 1] = '\0';
    141 	}
    142 
    143 	if(!can_execute(script)) {
    144 		fprintf(stderr, "%s: cannot run provided script. quitting.\n", argv0);
    145 		usage(ERR_NOT_EXECUTABLE);
    146 	}
    147 
    148 #ifdef __OpenBSD__
    149 	if ((pval = pledge("stdio unix rpath proc exec", NULL)) != 0) {
    150 		fprintf(stderr, "%s: call to pledge(2) failed -- %d. quitting.\n", argv0, pval);
    151 		exit(ERR_PLEDGE);
    152 	}
    153 #endif
    154 
    155 	xhotplug(script);
    156 }