A tiling window manager

send_command: get responses using dynamic heap buffer and process OOB

Rather than process one buffer at a time and do partial outputs each
loop on every partial response (sent to us from receive_command()), we
make a buffer on the heap and keep growing it as long as there's still
data coming in the response. So we get the whole message before
attempting to process it.

As before, we go until either the other end closes (as it does at the
end of the transmission), or there's no more data to read (ie, another
read would block). The latter happens when we received the whole
response buffer, but the receiver hasn't yet finished closing the client
socket. Either of these scenarios means we're at the end of the
message.

The reason for separating receipt and disposition is so that processing
of the response buffer's contents becomes independent, out-of-band from
filling the buffer itself. This way, we can make the message-receive
loop into a utility function of its own, and re-use it in
receive_command() so it can be made to process arbitrarily sized
send_command() messages; it's currently limited to a single BUFSZ buffer
and is read one character at a time. Already, we are *sending* the
complete command argument (eg, from "sdorfehs -c"), but it will get
truncated by receive_command() if it exceeds the buffer size on the
other side. (Actually, we try to send the whole thing in a single
write() and will bail the whole WM on failure, which we probably need to
fix as well...)

authored by

Scott Mcdermott and committed by jcs.org 5e7ba32f 0c6f4363

+40 -22
+40 -22
communications.c
··· 86 86 send_command(int interactive, char *cmd) 87 87 { 88 88 struct sockaddr_un sun; 89 - char *wcmd, *bufstart; 90 - char ret[BUFSZ+1]; 89 + char *wcmd, *response; 91 90 char success = 0; 92 91 size_t len; 93 92 ssize_t count; ··· 102 101 WARNX_DEBUG("%s: enter\n", dpfx); 103 102 104 103 len = 1 + strlen(cmd) + 2; 105 - wcmd = malloc(len); 104 + wcmd = xmalloc(len); 106 105 if (snprintf(wcmd, len, "%c%s\n", interactive, cmd) != (len - 1)) 107 106 errx(1, "snprintf"); 108 107 ··· 127 126 128 127 free(wcmd); 129 128 129 + response = xmalloc(BUFSZ); 130 + memset(response, 0, BUFSZ); 131 + len = 0; 130 132 firstloop = 1; 131 - while ((count = recv(fd, &ret, BUFSZ, flags))) { 132 - bufstart = ret; 133 + 134 + while ((count = recv(fd, response + len, BUFSZ, flags))) { 133 135 if (firstloop) { 134 136 WARNX_DEBUG("%s: first recv: %zu\n", dpfx, count); 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 137 /* 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 138 + * after blocking for the first buffer, we can 139 + * keep reading until it blocks again, which 140 + * should exhaust the message. 148 141 */ 149 142 flags += MSG_DONTWAIT; 150 143 } 151 144 if (count == -1) { 152 145 WARNX_DEBUG("%s: finish errno: %d\n", dpfx, errno); 146 + /* 147 + * message is complete. sometimes connection is 148 + * closed, other times it would block, depending 149 + * on whether responder finished before us. 150 + * either outcome signals end of the response. 151 + */ 153 152 if (errno == EAGAIN || errno == ECONNRESET) 154 - return success; 153 + break; 154 + else 155 + err(1, "unanticipated receive error"); 156 + } 157 + len += count; 158 + firstloop = 0; 159 + response = xrealloc(response, len + BUFSZ); 160 + memset(response + len, 0, BUFSZ); 161 + WARNX_DEBUG("%s: looping after count %zu\n", dpfx, count); 162 + } 163 + 164 + /* first byte is exit status */ 165 + success = *response; 166 + outf = success ? stdout : stderr; 167 + 168 + if (len > 1) { 169 + /* command had some output */ 170 + if (response[len] != '\0') { 171 + /* should not be possible, TODO remove */ 172 + warnx("%s\n", "last byte of response not null"); 173 + response[len] = '\0'; 155 174 } 156 - ret[count] = '\0'; 157 - fprintf(outf, "%s", bufstart); 175 + fprintf(outf, "%s", response + 1); 158 176 fflush(outf); 159 - firstloop = 0; 160 - WARNX_DEBUG("%s: looping\n", dpfx); 161 177 } 178 + free(response); 179 + 162 180 WARNX_DEBUG("%s: no more bytes\n", dpfx); 163 181 #ifdef SENDCMD_DEBUG 164 182 free(dpfx);