diff options
author | Nicholas Marriott <nicholas.marriott@gmail.com> | 2021-03-02 12:08:34 +0000 |
---|---|---|
committer | Nicholas Marriott <nicholas.marriott@gmail.com> | 2021-03-02 12:08:34 +0000 |
commit | c01251d02388efceca515c47c257e2b5342e3716 (patch) | |
tree | ca89407cb43bd2caa40a6854ba49ce1720e892ce /client.c | |
parent | 5c275c2a1a963876d4ac392067e42120417dbf43 (diff) | |
parent | 1466b570eedda0423d5a386d2b16b7ff0c0e477c (diff) | |
download | rtmux-c01251d02388efceca515c47c257e2b5342e3716.tar.gz rtmux-c01251d02388efceca515c47c257e2b5342e3716.tar.bz2 rtmux-c01251d02388efceca515c47c257e2b5342e3716.zip |
Merge branch 'master' into 3.2-rc
Diffstat (limited to 'client.c')
-rw-r--r-- | client.c | 343 |
1 files changed, 58 insertions, 285 deletions
@@ -24,7 +24,6 @@ #include <sys/file.h> #include <errno.h> -#include <event.h> #include <fcntl.h> #include <signal.h> #include <stdlib.h> @@ -62,7 +61,8 @@ static __dead void client_exec(const char *,const char *); static int client_get_lock(char *); static int client_connect(struct event_base *, const char *, uint64_t); -static void client_send_identify(const char *, const char *, int); +static void client_send_identify(const char *, const char *, + char **, u_int, const char *, int); static void client_signal(int); static void client_dispatch(struct imsg *, void *); static void client_dispatch_attached(struct imsg *); @@ -127,6 +127,8 @@ retry: log_debug("connect failed: %s", strerror(errno)); if (errno != ECONNREFUSED && errno != ENOENT) goto failed; + if (flags & CLIENT_NOSTARTSERVER) + goto failed; if (~flags & CLIENT_STARTSERVER) goto failed; close(fd); @@ -221,20 +223,7 @@ client_exit_message(void) static void client_exit(void) { - struct client_file *cf; - size_t left; - int waiting = 0; - - RB_FOREACH (cf, client_files, &client_files) { - if (cf->event == NULL) - continue; - left = EVBUFFER_LENGTH(cf->event->output); - if (left != 0) { - waiting++; - log_debug("file %u %zu bytes left", cf->stream, left); - } - } - if (waiting == 0) + if (!file_write_left(&client_files)) proc_exit(client_proc); } @@ -246,13 +235,14 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, struct cmd_parse_result *pr; struct msg_command *data; int fd, i; - const char *ttynam, *cwd; + const char *ttynam, *termname, *cwd; pid_t ppid; enum msgtype msg; struct termios tio, saved_tio; size_t size, linesize = 0; ssize_t linelen; - char *line = NULL; + char *line = NULL, **caps = NULL, *cause; + u_int ncaps = 0; /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ signal(SIGCHLD, SIG_IGN); @@ -308,6 +298,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, cwd = "/"; if ((ttynam = ttyname(STDIN_FILENO)) == NULL) ttynam = ""; + if ((termname = getenv("TERM")) == NULL) + termname = ""; /* * Drop privileges for client. "proc exec" is needed for -c and for @@ -323,6 +315,16 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, NULL) != 0) fatal("pledge failed"); + /* Load terminfo entry if any. */ + if (isatty(STDIN_FILENO) && + *termname != '\0' && + tty_term_read_list(termname, STDIN_FILENO, &caps, &ncaps, + &cause) != 0) { + fprintf(stderr, "%s\n", cause); + free(cause); + return (1); + } + /* Free stuff that is not used in the client. */ if (ptm_fd != -1) close(ptm_fd); @@ -353,7 +355,8 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, } /* Send identify messages. */ - client_send_identify(ttynam, cwd, feat); + client_send_identify(ttynam, termname, caps, ncaps, cwd, feat); + tty_term_free_list(caps, ncaps); /* Send first command. */ if (msg == MSG_COMMAND) { @@ -436,27 +439,32 @@ client_main(struct event_base *base, int argc, char **argv, uint64_t flags, /* Send identify messages to server. */ static void -client_send_identify(const char *ttynam, const char *cwd, int feat) +client_send_identify(const char *ttynam, const char *termname, char **caps, + u_int ncaps, const char *cwd, int feat) { - const char *s; - char **ss; - size_t sslen; - int fd, flags = client_flags; - pid_t pid; + char **ss; + size_t sslen; + int fd, flags = client_flags; + pid_t pid; + u_int i; proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); proc_send(client_peer, MSG_IDENTIFY_LONGFLAGS, -1, &client_flags, sizeof client_flags); - if ((s = getenv("TERM")) == NULL) - s = ""; - proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, termname, + strlen(termname) + 1); proc_send(client_peer, MSG_IDENTIFY_FEATURES, -1, &feat, sizeof feat); proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); + for (i = 0; i < ncaps; i++) { + proc_send(client_peer, MSG_IDENTIFY_TERMINFO, -1, + caps[i], strlen(caps[i]) + 1); + } + if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); @@ -477,257 +485,6 @@ client_send_identify(const char *ttynam, const char *cwd, int feat) proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } -/* File write error callback. */ -static void -client_write_error_callback(__unused struct bufferevent *bev, - __unused short what, void *arg) -{ - struct client_file *cf = arg; - - log_debug("write error file %d", cf->stream); - - bufferevent_free(cf->event); - cf->event = NULL; - - close(cf->fd); - cf->fd = -1; - - if (client_exitflag) - client_exit(); -} - -/* File write callback. */ -static void -client_write_callback(__unused struct bufferevent *bev, void *arg) -{ - struct client_file *cf = arg; - - if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { - bufferevent_free(cf->event); - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); - } - - if (client_exitflag) - client_exit(); -} - -/* Open write file. */ -static void -client_write_open(void *data, size_t datalen) -{ - struct msg_write_open *msg = data; - const char *path; - struct msg_write_ready reply; - struct client_file find, *cf; - const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; - int error = 0; - - if (datalen < sizeof *msg) - fatalx("bad MSG_WRITE_OPEN size"); - if (datalen == sizeof *msg) - path = "-"; - else - path = (const char *)(msg + 1); - log_debug("open write file %d %s", msg->stream, path); - - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { - cf = file_create(NULL, msg->stream, NULL, NULL); - RB_INSERT(client_files, &client_files, cf); - } else { - error = EBADF; - goto reply; - } - if (cf->closed) { - error = EBADF; - goto reply; - } - - cf->fd = -1; - if (msg->fd == -1) - cf->fd = open(path, msg->flags|flags, 0644); - else { - if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) - errno = EBADF; - else { - cf->fd = dup(msg->fd); - if (~client_flags & CLIENT_CONTROL) - close(msg->fd); /* can only be used once */ - } - } - if (cf->fd == -1) { - error = errno; - goto reply; - } - - cf->event = bufferevent_new(cf->fd, NULL, client_write_callback, - client_write_error_callback, cf); - bufferevent_enable(cf->event, EV_WRITE); - goto reply; - -reply: - reply.stream = msg->stream; - reply.error = error; - proc_send(client_peer, MSG_WRITE_READY, -1, &reply, sizeof reply); -} - -/* Write to client file. */ -static void -client_write_data(void *data, size_t datalen) -{ - struct msg_write_data *msg = data; - struct client_file find, *cf; - size_t size = datalen - sizeof *msg; - - if (datalen < sizeof *msg) - fatalx("bad MSG_WRITE size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) - fatalx("unknown stream number"); - log_debug("write %zu to file %d", size, cf->stream); - - if (cf->event != NULL) - bufferevent_write(cf->event, msg + 1, size); -} - -/* Close client file. */ -static void -client_write_close(void *data, size_t datalen) -{ - struct msg_write_close *msg = data; - struct client_file find, *cf; - - if (datalen != sizeof *msg) - fatalx("bad MSG_WRITE_CLOSE size"); - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) - fatalx("unknown stream number"); - log_debug("close file %d", cf->stream); - - if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { - if (cf->event != NULL) - bufferevent_free(cf->event); - if (cf->fd != -1) - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); - } -} - -/* File read callback. */ -static void -client_read_callback(__unused struct bufferevent *bev, void *arg) -{ - struct client_file *cf = arg; - void *bdata; - size_t bsize; - struct msg_read_data *msg; - size_t msglen; - - msg = xmalloc(sizeof *msg); - for (;;) { - bdata = EVBUFFER_DATA(cf->event->input); - bsize = EVBUFFER_LENGTH(cf->event->input); - - if (bsize == 0) - break; - if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) - bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; - log_debug("read %zu from file %d", bsize, cf->stream); - - msglen = (sizeof *msg) + bsize; - msg = xrealloc(msg, msglen); - msg->stream = cf->stream; - memcpy(msg + 1, bdata, bsize); - proc_send(client_peer, MSG_READ, -1, msg, msglen); - - evbuffer_drain(cf->event->input, bsize); - } - free(msg); -} - -/* File read error callback. */ -static void -client_read_error_callback(__unused struct bufferevent *bev, - __unused short what, void *arg) -{ - struct client_file *cf = arg; - struct msg_read_done msg; - - log_debug("read error file %d", cf->stream); - - msg.stream = cf->stream; - msg.error = 0; - proc_send(client_peer, MSG_READ_DONE, -1, &msg, sizeof msg); - - bufferevent_free(cf->event); - close(cf->fd); - RB_REMOVE(client_files, &client_files, cf); - file_free(cf); -} - -/* Open read file. */ -static void -client_read_open(void *data, size_t datalen) -{ - struct msg_read_open *msg = data; - const char *path; - struct msg_read_done reply; - struct client_file find, *cf; - const int flags = O_NONBLOCK|O_RDONLY; - int error; - - if (datalen < sizeof *msg) - fatalx("bad MSG_READ_OPEN size"); - if (datalen == sizeof *msg) - path = "-"; - else - path = (const char *)(msg + 1); - log_debug("open read file %d %s", msg->stream, path); - - find.stream = msg->stream; - if ((cf = RB_FIND(client_files, &client_files, &find)) == NULL) { - cf = file_create(NULL, msg->stream, NULL, NULL); - RB_INSERT(client_files, &client_files, cf); - } else { - error = EBADF; - goto reply; - } - if (cf->closed) { - error = EBADF; - goto reply; - } - - cf->fd = -1; - if (msg->fd == -1) - cf->fd = open(path, flags); - else { - if (msg->fd != STDIN_FILENO) - errno = EBADF; - else { - cf->fd = dup(msg->fd); - if (~client_flags & CLIENT_CONTROL) - close(msg->fd); /* can only be used once */ - } - } - if (cf->fd == -1) { - error = errno; - goto reply; - } - - cf->event = bufferevent_new(cf->fd, client_read_callback, NULL, - client_read_error_callback, cf); - bufferevent_enable(cf->event, EV_READ); - return; - -reply: - reply.stream = msg->stream; - reply.error = error; - proc_send(client_peer, MSG_READ_DONE, -1, &reply, sizeof reply); -} - /* Run command in shell; used for -c. */ static __dead void client_exec(const char *shell, const char *shellcmd) @@ -802,13 +559,25 @@ client_signal(int sig) } } +/* Callback for file write error or close. */ +static void +client_file_check_cb(__unused struct client *c, __unused const char *path, + __unused int error, __unused int closed, __unused struct evbuffer *buffer, + __unused void *data) +{ + if (client_exitflag) + client_exit(); +} + /* Callback for client read events. */ static void client_dispatch(struct imsg *imsg, __unused void *arg) { if (imsg == NULL) { - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; + if (!client_exitflag) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + } proc_exit(client_proc); return; } @@ -916,16 +685,20 @@ client_dispatch_wait(struct imsg *imsg) proc_exit(client_proc); break; case MSG_READ_OPEN: - client_read_open(data, datalen); + file_read_open(&client_files, client_peer, imsg, 1, + !(client_flags & CLIENT_CONTROL), client_file_check_cb, + NULL); break; case MSG_WRITE_OPEN: - client_write_open(data, datalen); + file_write_open(&client_files, client_peer, imsg, 1, + !(client_flags & CLIENT_CONTROL), client_file_check_cb, + NULL); break; case MSG_WRITE: - client_write_data(data, datalen); + file_write_data(&client_files, imsg); break; case MSG_WRITE_CLOSE: - client_write_close(data, datalen); + file_write_close(&client_files, imsg); break; case MSG_OLDSTDERR: case MSG_OLDSTDIN: |