A tiling window manager

communications: support arbitrary response sizes, not just 1k

This patch allows those of us with larger numbers of windows and use
"windows" format strings to print stuff to wrapper scripts, to continue
to use sdorfehs when switching from ratpoison.

Before this patch, client would read only a single up-to-1k bytes buffer
to get the entire response, resulting in truncation of the message
(windows list, etc) if it was longer than 1022 bytes. Ultimately this
was a consequence of retooling to a different IPC mechanism that
happened in a426e8b6a, the new code uses a single read of 1024 bytes to
get the response.

First thought was to just make the response buffer larger, but who knows
what uses people have? Can we predict the right size limit? This way it
can handle any sized response. Server is still the one controlling size
of result buffer, and fixed 1k is still used for the "request" buffer
sent to the server process.

Second thought was, since response already has "successfulness" as the
first byte of response (and we have to process it), we could declare an
"initial response" packet that would include some bytes that specified
the response buffer size, and then client would always make a second
second request, this time with an exact size it knew in advance. This
may have been a fine approach, but results in an extra round trip for
the common case of a single buffer. Thought a loop would be better...

So, instead we treat the first buffer specially, by continuing to strip
the successfulness response, but then also now arranging for subsequent
reads to be O_NONBLOCK. Presence of the first buffer means that
necessarily the server has prepared the full response, so we can keep
going until a blocked or closed connection to get the rest of it.

In testing, at response conclusion, sometimes the socket connection was
closed, and sometimes the end of the response would just block forever
waiting for another read. Was not able to tell what differentiates
these end-states, but also found that treating them identically (as
"command finished" signals) always seems to result in the right
behavior, so it might be ok not to know why...

authored by

Scott Mcdermott and committed by jcs.org a29862f0 99affcac

+67 -9
+67 -9
communications.c
··· 30 30 #include <unistd.h> 31 31 #include <err.h> 32 32 #include <errno.h> 33 + #include <stdlib.h> 33 34 34 35 #include "sdorfehs.h" 36 + 37 + #define BUFSZ 1024 35 38 36 39 void 37 40 init_control_socket_path(void) ··· 82 85 send_command(int interactive, unsigned char *cmd) 83 86 { 84 87 struct sockaddr_un sun; 85 - char *wcmd; 86 - char ret[1024]; 88 + char *wcmd, *bufstart; 89 + char ret[BUFSZ+1]; 90 + char success = 0; 87 91 size_t len; 88 - int fd; 92 + ssize_t count; 93 + int fd, firstloop; 94 + int flags = 0x0; 95 + FILE *outf = NULL; 96 + 97 + #ifdef DEBUG 98 + pid_t pid = getpid(); 99 + warnx("send_command_%d: enter", pid); 100 + #endif 89 101 90 102 len = 1 + strlen((char *)cmd) + 2; 91 103 wcmd = malloc(len); ··· 112 124 113 125 free(wcmd); 114 126 115 - len = read(fd, &ret, sizeof(ret) - 1); 116 - if (len > 2) { 117 - ret[len - 1] = '\0'; 118 - fprintf(ret[0] ? stdout : stderr, "%s\n", &ret[1]); 127 + firstloop = 1; 128 + while ((count = recv(fd, &ret, BUFSZ, flags))) { 129 + bufstart = ret; 130 + if (firstloop) { 131 + #ifdef DEBUG 132 + warnx("send_command_%d: first receive, count %zu", 133 + pid, count); 134 + #endif 135 + /* first byte is exit status */ 136 + success = *ret; 137 + outf = success ? stdout : stderr; 138 + bufstart++; 139 + if (count == 2 && *bufstart == '\n') 140 + /* commands that had no output */ 141 + return success; 142 + /* 143 + * after blocking for the first buffer, we can keep 144 + * reading until it blocks again, which should exhaust 145 + * the response. don't want to block when the message 146 + * is finished: sometimes connection is closed, other 147 + * times it blocks, not sure why? both end a response 148 + */ 149 + flags += MSG_DONTWAIT; 150 + } 151 + if (count == -1) { 152 + #ifdef DEBUG 153 + char *e = strerror(errno); 154 + #endif 155 + if (errno == EAGAIN || errno == ECONNRESET) { 156 + #ifdef DEBUG 157 + warnx("send_command_%d: finish: %s", pid, e); 158 + #endif 159 + return success; 160 + } 161 + #ifdef DEBUG 162 + warnx("send_command_%d: recvfail, err: %s", pid, e); 163 + #endif 164 + } 165 + ret[count] = '\0'; 166 + fprintf(outf, "%s", bufstart); 167 + fflush(outf); 168 + firstloop = 0; 169 + #ifdef DEBUG 170 + warnx("send_command_%d: looping", pid); 171 + #endif 119 172 } 173 + #ifdef DEBUG 174 + warnx("send_command_%d: no more bytes", pid); 175 + #endif 120 176 121 - return ret[0]; 177 + return success; 122 178 } 123 179 124 180 void 125 181 receive_command(void) 126 182 { 127 183 cmdret *cmd_ret; 128 - char cmd[1024] = { 0 }, c; 184 + char cmd[BUFSZ] = { 0 }, c; 129 185 char *result, *rcmd; 130 186 int cl, len = 0, interactive = 0; 131 187 ··· 180 236 181 237 if (write(cl, result, len) != len) 182 238 warn("%s: short write", __func__); 239 + 240 + PRINT_DEBUG(("receive_command: write finished, closing\n")); 183 241 184 242 close(cl); 185 243 }