summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@openbsd.org>2009-11-04 20:50:11 +0000
committerNicholas Marriott <nicm@openbsd.org>2009-11-04 20:50:11 +0000
commitabf3a5d50ec4003f58f460cc9ab3c00671c920f8 (patch)
tree214252ffd3ea45a0998d965b5e2736e80100a9a9
parentf575e39b0a367ae9b8a7cf54ceead865b8885eed (diff)
Initial changes to move tmux to libevent.
This moves the client-side loops are pretty much fully over to event-based only (tmux.c and client.c) but server-side (server.c and friends) treats libevent as a sort of clever poll, waking up after every event to run various things. Moving the server stuff over to bufferevents and timers and so on will come later.
-rw-r--r--Makefile2
-rw-r--r--buffer-poll.c10
-rw-r--r--client.c214
-rw-r--r--cmd-kill-server.c2
-rw-r--r--cmd-pipe-pane.c2
-rw-r--r--job.c4
-rw-r--r--server-client.c36
-rw-r--r--server-job.c10
-rw-r--r--server-window.c17
-rw-r--r--server.c575
-rw-r--r--tmux.c301
-rw-r--r--tmux.h28
-rw-r--r--window.c4
13 files changed, 587 insertions, 618 deletions
diff --git a/Makefile b/Makefile
index 68a34050..04102479 100644
--- a/Makefile
+++ b/Makefile
@@ -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)
diff --git a/client.c b/client.c
index 4d5fd696..60fef91a 100644
--- a/client.c
+++ b/client.c
@@ -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);
diff --git a/job.c b/job.c
index a638e3d8..fd91913f 100644
--- a/job.c
+++ b/job.c
@@ -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;
diff --git a/server.c b/server.c
index 1113d4dc..236cd201 100644
--- a/server.c
+++ b/server.c
@@ -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. */
void
server_child_signal(void)
{
- struct window *w;
- struct window_pane *wp;
- struct j