1#include <stdio.h>
2#include <errno.h>
3#include <string.h>
4#include <libgen.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <fcntl.h>
8#include <poll.h>
9#include <signal.h>
10
11#include <sys/types.h>
12#include <sys/wait.h>
13#include <X11/Xlib.h>
14
15#define die(msg) do { perror(msg); \
16 exit(EXIT_FAILURE); } while (0)
17
18static void
19chld(int num, siginfo_t *info, void *ctx)
20{
21 (void)num;
22 (void)ctx;
23 int wstatus;
24
25 if (waitpid(info->si_pid, &wstatus, WNOHANG) > 0)
26 exit(WEXITSTATUS(wstatus));
27}
28
29static void
30evloop(int fd, pid_t chld)
31{
32 struct pollfd fds[1];
33
34 fds[0] = (struct pollfd) {
35 .fd = fd,
36 .events = POLLIN|POLLERR|POLLHUP,
37 };
38
39 for (;;) {
40 if (poll(fds, 1, -1) == -1) {
41 if (errno == EINTR)
42 continue;
43
44 if (kill(chld, SIGTERM) == -1)
45 die("kill failed after poll failed");
46 die("poll failed");
47 }
48
49 /* From XSelectInput(3):
50 * The XSelectInput function requests that the X
51 * server report the events associated with the
52 * specified event mask. Initially, X will not report
53 * any of these events.
54 *
55 * Thus we should never receive new data on the file
56 * descriptor. If poll(3) returns the socket was most
57 * likely closed by the X server and we can terminate
58 * our program.
59 */
60 if (kill(chld, SIGHUP) == -1)
61 die("kill failed");
62 exit(EXIT_SUCCESS);
63 }
64}
65
66int
67main(int argc, char **argv)
68{
69 int fd;
70 Display *dpy;
71 pid_t pid;
72 struct sigaction sa;
73
74 if (argc < 2) {
75 fprintf(stderr, "Usage: %s COMMAND ...\n", basename(argv[0]));
76 return EXIT_FAILURE;
77 }
78
79 sa.sa_sigaction = chld;
80 sa.sa_flags = SA_SIGINFO | SA_RESTART;
81 if (sigemptyset(&sa.sa_mask) == -1)
82 die("sigemptyset failed");
83 if (sigaction(SIGCHLD, &sa, NULL) == -1)
84 die("sigaction failed");
85
86 if (!(dpy = XOpenDisplay(NULL))) {
87 fprintf(stderr, "Couldn't open display '%s'\n", XDisplayName(NULL));
88 return EXIT_FAILURE;
89 }
90
91 if ((fd = XConnectionNumber(dpy)) == -1) {
92 fprintf(stderr, "Couldn't obtain fd for X connection\n");
93 return EXIT_FAILURE;
94 }
95
96 if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
97 die("fcntl failed");
98
99 switch ((pid = fork())) {
100 case -1:
101 die("fork failed");
102 case 0:
103 memmove(&argv[0], &argv[1], --argc * sizeof(char*));
104 argv[argc] = (char*)NULL;
105
106 if (execvp(argv[0], argv) == -1)
107 die("execvp failed");
108 break;
109 default:
110 evloop(fd, pid);
111 break;
112 }
113
114 return EXIT_SUCCESS;
115}