Skip to content

Commit

Permalink
communications: make control socket writes recover from interruption
Browse files Browse the repository at this point in the history
Both send_message() and receive_message() use writes: the former to send
to the latter, and then a reply is sent back from the latter to the
former.  Both of them expected a single write to work unconditionally
without being able to tolerate any transient issues.

This commit factors out the writes into a common function called
send_unix() which serves as the companion to the recently added
recv_unix().  The send call is modified to handle both partial writes
and interrupted writes.  Especially the latter is not that unlikely: it
can happen when we're in the middle of writing a message and then we get
interrupted by a SIGCHLD.  This is not a difficult thing to happen
during the ordinary course of operations because the native 'exec' is
asynchronous.  It was observed to happen during testing.

Partial writes might also be possible if some of the buffer was sent and
then got interrupted by a signal, but this probably can't happen at
the small BUFSZ we use.  Nonetheless it will make the code more robust
to support the possibility of partial writes.

Any error besides EINTR is considered a fatal error for send_message()
because this is only called by a "client" invocation, ie "sdorfehs -c".
The "server" (ie receve_message()) only issues a warning and then aborts
processing the signal.  Otherwise, it would die.  This matches the
existing use of err() and warn() that was used after a mismatch in
written and wrote buffer sizes in the respective writers, prior to this
patch.
  • Loading branch information
smemsh authored and jcs committed Aug 29, 2024
1 parent 272c450 commit 336e5ab
Showing 1 changed file with 26 additions and 4 deletions.
30 changes: 26 additions & 4 deletions communications.c
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,28 @@ recv_unix(int fd, char **callerbuf)
return len;
}

static ssize_t
send_unix(int fd, char *buf, ssize_t sz)
{
ssize_t ret, off = 0;

WARNX_DEBUG("entered send_unix with sz %zd\n", sz);

while (sz > 0) {
if (((ret = write(fd, buf + off, sz)) != sz) && ret == -1) {
if (errno == EINTR)
continue;
warn("send_unix: bad write");
break;
}
sz -= ret;
off += ret;
}

WARNX_DEBUG("leaving send_unix, off %zd, errno %d\n", off, errno);
return off;
}

int
send_command(int interactive, char *cmd)
{
Expand Down Expand Up @@ -198,8 +220,8 @@ send_command(int interactive, char *cmd)
err(1, "failed to connect to control socket at %s",
rp_glob_screen.control_socket_path);

if (write(fd, wcmd, len) != len)
err(1, "short write to control socket");
if (send_unix(fd, wcmd, len) != len)
err(1, "%s: aborting after bad write", __func__);

free(wcmd);

Expand Down Expand Up @@ -277,8 +299,8 @@ receive_command(void)

PRINT_DEBUG(("writing back %d to command client: %s", len, result + 1));

if (write(cl, result, len) != len)
warn("%s: short write", __func__);
if (send_unix(cl, result, len) != len)
warnx("%s: proceeding after bad write", __func__);

PRINT_DEBUG(("receive_command: write finished, closing\n"));
done:
Expand Down

0 comments on commit 336e5ab

Please sign in to comment.