summaryrefslogtreecommitdiffstats
path: root/server.c
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 /server.c
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.
Diffstat (limited to 'server.c')
-rw-r--r--server.c575
1 files changed, 263 insertions, 312 deletions
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 job *job;
- int status;
- pid_t pid;
- u_int i;
+ int status;
+ pid_t pid;
for (;;) {
switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
@@ -474,63 +441,71 @@ server_child_signal(void)
case 0:
return;
}
- if (!WIFSTOPPED(status)) {
- SLIST_FOREACH(job, &all_jobs, lentry) {
- if (pid == job->pid) {
- job->pid = -1;
- job->status = status;
- }
- }
+ if (WIFSTOPPED(status))
+ server_child_stopped(pid, status);
+ else if (WIFEXITED(status))
+ server_child_exited(pid, status);
+ }
+}
+
+/* Handle exited children. */
+void
+server_child_exited(pid_t pid, int status)
+{
+ struct window *w;
+ struct window_pane *wp;
+ struct job *job;
+ u_int i;
+
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ if ((w = ARRAY_ITEM(&windows, i)) == NULL)
continue;
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->pid == pid) {
+ close(wp->fd);
+ wp->fd = -1;
+ }
}
- if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
- continue;
+ }
- for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
- w = ARRAY_ITEM(&windows, i);
- if (w == NULL)
- continue;
- TAILQ_FOREACH(wp, &w->panes, entry) {
- if (wp->pid == pid) {
- if (killpg(pid, SIGCONT) != 0)
- kill(pid, SIGCONT);
- }
- }
+ SLIST_FOREACH(job, &all_jobs, lentry) {
+ if (pid == job->pid) {
+ job->pid = -1;
+ job->status = status;
}
}
}
-/* Free dead, unreferenced clients and sessions. */
+/* Handle stopped children. */
void
-server_clean_dead(void)
+server_child_stopped(pid_t pid, int status)
{
- struct session *s;
- struct client *c;
- u_int i;
+ struct window *w;
+ struct window_pane *wp;
+ 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);
- }
+ if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
+ return;
- for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) {
- c = ARRAY_ITEM(&dead_clients, i);
- if (c == NULL || c->references != 0)
+ for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
+ if ((w = ARRAY_ITEM(&windows, i)) == NULL)
continue;
- ARRAY_SET(&dead_clients, i, NULL);
- xfree(c);
+ TAILQ_FOREACH(wp, &w->panes, entry) {
+ if (wp->pid == pid) {
+ if (killpg(pid, SIGCONT) != 0)
+ kill(pid, SIGCONT);
+ }
+ }
}
}
-/* Call any once-per-second timers. */
+/* Handle once-per-second timer events. */
void
-server_second_timers(void)
+server_second_callback(unused int fd, unused short events, unused void *arg)
{
struct window *w;
struct window_pane *wp;
+ struct timeval tv;
u_int i;
if (options_get_number(&global_s_options, "lock-server"))
@@ -548,6 +523,11 @@ server_second_timers(void)
wp->mode->timer(wp);
}
}
+
+ evtimer_del(&server_ev_second);
+ memset(&tv, 0, sizeof tv);
+ tv.tv_sec = 1;
+ evtimer_add(&server_ev_second, &tv);
}
/* Lock the server if ALL sessions have hit the time limit. */
@@ -606,32 +586,3 @@ server_lock_sessions(void)
}
}
}
-
-/* Update socket execute permissions based on whether sessions are attached. */
-int
-server_update_socket(void)
-{
- struct session *s;
- u_int i;
- static int last = -1;
- int n;
-
- n = 0;
- for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
- s = ARRAY_ITEM(&sessions, i);
- if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
- n++;
- break;
- }
- }
-
- if (n != last) {
- last = n;
- if (n != 0)
- chmod(socket_path, S_IRWXU);
- else
- chmod(socket_path, S_IRUSR|S_IWUSR);
- }
-
- return (n);
-}