diff options
author | nicm <nicm> | 2015-10-27 13:23:24 +0000 |
---|---|---|
committer | nicm <nicm> | 2015-10-27 13:23:24 +0000 |
commit | 07b0ea03c33893bd2b104db5ea4e1397f92e0477 (patch) | |
tree | c24ffdc1db106d5b81ce943c0567f186484ef5eb | |
parent | 9952201ca72b42819d64a9174fa7b5b898215668 (diff) |
Break the common process set up, event loop and imsg dispatch code
between server and client out into a separate internal API. This will
make it easier to add another process.
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | client.c | 509 | ||||
-rw-r--r-- | cmd-attach-session.c | 18 | ||||
-rw-r--r-- | cmd-detach-client.c | 13 | ||||
-rw-r--r-- | cmd-find.c | 14 | ||||
-rw-r--r-- | cmd-new-session.c | 7 | ||||
-rw-r--r-- | proc.c | 249 | ||||
-rw-r--r-- | server-client.c | 242 | ||||
-rw-r--r-- | server-fn.c | 64 | ||||
-rw-r--r-- | server.c | 72 | ||||
-rw-r--r-- | signal.c | 14 | ||||
-rw-r--r-- | tmux.c | 4 | ||||
-rw-r--r-- | tmux.h | 31 |
13 files changed, 636 insertions, 602 deletions
@@ -94,6 +94,7 @@ SRCS= alerts.c \ options-table.c \ options.c \ paste.c \ + proc.c \ procname.c \ resize.c \ screen-redraw.c \ @@ -26,6 +26,7 @@ #include <errno.h> #include <event.h> #include <fcntl.h> +#include <imsg.h> #include <signal.h> #include <stdlib.h> #include <string.h> @@ -33,10 +34,10 @@ #include "tmux.h" -int client_flags; -struct imsgbuf client_ibuf; -struct event client_event; -struct event client_stdin; +struct tmuxproc *client_proc; +struct tmuxpeer *client_peer; +int client_flags; +struct event client_stdin; enum { CLIENT_EXIT_NONE, CLIENT_EXIT_DETACHED, @@ -47,24 +48,21 @@ enum { CLIENT_EXIT_EXITED, CLIENT_EXIT_SERVER_EXITED, } client_exitreason = CLIENT_EXIT_NONE; -int client_exitval; -enum msgtype client_exittype; -const char *client_exitsession; -int client_attached; +int client_exitval; +enum msgtype client_exittype; +const char *client_exitsession; +int client_attached; __dead void client_exec(const char *); int client_get_lock(char *); int client_connect(struct event_base *, char *, int); void client_send_identify(const char *, const char *); -int client_write_one(enum msgtype, int, const void *, size_t); -int client_write_server(enum msgtype, const void *, size_t); -void client_update_event(void); -void client_signal(int, short, void *); void client_stdin_callback(int, short, void *); void client_write(int, const char *, size_t); -void client_callback(int, short, void *); -int client_dispatch_attached(void); -int client_dispatch_wait(void); +void client_signal(int); +void client_dispatch(struct imsg *, void *); +void client_dispatch_attached(struct imsg *); +void client_dispatch_wait(struct imsg *); const char *client_exit_message(void); /* @@ -222,6 +220,9 @@ client_main(struct event_base *base, int argc, char **argv, int flags) struct termios tio, saved_tio; size_t size; + /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */ + signal(SIGCHLD, SIG_IGN); + /* Save the flags. */ client_flags = flags; @@ -254,13 +255,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) cmd_list_free(cmdlist); } - /* Set process title, log and signals now this is the client. */ - setproctitle("client (%s)", socket_path); - logfile("client"); - - /* Establish signal handlers. */ - set_signals(client_signal); - /* Initialize the client socket and start the server. */ fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER); if (fd == -1) { @@ -274,6 +268,10 @@ client_main(struct event_base *base, int argc, char **argv, int flags) return (1); } + /* Build process state. */ + client_proc = proc_start("client", base, 0, client_signal); + client_peer = proc_add_peer(client_proc, fd, client_dispatch, NULL); + /* Save these before pledge(). */ if ((cwd = getcwd(path, sizeof path)) == NULL) cwd = "/"; @@ -298,10 +296,6 @@ client_main(struct event_base *base, int argc, char **argv, int flags) options_free(&global_w_options); environ_free(&global_environ); - /* Create imsg. */ - imsg_init(&client_ibuf, fd); - event_set(&client_event, fd, EV_READ, client_callback, NULL); - /* Create stdin handler. */ setblocking(STDIN_FILENO, 0); event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST, @@ -345,18 +339,17 @@ client_main(struct event_base *base, int argc, char **argv, int flags) size += sizeof *data; /* Send the command. */ - if (client_write_server(msg, data, size) != 0) { + if (proc_send(client_peer, msg, -1, data, size) != 0) { fprintf(stderr, "failed to send command\n"); free(data); return (1); } free(data); } else if (msg == MSG_SHELL) - client_write_server(msg, NULL, 0); + proc_send(client_peer, msg, -1, NULL, 0); - /* Set the event and dispatch. */ - client_update_event(); - event_dispatch(); + /* Start main loop. */ + proc_loop(client_proc, NULL); /* Print the exit message, if any, and exit. */ if (client_attached) { @@ -388,144 +381,29 @@ client_send_identify(const char *ttynam, const char *cwd) int fd, flags = client_flags; pid_t pid; - client_write_one(MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); + proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags); if ((s = getenv("TERM")) == NULL) s = ""; - client_write_one(MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); + proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1); - client_write_one(MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); - client_write_one(MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); + proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam, strlen(ttynam) + 1); + proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1); if ((fd = dup(STDIN_FILENO)) == -1) fatal("dup failed"); - client_write_one(MSG_IDENTIFY_STDIN, fd, NULL, 0); + proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0); pid = getpid(); - client_write_one(MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); + proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid); for (ss = environ; *ss != NULL; ss++) { sslen = strlen(*ss) + 1; if (sslen <= MAX_IMSGSIZE - IMSG_HEADER_SIZE) - client_write_one(MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); + proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen); } - client_write_one(MSG_IDENTIFY_DONE, -1, NULL, 0); -} - -/* Helper to send one message. */ -int -client_write_one(enum msgtype type, int fd, const void *buf, size_t len) -{ - int retval; - - retval = imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, fd, - (void *)buf, len); - if (retval != 1) - return (-1); - return (0); -} - -/* Write a message to the server without a file descriptor. */ -int -client_write_server(enum msgtype type, const void *buf, size_t len) -{ - int retval; - - retval = client_write_one(type, -1, buf, len); - if (retval == 0) - client_update_event(); - return (retval); -} - -/* Update client event based on whether it needs to read or read and write. */ -void -client_update_event(void) -{ - short events; - - event_del(&client_event); - events = EV_READ; - if (client_ibuf.w.queued > 0) - events |= EV_WRITE; - event_set(&client_event, client_ibuf.fd, events, client_callback, NULL); - event_add(&client_event, NULL); -} - -/* Callback to handle signals in the client. */ -void -client_signal(int sig, unused short events, unused void *arg) -{ - struct sigaction sigact; - int status; - - if (sig == SIGCHLD) - waitpid(WAIT_ANY, &status, WNOHANG); - else if (!client_attached) { - if (sig == SIGTERM) - event_loopexit(NULL); - } else { - switch (sig) { - case SIGHUP: - client_exitreason = CLIENT_EXIT_LOST_TTY; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGTERM: - client_exitreason = CLIENT_EXIT_TERMINATED; - client_exitval = 1; - client_write_server(MSG_EXITING, NULL, 0); - break; - case SIGWINCH: - client_write_server(MSG_RESIZE, NULL, 0); - break; - case SIGCONT: - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_IGN; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - client_write_server(MSG_WAKEUP, NULL, 0); - break; - } - } - - client_update_event(); -} - -/* Callback for client imsg read events. */ -void -client_callback(unused int fd, short events, unused void *arg) -{ - ssize_t n; - int retval; - - if (events & EV_READ) { - if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) - goto lost_server; - if (client_attached) - retval = client_dispatch_attached(); - else - retval = client_dispatch_wait(); - if (retval != 0) { - event_loopexit(NULL); - return; - } - } - - if (events & EV_WRITE) { - if (msgbuf_write(&client_ibuf.w) <= 0 && errno != EAGAIN) - goto lost_server; - } - - client_update_event(); - return; - -lost_server: - client_exitreason = CLIENT_EXIT_LOST_SERVER; - client_exitval = 1; - event_loopexit(NULL); + proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0); } /* Callback for client stdin read events. */ @@ -538,10 +416,9 @@ client_stdin_callback(unused int fd, unused short events, unused void *arg) if (data.size < 0 && (errno == EINTR || errno == EAGAIN)) return; - client_write_server(MSG_STDIN, &data, sizeof data); + proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data); if (data.size <= 0) event_del(&client_stdin); - client_update_event(); } /* Force write to file descriptor. */ @@ -591,13 +468,65 @@ client_exec(const char *shell) fatal("execl failed"); } +/* Callback to handle signals in the client. */ +void +client_signal(int sig) +{ + struct sigaction sigact; + int status; + + if (sig == SIGCHLD) + waitpid(WAIT_ANY, &status, WNOHANG); + else if (!client_attached) { + if (sig == SIGTERM) + proc_exit(client_proc); + } else { + switch (sig) { + case SIGHUP: + client_exitreason = CLIENT_EXIT_LOST_TTY; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGTERM: + client_exitreason = CLIENT_EXIT_TERMINATED; + client_exitval = 1; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case SIGWINCH: + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case SIGCONT: + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0); + break; + } + } +} + +/* Callback for client read events. */ +void +client_dispatch(struct imsg *imsg, unused void *arg) +{ + if (imsg == NULL) { + client_exitreason = CLIENT_EXIT_LOST_SERVER; + client_exitval = 1; + } else if (client_attached) + client_dispatch_attached(imsg); + else + client_dispatch_wait(imsg); +} + /* Dispatch imsgs when in wait state (before MSG_READY). */ -int -client_dispatch_wait(void) +void +client_dispatch_wait(struct imsg *imsg) { - struct imsg imsg; char *data; - ssize_t n, datalen; + ssize_t datalen; struct msg_stdout_data stdoutdata; struct msg_stderr_data stderrdata; int retval; @@ -615,163 +544,141 @@ client_dispatch_wait(void) pledge_applied = 1; }; - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); - - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_EXIT: - case MSG_SHUTDOWN: - if (datalen != sizeof retval && datalen != 0) - fatalx("bad MSG_EXIT size"); - if (datalen == sizeof retval) { - memcpy(&retval, data, sizeof retval); - client_exitval = retval; - } - imsg_free(&imsg); - return (-1); - case MSG_READY: - if (datalen != 0) - fatalx("bad MSG_READY size"); - - event_del(&client_stdin); - client_attached = 1; - client_write_server(MSG_RESIZE, NULL, 0); - break; - case MSG_STDIN: - if (datalen != 0) - fatalx("bad MSG_STDIN size"); - - event_add(&client_stdin, NULL); - break; - case MSG_STDOUT: - if (datalen != sizeof stdoutdata) - fatalx("bad MSG_STDOUT size"); - memcpy(&stdoutdata, data, sizeof stdoutdata); - - client_write(STDOUT_FILENO, stdoutdata.data, - stdoutdata.size); - break; - case MSG_STDERR: - if (datalen != sizeof stderrdata) - fatalx("bad MSG_STDERR size"); - memcpy(&stderrdata, data, sizeof stderrdata); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + + switch (imsg->hdr.type) { + case MSG_EXIT: + case MSG_SHUTDOWN: + if (datalen != sizeof retval && datalen != 0) + fatalx("bad MSG_EXIT size"); + if (datalen == sizeof retval) { + memcpy(&retval, data, sizeof retval); + client_exitval = retval; + } + proc_exit(client_proc); + break; + case MSG_READY: + if (datalen != 0) + fatalx("bad MSG_READY size"); - client_write(STDERR_FILENO, stderrdata.data, - stderrdata.size); - break; - case MSG_VERSION: - if (datalen != 0) - fatalx("bad MSG_VERSION size"); + event_del(&client_stdin); + client_attached = 1; + proc_send(client_peer, MSG_RESIZE, -1, NULL, 0); + break; + case MSG_STDIN: + if (datalen != 0) + fatalx("bad MSG_STDIN size"); - fprintf(stderr, "protocol version mismatch " - "(client %d, server %u)\n", PROTOCOL_VERSION, - imsg.hdr.peerid); - client_exitval = 1; + event_add(&client_stdin, NULL); + break; + case MSG_STDOUT: + if (datalen != sizeof stdoutdata) + fatalx("bad MSG_STDOUT size"); + memcpy(&stdoutdata, data, sizeof stdoutdata); - imsg_free(&imsg); - return (-1); - case MSG_SHELL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_SHELL string"); - - clear_signals(0); - client_exec(data); - /* NOTREACHED */ - case MSG_DETACH: - case MSG_DETACHKILL: - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXITED: - imsg_free(&imsg); - return (-1); - } + client_write(STDOUT_FILENO, stdoutdata.data, + stdoutdata.size); + break; + case MSG_STDERR: + if (datalen != sizeof stderrdata) + fatalx("bad MSG_STDERR size"); + memcpy(&stderrdata, data, sizeof stderrdata); - imsg_free(&imsg); + client_write(STDERR_FILENO, stderrdata.data, + stderrdata.size); + break; + case MSG_VERSION: + if (datalen != 0) + fatalx("bad MSG_VERSION size"); + + fprintf(stderr, "protocol version mismatch " + "(client %d, server %u)\n", PROTOCOL_VERSION, + imsg->hdr.peerid); + client_exitval = 1; + proc_exit(client_proc); + break; + case MSG_SHELL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_SHELL string"); + + clear_signals(0); + client_exec(data); + /* NOTREACHED */ + case MSG_DETACH: + case MSG_DETACHKILL: + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXITED: + proc_exit(client_proc); + break; } } /* Dispatch imsgs in attached state (after MSG_READY). */ -int -client_dispatch_attached(void) +void +client_dispatch_attached(struct imsg *imsg) { - struct imsg imsg; struct sigaction sigact; char *data; - ssize_t n, datalen; - - for (;;) { - if ((n = imsg_get(&client_ibuf, &imsg)) == -1) - fatalx("imsg_get failed"); - if (n == 0) - return (0); - - data = imsg.data; - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - log_debug("got %u from server", imsg.hdr.type); - switch (imsg.hdr.type) { - case MSG_DETACH: - case MSG_DETACHKILL: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_DETACH string"); - - client_exitsession = xstrdup(data); - client_exittype = imsg.hdr.type; - if (imsg.hdr.type == MSG_DETACHKILL) - client_exitreason = CLIENT_EXIT_DETACHED_HUP; - else - client_exitreason = CLIENT_EXIT_DETACHED; - client_write_server(MSG_EXITING, NULL, 0); - break; - case MSG_EXIT: - if (datalen != 0 && datalen != sizeof (int)) - fatalx("bad MSG_EXIT size"); + ssize_t datalen; - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_EXITED; - break; - case MSG_EXITED: - if (datalen != 0) - fatalx("bad MSG_EXITED size"); + data = imsg->data; + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - imsg_free(&imsg); - return (-1); - case MSG_SHUTDOWN: - if (datalen != 0) - fatalx("bad MSG_SHUTDOWN size"); + switch (imsg->hdr.type) { + case MSG_DETACH: + case MSG_DETACHKILL: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_DETACH string"); - client_write_server(MSG_EXITING, NULL, 0); - client_exitreason = CLIENT_EXIT_SERVER_EXITED; - client_exitval = 1; - break; - case MSG_SUSPEND: - if (datalen != 0) - fatalx("bad MSG_SUSPEND size"); + client_exitsession = xstrdup(data); + client_exittype = imsg->hdr.type; + if (imsg->hdr.type == MSG_DETACHKILL) + client_exitreason = CLIENT_EXIT_DETACHED_HUP; + else + client_exitreason = CLIENT_EXIT_DETACHED; + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + break; + case MSG_EXIT: + if (datalen != 0 && datalen != sizeof (int)) + fatalx("bad MSG_EXIT size"); - memset(&sigact, 0, sizeof sigact); - sigemptyset(&sigact.sa_mask); - sigact.sa_flags = SA_RESTART; - sigact.sa_handler = SIG_DFL; - if (sigaction(SIGTSTP, &sigact, NULL) != 0) - fatal("sigaction failed"); - kill(getpid(), SIGTSTP); - break; - case MSG_LOCK: - if (datalen == 0 || data[datalen - 1] != '\0') - fatalx("bad MSG_LOCK string"); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_EXITED; + break; + case MSG_EXITED: + if (datalen != 0) + fatalx("bad MSG_EXITED size"); - system(data); - client_write_server(MSG_UNLOCK, NULL, 0); - break; - } + proc_exit(client_proc); + break; + case MSG_SHUTDOWN: + if (datalen != 0) + fatalx("bad MSG_SHUTDOWN size"); - imsg_free(&imsg); + proc_send(client_peer, MSG_EXITING, -1, NULL, 0); + client_exitreason = CLIENT_EXIT_SERVER_EXITED; + client_exitval = 1; + break; + case MSG_SUSPEND: + if (datalen != 0) + fatalx("bad MSG_SUSPEND size"); + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + kill(getpid(), SIGTSTP); + break; + case MSG_LOCK: + if (datalen == 0 || data[datalen - 1] != '\0') + fatalx("bad MSG_LOCK string"); + + system(data); + proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0); + break; } } diff --git a/cmd-attach-session.c b/cmd-attach-session.c index a7ef1cd9..4e390323 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -113,16 +113,10 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, if (c->session != NULL) { if (dflag) { - /* - * Can't use server_write_session in case attaching to - * the same session as currently attached to. - */ TAILQ_FOREACH(c_loop, &clients, entry) { if (c_loop->session != s || c == c_loop) continue; - server_write_client(c, MSG_DETACH, - c_loop->session->name, - strlen(c_loop->session->name) + 1); + proc_send_s(c->peer, MSG_DETACH, s->name); } } @@ -150,8 +144,11 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, c->flags |= CLIENT_READONLY; if (dflag) { - server_write_session(s, MSG_DETACH, s->name, - strlen(s->name) + 1); + TAILQ_FOREACH(c_loop, &clients, entry) { + if (c_loop->session != s || c == c_loop) + continue; + proc_send_s(c->peer, MSG_DETACH, s->name); + } } if (!Eflag) { @@ -168,7 +165,8 @@ cmd_attach_session(struct cmd_q *cmdq, const char *tflag, int dflag, int rflag, server_redraw_client(c); s->curw->flags &= ~WINLINK_ALERTFLAGS; - server_write_ready(c); + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); cmdq->client_exit = 0; } recalculate_sizes(); diff --git a/cmd-detach-client.c b/cmd-detach-client.c index 4bae9997..813ac032 100644 --- a/cmd-detach-client.c +++ b/cmd-detach-client.c @@ -57,7 +57,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_ERROR); tty_stop_tty(&c->tty); c->flags |= CLIENT_SUSPENDED; - server_write_client(c, MSG_SUSPEND, NULL, 0); + proc_send(c->peer, MSG_SUSPEND, -1, NULL, 0); return (CMD_RETURN_NORMAL); } @@ -74,9 +74,7 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session != s) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_STOP); } @@ -89,14 +87,11 @@ cmd_detach_client_exec(struct cmd *self, struct cmd_q *cmdq) TAILQ_FOREACH(cloop, &clients, entry) { if (cloop->session == NULL || cloop == c) continue; - server_write_client(cloop, msgtype, - cloop->session->name, - strlen(cloop->session->name) + 1); + proc_send_s(cloop->peer, msgtype, cloop->session->name); } return (CMD_RETURN_NORMAL); } - server_write_client(c, msgtype, c->session->name, - strlen(c->session->name) + 1); + proc_send_s(c->peer, msgtype, c->session->name); return (CMD_RETURN_STOP); } @@ -129,8 +129,8 @@ cmd_find_try_TMUX(struct client *c, struct window *w) return (NULL); if (pid != getpid()) return (NULL); - log_debug("client %d TMUX is %s (session @%u)", c->ibuf.fd, - envent->value, session); + log_debug("client %p TMUX is %s (session @%u)", c, envent->value, + session); s = session_find_by_id(session); if (s == NULL || (w != NULL && !session_has(s, w))) @@ -333,6 +333,8 @@ cmd_find_current_session(struct cmd_find_state *fs) { /* If we know the current client, use it. */ if (fs->cmdq->client != NULL) { + log_debug("%s: have client %p%s", __func__, fs->cmdq->client, + fs->cmdq->client->session == NULL ? "" : " (with session)"); if (fs->cmdq->client->session == NULL) return (cmd_find_current_session_with_client(fs)); fs->s = fs->cmdq->client->session; @@ -365,8 +367,11 @@ cmd_find_current_client(struct cmd_q *cmdq) u_int csize; /* If the queue client has a session, use it. */ - if (cmdq->client != NULL && cmdq->client->session != NULL) + if (cmdq->client != NULL && cmdq->client->session != NULL) { + log_debug("%s: using cmdq %p client %p", __func__, cmdq, + cmdq->client); return (cmdq->client); + } /* Otherwise find the current session. */ cmd_find_clear_state(¤t, cmdq, 0); @@ -375,6 +380,7 @@ cmd_find_current_client(struct cmd_q *cmdq) /* If it is attached, find the best of it's clients. */ s = current.s; + log_debug("%s: current session $%u %s", __func__, s->id, s->name); if (~s->flags & SESSION_UNATTACHED) { csize = 0; TAILQ_FOREACH(c, &clients, entry) { @@ -1220,6 +1226,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) c = cmd_find_current_client(cmdq); if (c == NULL && !quiet) cmdq_error(cmdq, "no current client"); + log_debug("%s: no target, return %p", __func__, c); return (c); } copy = xstrdup(target); @@ -1251,6 +1258,7 @@ cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) cmdq_error(cmdq, "can't find client %s", copy); free(copy); + log_debug("%s: target %s, return %p", __func__, target, c); return (c); } diff --git a/cmd-new-session.c b/cmd-new-session.c index b3bf3c74..f1a6167a 100644 --- a/cmd-new-session.c +++ b/cmd-new-session.c @@ -270,9 +270,10 @@ cmd_new_session_exec(struct cmd *self, struct cmd_q *cmdq) * taking this session and needs to get MSG_READY and stay around. */ if (!detached) { - if (!already_attached) - server_write_ready(c); - else if (c->session != NULL) + if (!already_attached) { + if (~c->flags & CLIENT_CONTROL) + proc_send(c->peer, MSG_READY, -1, NULL, 0); + } else if (c->session != NULL) c->last_session = c->session; c->session = s; status_timer_start(c); @@ -0,0 +1,249 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2015 Nicholas Marriott <nicm@users.sourceforge.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/uio.h> + +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "tmux.h" + +struct tmuxproc { + const char *name; + int exit; + + void (*signalcb)(int); +}; + +struct tmuxpeer { + struct tmuxproc *parent; + + struct imsgbuf ibuf; + struct event event; + + int flags; +#define PEER_BAD 0x1 + + void (*dispatchcb)(struct imsg *, void *); + void *arg; +}; + +static void proc_update_event(struct tmuxpeer *); + +static void +proc_event_cb(unused int fd, short events, void *arg) +{ + struct tmuxpeer *peer = arg; + ssize_t n; + struct imsg imsg; + int v; + + if (!(peer->flags & PEER_BAD) && (events & EV_READ)) { + if ((n = imsg_read(&peer->ibuf)) == -1 || n == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + for (;;) { + if ((n = imsg_get(&peer->ibuf, &imsg)) == -1) { + peer->dispatchcb(NULL, peer->arg); + return; + } + if (n == 0) + break; + log_debug("peer %p message %d", peer, imsg.hdr.type); + + v = imsg.hdr.peerid; + if (imsg.hdr.type != MSG_VERSION && + v != PROTOCOL_VERSION) { + log_debug("peer %p bad version %d", peer, v); + + proc_send(peer, MSG_VERSION, -1, NULL, 0); + peer->flags |= PEER_BAD; + + if (imsg.fd != -1) + close(imsg.fd); + imsg_free(&imsg); + break; + } + + peer->dispatchcb(&imsg, peer->arg); + imsg_free(&imsg); + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&peer->ibuf.w) <= 0 && errno != EAGAIN) { + peer->dispatchcb(NULL, peer->arg); + return; + } + } + + if ((peer->flags & PEER_BAD) && peer->ibuf.w.queued == 0) { + peer->dispatchcb(NULL, peer->arg); + return; + } + + proc_update_event(peer); +} + +static void +proc_signal_cb(int signo, unused short events, void *arg) +{ + struct tmuxproc *tp = arg; + + tp->signalcb(signo); +} + +static void +proc_update_event(struct tmuxpeer *peer) +{ + short events; + + event_del(&peer->event); + + events = EV_READ; + if (peer->ibuf.w.queued > 0) + events |= EV_WRITE; + event_set(&peer->event, peer->ibuf.fd, events, proc_event_cb, peer); + + event_add(&peer->event, NULL); +} + +int +proc_send(struct tmuxpeer *peer, enum msgtype type, int fd, const void *buf, + size_t len) +{ + struct imsgbuf *ibuf = &peer->ibuf; + void *vp = (void *)buf; + int retval; + + if (peer->flags & PEER_BAD) + return (-1); + log_debug("sending message %d to peer %p (%zu bytes)", type, peer, len); + + retval = imsg_compose(ibuf, type, PROTOCOL_VERSION, -1, fd, vp, len); + if (retval != 1) + return (-1); + proc_update_event(peer); + return (0); +} + +int +proc_send_s(struct tmuxpeer *peer, enum msgtype type, const char *s) +{ + return (proc_send(peer, type, -1, s, strlen(s) + 1)); +} + +struct tmuxproc * +proc_start(const char *name, struct event_base *base, int forkflag, + void (*signalcb)(int)) +{ + struct tmuxproc *tp; + + if (forkflag) { + switch (fork()) { + case -1: + fatal("fork failed"); + case 0: + break; + default: + return (NULL); + } + if (daemon(1, 0) != 0) + fatal("daemon failed"); + + clear_signals(0); + if (event_reinit(base) != 0) + fatalx("event_reinit failed"); + } + + logfile(name); + setproctitle("%s (%s)", name, socket_path); + + log_debug("%s started (%ld): socket %s, protocol %d", name, + (long)getpid(), socket_path, PROTOCOL_VERSION); + + tp = xcalloc(1, sizeof *tp); + tp->name = xstrdup(name); + + tp->signalcb = signalcb; + set_signals(proc_signal_cb, tp); + + return (tp); +} + +void +proc_loop(struct tmuxproc *tp, int (*loopcb)(void)) +{ + log_debug("%s loop enter", tp->name); + do + event_loop(EVLOOP_ONCE); + while (!tp->exit && (loopcb == NULL || !loopcb ())); + log_debug("%s loop exit", tp->name); +} + +void +proc_exit(struct tmuxproc *tp) +{ + tp->exit = 1; +} + +struct tmuxpeer * +proc_add_peer(struct tmuxproc *tp, int fd, + void (*dispatchcb)(struct imsg *, void *), void *arg) +{ + struct tmuxpeer *peer; + + peer = xcalloc(1, sizeof *peer); + peer->parent = tp; + + peer->dispatchcb = dispatchcb; + peer->arg = arg; + + imsg_init(&peer->ibuf, fd); + event_set(&peer->event, fd, EV_READ, proc_event_cb, peer); + + log_debug("add peer %p: %d (%p)", peer, fd, arg); + + proc_update_event(peer); + return (peer); +} + +void +proc_remove_peer(struct tmuxpeer *peer) +{ + log_debug("remove peer %p", peer); + + event_del(&peer->event); + imsg_clear(&peer->ibuf); + + close(peer->ibuf.fd); + free(peer); +} + +void +proc_kill_peer(struct tmuxpeer *peer) +{ + peer->flags |= PEER_BAD; +} |