support generic scrollbuffer implementations

This commit is contained in:
Leon M. Busch-George 2026-03-01 11:39:20 +01:00
parent 8c32909a15
commit 185eb4bf0d
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 .It Fl f
Force creation of session when there is an already terminated session of the same name, Force creation of session when there is an already terminated session of the same name,
after showing its exit status. 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 .It Fl l
Attach with the lowest priority, meaning this client will be the last to control the size. Attach with the lowest priority, meaning this client will be the last to control the size.
.It Fl p .It Fl p

View File

@ -116,6 +116,7 @@ typedef struct {
const char *session_name; const char *session_name;
char host[255]; char host[255];
bool read_pty; bool read_pty;
char scrollback_sock[PATH_MAX];
} Server; } Server;
static Server server = { .running = true, .exit_status = -1, .host = "@localhost" }; static Server server = { .running = true, .exit_status = -1, .host = "@localhost" };
@ -223,7 +224,7 @@ static void die(const char *s) {
} }
static void usage(void) { 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); exit(EXIT_FAILURE);
} }
@ -604,9 +605,10 @@ int main(int argc, char *argv[]) {
} }
server.name = basename(argv[0]); server.name = basename(argv[0]);
server.scrollback_sock[0] = '\0';
gethostname(server.host+1, sizeof(server.host) - 1); 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) { switch (opt) {
case 'a': case 'a':
case 'A': case 'A':
@ -633,6 +635,18 @@ int main(int argc, char *argv[]) {
case 'r': case 'r':
client.flags |= CLIENT_READONLY; client.flags |= CLIENT_READONLY;
break; 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': case 'l':
client.flags |= CLIENT_LOWPRIORITY; client.flags |= CLIENT_LOWPRIORITY;
break; break;

View File

@ -136,6 +136,62 @@ static void server_sigterm_handler(int sig) {
exit(EXIT_FAILURE); /* invoke atexit handler */ 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) { static Client *server_accept_client(void) {
int newfd = accept(server.socket, NULL, NULL); int newfd = accept(server.socket, NULL, NULL);
if (newfd == -1 || server_set_socket_non_blocking(newfd) == -1) if (newfd == -1 || server_set_socket_non_blocking(newfd) == -1)
@ -157,6 +213,8 @@ static Client *server_accept_client(void) {
.u.l = getpid(), .u.l = getpid(),
}; };
server_send_packet(c, &pkt); server_send_packet(c, &pkt);
if (server.scrollback_sock[0])
server_print_scrollback(server.scrollback_sock, c);
return c; return c;
error: error: