This commit is contained in:
Leon Busch-George 2026-03-05 12:49:21 +00:00 committed by GitHub
commit ebe5474e32
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 78 additions and 2 deletions

View File

@ -102,6 +102,10 @@ which is specified as ^\\ i.e. Ctrl is represented as a caret
.It Fl f
Force creation of session when there is an already terminated session of the same name,
after showing its exit status.
.It Fl s Ar scrollback_fd_socket
A unix socket to send the writing end of a pipe through. This can be used for receiving the scrollback
buffer from when a new client attaches. Wait for the accepted socket to be closed before resuming
regular pass-through behavior.
.It Fl l
Attach with the lowest priority, meaning this client will be the last to control the size.
.It Fl p

View File

@ -116,6 +116,7 @@ typedef struct {
const char *session_name;
char host[255];
bool read_pty;
char scrollback_sock[PATH_MAX];
} Server;
static Server server = { .running = true, .exit_status = -1, .host = "@localhost" };
@ -223,7 +224,7 @@ static void die(const char *s) {
}
static void usage(void) {
fprintf(stderr, "usage: abduco [-a|-A|-c|-n] [-p] [-r] [-q] [-l] [-f] [-e detachkey] name command\n");
fprintf(stderr, "usage: abduco [-a|-A|-c|-n] [-p] [-r] [-q] [-l] [-f] [-e detachkey] [-s scrollback-sock] name command\n");
exit(EXIT_FAILURE);
}
@ -604,9 +605,10 @@ int main(int argc, char *argv[]) {
}
server.name = basename(argv[0]);
server.scrollback_sock[0] = '\0';
gethostname(server.host+1, sizeof(server.host) - 1);
while ((opt = getopt(argc, argv, "aAclne:fpqrv")) != -1) {
while ((opt = getopt(argc, argv, "aAclne:fpqrs:v")) != -1) {
switch (opt) {
case 'a':
case 'A':
@ -633,6 +635,18 @@ int main(int argc, char *argv[]) {
case 'r':
client.flags |= CLIENT_READONLY;
break;
case 's':
if (!realpath(".", server.scrollback_sock)) {
fprintf(stderr, "can't determine current directory: %s\n", strerror(errno));
return 2;
}
if (strlen(server.scrollback_sock) + strlen(optarg) + 2 > PATH_MAX) {
fprintf(stderr, "scrollback_sock path too long\n");
return 2;
}
strcat(server.scrollback_sock, "/");
strcat(server.scrollback_sock, optarg);
break;
case 'l':
client.flags |= CLIENT_LOWPRIORITY;
break;

View File

@ -136,6 +136,62 @@ static void server_sigterm_handler(int sig) {
exit(EXIT_FAILURE); /* invoke atexit handler */
}
static void server_print_scrollback(const char *scrollback_sock, Client *c) {
int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1) {
debug("failed to open unix socket: %s\n", strerror(errno));
return;
}
struct sockaddr_un addr = { .sun_family = AF_UNIX };
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", scrollback_sock);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
debug("failed to connect to %s: %s\n", scrollback_sock, strerror(errno));
goto cleanup;
}
char buf[1];
struct iovec iov = { .iov_base = &buf, .iov_len = sizeof(buf) };
char cmsg_buf[CMSG_SPACE(sizeof(int))];
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsg_buf,
.msg_controllen = sizeof(cmsg_buf)
};
int scrlb_fd = -1;
if (recvmsg(sock, &msg, 0) == -1)
goto cleanup;
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
if (cmsg->cmsg_level != SOL_SOCKET
|| cmsg->cmsg_type != SCM_RIGHTS
|| cmsg->cmsg_len != CMSG_LEN(sizeof(int)))
goto cleanup;
memcpy(&scrlb_fd, CMSG_DATA(cmsg), sizeof(int));
Packet pkt = { .type = MSG_CONTENT };
ssize_t rlen;
while ((rlen = read(scrlb_fd, pkt.u.msg, sizeof(pkt.u.msg))) > 0) {
pkt.len = rlen;
server_send_packet(c, &pkt);
}
// no further data is handled by the scrollback buffer until the unix
// socket is closed. this is the time to attach the client to receive
// any future data. since this code is blocking the server loop, we're
// done.
close(scrlb_fd);
cleanup:
close(sock);
}
static Client *server_accept_client(void) {
int newfd = accept(server.socket, NULL, NULL);
if (newfd == -1 || server_set_socket_non_blocking(newfd) == -1)
@ -157,6 +213,8 @@ static Client *server_accept_client(void) {
.u.l = getpid(),
};
server_send_packet(c, &pkt);
if (server.scrollback_sock[0])
server_print_scrollback(server.scrollback_sock, c);
return c;
error: