|
23 | 23 | #include <X11/Xatom.h>
|
24 | 24 | #include <X11/Xproto.h>
|
25 | 25 |
|
| 26 | +#include <sys/socket.h> |
| 27 | +#include <sys/un.h> |
| 28 | +#include <fcntl.h> |
26 | 29 | #include <string.h>
|
| 30 | +#include <unistd.h> |
| 31 | +#include <err.h> |
| 32 | +#include <errno.h> |
27 | 33 |
|
28 | 34 | #include "sdorfehs.h"
|
29 | 35 |
|
30 |
| -/* Sending commands to us from another process */ |
31 |
| -static int |
32 |
| -receive_command_result(Window w) |
| 36 | +void |
| 37 | +init_control_socket_path(void) |
33 | 38 | {
|
34 |
| - int query; |
35 |
| - int return_status = RET_FAILURE; |
36 |
| - Atom type_ret; |
37 |
| - int format_ret; |
38 |
| - unsigned long nitems; |
39 |
| - unsigned long bytes_after; |
40 |
| - unsigned char *result = NULL; |
41 |
| - |
42 |
| - /* First, find out how big the property is. */ |
43 |
| - query = XGetWindowProperty(dpy, w, rp_command_result, |
44 |
| - 0, 0, False, xa_string, |
45 |
| - &type_ret, &format_ret, &nitems, &bytes_after, |
46 |
| - &result); |
47 |
| - |
48 |
| - /* Failed to retrieve property. */ |
49 |
| - if (query != Success || result == NULL) { |
50 |
| - PRINT_DEBUG(("failed to get command result length\n")); |
51 |
| - return return_status; |
52 |
| - } |
53 |
| - /* |
54 |
| - * XGetWindowProperty always allocates one extra byte even if the |
55 |
| - * property is zero length. |
56 |
| - */ |
57 |
| - XFree(result); |
58 |
| - |
59 |
| - /* |
60 |
| - * Now that we have the length of the message, we can get the whole |
61 |
| - * message. |
62 |
| - */ |
63 |
| - query = XGetWindowProperty(dpy, w, rp_command_result, |
64 |
| - 0, (bytes_after / 4) + (bytes_after % 4 ? 1 : 0), |
65 |
| - True, xa_string, &type_ret, &format_ret, &nitems, |
66 |
| - &bytes_after, &result); |
67 |
| - |
68 |
| - /* Failed to retrieve property. */ |
69 |
| - if (query != Success || result == NULL) { |
70 |
| - PRINT_DEBUG(("failed to get command result\n")); |
71 |
| - return return_status; |
72 |
| - } |
73 |
| - /* |
74 |
| - * We can receive: |
75 |
| - * - an empty string, indicating a success but no output |
76 |
| - * - a string starting with '1', indicating a success and an output |
77 |
| - * - a string starting with '0', indicating a failure and an optional |
78 |
| - * output |
79 |
| - */ |
80 |
| - switch (result[0]) { |
81 |
| - case '\0': |
82 |
| - /* Command succeeded but no string to print */ |
83 |
| - return_status = RET_SUCCESS; |
84 |
| - break; |
85 |
| - case '0': |
86 |
| - /* Command failed, don't print an empty line if no explanation |
87 |
| - * was given */ |
88 |
| - if (result[1] != '\0') |
89 |
| - fprintf(stderr, "%s\n", &result[1]); |
90 |
| - return_status = RET_FAILURE; |
91 |
| - break; |
92 |
| - case '1': |
93 |
| - /* Command succeeded, print the output */ |
94 |
| - printf("%s\n", &result[1]); |
95 |
| - return_status = RET_SUCCESS; |
96 |
| - break; |
97 |
| - default: |
98 |
| - /* We probably got junk, so ignore it */ |
99 |
| - return_status = RET_FAILURE; |
100 |
| - } |
| 39 | + char *config_dir; |
101 | 40 |
|
102 |
| - /* Free the result. */ |
103 |
| - XFree(result); |
104 |
| - |
105 |
| - return return_status; |
| 41 | + config_dir = get_config_dir(); |
| 42 | + rp_glob_screen.control_socket_path = xsprintf("%s/control", config_dir); |
| 43 | + free(config_dir); |
106 | 44 | }
|
107 | 45 |
|
108 |
| -int |
109 |
| -send_command(unsigned char interactive, unsigned char *cmd) |
| 46 | +void |
| 47 | +listen_for_commands(void) |
110 | 48 | {
|
111 |
| - Window w, root; |
112 |
| - int done = 0, return_status = RET_FAILURE; |
113 |
| - struct sbuf *s; |
| 49 | + struct sockaddr_un sun; |
| 50 | + |
| 51 | + if ((rp_glob_screen.control_socket_fd = socket(AF_UNIX, |
| 52 | + SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) |
| 53 | + err(1, "socket"); |
114 | 54 |
|
115 |
| - s = sbuf_new(0); |
116 |
| - sbuf_printf(s, "%c%s", interactive, cmd); |
| 55 | + sun.sun_family = AF_UNIX; |
| 56 | + if (strlcpy(sun.sun_path, rp_glob_screen.control_socket_path, |
| 57 | + sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) |
| 58 | + err(1, "control socket path too long: %s", |
| 59 | + rp_glob_screen.control_socket_path); |
| 60 | + |
| 61 | + if (unlink(rp_glob_screen.control_socket_path) == -1 && |
| 62 | + errno != ENOENT) |
| 63 | + err(1, "unlink %s",rp_glob_screen.control_socket_path); |
| 64 | + |
| 65 | + if (bind(rp_glob_screen.control_socket_fd, (struct sockaddr *)&sun, |
| 66 | + sizeof(sun)) == -1) |
| 67 | + err(1, "bind %s", rp_glob_screen.control_socket_path); |
| 68 | + |
| 69 | + if (chmod(rp_glob_screen.control_socket_path, 0600) == -1) |
| 70 | + err(1, "chmod %s", rp_glob_screen.control_socket_path); |
| 71 | + |
| 72 | + if (listen(rp_glob_screen.control_socket_fd, 2) == -1) |
| 73 | + err(1, "listen %s", rp_glob_screen.control_socket_path); |
| 74 | + |
| 75 | + PRINT_DEBUG(("listening for commands at %s\n", |
| 76 | + rp_glob_screen.control_socket_path)); |
| 77 | +} |
117 | 78 |
|
118 |
| - root = RootWindow(dpy, DefaultScreen(dpy)); |
119 |
| - w = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); |
| 79 | +int |
| 80 | +send_command(int interactive, unsigned char *cmd) |
| 81 | +{ |
| 82 | + struct sockaddr_un sun; |
| 83 | + char *wcmd; |
| 84 | + char ret[1024]; |
| 85 | + size_t len; |
| 86 | + int fd; |
| 87 | + |
| 88 | + len = 1 + strlen(cmd) + 2; |
| 89 | + wcmd = malloc(len); |
| 90 | + if (snprintf(wcmd, len, "%c%s\n", interactive, cmd) != (len - 1)) |
| 91 | + errx(1, "snprintf"); |
| 92 | + |
| 93 | + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) |
| 94 | + err(1, "socket"); |
| 95 | + |
| 96 | + sun.sun_family = AF_UNIX; |
| 97 | + if (strlcpy(sun.sun_path, rp_glob_screen.control_socket_path, |
| 98 | + sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) |
| 99 | + err(1, "control socket path too long: %s", |
| 100 | + rp_glob_screen.control_socket_path); |
| 101 | + |
| 102 | + if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) |
| 103 | + err(1, "failed to connect to control socket at %s", |
| 104 | + rp_glob_screen.control_socket_path); |
| 105 | + |
| 106 | + if (write(fd, wcmd, len) != len) |
| 107 | + err(1, "short write to control socket"); |
| 108 | + |
| 109 | + free(wcmd); |
| 110 | + |
| 111 | + len = read(fd, &ret, sizeof(ret) - 1); |
| 112 | + if (len > 2) { |
| 113 | + ret[len - 1] = '\0'; |
| 114 | + fprintf(stderr, "%s\n", &ret[1]); |
| 115 | + } |
120 | 116 |
|
121 |
| - /* Select first to avoid race condition */ |
122 |
| - XSelectInput(dpy, w, PropertyChangeMask); |
| 117 | + return ret[0]; |
| 118 | +} |
123 | 119 |
|
124 |
| - XChangeProperty(dpy, w, rp_command, xa_string, 8, PropModeReplace, |
125 |
| - (unsigned char *)sbuf_get(s), strlen((char *) cmd) + 2); |
| 120 | +void |
| 121 | +receive_command(void) |
| 122 | +{ |
| 123 | + cmdret *cmd_ret; |
| 124 | + char cmd[1024] = { 0 }, c; |
| 125 | + char *result, *rcmd; |
| 126 | + int cl, len = 0, interactive = 0; |
126 | 127 |
|
127 |
| - XChangeProperty(dpy, root, rp_command_request, XA_WINDOW, |
128 |
| - 8, PropModeAppend, (unsigned char *) &w, sizeof(Window)); |
| 128 | + PRINT_DEBUG(("have connection waiting on command socket\n")); |
129 | 129 |
|
130 |
| - sbuf_free(s); |
| 130 | + if ((cl = accept(rp_glob_screen.control_socket_fd, NULL, NULL)) == -1) { |
| 131 | + warn("accept"); |
| 132 | + return; |
| 133 | + } |
131 | 134 |
|
132 |
| - while (!done) { |
133 |
| - XEvent ev; |
| 135 | + while (len <= sizeof(cmd)) { |
| 136 | + if (len == sizeof(cmd)) { |
| 137 | + warn("%s: bogus command length", __func__); |
| 138 | + close(cl); |
| 139 | + return; |
| 140 | + } |
134 | 141 |
|
135 |
| - XMaskEvent(dpy, PropertyChangeMask, &ev); |
136 |
| - if (ev.xproperty.atom == rp_command_result |
137 |
| - && ev.xproperty.state == PropertyNewValue) { |
138 |
| - return_status = |
139 |
| - receive_command_result(ev.xproperty.window); |
140 |
| - done = 1; |
| 142 | + if (read(cl, &c, 1) == 1) { |
| 143 | + if (c == '\n') { |
| 144 | + cmd[len++] = '\0'; |
| 145 | + break; |
| 146 | + } |
| 147 | + cmd[len++] = c; |
| 148 | + } else if (errno != EAGAIN) { |
| 149 | + PRINT_DEBUG(("bad read result on control socket: %s\n", |
| 150 | + strerror(errno))); |
| 151 | + break; |
141 | 152 | }
|
142 | 153 | }
|
143 | 154 |
|
144 |
| - XDestroyWindow(dpy, w); |
| 155 | + interactive = cmd[0]; |
| 156 | + rcmd = cmd + 1; |
| 157 | + |
| 158 | + PRINT_DEBUG(("read %d byte(s) on command socket: %s\n", len, rcmd)); |
| 159 | + |
| 160 | + cmd_ret = command(interactive, rcmd); |
| 161 | + |
| 162 | + /* notify the client of any text that was returned by the command */ |
| 163 | + len = 2; |
| 164 | + if (cmd_ret->output) { |
| 165 | + result = xsprintf("%c%s\n", cmd_ret->success ? 1 : 0, |
| 166 | + cmd_ret->output); |
| 167 | + len = 1 + strlen(cmd_ret->output) + 1; |
| 168 | + } else if (cmd_ret->success) |
| 169 | + result = xsprintf("%c\n", 1); |
| 170 | + else |
| 171 | + result = xsprintf("%c\n", 0); |
| 172 | + |
| 173 | + cmdret_free(cmd_ret); |
| 174 | + |
| 175 | + PRINT_DEBUG(("writing back %d to command client: %s", len, result + 1)); |
145 | 176 |
|
146 |
| - return return_status; |
| 177 | + write(cl, result, len); |
| 178 | + close(cl); |
147 | 179 | }
|
0 commit comments