mirror of https://github.com/martanne/abduco
Add screen buffer to server
When a client attaches to an existing session, the session command output is not available to him. The problem is not noticeable when the program (top, man, etc.) in the session is able to redraw its output. But this does not work with non-interactive utilities. The output of commands in bash will not be available to the client connecting to the session. To solve this, we add a buffer that contains the last few lines of the session output. When a new client attaches, the contents of this buffer are sent to him. Signed-off-by: Alexey Gladkov <gladkov.alexey@gmail.com>
This commit is contained in:
parent
b5598d5dd1
commit
a9b345a24f
28
abduco.c
28
abduco.c
|
|
@ -37,6 +37,7 @@
|
|||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/queue.h>
|
||||
#if defined(__linux__) || defined(__CYGWIN__)
|
||||
# include <pty.h>
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
|
|
@ -102,6 +103,15 @@ struct Client {
|
|||
Client *next;
|
||||
};
|
||||
|
||||
struct entry {
|
||||
char *data;
|
||||
int len;
|
||||
bool complete;
|
||||
TAILQ_ENTRY(entry) entries;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(screenhead, entry);
|
||||
|
||||
typedef struct {
|
||||
Client *clients;
|
||||
int socket;
|
||||
|
|
@ -109,6 +119,8 @@ typedef struct {
|
|||
int exit_status;
|
||||
struct termios term;
|
||||
struct winsize winsize;
|
||||
struct screenhead screen;
|
||||
int screen_rows;
|
||||
pid_t pid;
|
||||
volatile sig_atomic_t running;
|
||||
const char *name;
|
||||
|
|
@ -117,10 +129,11 @@ typedef struct {
|
|||
bool read_pty;
|
||||
} Server;
|
||||
|
||||
static Server server = { .running = true, .exit_status = -1, .host = "@localhost" };
|
||||
static Server server = { .running = true, .exit_status = -1, .host = "@localhost", .screen_rows = 0 };
|
||||
static Client client;
|
||||
static struct termios orig_term, cur_term;
|
||||
static bool has_term, alternate_buffer, quiet, passthrough;
|
||||
static int screen_max_rows = 120;
|
||||
|
||||
static struct sockaddr_un sockaddr = {
|
||||
.sun_family = AF_UNIX,
|
||||
|
|
@ -222,7 +235,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] [-L num] name command\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
|
@ -605,7 +618,7 @@ int main(int argc, char *argv[]) {
|
|||
server.name = basename(argv[0]);
|
||||
gethostname(server.host+1, sizeof(server.host) - 1);
|
||||
|
||||
while ((opt = getopt(argc, argv, "aAclne:fpqrv")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "aAclne:fpqrvL:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
|
|
@ -635,6 +648,15 @@ int main(int argc, char *argv[]) {
|
|||
case 'l':
|
||||
client.flags |= CLIENT_LOWPRIORITY;
|
||||
break;
|
||||
case 'L':
|
||||
if (!optarg)
|
||||
usage();
|
||||
screen_max_rows = atoi(optarg);
|
||||
if (screen_max_rows < 0) {
|
||||
fputs("ERROR: a negative value for the number of rows is meaningless.\n", stderr);
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
puts("abduco-"VERSION" © 2013-2018 Marc André Tanner");
|
||||
exit(EXIT_SUCCESS);
|
||||
|
|
|
|||
105
server.c
105
server.c
|
|
@ -178,6 +178,93 @@ static void server_atexit_handler(void) {
|
|||
unlink(sockaddr.sun_path);
|
||||
}
|
||||
|
||||
static void server_send_screen_buffer(Client *c) {
|
||||
struct entry *np;
|
||||
|
||||
TAILQ_FOREACH_REVERSE(np, &server.screen, screenhead, entries) {
|
||||
Packet pkt = {
|
||||
.type = MSG_CONTENT,
|
||||
.len = np->len,
|
||||
};
|
||||
strncpy(pkt.u.msg, np->data, np->len);
|
||||
server_send_packet(c, &pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static void server_preserve_screen_data(Packet *pkt) {
|
||||
char *str, *end;
|
||||
uint32_t len;
|
||||
struct entry *scrline = NULL;
|
||||
|
||||
if (screen_max_rows == 0 || pkt->len <= 0 || pkt->type != MSG_CONTENT)
|
||||
return;
|
||||
|
||||
str = pkt->u.msg;
|
||||
len = pkt->len;
|
||||
end = str + len;
|
||||
|
||||
while (str != end) {
|
||||
char *data;
|
||||
uint32_t i, dlen;
|
||||
|
||||
bool newline = false;
|
||||
char *token = end;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (str[i] == '\n') {
|
||||
token = str + i + 1;
|
||||
newline = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dlen = token - str) <= 0)
|
||||
break;
|
||||
|
||||
scrline = TAILQ_FIRST(&server.screen);
|
||||
|
||||
if (scrline && !scrline->complete) {
|
||||
data = realloc(scrline->data, scrline->len + dlen);
|
||||
if (!data)
|
||||
die("unable to extend string in the screen buffer");
|
||||
|
||||
memcpy(data + scrline->len, str, dlen);
|
||||
|
||||
scrline->complete = newline;
|
||||
scrline->data = data;
|
||||
scrline->len += dlen;
|
||||
} else {
|
||||
data = malloc(dlen);
|
||||
if (!data)
|
||||
die("unable to allocate memory for new line in the screen buffer");
|
||||
|
||||
memcpy(data, str, dlen);
|
||||
|
||||
scrline = malloc(sizeof(*scrline));
|
||||
if (!scrline)
|
||||
die("unable to allocate memory for screen buffer element");
|
||||
|
||||
scrline->complete = newline;
|
||||
scrline->data = data;
|
||||
scrline->len = dlen;
|
||||
|
||||
TAILQ_INSERT_HEAD(&server.screen, scrline, entries);
|
||||
server.screen_rows++;
|
||||
|
||||
if (server.screen_rows > screen_max_rows) {
|
||||
scrline = TAILQ_LAST(&server.screen, screenhead);
|
||||
TAILQ_REMOVE(&server.screen, scrline, entries);
|
||||
free(scrline->data);
|
||||
free(scrline);
|
||||
server.screen_rows--;
|
||||
}
|
||||
}
|
||||
|
||||
str = token;
|
||||
len -= dlen;
|
||||
}
|
||||
}
|
||||
|
||||
static void server_mainloop(void) {
|
||||
atexit(server_atexit_handler);
|
||||
fd_set new_readfds, new_writefds;
|
||||
|
|
@ -187,6 +274,8 @@ static void server_mainloop(void) {
|
|||
int new_fdmax = server.socket;
|
||||
bool exit_packet_delivered = false;
|
||||
|
||||
TAILQ_INIT(&server.screen);
|
||||
|
||||
if (server.read_pty)
|
||||
FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
|
||||
|
||||
|
|
@ -213,8 +302,11 @@ static void server_mainloop(void) {
|
|||
if (FD_ISSET(server.socket, &readfds))
|
||||
server_accept_client();
|
||||
|
||||
if (FD_ISSET(server.pty, &readfds))
|
||||
if (FD_ISSET(server.pty, &readfds)) {
|
||||
pty_data = server_read_pty(&server_packet);
|
||||
if (pty_data)
|
||||
server_preserve_screen_data(&server_packet);
|
||||
}
|
||||
|
||||
for (Client **prev_next = &server.clients, *c = server.clients; c;) {
|
||||
if (FD_ISSET(c->socket, &readfds) && server_recv_packet(c, &client_packet)) {
|
||||
|
|
@ -226,6 +318,7 @@ static void server_mainloop(void) {
|
|||
c->flags = client_packet.u.i;
|
||||
if (c->flags & CLIENT_LOWPRIORITY)
|
||||
server_sink_client();
|
||||
server_send_screen_buffer(c);
|
||||
break;
|
||||
case MSG_RESIZE:
|
||||
c->state = STATE_ATTACHED;
|
||||
|
|
@ -291,5 +384,15 @@ static void server_mainloop(void) {
|
|||
FD_SET_MAX(server.pty, &new_readfds, new_fdmax);
|
||||
}
|
||||
|
||||
struct entry *n1, *n2;
|
||||
|
||||
n1 = TAILQ_FIRST(&server.screen);
|
||||
while (n1 != NULL) {
|
||||
n2 = TAILQ_NEXT(n1, entries);
|
||||
free(n1->data);
|
||||
free(n1);
|
||||
n1 = n2;
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue