diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | buffer-poll.c | 10 | ||||
-rw-r--r-- | client.c | 214 | ||||
-rw-r--r-- | cmd-kill-server.c | 2 | ||||
-rw-r--r-- | cmd-pipe-pane.c | 2 | ||||
-rw-r--r-- | job.c | 4 | ||||
-rw-r--r-- | server-client.c | 36 | ||||
-rw-r--r-- | server-job.c | 10 | ||||
-rw-r--r-- | server-window.c | 17 | ||||
-rw-r--r-- | server.c | 575 | ||||
-rw-r--r-- | tmux.c | 301 | ||||
-rw-r--r-- | tmux.h | 28 | ||||
-rw-r--r-- | window.c | 4 |
13 files changed, 587 insertions, 618 deletions
@@ -45,7 +45,7 @@ CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare CDIAGFLAGS+= -Wundef -Wbad-function-cast -Winline -Wcast-align -LDADD= -lutil -lcurses +LDADD= -lutil -lcurses -levent DPADD= ${LIBUTIL} .include <bsd.prog.mk> diff --git a/buffer-poll.c b/buffer-poll.c index cf70c04c..d95510c1 100644 --- a/buffer-poll.c +++ b/buffer-poll.c @@ -19,6 +19,7 @@ #include <sys/types.h> #include <errno.h> +#include <event.h> #include <unistd.h> #include "tmux.h" @@ -29,9 +30,7 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out) { ssize_t n; - if (events & (POLLERR|POLLNVAL)) - return (-1); - if (in != NULL && events & POLLIN) { + if (in != NULL && events & EV_READ) { buffer_ensure(in, BUFSIZ); n = read(fd, BUFFER_IN(in), BUFFER_FREE(in)); if (n == 0) @@ -41,9 +40,8 @@ buffer_poll(int fd, int events, struct buffer *in, struct buffer *out) return (-1); } else buffer_add(in, n); - } else if (events & POLLHUP) - return (-1); - if (out != NULL && BUFFER_USED(out) > 0 && events & POLLOUT) { + } + if (out != NULL && BUFFER_USED(out) > 0 && events & EV_WRITE) { n = write(fd, BUFFER_OUT(out), BUFFER_USED(out)); if (n == -1) { if (errno != EINTR && errno != EAGAIN) @@ -24,6 +24,7 @@ #include <sys/wait.h> #include <errno.h> +#include <event.h> #include <fcntl.h> #include <pwd.h> #include <stdlib.h> @@ -34,13 +35,15 @@ #include "tmux.h" struct imsgbuf client_ibuf; +struct event client_event; const char *client_exitmsg; -void client_send_identify(int); -void client_send_environ(void); -void client_write_server(enum msgtype, void *, size_t); -int client_dispatch(void); -void client_suspend(void); +void client_send_identify(int); +void client_send_environ(void); +void client_write_server(enum msgtype, void *, size_t); +void client_signal(int, short, void *); +void client_callback(int, short, void *); +int client_dispatch(void); struct imsgbuf * client_init(char *path, int cmdflags, int flags) @@ -154,76 +157,54 @@ client_write_server(enum msgtype type, void *buf, size_t len) __dead void client_main(void) { - struct pollfd pfd; - int n, nfds; - - siginit(); + struct event ev_sigcont, ev_sigterm, ev_sigwinch; + struct sigaction sigact; + short events; logfile("client"); + /* Note: event_init() has already been called. */ + + /* Set up signals. */ + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGPIPE, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR1, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + + signal_set(&ev_sigcont, SIGCONT, client_signal, NULL); + signal_add(&ev_sigcont, NULL); + signal_set(&ev_sigterm, SIGTERM, client_signal, NULL); + signal_add(&ev_sigterm, NULL); + signal_set(&ev_sigwinch, SIGWINCH, client_signal, NULL); + signal_add(&ev_sigwinch, NULL); + /* * imsg_read in the first client poll loop (before the terminal has - * been initialiased) may have read messages into the buffer after the - * MSG_READY switched to here. Process anything outstanding now so poll - * doesn't hang waiting for messages that have already arrived. + * been initialised) may have read messages into the buffer after the + * MSG_READY switched to here. Process anything outstanding now to + * avoid hanging waiting for messages that have already arrived. */ if (client_dispatch() != 0) goto out; - for (;;) { - if (sigterm) { - client_exitmsg = "terminated"; - client_write_server(MSG_EXITING, NULL, 0); - } - if (sigchld) { - sigchld = 0; - waitpid(WAIT_ANY, NULL, WNOHANG); - continue; - } - if (sigwinch) { - sigwinch = 0; - client_write_server(MSG_RESIZE, NULL, 0); - continue; - } - if (sigcont) { - sigcont = 0; - siginit(); - client_write_server(MSG_WAKEUP, NULL, 0); - continue; - } - - pfd.fd = client_ibuf.fd; - pfd.events = POLLIN; - if (client_ibuf.w.queued > 0) - pfd.events |= POLLOUT; - - if ((nfds = poll(&pfd, 1, INFTIM)) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - fatal("poll failed"); - } - if (nfds == 0) - continue; - - if (pfd.revents & (POLLERR|POLLHUP|POLLNVAL)) - fatalx("socket error"); - - if (pfd.revents & POLLIN) { - if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) { - client_exitmsg = "lost server"; - break; - } - if (client_dispatch() != 0) - break; - } - - if (pfd.revents & POLLOUT) { - if (msgbuf_write(&client_ibuf.w) < 0) { - client_exitmsg = "lost server"; - break; - } - } - } + /* Set up the client-server socket 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); + + event_dispatch(); out: /* Print the exit message, if any, and exit. */ @@ -235,12 +216,78 @@ out: exit(0); } +void +client_signal(int sig, short events, unused void *data) +{ + struct sigaction sigact; + + switch (sig) { + case SIGTERM: + client_exitmsg = "terminated"; + 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; + } + + 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); +} + +void +client_callback(unused int fd, short events, unused void *data) +{ + int n; + + if (events & EV_READ) { + if ((n = imsg_read(&client_ibuf)) == -1 || n == 0) + goto lost_server; + if (client_dispatch() != 0) { + event_loopexit(NULL); + return; + } + } + + if (events & EV_WRITE) { + if (msgbuf_write(&client_ibuf.w) < 0) + goto lost_server; + } + + 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); + + return; + +lost_server: + client_exitmsg = "lost server"; + event_loopexit(NULL); +} + int client_dispatch(void) { - struct imsg imsg; - struct msg_lock_data lockdata; - ssize_t n, datalen; + struct imsg imsg; + struct msg_lock_data lockdata; + struct sigaction sigact; + ssize_t n, datalen; for (;;) { if ((n = imsg_get(&client_ibuf, &imsg)) == -1) @@ -249,6 +296,7 @@ client_dispatch(void) return (0); datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + log_debug("client got %d", imsg.hdr.type); switch (imsg.hdr.type) { case MSG_DETACH: if (datalen != 0) @@ -281,7 +329,13 @@ client_dispatch(void) if (datalen != 0) fatalx("bad MSG_SUSPEND size"); - client_suspend(); + 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 != sizeof lockdata) @@ -299,23 +353,3 @@ client_dispatch(void) imsg_free(&imsg); } } - -void -client_suspend(void) -{ - struct sigaction act; - - memset(&act, 0, sizeof act); - sigemptyset(&act.sa_mask); - act.sa_flags = SA_RESTART; - - act.sa_handler = SIG_DFL; - if (sigaction(SIGTSTP, &act, NULL) != 0) - fatal("sigaction failed"); - - act.sa_handler = sighandler; - if (sigaction(SIGCONT, &act, NULL) != 0) - fatal("sigaction failed"); - - kill(getpid(), SIGTSTP); -} diff --git a/cmd-kill-server.c b/cmd-kill-server.c index 56aa47cf..d7033a06 100644 --- a/cmd-kill-server.c +++ b/cmd-kill-server.c @@ -43,7 +43,7 @@ const struct cmd_entry cmd_kill_server_entry = { int cmd_kill_server_exec(unused struct cmd *self, unused struct cmd_ctx *ctx) { - sigterm = 1; + kill(getpid(), SIGTERM); return (0); } diff --git a/cmd-pipe-pane.c b/cmd-pipe-pane.c index 4fa43184..10cd3323 100644 --- a/cmd-pipe-pane.c +++ b/cmd-pipe-pane.c @@ -88,7 +88,7 @@ cmd_pipe_pane_exec(struct cmd *self, struct cmd_ctx *ctx) case 0: /* Child process. */ close(pipe_fd[0]); - sigreset(); + server_signal_clear(); if (dup2(pipe_fd[1], STDIN_FILENO) == -1) _exit(1); @@ -87,6 +87,7 @@ job_add(struct jobs *jobs, int flags, struct client *c, const char *cmd, job->fd = -1; job->out = buffer_create(BUFSIZ); + memset(&job->event, 0, sizeof job->event); job->callbackfn = callbackfn; job->freefn = freefn; @@ -126,6 +127,7 @@ job_free(struct job *job) close(job->fd); if (job->out != NULL) buffer_destroy(job->out); + event_del(&job->event); xfree(job); } @@ -147,7 +149,7 @@ job_run(struct job *job) case -1: return (-1); case 0: /* child */ - sigreset(); + server_signal_clear(); /* XXX environ? */ if (dup2(out[1], STDOUT_FILENO) == -1) diff --git a/server-client.c b/server-client.c index 3ff16e25..64909d87 100644 --- a/server-client.c +++ b/server-client.c @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <event.h> #include <fcntl.h> #include <string.h> #include <time.h> @@ -134,7 +135,9 @@ server_client_lost(struct client *c) close(c->ibuf.fd); imsg_clear(&c->ibuf); - + event_del(&c->event); + event_del(&c->tty.event); + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { if (ARRAY_ITEM(&dead_clients, i) == NULL) { ARRAY_SET(&dead_clients, i, c); @@ -162,25 +165,31 @@ server_client_prepare(void) events = 0; if (!(c->flags & CLIENT_BAD)) - events |= POLLIN; + events |= EV_READ; if (c->ibuf.w.queued > 0) - events |= POLLOUT; - server_poll_add(c->ibuf.fd, events, server_client_callback, c); + events |= EV_WRITE; + event_del(&c->event); + event_set(&c->event, + c->ibuf.fd, events, server_client_callback, c); + event_add(&c->event, NULL); if (c->tty.fd == -1) continue; if (c->flags & CLIENT_SUSPENDED || c->session == NULL) continue; - events = POLLIN; + events = EV_READ; if (BUFFER_USED(c->tty.out) > 0) - events |= POLLOUT; - server_poll_add(c->tty.fd, events, server_client_callback, c); + events |= EV_WRITE; + event_del(&c->tty.event); + event_set(&c->tty.event, + c->tty.fd, events, server_client_callback, c); + event_add(&c->tty.event, NULL); } } /* Process a single client event. */ void -server_client_callback(int fd, int events, void *data) +server_client_callback(int fd, short events, void *data) { struct client *c = data; @@ -188,10 +197,7 @@ server_client_callback(int fd, int events, void *data) return; if (fd == c->ibuf.fd) { - if (events & (POLLERR|POLLNVAL|POLLHUP)) - goto client_lost; - - if (events & POLLOUT && msgbuf_write(&c->ibuf.w) < 0) + if (events & EV_WRITE && msgbuf_write(&c->ibuf.w) < 0) goto client_lost; if (c->flags & CLIENT_BAD) { @@ -200,7 +206,7 @@ server_client_callback(int fd, int events, void *data) return; } - if (events & POLLIN && server_client_msg_dispatch(c) != 0) + if (events & EV_READ && server_client_msg_dispatch(c) != 0) goto client_lost; } @@ -211,7 +217,7 @@ server_client_callback(int fd, int events, void *data) if (buffer_poll(fd, events, c->tty.in, c->tty.out) != 0) goto client_lost; } - + return; client_lost: @@ -424,7 +430,7 @@ server_client_check_redraw(struct client *c) if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { if (options_get_number(&s->options, "set-titles")) server_client_set_title(c); - + if (c->message_string != NULL) redraw = status_message_redraw(c); else if (c->prompt_string != NULL) diff --git a/server-job.c b/server-job.c index 6ff15860..5d83ff92 100644 --- a/server-job.c +++ b/server-job.c @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <event.h> #include <unistd.h> #include "tmux.h" @@ -31,13 +32,16 @@ server_job_prepare(void) SLIST_FOREACH(job, &all_jobs, lentry) { if (job->fd == -1) continue; - server_poll_add(job->fd, POLLIN, server_job_callback, job); + event_del(&job->event); + event_set( + &job->event, job->fd, EV_READ, server_job_callback, job); + event_add(&job->event, NULL); } } /* Process a single job event. */ void -server_job_callback(int fd, int events, void *data) +server_job_callback(int fd, short events, void *data) { struct job *job = data; @@ -55,7 +59,7 @@ void server_job_loop(void) { struct job *job; - + restart: SLIST_FOREACH(job, &all_jobs, lentry) { if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1) diff --git a/server-window.c b/server-window.c index 3a74ead8..a6f2bba5 100644 --- a/server-window.c +++ b/server-window.c @@ -18,6 +18,7 @@ #include <sys/types.h> +#include <event.h> #include <unistd.h> #include "tmux.h" @@ -47,19 +48,23 @@ server_window_prepare(void) continue; events = 0; if (!server_window_backoff(wp)) - events |= POLLIN; + events = EV_READ; if (BUFFER_USED(wp->out) > 0) - events |= POLLOUT; - server_poll_add( + events |= EV_WRITE; + event_del(&wp->event); + event_set(&wp->event, wp->fd, events, server_window_callback, wp); + event_add(&wp->event, NULL); if (wp->pipe_fd == -1) continue; events = 0; if (BUFFER_USED(wp->pipe_buf) > 0) - events |= POLLOUT; - server_poll_add( + events |= EV_WRITE; + event_del(&wp->pipe_event); + event_set(&wp->pipe_event, wp->pipe_fd, events, server_window_callback, wp); + event_add(&wp->pipe_event, NULL); } } } @@ -90,7 +95,7 @@ server_window_backoff(struct window_pane *wp) /* Process a single window pane event. */ void -server_window_callback(int fd, int events, void *data) +server_window_callback(int fd, short events, void *data) { struct window_pane *wp = data; @@ -24,6 +24,7 @@ #include <sys/wait.h> #include <errno.h> +#include <event.h> #include <fcntl.h> #include <paths.h> #include <signal.h> @@ -45,112 +46,28 @@ struct clients clients; struct clients dead_clients; -/* Mapping of a pollfd to an fd independent of its position in the array. */ -struct poll_item { - int fd; - int events; - - void (*fn)(int, int, void *); - void *data; - - RB_ENTRY(poll_item) entry; -}; -RB_HEAD(poll_items, poll_item) poll_items; - -int server_poll_cmp(struct poll_item *, struct poll_item *); -struct poll_item*server_poll_lookup(int); -struct pollfd *server_poll_flatten(int *); -void server_poll_dispatch(struct pollfd *, int); -void server_poll_reset(void); -RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp); -RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp); +int server_fd; +int server_shutdown; +struct event server_ev_accept; +struct event server_ev_sigterm; +struct event server_ev_sigusr1; +struct event server_ev_sigchld; +struct event server_ev_second; int server_create_socket(void); -void server_callback(int, int, void *); -int server_main(int); -void server_shutdown(void); +void server_loop(void); int server_should_shutdown(void); -void server_child_signal(void); +void server_send_shutdown(void); void server_clean_dead(void); -void server_second_timers(void); +int server_update_socket(void); +void server_accept_callback(int, short, void *); +void server_signal_callback(int, short, void *); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); +void server_second_callback(int, short, void *); void server_lock_server(void); void server_lock_sessions(void); -int server_update_socket(void); - -int -server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2) -{ - return (pitem1->fd - pitem2->fd); -} - -void -server_poll_add(int fd, int events, void (*fn)(int, int, void *), void *data) -{ - struct poll_item *pitem; - - pitem = xmalloc(sizeof *pitem); - pitem->fd = fd; - pitem->events = events; - - pitem->fn = fn; - pitem->data = data; - - RB_INSERT(poll_items, &poll_items, pitem); -} - -struct poll_item * -server_poll_lookup(int fd) -{ - struct poll_item pitem; - - pitem.fd = fd; - return (RB_FIND(poll_items, &poll_items, &pitem)); -} - -struct pollfd * -server_poll_flatten(int *nfds) -{ - struct poll_item *pitem; - struct pollfd *pfds; - - pfds = NULL; - *nfds = 0; - RB_FOREACH(pitem, poll_items, &poll_items) { - pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds); - pfds[*nfds].fd = pitem->fd; - pfds[*nfds].events = pitem->events; - (*nfds)++; - } - return (pfds); -} - -void -server_poll_dispatch(struct pollfd *pfds, int nfds) -{ - struct poll_item *pitem; - struct pollfd *pfd; - - while (nfds > 0) { - pfd = &pfds[--nfds]; - if (pfd->revents != 0) { - pitem = server_poll_lookup(pfd->fd); - pitem->fn(pitem->fd, pfd->revents, pitem->data); - } - } - xfree(pfds); -} - -void -server_poll_reset(void) -{ - struct poll_item *pitem; - - while (!RB_EMPTY(&poll_items)) { - pitem = RB_ROOT(&poll_items); - RB_REMOVE(poll_items, &poll_items, pitem); - xfree(pitem); - } -} /* Create server socket. */ int @@ -191,40 +108,14 @@ server_create_socket(void) return (fd); } -/* Callback for server socket. */ -void -server_callback(int fd, int events, unused void *data) -{ - struct sockaddr_storage sa; - socklen_t slen = sizeof sa; - int newfd; - - if (events & (POLLERR|POLLNVAL|POLLHUP)) - fatalx("lost server socket"); - if (!(events & POLLIN)) - return; - - newfd = accept(fd, (struct sockaddr *) &sa, &slen); - if (newfd == -1) { - if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) - return; - fatal("accept failed"); - } - if (sigterm) { - close(newfd); - return; - } - server_client_create(newfd); -} - /* Fork new server. */ int server_start(char *path) { struct client *c; - int pair[2], srv_fd; - char *cause; - char rpathbuf[MAXPATHLEN]; + int pair[2]; + char *cause, rpathbuf[MAXPATHLEN]; + struct timeval tv; /* The first client is special and gets a socketpair; create it. */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) @@ -269,7 +160,7 @@ server_start(char *path) log_debug("socket path %s", socket_path); setproctitle("server (%s)", rpathbuf); - srv_fd = server_create_socket(); + server_fd = server_create_socket(); server_client_create(pair[1]); if (access(SYSTEM_CFG, R_OK) == 0) { @@ -282,7 +173,20 @@ server_start(char *path) if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0) goto error; - exit(server_main(srv_fd)); + event_init(); + + event_set(&server_ev_accept, + server_fd, EV_READ|EV_PERSIST, server_accept_callback, NULL); + event_add(&server_ev_accept, NULL); + + memset(&tv, 0, sizeof tv); + tv.tv_sec = 1; + evtimer_set(&server_ev_second, server_second_callback, NULL); + evtimer_add(&server_ev_second, &tv); + + server_signal_set(); + server_loop(); + exit(0); error: /* Write the error and shutdown the server. */ @@ -291,127 +195,65 @@ error: server_write_error(c, cause); xfree(cause); - sigterm = 1; - server_shutdown(); + server_shutdown = 1; - exit(server_main(srv_fd)); + server_signal_set(); + server_loop(); + exit(1); } /* Main server loop. */ -int -server_main(int srv_fd) +void +server_loop(void) { - struct pollfd *pfds; - int nfds, xtimeout; - u_int i; - time_t now, last; - - siginit(); - log_debug("server socket is %d", srv_fd); + struct timeval tv; - last = time(NULL); + memset(&tv, 0, sizeof tv); + tv.tv_usec = POLL_TIMEOUT * 1000; - pfds = NULL; - for (;;) { - /* If sigterm, kill all windows and clients. */ - if (sigterm) - server_shutdown(); + while (!server_should_shutdown()) { + server_update_socket(); - /* Stop if no sessions or clients left. */ - if (server_should_shutdown()) - break; - - /* Handle child exit. */ - if (sigchld) { - sigchld = 0; - server_child_signal(); - continue; - } - - /* Recreate socket on SIGUSR1. */ - if (sigusr1) { - sigusr1 = 0; - close(srv_fd); - srv_fd = server_create_socket(); - continue; - } - - /* Initialise pollfd array and add server socket. */ - server_poll_reset(); - server_poll_add(srv_fd, POLLIN, server_callback, NULL); - - /* Fill window and client sockets. */ server_job_prepare(); server_window_prepare(); server_client_prepare(); - - /* Update socket permissions. */ - xtimeout = INFTIM; - if (server_update_socket() != 0) - xtimeout = POLL_TIMEOUT; - - /* Do the poll. */ - pfds = server_poll_flatten(&nfds); - if (poll(pfds, nfds, xtimeout) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - fatal("poll failed"); - } - server_poll_dispatch(pfds, nfds); - /* Call second-based timers. */ - now = time(NULL); - if (now != last) { - last = now; - server_second_timers(); - } + event_loopexit(&tv); + event_loop(EVLOOP_ONCE); - /* Run once-per-loop events. */ server_job_loop(); server_window_loop(); server_client_loop(); - /* Collect any unset key bindings. */ key_bindings_clean(); - - /* Collect dead clients and sessions. */ server_clean_dead(); - } - server_poll_reset(); + } +} + +/* Check if the server should be shutting down (no more clients or windows). */ +int +server_should_shutdown(void) +{ + u_int i; for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { if (ARRAY_ITEM(&sessions, i) != NULL) - session_destroy(ARRAY_ITEM(&sessions, i)); + return (0); } - ARRAY_FREE(&sessions); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) != NULL) - server_client_lost(ARRAY_ITEM(&clients, i)); + return (0); } - ARRAY_FREE(&clients); - - mode_key_free_trees(); - key_bindings_free(); - - close(srv_fd); - - unlink(socket_path); - xfree(socket_path); - - options_free(&global_s_options); - options_free(&global_w_options); - - return (0); + return (1); } -/* Kill all clients. */ +/* Shutdown the server by killing all clients and windows. */ void -server_shutdown(void) +server_send_shutdown(void) { - struct session *s; struct client *c; - u_int i, j; + struct session *s; + u_int i; for (i = 0; i < ARRAY_LENGTH(&clients); i++) { c = ARRAY_ITEM(&clients, i); @@ -420,50 +262,175 @@ server_shutdown(void) server_client_lost(c); else server_write_client(c, MSG_SHUTDOWN, NULL, 0); + c->session = NULL; } } for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - s = ARRAY_ITEM(&sessions, i); - for (j = 0; j < ARRAY_LENGTH(&clients); j++) { - c = ARRAY_ITEM(&clients, j); - if (c != NULL && c->session == s) { - s = NULL; - break; - } - } - if (s != NULL) + if ((s = ARRAY_ITEM(&sessions, i)) != NULL) session_destroy(s); } } -/* Check if the server should be shutting down (no more clients or windows). */ +/* Free dead, unreferenced clients and sessions. */ +void +server_clean_dead(void) +{ + struct session *s; + struct client *c; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { + s = ARRAY_ITEM(&dead_sessions, i); + if (s == NULL || s->references != 0) + continue; + ARRAY_SET(&dead_sessions, i, NULL); + xfree(s); + } + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + c = ARRAY_ITEM(&dead_clients, i); + if (c == NULL || c->references != 0) + continue; + ARRAY_SET(&dead_clients, i, NULL); + xfree(c); + } +} + +/* Update socket execute permissions based on whether sessions are attached. */ int -server_should_shutdown(void) +server_update_socket(void) { - u_int i; + struct session *s; + u_int i; + static int last = -1; + int n; + n = 0; for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - if (ARRAY_ITEM(&sessions, i) != NULL) - return (0); + s = ARRAY_ITEM(&sessions, i); + if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { + n++; + break; + } } - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) != NULL) - return (0); + + if (n != last) { + last = n; + if (n != 0) + chmod(socket_path, S_IRWXU); + else + chmod(socket_path, S_IRUSR|S_IWUSR); + } + + return (n); +} + +/* Callback for server socket. */ +void +server_accept_callback(int fd, short events, unused void *data) +{ + struct sockaddr_storage sa; + socklen_t slen = sizeof sa; + int newfd; + + if (!(events & EV_READ)) + return; + + newfd = accept(fd, (struct sockaddr *) &sa, &slen); + if (newfd == -1) { + if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) + return; + fatal("accept failed"); + } + if (server_shutdown) { + close(newfd); + return; + } + server_client_create(newfd); + +} + +/* Set up server signal handling. */ +void +server_signal_set(void) +{ + struct sigaction sigact; + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_IGN; + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGPIPE, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + + signal_set(&server_ev_sigchld, SIGCHLD, server_signal_callback, NULL); + signal_add(&server_ev_sigchld, NULL); + signal_set(&server_ev_sigterm, SIGTERM, server_signal_callback, NULL); + signal_add(&server_ev_sigterm, NULL); + signal_set(&server_ev_sigusr1, SIGUSR1, server_signal_callback, NULL); + signal_add(&server_ev_sigusr1, NULL); +} + +/* Destroy server signal events. */ +void +server_signal_clear(void) +{ + struct sigaction sigact; + + memset(&sigact, 0, sizeof sigact); + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = SA_RESTART; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGINT, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGPIPE, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGUSR2, &sigact, NULL) != 0) + fatal("sigaction failed"); + if (sigaction(SIGTSTP, &sigact, NULL) != 0) + fatal("sigaction failed"); + + signal_del(&server_ev_sigchld); + signal_del(&server_ev_sigterm); + signal_del(&server_ev_sigusr1); +} + +/* Signal handler. */ +void +server_signal_callback(int sig, unused short events, unused void *data) +{ + switch (sig) { + case SIGTERM: + server_shutdown = 1; + server_send_shutdown(); + break; + case SIGCHLD: + server_child_signal(); + break; + case SIGUSR1: + event_del(&server_ev_accept); + close(server_fd); + server_fd = server_create_socket(); + event_set(&server_ev_accept, server_fd, + EV_READ|EV_PERSIST, server_accept_callback, NULL); + event_add(&server_ev_accept, NULL); + break; } - return (1); } /* Handle SIGCHLD. */ v |