A tiling window manager
at master 314 lines 7.7 kB view raw
1/* 2 * communications.c -- Send commands to a running copy of sdorfehs. 3 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the Free 7 * Software Foundation; either version 2 of the License, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 * 15 * You should have received a copy of the GNU General Public License along with 16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple 17 * Place, Suite 330, Boston, MA 02111-1307 USA. 18 */ 19 20#include <X11/X.h> 21#include <X11/Xlib.h> 22#include <X11/Xutil.h> 23#include <X11/Xatom.h> 24#include <X11/Xproto.h> 25 26#include <sys/socket.h> 27#include <sys/un.h> 28#include <fcntl.h> 29#include <string.h> 30#include <unistd.h> 31#include <err.h> 32#include <errno.h> 33#include <stdlib.h> 34 35#include "sdorfehs.h" 36 37#define BUFSZ 1024 38 39void 40init_control_socket_path(void) 41{ 42 char *config_dir; 43 44 config_dir = get_config_dir(); 45 rp_glob_screen.control_socket_path = xsprintf("%s/control", config_dir); 46 free(config_dir); 47} 48 49void 50listen_for_commands(void) 51{ 52 struct sockaddr_un sun; 53 54 if ((rp_glob_screen.control_socket_fd = socket(AF_UNIX, 55 SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)) == -1) 56 err(1, "socket"); 57 58 if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path)) 59 err(1, "control socket path too long: %s", 60 rp_glob_screen.control_socket_path); 61 62 strncpy(sun.sun_path, rp_glob_screen.control_socket_path, 63 sizeof(sun.sun_path)-1); 64 sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; 65 sun.sun_family = AF_UNIX; 66 67 if (unlink(rp_glob_screen.control_socket_path) == -1 && 68 errno != ENOENT) 69 err(1, "unlink %s",rp_glob_screen.control_socket_path); 70 71 if (bind(rp_glob_screen.control_socket_fd, (struct sockaddr *)&sun, 72 sizeof(sun)) == -1) 73 err(1, "bind %s", rp_glob_screen.control_socket_path); 74 75 if (chmod(rp_glob_screen.control_socket_path, 0600) == -1) 76 err(1, "chmod %s", rp_glob_screen.control_socket_path); 77 78 if (listen(rp_glob_screen.control_socket_fd, 2) == -1) 79 err(1, "listen %s", rp_glob_screen.control_socket_path); 80 81 PRINT_DEBUG(("listening for commands at %s\n", 82 rp_glob_screen.control_socket_path)); 83} 84 85static ssize_t 86recv_unix(int fd, char **callerbuf) 87{ 88 int firstloop; 89 char *message; 90 ssize_t len, count; 91 92 int flags = 0x0; 93 int retries = 0; 94 95#ifdef SENDCMD_DEBUG 96 pid_t pid = getpid(); 97 char *dpfx = xsprintf("recv_unix_%d", pid); 98#endif 99 WARNX_DEBUG("%s: enter\n", dpfx); 100 101 message = xmalloc(BUFSZ); 102 memset(message, 0, BUFSZ); 103 104 len = 0; 105 firstloop = 1; 106 107 while ((count = recv(fd, message + len, BUFSZ, flags))) { 108 if (count == -1) { 109 switch (errno) { 110 /* 111 * message is complete 112 */ 113 case ECONNRESET: /* sender finished and closed */ 114 case EAGAIN: /* no more left to read */ 115 WARNX_DEBUG("%s: done: e%d\n", dpfx, errno); 116 break; 117 /* 118 * transient conditions for retrying 119 */ 120 case ECONNREFUSED: 121 case ENOMEM: 122 if (retries++ >= 5) { 123 warn("recv_unix: retries exhausted"); 124 len = -1; 125 break; 126 } 127 usleep(200); 128 /* fallthrough */ 129 case EINTR: 130 warn("recv_unix: trying again"); 131 continue; 132 /* 133 * usage error or untenable situation: 134 * EBADF, EFAULT, EINVAL, ENOTCONN, ENOTSOCK 135 */ 136 default: 137 warn("unanticipated receive error"); 138 len = -1; 139 } 140 break; 141 } 142 if (firstloop) { 143 WARNX_DEBUG("%s: first recv: %zd\n", dpfx, count); 144 /* 145 * after blocking for the first buffer, we can 146 * keep reading until it blocks again, which 147 * should exhaust the message. 148 */ 149 flags += MSG_DONTWAIT; 150 } 151 len += count; 152 message = xrealloc(message, len + BUFSZ); 153 memset(message + len, 0, BUFSZ); 154 WARNX_DEBUG("%s: looping after count %zd\n", dpfx, count); 155 firstloop = 0; 156 } 157#ifdef SENDCMD_DEBUG 158 free(dpfx); 159#endif 160 *callerbuf = message; 161 return len; 162} 163 164static ssize_t 165send_unix(int fd, char *buf, ssize_t sz) 166{ 167 ssize_t ret, off = 0; 168 169 WARNX_DEBUG("entered send_unix with sz %zd\n", sz); 170 171 while (sz > 0) { 172 if (((ret = write(fd, buf + off, sz)) != sz) && ret == -1) { 173 if (errno == EINTR) 174 continue; 175 warn("send_unix: bad write"); 176 break; 177 } 178 sz -= ret; 179 off += ret; 180 } 181 182 WARNX_DEBUG("leaving send_unix, off %zd, errno %d\n", off, errno); 183 return off; 184} 185 186int 187send_command(int interactive, char *cmd) 188{ 189 struct sockaddr_un sun; 190 char *wcmd, *response; 191 char success = 0; 192 ssize_t len; 193 int fd; 194 FILE *outf = NULL; 195 196#ifdef SENDCMD_DEBUG 197 pid_t pid = getpid(); 198 char *dpfx = xsprintf("send_command_%d", pid); 199#endif 200 WARNX_DEBUG("%s: enter\n", dpfx); 201 202 len = 1 + strlen(cmd) + 1; 203 wcmd = xmalloc(len); 204 *wcmd = (unsigned char)interactive; 205 strncpy(wcmd + 1, cmd, len - 1); 206 207 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 208 err(1, "socket"); 209 210 if (strlen(rp_glob_screen.control_socket_path) >= sizeof(sun.sun_path)) 211 err(1, "control socket path too long: %s", 212 rp_glob_screen.control_socket_path); 213 214 strncpy(sun.sun_path, rp_glob_screen.control_socket_path, 215 sizeof(sun.sun_path)-1); 216 sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; 217 sun.sun_family = AF_UNIX; 218 219 if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) 220 err(1, "failed to connect to control socket at %s", 221 rp_glob_screen.control_socket_path); 222 223 if (send_unix(fd, wcmd, len) != len) 224 err(1, "%s: aborting after bad write", __func__); 225 226 free(wcmd); 227 228 if ((len = recv_unix(fd, &response)) == -1) 229 warnx("send_message: aborted reply from receiver"); 230 231 /* first byte is exit status */ 232 success = *response; 233 outf = success ? stdout : stderr; 234 235 if (len > 1) { 236 /* command had some output */ 237 if (response[len] != '\0') { 238 /* should not be possible, TODO remove */ 239 warnx("%s\n", "last byte of response not null"); 240 response[len] = '\0'; 241 } 242 fprintf(outf, "%s", response + 1); 243 fflush(outf); 244 } 245 free(response); 246 247 WARNX_DEBUG("%s: no more bytes\n", dpfx); 248#ifdef SENDCMD_DEBUG 249 free(dpfx); 250#endif 251 252 return success; 253} 254 255void 256receive_command(void) 257{ 258 cmdret *cmd_ret; 259 char *result, *rcmd, *cmd; 260 int cl, len = 0, interactive = 0; 261 262 PRINT_DEBUG(("have connection waiting on command socket\n")); 263 264 if ((cl = accept(rp_glob_screen.control_socket_fd, NULL, NULL)) == -1) { 265 warn("accept"); 266 return; 267 } 268 if ((fcntl(cl, F_SETFD, FD_CLOEXEC)) == -1) { 269 warn("cloexec"); 270 close(cl); 271 return; 272 } 273 274 if ((len = recv_unix(cl, &cmd)) <= 1) { 275 warnx("receive_command: %s\n", 276 (len == -1 ? "encountered error during receive" 277 : "received command was malformed")); 278 goto done; 279 } 280 if (cmd[len] != '\0') { 281 /* should not be possible, TODO remove */ 282 warnx("%s\n", "last byte of sent command not null"); 283 cmd[len] = '\0'; 284 } 285 interactive = cmd[0]; 286 rcmd = cmd + 1; 287 288 PRINT_DEBUG(("read %d byte(s) on command socket: %s\n", len, rcmd)); 289 290 cmd_ret = command(interactive, rcmd); 291 292 /* notify the client of any text that was returned by the command */ 293 len = 2; 294 if (cmd_ret->output) { 295 result = xsprintf("%c%s", cmd_ret->success ? 1 : 0, 296 cmd_ret->output); 297 len = 1 + strlen(cmd_ret->output) + 1; 298 } else if (cmd_ret->success) 299 result = xsprintf("%c", 1); 300 else 301 result = xsprintf("%c", 0); 302 303 cmdret_free(cmd_ret); 304 305 PRINT_DEBUG(("writing back %d to command client: %s", len, result + 1)); 306 307 if (send_unix(cl, result, len) != len) 308 warnx("%s: proceeding after bad write", __func__); 309 310 PRINT_DEBUG(("receive_command: write finished, closing\n")); 311done: 312 free(cmd); 313 close(cl); 314}