diff --git a/buffer-poll.c b/buffer-poll.c index 87153d16..63b45f47 100644 --- a/buffer-poll.c +++ b/buffer-poll.c @@ -1,4 +1,4 @@ -/* $Id: buffer-poll.c,v 1.17 2009-10-11 23:55:26 tcunha Exp $ */ +/* $Id: buffer-poll.c,v 1.18 2009-10-23 17:49:47 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -25,15 +25,15 @@ /* Fill buffers from socket based on poll results. */ int -buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out) +buffer_poll(int fd, int events, struct buffer *in, struct buffer *out) { ssize_t n; - if (pfd->revents & (POLLERR|POLLNVAL)) + if (events & (POLLERR|POLLNVAL)) return (-1); - if (in != NULL && pfd->revents & POLLIN) { + if (in != NULL && events & POLLIN) { buffer_ensure(in, BUFSIZ); - n = read(pfd->fd, BUFFER_IN(in), BUFFER_FREE(in)); + n = read(fd, BUFFER_IN(in), BUFFER_FREE(in)); if (n == 0) return (-1); if (n == -1) { @@ -41,10 +41,10 @@ buffer_poll(struct pollfd *pfd, struct buffer *in, struct buffer *out) return (-1); } else buffer_add(in, n); - } else if (pfd->revents & POLLHUP) + } else if (events & POLLHUP) return (-1); - if (out != NULL && BUFFER_USED(out) > 0 && pfd->revents & POLLOUT) { - n = write(pfd->fd, BUFFER_OUT(out), BUFFER_USED(out)); + if (out != NULL && BUFFER_USED(out) > 0 && events & POLLOUT) { + n = write(fd, BUFFER_OUT(out), BUFFER_USED(out)); if (n == -1) { if (errno != EINTR && errno != EAGAIN) return (-1); diff --git a/server-client.c b/server-client.c new file mode 100644 index 00000000..f031be14 --- /dev/null +++ b/server-client.c @@ -0,0 +1,728 @@ +/* $Id: server-client.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include +#include +#include +#include + +#include "tmux.h" + +void server_client_handle_data(struct client *); +void server_client_check_redraw(struct client *); +void server_client_set_title(struct client *); +void server_client_check_timers(struct client *); + +int server_client_msg_dispatch(struct client *); +void server_client_msg_command(struct client *, struct msg_command_data *); +void server_client_msg_identify( + struct client *, struct msg_identify_data *, int); +void server_client_msg_shell(struct client *); + +void printflike2 server_client_msg_error(struct cmd_ctx *, const char *, ...); +void printflike2 server_client_msg_print(struct cmd_ctx *, const char *, ...); +void printflike2 server_client_msg_info(struct cmd_ctx *, const char *, ...); + + +/* Create a new client. */ +void +server_client_create(int fd) +{ + struct client *c; + int mode; + u_int i; + + if ((mode = fcntl(fd, F_GETFL)) == -1) + fatal("fcntl failed"); + if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) + fatal("fcntl failed"); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + fatal("fcntl failed"); + + c = xcalloc(1, sizeof *c); + c->references = 0; + imsg_init(&c->ibuf, fd); + + if (gettimeofday(&c->tv, NULL) != 0) + fatal("gettimeofday failed"); + + ARRAY_INIT(&c->prompt_hdata); + + c->tty.fd = -1; + c->title = NULL; + + c->session = NULL; + c->tty.sx = 80; + c->tty.sy = 24; + + screen_init(&c->status, c->tty.sx, 1, 0); + job_tree_init(&c->status_jobs); + + c->message_string = NULL; + + c->prompt_string = NULL; + c->prompt_buffer = NULL; + c->prompt_index = 0; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) == NULL) { + ARRAY_SET(&clients, i, c); + return; + } + } + ARRAY_ADD(&clients, c); + log_debug("new client %d", fd); +} + +/* Lost a client. */ +void +server_client_lost(struct client *c) +{ + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + if (ARRAY_ITEM(&clients, i) == c) + ARRAY_SET(&clients, i, NULL); + } + log_debug("lost client %d", c->ibuf.fd); + + /* + * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called + * and tty_free might close an unrelated fd. + */ + if (c->flags & CLIENT_TERMINAL) + tty_free(&c->tty); + + screen_free(&c->status); + job_tree_free(&c->status_jobs); + + if (c->title != NULL) + xfree(c->title); + + if (c->message_string != NULL) + xfree(c->message_string); + + if (c->prompt_string != NULL) + xfree(c->prompt_string); + if (c->prompt_buffer != NULL) + xfree(c->prompt_buffer); + for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++) + xfree(ARRAY_ITEM(&c->prompt_hdata, i)); + ARRAY_FREE(&c->prompt_hdata); + + if (c->cwd != NULL) + xfree(c->cwd); + + close(c->ibuf.fd); + imsg_clear(&c->ibuf); + + for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { + if (ARRAY_ITEM(&dead_clients, i) == NULL) { + ARRAY_SET(&dead_clients, i, c); + break; + } + } + if (i == ARRAY_LENGTH(&dead_clients)) + ARRAY_ADD(&dead_clients, c); + c->flags |= CLIENT_DEAD; + + recalculate_sizes(); +} + +/* Process a single client event. */ +void +server_client_callback(int fd, int events, void *data) +{ + struct client *c = data; + + if (fd == c->ibuf.fd) { + if (events & (POLLERR|POLLNVAL|POLLHUP)) + goto client_lost; + + if (events & POLLOUT && msgbuf_write(&c->ibuf.w) < 0) + goto client_lost; + + if (c->flags & CLIENT_BAD) { + if (c->ibuf.w.queued == 0) + goto client_lost; + return; + } + + if (events & POLLIN && server_client_msg_dispatch(c) != 0) + goto client_lost; + } + + if (c->tty.fd != -1 && fd == c->tty.fd) { + if (c->flags & CLIENT_SUSPENDED || c->session == NULL) + return; + + if (buffer_poll(fd, events, c->tty.in, c->tty.out) != 0) + goto client_lost; + server_client_handle_data(c); + } + + return; + +client_lost: + server_client_lost(c); +} + +/* Input data from client. */ +void +server_client_handle_data(struct client *c) +{ + struct window *w; + struct window_pane *wp; + struct screen *s; + struct options *oo; + struct timeval tv; + struct key_binding *bd; + struct keylist *keylist; + struct mouse_event mouse; + int key, status, xtimeout, mode, isprefix; + u_int i; + + xtimeout = options_get_number(&c->session->options, "repeat-time"); + if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday failed"); + if (timercmp(&tv, &c->repeat_timer, >)) + c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); + } + + /* Process keys. */ + keylist = options_get_data(&c->session->options, "prefix"); + while (tty_keys_next(&c->tty, &key, &mouse) == 0) { + if (c->session == NULL) + return; + + c->session->activity = time(NULL); + w = c->session->curw->window; + wp = w->active; /* could die */ + oo = &c->session->options; + + /* Special case: number keys jump to pane in identify mode. */ + if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { + wp = window_pane_at_index(w, key - '0'); + if (wp != NULL && window_pane_visible(wp)) + window_set_active_pane(w, wp); + server_clear_identify(c); + continue; + } + + status_message_clear(c); + server_clear_identify(c); + if (c->prompt_string != NULL) { + status_prompt_key(c, key); + continue; + } + + /* Check for mouse keys. */ + if (key == KEYC_MOUSE) { + if (options_get_number(oo, "mouse-select-pane")) { + window_set_active_at(w, mouse.x, mouse.y); + wp = w->active; + } + window_pane_mouse(wp, c, &mouse); + continue; + } + + /* Is this a prefix key? */ + isprefix = 0; + for (i = 0; i < ARRAY_LENGTH(keylist); i++) { + if (key == ARRAY_ITEM(keylist, i)) { + isprefix = 1; + break; + } + } + + /* No previous prefix key. */ + if (!(c->flags & CLIENT_PREFIX)) { + if (isprefix) + c->flags |= CLIENT_PREFIX; + else { + /* Try as a non-prefix key binding. */ + if ((bd = key_bindings_lookup(key)) == NULL) + window_pane_key(wp, c, key); + else + key_bindings_dispatch(bd, c); + } + continue; + } + + /* Prefix key already pressed. Reset prefix and lookup key. */ + c->flags &= ~CLIENT_PREFIX; + if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { + /* If repeating, treat this as a key, else ignore. */ + if (c->flags & CLIENT_REPEAT) { + c->flags &= ~CLIENT_REPEAT; + if (isprefix) + c->flags |= CLIENT_PREFIX; + else + window_pane_key(wp, c, key); + } + continue; + } + + /* If already repeating, but this key can't repeat, skip it. */ + if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { + c->flags &= ~CLIENT_REPEAT; + if (isprefix) + c->flags |= CLIENT_PREFIX; + else + window_pane_key(wp, c, key); + continue; + } + + /* If this key can repeat, reset the repeat flags and timer. */ + if (xtimeout != 0 && bd->can_repeat) { + c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; + + tv.tv_sec = xtimeout / 1000; + tv.tv_usec = (xtimeout % 1000) * 1000L; + if (gettimeofday(&c->repeat_timer, NULL) != 0) + fatal("gettimeofday failed"); + timeradd(&c->repeat_timer, &tv, &c->repeat_timer); + } + + /* Dispatch the command. */ + key_bindings_dispatch(bd, c); + } + if (c->session == NULL) + return; + w = c->session->curw->window; + wp = w->active; + oo = &c->session->options; + s = wp->screen; + + /* + * Update cursor position and mode settings. The scroll region and + * attributes are cleared across poll(2) as this is the most likely + * time a user may interrupt tmux, for example with ~^Z in ssh(1). This + * is a compromise between excessive resets and likelihood of an + * interrupt. + * + * tty_region/tty_reset/tty_update_mode already take care of not + * resetting things that are already in their default state. + */ + tty_region(&c->tty, 0, c->tty.sy - 1); + + status = options_get_number(oo, "status"); + if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) + tty_cursor(&c->tty, 0, 0); + else + tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy); + + mode = s->mode; + if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && + options_get_number(oo, "mouse-select-pane")) + mode |= MODE_MOUSE; + tty_update_mode(&c->tty, mode); + tty_reset(&c->tty); +} + +/* Client functions that need to happen every loop. */ +void +server_client_loop(void) +{ + struct client *c; + struct window *w; + struct window_pane *wp; + u_int i; + + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session == NULL) + continue; + + server_client_check_timers(c); + server_client_check_redraw(c); + } + + /* + * Any windows will have been redrawn as part of clients, so clear + * their flags now. + */ + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + + w->flags &= ~WINDOW_REDRAW; + TAILQ_FOREACH(wp, &w->panes, entry) + wp->flags &= ~PANE_REDRAW; + } +} + +/* Check for client redraws. */ +void +server_client_check_redraw(struct client *c) +{ + struct session *s = c->session; + struct window_pane *wp; + int flags, redraw; + + flags = c->tty.flags & TTY_FREEZE; + c->tty.flags &= ~TTY_FREEZE; + + 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) + redraw = status_prompt_redraw(c); + else + redraw = status_redraw(c); + if (!redraw) + c->flags &= ~CLIENT_STATUS; + } + + if (c->flags & CLIENT_REDRAW) { + screen_redraw_screen(c, 0); + c->flags &= ~CLIENT_STATUS; + } else { + TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { + if (wp->flags & PANE_REDRAW) + screen_redraw_pane(c, wp); + } + } + + if (c->flags & CLIENT_STATUS) + screen_redraw_screen(c, 1); + + c->tty.flags |= flags; + + c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); +} + +/* Set client title. */ +void +server_client_set_title(struct client *c) +{ + struct session *s = c->session; + const char *template; + char *title; + + template = options_get_string(&s->options, "set-titles-string"); + + title = status_replace(c, template, time(NULL)); + if (c->title == NULL || strcmp(title, c->title) != 0) { + if (c->title != NULL) + xfree(c->title); + c->title = xstrdup(title); + tty_set_title(&c->tty, c->title); + } + xfree(title); +} + +/* Check client timers. */ +void +server_client_check_timers(struct client *c) +{ + struct session *s = c->session; + struct job *job; + struct timeval tv; + u_int interval; + + if (gettimeofday(&tv, NULL) != 0) + fatal("gettimeofday failed"); + + if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >)) + server_clear_identify(c); + + if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >)) + status_message_clear(c); + + if (c->message_string != NULL || c->prompt_string != NULL) { + /* + * Don't need timed redraw for messages/prompts so bail now. + * The status timer isn't reset when they are redrawn anyway. + */ + return; + + } + if (!options_get_number(&s->options, "status")) + return; + + /* Check timer; resolution is only a second so don't be too clever. */ + interval = options_get_number(&s->options, "status-interval"); + if (interval == 0) + return; + if (tv.tv_sec < c->status_timer.tv_sec || + ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) { + /* Run the jobs for this client and schedule for redraw. */ + RB_FOREACH(job, jobs, &c->status_jobs) + job_run(job); + c->flags |= CLIENT_STATUS; + } +} + +/* Dispatch message from client. */ +int +server_client_msg_dispatch(struct client *c) +{ + struct imsg imsg; + struct msg_command_data commanddata; + struct msg_identify_data identifydata; + struct msg_environ_data environdata; + ssize_t n, datalen; + + if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) + return (-1); + + for (;;) { + if ((n = imsg_get(&c->ibuf, &imsg)) == -1) + return (-1); + if (n == 0) + return (0); + datalen = imsg.hdr.len - IMSG_HEADER_SIZE; + + if (imsg.hdr.peerid != PROTOCOL_VERSION) { + server_write_client(c, MSG_VERSION, NULL, 0); + c->flags |= CLIENT_BAD; + imsg_free(&imsg); + continue; + } + + log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); + switch (imsg.hdr.type) { + case MSG_COMMAND: + if (datalen != sizeof commanddata) + fatalx("bad MSG_COMMAND size"); + memcpy(&commanddata, imsg.data, sizeof commanddata); + + server_client_msg_command(c, &commanddata); + break; + case MSG_IDENTIFY: + if (datalen != sizeof identifydata) + fatalx("bad MSG_IDENTIFY size"); + if (imsg.fd == -1) + fatalx("MSG_IDENTIFY missing fd"); + memcpy(&identifydata, imsg.data, sizeof identifydata); + + server_client_msg_identify(c, &identifydata, imsg.fd); + break; + case MSG_RESIZE: + if (datalen != 0) + fatalx("bad MSG_RESIZE size"); + + tty_resize(&c->tty); + recalculate_sizes(); + server_redraw_client(c); + break; + case MSG_EXITING: + if (datalen != 0) + fatalx("bad MSG_EXITING size"); + + c->session = NULL; + tty_close(&c->tty); + server_write_client(c, MSG_EXITED, NULL, 0); + break; + case MSG_WAKEUP: + case MSG_UNLOCK: + if (datalen != 0) + fatalx("bad MSG_WAKEUP size"); + + if (!(c->flags & CLIENT_SUSPENDED)) + break; + c->flags &= ~CLIENT_SUSPENDED; + tty_start_tty(&c->tty); + server_redraw_client(c); + recalculate_sizes(); + if (c->session != NULL) + c->session->activity = time(NULL); + break; + case MSG_ENVIRON: + if (datalen != sizeof environdata) + fatalx("bad MSG_ENVIRON size"); + memcpy(&environdata, imsg.data, sizeof environdata); + + environdata.var[(sizeof environdata.var) - 1] = '\0'; + if (strchr(environdata.var, '=') != NULL) + environ_put(&c->environ, environdata.var); + break; + case MSG_SHELL: + if (datalen != 0) + fatalx("bad MSG_SHELL size"); + + server_client_msg_shell(c); + break; + default: + fatalx("unexpected message"); + } + + imsg_free(&imsg); + } +} + +/* Callback to send error message to client. */ +void printflike2 +server_client_msg_error(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct msg_print_data data; + va_list ap; + + va_start(ap, fmt); + xvsnprintf(data.msg, sizeof data.msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data); +} + +/* Callback to send print message to client. */ +void printflike2 +server_client_msg_print(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct msg_print_data data; + va_list ap; + + va_start(ap, fmt); + xvsnprintf(data.msg, sizeof data.msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); +} + +/* Callback to send print message to client, if not quiet. */ +void printflike2 +server_client_msg_info(struct cmd_ctx *ctx, const char *fmt, ...) +{ + struct msg_print_data data; + va_list ap; + + if (be_quiet) + return; + + va_start(ap, fmt); + xvsnprintf(data.msg, sizeof data.msg, fmt, ap); + va_end(ap); + + server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); +} + +/* Handle command message. */ +void +server_client_msg_command(struct client *c, struct msg_command_data *data) +{ + struct cmd_ctx ctx; + struct cmd_list *cmdlist = NULL; + struct cmd *cmd; + int argc; + char **argv, *cause; + + if (c->session != NULL) + c->session->activity = time(NULL); + + ctx.error = server_client_msg_error; + ctx.print = server_client_msg_print; + ctx.info = server_client_msg_info; + + ctx.msgdata = data; + ctx.curclient = NULL; + + ctx.cmdclient = c; + + argc = data->argc; + data->argv[(sizeof data->argv) - 1] = '\0'; + if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { + server_client_msg_error(&ctx, "command too long"); + goto error; + } + + if (argc == 0) { + argc = 1; + argv = xcalloc(1, sizeof *argv); + *argv = xstrdup("new-session"); + } + + if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { + server_client_msg_error(&ctx, "%s", cause); + cmd_free_argv(argc, argv); + goto error; + } + cmd_free_argv(argc, argv); + + if (data->pid != -1) { + TAILQ_FOREACH(cmd, cmdlist, qentry) { + if (cmd->entry->flags & CMD_CANTNEST) { + server_client_msg_error(&ctx, + "sessions should be nested with care. " + "unset $TMUX to force"); + goto error; + } + } + } + + if (cmd_list_exec(cmdlist, &ctx) != 1) + server_write_client(c, MSG_EXIT, NULL, 0); + cmd_list_free(cmdlist); + return; + +error: + if (cmdlist != NULL) + cmd_list_free(cmdlist); + server_write_client(c, MSG_EXIT, NULL, 0); +} + +/* Handle identify message. */ +void +server_client_msg_identify( + struct client *c, struct msg_identify_data *data, int fd) +{ + c->cwd = NULL; + data->cwd[(sizeof data->cwd) - 1] = '\0'; + if (*data->cwd != '\0') + c->cwd = xstrdup(data->cwd); + + data->term[(sizeof data->term) - 1] = '\0'; + tty_init(&c->tty, fd, data->term); + if (data->flags & IDENTIFY_UTF8) + c->tty.flags |= TTY_UTF8; + if (data->flags & IDENTIFY_256COLOURS) + c->tty.term_flags |= TERM_256COLOURS; + else if (data->flags & IDENTIFY_88COLOURS) + c->tty.term_flags |= TERM_88COLOURS; + if (data->flags & IDENTIFY_HASDEFAULTS) + c->tty.term_flags |= TERM_HASDEFAULTS; + + tty_resize(&c->tty); + + c->flags |= CLIENT_TERMINAL; +} + +/* Handle shell message. */ +void +server_client_msg_shell(struct client *c) +{ + struct msg_shell_data data; + const char *shell; + + shell = options_get_string(&global_s_options, "default-shell"); + + if (*shell == '\0' || areshell(shell)) + shell = _PATH_BSHELL; + if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) + strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); + + server_write_client(c, MSG_SHELL, &data, sizeof data); + c->flags |= CLIENT_BAD; /* it will die after exec */ +} diff --git a/server-job.c b/server-job.c new file mode 100644 index 00000000..a3d4d0dd --- /dev/null +++ b/server-job.c @@ -0,0 +1,57 @@ +/* $Id: server-job.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include + +#include "tmux.h" + +/* Process a single job event. */ +void +server_job_callback(int fd, int events, void *data) +{ + struct job *job = data; + + if (job->fd == -1) + return; + + if (buffer_poll(fd, events, job->out, NULL) != 0) { + close(job->fd); + job->fd = -1; + } +} + +/* Job functions that happen once a loop. */ +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) + continue; + job->flags |= JOB_DONE; + + if (job->callbackfn != NULL) { + job->callbackfn(job); + goto restart; /* could be freed by callback */ + } + } +} diff --git a/server-msg.c b/server-msg.c deleted file mode 100644 index 3c01d7cc..00000000 --- a/server-msg.c +++ /dev/null @@ -1,279 +0,0 @@ -/* $Id: server-msg.c,v 1.92 2009-10-23 17:33:26 tcunha Exp $ */ - -/* - * Copyright (c) 2007 Nicholas Marriott - * - * 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 -#include - -#include -#include -#include -#include -#include - -#include "tmux.h" - -void server_msg_command(struct client *, struct msg_command_data *); -void server_msg_identify(struct client *, struct msg_identify_data *, int); -void server_msg_shell(struct client *); - -void printflike2 server_msg_command_error(struct cmd_ctx *, const char *, ...); -void printflike2 server_msg_command_print(struct cmd_ctx *, const char *, ...); -void printflike2 server_msg_command_info(struct cmd_ctx *, const char *, ...); - -int -server_msg_dispatch(struct client *c) -{ - struct imsg imsg; - struct msg_command_data commanddata; - struct msg_identify_data identifydata; - struct msg_environ_data environdata; - ssize_t n, datalen; - - if ((n = imsg_read(&c->ibuf)) == -1 || n == 0) - return (-1); - - for (;;) { - if ((n = imsg_get(&c->ibuf, &imsg)) == -1) - return (-1); - if (n == 0) - return (0); - datalen = imsg.hdr.len - IMSG_HEADER_SIZE; - - if (imsg.hdr.peerid != PROTOCOL_VERSION) { - server_write_client(c, MSG_VERSION, NULL, 0); - c->flags |= CLIENT_BAD; - imsg_free(&imsg); - continue; - } - - log_debug("got %d from client %d", imsg.hdr.type, c->ibuf.fd); - switch (imsg.hdr.type) { - case MSG_COMMAND: - if (datalen != sizeof commanddata) - fatalx("bad MSG_COMMAND size"); - memcpy(&commanddata, imsg.data, sizeof commanddata); - - server_msg_command(c, &commanddata); - break; - case MSG_IDENTIFY: - if (datalen != sizeof identifydata) - fatalx("bad MSG_IDENTIFY size"); - if (imsg.fd == -1) - fatalx("MSG_IDENTIFY missing fd"); - memcpy(&identifydata, imsg.data, sizeof identifydata); - - server_msg_identify(c, &identifydata, imsg.fd); - break; - case MSG_RESIZE: - if (datalen != 0) - fatalx("bad MSG_RESIZE size"); - - tty_resize(&c->tty); - recalculate_sizes(); - server_redraw_client(c); - break; - case MSG_EXITING: - if (datalen != 0) - fatalx("bad MSG_EXITING size"); - - c->session = NULL; - tty_close(&c->tty); - server_write_client(c, MSG_EXITED, NULL, 0); - break; - case MSG_WAKEUP: - case MSG_UNLOCK: - if (datalen != 0) - fatalx("bad MSG_WAKEUP size"); - - if (!(c->flags & CLIENT_SUSPENDED)) - break; - c->flags &= ~CLIENT_SUSPENDED; - tty_start_tty(&c->tty); - server_redraw_client(c); - recalculate_sizes(); - if (c->session != NULL) - c->session->activity = time(NULL); - break; - case MSG_ENVIRON: - if (datalen != sizeof environdata) - fatalx("bad MSG_ENVIRON size"); - memcpy(&environdata, imsg.data, sizeof environdata); - - environdata.var[(sizeof environdata.var) - 1] = '\0'; - if (strchr(environdata.var, '=') != NULL) - environ_put(&c->environ, environdata.var); - break; - case MSG_SHELL: - if (datalen != 0) - fatalx("bad MSG_SHELL size"); - - server_msg_shell(c); - break; - default: - fatalx("unexpected message"); - } - - imsg_free(&imsg); - } -} - -void printflike2 -server_msg_command_error(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct msg_print_data data; - va_list ap; - - va_start(ap, fmt); - xvsnprintf(data.msg, sizeof data.msg, fmt, ap); - va_end(ap); - - server_write_client(ctx->cmdclient, MSG_ERROR, &data, sizeof data); -} - -void printflike2 -server_msg_command_print(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct msg_print_data data; - va_list ap; - - va_start(ap, fmt); - xvsnprintf(data.msg, sizeof data.msg, fmt, ap); - va_end(ap); - - server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); -} - -void printflike2 -server_msg_command_info(struct cmd_ctx *ctx, const char *fmt, ...) -{ - struct msg_print_data data; - va_list ap; - - if (be_quiet) - return; - - va_start(ap, fmt); - xvsnprintf(data.msg, sizeof data.msg, fmt, ap); - va_end(ap); - - server_write_client(ctx->cmdclient, MSG_PRINT, &data, sizeof data); -} - -void -server_msg_command(struct client *c, struct msg_command_data *data) -{ - struct cmd_ctx ctx; - struct cmd_list *cmdlist = NULL; - struct cmd *cmd; - int argc; - char **argv, *cause; - - if (c->session != NULL) - c->session->activity = time(NULL); - - ctx.error = server_msg_command_error; - ctx.print = server_msg_command_print; - ctx.info = server_msg_command_info; - - ctx.msgdata = data; - ctx.curclient = NULL; - - ctx.cmdclient = c; - - argc = data->argc; - data->argv[(sizeof data->argv) - 1] = '\0'; - if (cmd_unpack_argv(data->argv, sizeof data->argv, argc, &argv) != 0) { - server_msg_command_error(&ctx, "command too long"); - goto error; - } - - if (argc == 0) { - argc = 1; - argv = xcalloc(1, sizeof *argv); - *argv = xstrdup("new-session"); - } - - if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) { - server_msg_command_error(&ctx, "%s", cause); - cmd_free_argv(argc, argv); - goto error; - } - cmd_free_argv(argc, argv); - - if (data->pid != -1) { - TAILQ_FOREACH(cmd, cmdlist, qentry) { - if (cmd->entry->flags & CMD_CANTNEST) { - server_msg_command_error(&ctx, - "sessions should be nested with care. " - "unset $TMUX to force"); - goto error; - } - } - } - - if (cmd_list_exec(cmdlist, &ctx) != 1) - server_write_client(c, MSG_EXIT, NULL, 0); - cmd_list_free(cmdlist); - return; - -error: - if (cmdlist != NULL) - cmd_list_free(cmdlist); - server_write_client(c, MSG_EXIT, NULL, 0); -} - -void -server_msg_identify(struct client *c, struct msg_identify_data *data, int fd) -{ - c->cwd = NULL; - data->cwd[(sizeof data->cwd) - 1] = '\0'; - if (*data->cwd != '\0') - c->cwd = xstrdup(data->cwd); - - data->term[(sizeof data->term) - 1] = '\0'; - tty_init(&c->tty, fd, data->term); - if (data->flags & IDENTIFY_UTF8) - c->tty.flags |= TTY_UTF8; - if (data->flags & IDENTIFY_256COLOURS) - c->tty.term_flags |= TERM_256COLOURS; - else if (data->flags & IDENTIFY_88COLOURS) - c->tty.term_flags |= TERM_88COLOURS; - if (data->flags & IDENTIFY_HASDEFAULTS) - c->tty.term_flags |= TERM_HASDEFAULTS; - - tty_resize(&c->tty); - - c->flags |= CLIENT_TERMINAL; -} - -void -server_msg_shell(struct client *c) -{ - struct msg_shell_data data; - const char *shell; - - shell = options_get_string(&global_s_options, "default-shell"); - - if (*shell == '\0' || areshell(shell)) - shell = _PATH_BSHELL; - if (strlcpy(data.shell, shell, sizeof data.shell) >= sizeof data.shell) - strlcpy(data.shell, _PATH_BSHELL, sizeof data.shell); - - server_write_client(c, MSG_SHELL, &data, sizeof data); - c->flags |= CLIENT_BAD; /* it will die after exec */ -} diff --git a/server-window.c b/server-window.c new file mode 100644 index 00000000..fe554afd --- /dev/null +++ b/server-window.c @@ -0,0 +1,281 @@ +/* $Id: server-window.c,v 1.1 2009-10-23 17:49:47 tcunha Exp $ */ + +/* + * Copyright (c) 2009 Nicholas Marriott + * + * 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 + +#include + +#include "tmux.h" + +int server_window_check_bell(struct session *, struct window *); +int server_window_check_activity(struct session *, struct window *); +int server_window_check_content( + struct session *, struct window *, struct window_pane *); +void server_window_check_alive(struct window *); + +/* Process a single window pane event. */ +void +server_window_callback(int fd, int events, void *data) +{ + struct window_pane *wp = data; + + if (wp->fd == -1) + return; + + if (fd == wp->fd) { + if (buffer_poll(fd, events, wp->in, wp->out) != 0) { + close(wp->fd); + wp->fd = -1; + } else + window_pane_parse(wp); + } + + if (fd == wp->pipe_fd) { + if (buffer_poll(fd, events, NULL, wp->pipe_buf) != 0) { + buffer_destroy(wp->pipe_buf); + close(wp->pipe_fd); + wp->pipe_fd = -1; + } + } +} + +/* Window functions that need to happen every loop. */ +void +server_window_loop(void) +{ + struct window *w; + struct window_pane *wp; + struct session *s; + u_int i, j; + + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) + continue; + + for (j = 0; j < ARRAY_LENGTH(&sessions); j++) { + s = ARRAY_ITEM(&sessions, j); + if (s == NULL || !session_has(s, w)) + continue; + + if (server_window_check_bell(s, w) || + server_window_check_activity(s, w)) + server_status_session(s); + TAILQ_FOREACH(wp, &w->panes, entry) + server_window_check_content(s, w, wp); + } + w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT); + + server_window_check_alive(w); + } + + set_window_names(); +} + +/* Check for bell in window. */ +int +server_window_check_bell(struct session *s, struct window *w) +{ + struct client *c; + u_int i; + int action, visual; + + if (!(w->flags & WINDOW_BELL)) + return (0); + + if (session_alert_has_window(s, w, WINDOW_BELL)) + return (0); + session_alert_add(s, w, WINDOW_BELL); + + action = options_get_number(&s->options, "bell-action"); + switch (action) { + case BELL_ANY: + if (s->flags & SESSION_UNATTACHED) + break; + visual = options_get_number(&s->options, "visual-bell"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (!visual) { + tty_putcode(&c->tty, TTYC_BEL); + continue; + } + if (c->session->curw->window == w) { + status_message_set(c, "Bell in current window"); + continue; + } + status_message_set(c, "Bell in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + break; + case BELL_CURRENT: + if (s->flags & SESSION_UNATTACHED) + break; + visual = options_get_number(&s->options, "visual-bell"); + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + if (c->session->curw->window != w) + continue; + if (!visual) { + tty_putcode(&c->tty, TTYC_BEL); + continue; + } + status_message_set(c, "Bell in current window"); + } + break; + } + + return (1); +} + +/* Check for activity in window. */ +int +server_window_check_activity(struct session *s, struct window *w) +{ + struct client *c; + u_int i; + + if (!(w->flags & WINDOW_ACTIVITY)) + return (0); + if (s->curw->window == w) + return (0); + + if (!options_get_number(&w->options, "monitor-activity")) + return (0); + + if (session_alert_has_window(s, w, WINDOW_ACTIVITY)) + return (0); + session_alert_add(s, w, WINDOW_ACTIVITY); + + if (s->flags & SESSION_UNATTACHED) + return (0); + if (options_get_number(&s->options, "visual-activity")) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + status_message_set(c, "Activity in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + } + + return (1); +} + +/* Check for content change in window. */ +int +server_window_check_content( + struct session *s, struct window *w, struct window_pane *wp) +{ + struct client *c; + u_int i; + char *found, *ptr; + + if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */ + return (0); + if (s->curw->window == w) + return (0); + + ptr = options_get_string(&w->options, "monitor-content"); + if (ptr == NULL || *ptr == '\0') + return (0); + + if (session_alert_has_window(s, w, WINDOW_CONTENT)) + return (0); + + if ((found = window_pane_search(wp, ptr, NULL)) == NULL) + return (0); + xfree(found); + + session_alert_add(s, w, WINDOW_CONTENT); + if (s->flags & SESSION_UNATTACHED) + return (0); + if (options_get_number(&s->options, "visual-content")) { + for (i = 0; i < ARRAY_LENGTH(&clients); i++) { + c = ARRAY_ITEM(&clients, i); + if (c == NULL || c->session != s) + continue; + status_message_set(c, "Content in window %u", + winlink_find_by_window(&s->windows, w)->idx); + } + } + + return (1); +} + +/* Check if window still exists. */ +void +server_window_check_alive(struct window *w) +{ + struct window_pane *wp, *wq; + struct options *oo = &w->options; + struct session *s; + struct winlink *wl; + u_int i; + int destroyed; + + destroyed = 1; + + wp = TAILQ_FIRST(&w->panes); + while (wp != NULL) { + wq = TAILQ_NEXT(wp, entry); + /* + * If the pane has died and the remain-on-exit flag is not set, + * remove the pane; otherwise, if the flag is set, don't allow + * the window to be destroyed (or it'll close when the last + * pane dies). + */ + if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) { + layout_close_pane(wp); + window_remove_pane(w, wp); + server_redraw_window(w); + } else + destroyed = 0; + wp = wq; + } + + if (!destroyed) + return; + + for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { + s = ARRAY_ITEM(&sessions, i); + if (s == NULL) + continue; + if (!session_has(s, w)) + continue; + + restart: + /* Detach window and either redraw or kill clients. */ + RB_FOREACH(wl, winlinks, &s->windows) { + if (wl->window != w) + continue; + if (session_detach(s, wl)) { + server_destroy_session_group(s); + break; + } + server_redraw_session(s); + server_status_session_group(s); + goto restart; + } + } + + recalculate_sizes(); +} diff --git a/server.c b/server.c index f59c1664..c2daaf59 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.210 2009-10-15 01:43:16 tcunha Exp $ */ +/* $Id: server.c,v 1.211 2009-10-23 17:49:47 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -46,80 +46,70 @@ struct clients dead_clients; /* Mapping of a pollfd to an fd independent of its position in the array. */ struct poll_item { - struct pollfd pfd; + 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 pollfd *server_poll_lookup(int); -void server_poll_add(int, int); +struct poll_item*server_poll_lookup(int); +void server_poll_add(int, int, void (*)(int, int, void *), void *); struct pollfd *server_poll_flatten(int *); -void server_poll_parse(struct pollfd *); +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); -void server_create_client(int); int server_create_socket(void); +void server_callback(int, int, void *); int server_main(int); void server_shutdown(void); int server_should_shutdown(void); void server_child_signal(void); void server_fill_windows(void); -void server_handle_windows(void); void server_fill_clients(void); -void server_handle_clients(void); void server_fill_jobs(void); -void server_handle_jobs(void); -void server_accept_client(int); -void server_handle_client(struct client *); -void server_handle_window(struct window *, struct window_pane *); -int server_check_window_bell(struct session *, struct window *); -int server_check_window_activity(struct session *, - struct window *); -int server_check_window_content(struct session *, struct window *, - struct window_pane *); void server_clean_dead(void); -void server_lost_client(struct client *); -void server_check_window(struct window *); -void server_check_redraw(struct client *); -void server_set_title(struct client *); -void server_check_timers(struct client *); -void server_check_jobs(void); +void server_second_timers(void); void server_lock_server(void); void server_lock_sessions(void); -void server_check_clients(void); -void server_second_timers(void); int server_update_socket(void); int server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2) { - return (pitem1->pfd.fd - pitem2->pfd.fd); -} - -struct pollfd * -server_poll_lookup(int fd) -{ - struct poll_item pitem; - - pitem.pfd.fd = fd; - return (&RB_FIND(poll_items, &poll_items, &pitem)->pfd); + return (pitem1->fd - pitem2->fd); } void -server_poll_add(int fd, int events) +server_poll_add(int fd, int events, void (*fn)(int, int, void *), void *data) { struct poll_item *pitem; pitem = xmalloc(sizeof *pitem); - pitem->pfd.fd = fd; - pitem->pfd.events = events; + 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) { @@ -130,23 +120,25 @@ server_poll_flatten(int *nfds) *nfds = 0; RB_FOREACH(pitem, poll_items, &poll_items) { pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds); - pfds[*nfds].fd = pitem->pfd.fd; - pfds[*nfds].events = pitem->pfd.events; + pfds[*nfds].fd = pitem->fd; + pfds[*nfds].events = pitem->events; (*nfds)++; } return (pfds); } void -server_poll_parse(struct pollfd *pfds) +server_poll_dispatch(struct pollfd *pfds, int nfds) { struct poll_item *pitem; - int nfds; + struct pollfd *pfd; - nfds = 0; - RB_FOREACH(pitem, poll_items, &poll_items) { - pitem->pfd.revents = pfds[nfds].revents; - nfds++; + 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); } @@ -163,13 +155,34 @@ server_poll_reset(void) } } -/* Create a new client. */ -void -server_create_client(int fd) +/* Create server socket. */ +int +server_create_socket(void) { - struct client *c; - int mode; - u_int i; + struct sockaddr_un sa; + size_t size; + mode_t mask; + int fd, mode; + + memset(&sa, 0, sizeof sa); + sa.sun_family = AF_UNIX; + size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); + if (size >= sizeof sa.sun_path) { + errno = ENAMETOOLONG; + fatal("socket failed"); + } + unlink(sa.sun_path); + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) + fatal("socket failed"); + + mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) + fatal("bind failed"); + umask(mask); + + if (listen(fd, 16) == -1) + fatal("listen failed"); if ((mode = fcntl(fd, F_GETFL)) == -1) fatal("fcntl failed"); @@ -178,39 +191,33 @@ server_create_client(int fd) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) fatal("fcntl failed"); - c = xcalloc(1, sizeof *c); - c->references = 0; - imsg_init(&c->ibuf, fd); - - if (gettimeofday(&c->tv, NULL) != 0) - fatal("gettimeofday failed"); + return (fd); +} - ARRAY_INIT(&c->prompt_hdata); +/* 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; - c->tty.fd = -1; - c->title = NULL; + if (events & (POLLERR|POLLNVAL|POLLHUP)) + fatalx("lost server socket"); + if (!(events & POLLIN)) + return; - c->session = NULL; - c->tty.sx = 80; - c->tty.sy = 24; - - screen_init(&c->status, c->tty.sx, 1, 0); - job_tree_init(&c->status_jobs); - - c->message_string = NULL; - - c->prompt_string = NULL; - c->prompt_buffer = NULL; - c->prompt_index = 0; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == NULL) { - ARRAY_SET(&clients, i, c); + newfd = accept(fd, (struct sockaddr *) &sa, &slen); + if (newfd == -1) { + if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) return; - } + fatal("accept failed"); } - ARRAY_ADD(&clients, c); - log_debug("new client %d", fd); + if (sigterm) { + close(newfd); + return; + } + server_client_create(newfd); } /* Fork new server. */ @@ -270,7 +277,7 @@ server_start(char *path) #endif srv_fd = server_create_socket(); - server_create_client(pair[1]); + server_client_create(pair[1]); if (access(SYSTEM_CFG, R_OK) != 0) { if (errno != ENOENT) { @@ -298,50 +305,11 @@ error: exit(server_main(srv_fd)); } -/* Create server socket. */ -int -server_create_socket(void) -{ - struct sockaddr_un sa; - size_t size; - mode_t mask; - int fd, mode; - - memset(&sa, 0, sizeof sa); - sa.sun_family = AF_UNIX; - size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); - if (size >= sizeof sa.sun_path) { - errno = ENAMETOOLONG; - fatal("socket failed"); - } - unlink(sa.sun_path); - - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) - fatal("socket failed"); - - mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) - fatal("bind failed"); - umask(mask); - - if (listen(fd, 16) == -1) - fatal("listen failed"); - - if ((mode = fcntl(fd, F_GETFL)) == -1) - fatal("fcntl failed"); - if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) - fatal("fcntl failed"); - if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - fatal("fcntl failed"); - - return (fd); -} - /* Main server loop. */ int server_main(int srv_fd) { - struct pollfd *pfds, *pfd; + struct pollfd *pfds; int nfds, xtimeout; u_int i; time_t now, last; @@ -374,13 +342,9 @@ server_main(int srv_fd) sigusr1 = 0; } - /* Collect any jobs that have died and process clients. */ - server_check_jobs(); - server_check_clients(); - /* Initialise pollfd array and add server socket. */ server_poll_reset(); - server_poll_add(srv_fd, POLLIN); + server_poll_add(srv_fd, POLLIN, server_callback, NULL); /* Fill window and client sockets. */ server_fill_jobs(); @@ -399,16 +363,7 @@ server_main(int srv_fd) continue; fatal("poll failed"); } - server_poll_parse(pfds); - - /* Handle server socket. */ - pfd = server_poll_lookup(srv_fd); - if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) - fatalx("lost server socket"); - if (pfd->revents & POLLIN) { - server_accept_client(srv_fd); - continue; - } + server_poll_dispatch(pfds, nfds); /* Call second-based timers. */ now = time(NULL); @@ -417,13 +372,10 @@ server_main(int srv_fd) server_second_timers(); } - /* Set window names. */ - set_window_names(); - - /* Handle window and client sockets. */ - server_handle_jobs(); - server_handle_windows(); - server_handle_clients(); + /* Run once-per-loop events. */ + server_job_loop(); + server_window_loop(); + server_client_loop(); /* Collect any unset key bindings. */ key_bindings_clean(); @@ -441,7 +393,7 @@ server_main(int srv_fd) for (i = 0; i < ARRAY_LENGTH(&clients); i++) { if (ARRAY_ITEM(&clients, i) != NULL) - server_lost_client(ARRAY_ITEM(&clients, i)); + server_client_lost(ARRAY_ITEM(&clients, i)); } ARRAY_FREE(&clients); @@ -471,7 +423,7 @@ server_shutdown(void) c = ARRAY_ITEM(&clients, i); if (c != NULL) { if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) - server_lost_client(c); + server_client_lost(c); else server_write_client(c, MSG_SHUTDOWN, NULL, 0); } @@ -574,195 +526,20 @@ server_fill_windows(void) events = POLLIN; if (BUFFER_USED(wp->out) > 0) events |= POLLOUT; - server_poll_add(wp->fd, events); + server_poll_add( + wp->fd, events, server_window_callback, wp); if (wp->pipe_fd == -1) continue; events = 0; if (BUFFER_USED(wp->pipe_buf) > 0) events |= POLLOUT; - server_poll_add(wp->pipe_fd, events); + server_poll_add( + wp->pipe_fd, events, server_window_callback, wp); } } } -/* Handle window pollfds. */ -void -server_handle_windows(void) -{ - struct window *w; - struct window_pane *wp; - struct pollfd *pfd; - u_int i; - - 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->fd == -1) - continue; - if ((pfd = server_poll_lookup(wp->fd)) == NULL) - continue; - if (buffer_poll(pfd, wp->in, wp->out) != 0) { - close(wp->fd); - wp->fd = -1; - } else - server_handle_window(w, wp); - - if (wp->pipe_fd == -1) - continue; - if ((pfd = server_poll_lookup(wp->pipe_fd)) == NULL) - continue; - if (buffer_poll(pfd, NULL, wp->pipe_buf) != 0) { - buffer_destroy(wp->pipe_buf); - close(wp->pipe_fd); - wp->pipe_fd = -1; - } - } - - server_check_window(w); - } -} - -/* Check clients for redraw and timers. */ -void -server_check_clients(void) -{ - struct client *c; - struct window *w; - struct window_pane *wp; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session == NULL) - continue; - - server_check_timers(c); - server_check_redraw(c); - } - - /* - * Clear any window redraw flags (will have been redrawn as part of - * client). - */ - for (i = 0; i < ARRAY_LENGTH(&windows); i++) { - w = ARRAY_ITEM(&windows, i); - if (w == NULL) - continue; - - w->flags &= ~WINDOW_REDRAW; - TAILQ_FOREACH(wp, &w->panes, entry) - wp->flags &= ~PANE_REDRAW; - } -} - -/* Check for general redraw on client. */ -void -server_check_redraw(struct client *c) -{ - struct session *s = c->session; - struct window_pane *wp; - int flags, redraw; - - flags = c->tty.flags & TTY_FREEZE; - c->tty.flags &= ~TTY_FREEZE; - - if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { - if (options_get_number(&s->options, "set-titles")) - server_set_title(c); - - if (c->message_string != NULL) - redraw = status_message_redraw(c); - else if (c->prompt_string != NULL) - redraw = status_prompt_redraw(c); - else - redraw = status_redraw(c); - if (!redraw) - c->flags &= ~CLIENT_STATUS; - } - - if (c->flags & CLIENT_REDRAW) { - screen_redraw_screen(c, 0); - c->flags &= ~CLIENT_STATUS; - } else { - TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { - if (wp->flags & PANE_REDRAW) - screen_redraw_pane(c, wp); - } - } - - if (c->flags & CLIENT_STATUS) - screen_redraw_screen(c, 1); - - c->tty.flags |= flags; - - c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); -} - -/* Set client title. */ -void -server_set_title(struct client *c) -{ - struct session *s = c->session; - const char *template; - char *title; - - template = options_get_string(&s->options, "set-titles-string"); - - title = status_replace(c, template, time(NULL)); - if (c->title == NULL || strcmp(title, c->title) != 0) { - if (c->title != NULL) - xfree(c->title); - c->title = xstrdup(title); - tty_set_title(&c->tty, c->title); - } - xfree(title); -} - -/* Check for timers on client. */ -void -server_check_timers(struct client *c) -{ - struct session *s = c->session; - struct job *job; - struct timeval tv; - u_int interval; - - if (gettimeofday(&tv, NULL) != 0) - fatal("gettimeofday failed"); - - if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >)) - server_clear_identify(c); - - if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >)) - status_message_clear(c); - - if (c->message_string != NULL || c->prompt_string != NULL) { - /* - * Don't need timed redraw for messages/prompts so bail now. - * The status timer isn't reset when they are redrawn anyway. - */ - return; - } - if (!options_get_number(&s->options, "status")) - return; - - /* Check timer; resolution is only a second so don't be too clever. */ - interval = options_get_number(&s->options, "status-interval"); - if (interval == 0) - return; - if (tv.tv_sec < c->status_timer.tv_sec || - ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) { - /* Run the jobs for this client and schedule for redraw. */ - RB_FOREACH(job, jobs, &c->status_jobs) - job_run(job); - c->flags |= CLIENT_STATUS; - } -} - /* Fill client pollfds. */ void server_fill_clients(void) @@ -780,7 +557,8 @@ server_fill_clients(void) events |= POLLIN; if (c->ibuf.w.queued > 0) events |= POLLOUT; - server_poll_add(c->ibuf.fd, events); + server_poll_add( + c->ibuf.fd, events, server_client_callback, c); } if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && @@ -788,7 +566,8 @@ server_fill_clients(void) events = POLLIN; if (BUFFER_USED(c->tty.out) > 0) events |= POLLOUT; - server_poll_add(c->tty.fd, events); + server_poll_add( + c->tty.fd, events, server_client_callback, c); } } } @@ -802,328 +581,10 @@ server_fill_jobs(void) SLIST_FOREACH(job, &all_jobs, lentry) { if (job->fd == -1) continue; - server_poll_add(job->fd, POLLIN); + server_poll_add(job->fd, POLLIN, server_job_callback, job); } } -/* Handle job fds. */ -void -server_handle_jobs(void) -{ - struct job *job; - struct pollfd *pfd; - - SLIST_FOREACH(job, &all_jobs, lentry) { - if (job->fd == -1) - continue; - if ((pfd = server_poll_lookup(job->fd)) == NULL) - continue; - if (buffer_poll(pfd, job->out, NULL) != 0) { - close(job->fd); - job->fd = -1; - } - } -} - -/* Handle job fds. */ -void -server_check_jobs(void) -{ - struct job *job; - -restart: - SLIST_FOREACH(job, &all_jobs, lentry) { - if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1) - continue; - job->flags |= JOB_DONE; - - if (job->callbackfn != NULL) { - job->callbackfn(job); - goto restart; /* could be freed by callback */ - } - } -} - -/* Handle client pollfds. */ -void -server_handle_clients(void) -{ - struct client *c; - struct pollfd *pfd; - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - - if (c != NULL) { - if ((pfd = server_poll_lookup(c->ibuf.fd)) == NULL) - continue; - if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) { - server_lost_client(c); - continue; - } - - if (pfd->revents & POLLOUT) { - if (msgbuf_write(&c->ibuf.w) < 0) { - server_lost_client(c); - continue; - } - } - - if (c->flags & CLIENT_BAD) { - if (c->ibuf.w.queued == 0) - server_lost_client(c); - continue; - } else if (pfd->revents & POLLIN) { - if (server_msg_dispatch(c) != 0) { - server_lost_client(c); - continue; - } - } - } - - if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && - c->tty.fd != -1 && c->session != NULL) { - if ((pfd = server_poll_lookup(c->tty.fd)) == NULL) - continue; - if (buffer_poll(pfd, c->tty.in, c->tty.out) != 0) - server_lost_client(c); - else - server_handle_client(c); - } - } -} - -/* accept(2) and create new client. */ -void -server_accept_client(int srv_fd) -{ - struct sockaddr_storage sa; - socklen_t slen = sizeof sa; - int fd; - - fd = accept(srv_fd, (struct sockaddr *) &sa, &slen); - if (fd == -1) { - if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) - return; - fatal("accept failed"); - } - if (sigterm) { - close(fd); - return; - } - server_create_client(fd); -} - -/* Input data from client. */ -void -server_handle_client(struct client *c) -{ - struct window *w; - struct window_pane *wp; - struct screen *s; - struct options *oo; - struct timeval tv; - struct key_binding *bd; - struct keylist *keylist; - struct mouse_event mouse; - int key, status, xtimeout, mode, isprefix; - u_int i; - - xtimeout = options_get_number(&c->session->options, "repeat-time"); - if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { - if (gettimeofday(&tv, NULL) != 0) - fatal("gettimeofday failed"); - if (timercmp(&tv, &c->repeat_timer, >)) - c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); - } - - /* Process keys. */ - keylist = options_get_data(&c->session->options, "prefix"); - while (tty_keys_next(&c->tty, &key, &mouse) == 0) { - if (c->session == NULL) - return; - - c->session->activity = time(NULL); - w = c->session->curw->window; - wp = w->active; /* could die */ - oo = &c->session->options; - - /* Special case: number keys jump to pane in identify mode. */ - if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { - wp = window_pane_at_index(w, key - '0'); - if (wp != NULL && window_pane_visible(wp)) - window_set_active_pane(w, wp); - server_clear_identify(c); - continue; - } - - status_message_clear(c); - server_clear_identify(c); - if (c->prompt_string != NULL) { - status_prompt_key(c, key); - continue; - } - - /* Check for mouse keys. */ - if (key == KEYC_MOUSE) { - if (options_get_number(oo, "mouse-select-pane")) { - window_set_active_at(w, mouse.x, mouse.y); - wp = w->active; - } - window_pane_mouse(wp, c, &mouse); - continue; - } - - /* Is this a prefix key? */ - isprefix = 0; - for (i = 0; i < ARRAY_LENGTH(keylist); i++) { - if (key == ARRAY_ITEM(keylist, i)) { - isprefix = 1; - break; - } - } - - /* No previous prefix key. */ - if (!(c->flags & CLIENT_PREFIX)) { - if (isprefix) - c->flags |= CLIENT_PREFIX; - else { - /* Try as a non-prefix key binding. */ - if ((bd = key_bindings_lookup(key)) == NULL) - window_pane_key(wp, c, key); - else - key_bindings_dispatch(bd, c); - } - continue; - } - - /* Prefix key already pressed. Reset prefix and lookup key. */ - c->flags &= ~CLIENT_PREFIX; - if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { - /* If repeating, treat this as a key, else ignore. */ - if (c->flags & CLIENT_REPEAT) { - c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else - window_pane_key(wp, c, key); - } - continue; - } - - /* If already repeating, but this key can't repeat, skip it. */ - if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { - c->flags &= ~CLIENT_REPEAT; - if (isprefix) - c->flags |= CLIENT_PREFIX; - else - window_pane_key(wp, c, key); - continue; - } - - /* If this key can repeat, reset the repeat flags and timer. */ - if (xtimeout != 0 && bd->can_repeat) { - c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; - - tv.tv_sec = xtimeout / 1000; - tv.tv_usec = (xtimeout % 1000) * 1000L; - if (gettimeofday(&c->repeat_timer, NULL) != 0) - fatal("gettimeofday failed"); - timeradd(&c->repeat_timer, &tv, &c->repeat_timer); - } - - /* Dispatch the command. */ - key_bindings_dispatch(bd, c); - } - if (c->session == NULL) - return; - w = c->session->curw->window; - wp = w->active; - oo = &c->session->options; - s = wp->screen; - - /* - * Update cursor position and mode settings. The scroll region and - * attributes are cleared across poll(2) as this is the most likely - * time a user may interrupt tmux, for example with ~^Z in ssh(1). This - * is a compromise between excessive resets and likelihood of an - * interrupt. - * - * tty_region/tty_reset/tty_update_mode already take care of not - * resetting things that are already in their default state. - */ - tty_region(&c->tty, 0, c->tty.sy - 1); - - status = options_get_number(oo, "status"); - if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) - tty_cursor(&c->tty, 0, 0); - else - tty_cursor(&c->tty, wp->xoff + s->cx, wp->yoff + s->cy); - - mode = s->mode; - if (TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry) != NULL && - options_get_number(oo, "mouse-select-pane")) - mode |= MODE_MOUSE; - tty_update_mode(&c->tty, mode); - tty_reset(&c->tty); -} - -/* Lost a client. */ -void -server_lost_client(struct client *c) -{ - u_int i; - - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - if (ARRAY_ITEM(&clients, i) == c) - ARRAY_SET(&clients, i, NULL); - } - log_debug("lost client %d", c->ibuf.fd); - - /* - * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called - * and tty_free might close an unrelated fd. - */ - if (c->flags & CLIENT_TERMINAL) - tty_free(&c->tty); - - screen_free(&c->status); - job_tree_free(&c->status_jobs); - - if (c->title != NULL) - xfree(c->title); - - if (c->message_string != NULL) - xfree(c->message_string); - - if (c->prompt_string != NULL) - xfree(c->prompt_string); - if (c->prompt_buffer != NULL) - xfree(c->prompt_buffer); - for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++) - xfree(ARRAY_ITEM(&c->prompt_hdata, i)); - ARRAY_FREE(&c->prompt_hdata); - - if (c->cwd != NULL) - xfree(c->cwd); - - close(c->ibuf.fd); - imsg_clear(&c->ibuf); - - for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { - if (ARRAY_ITEM(&dead_clients, i) == NULL) { - ARRAY_SET(&dead_clients, i, c); - break; - } - } - if (i == ARRAY_LENGTH(&dead_clients)) - ARRAY_ADD(&dead_clients, c); - c->flags |= CLIENT_DEAD; - - recalculate_sizes(); -} - /* Free dead, unreferenced clients and sessions. */ void server_clean_dead(void) @@ -1149,221 +610,29 @@ server_clean_dead(void) } } -/* Handle window data. */ +/* Call any once-per-second timers. */ void -server_handle_window(struct window *w, struct window_pane *wp) +server_second_timers(void) { - struct session *s; - u_int i; - int update; - - window_pane_parse(wp); - - if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0) - return; - - update = 0; - for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - s = ARRAY_ITEM(&sessions, i); - if (s == NULL || !session_has(s, w)) - continue; - - update += server_check_window_bell(s, w); - update += server_check_window_activity(s, w); - update += server_check_window_content(s, w, wp); - } - if (update) - server_status_window(w); - - w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT); -} - -int -server_check_window_bell(struct session *s, struct window *w) -{ - struct client *c; - u_int i; - int action, visual; - - if (!(w->flags & WINDOW_BELL)) - return (0); - if (session_alert_has_window(s, w, WINDOW_BELL)) - return (0); - session_alert_add(s, w, WINDOW_BELL); - - action = options_get_number(&s->options, "bell-action"); - switch (action) { - case BELL_ANY: - if (s->flags & SESSION_UNATTACHED) - break; - visual = options_get_number(&s->options, "visual-bell"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - if (!visual) { - tty_putcode(&c->tty, TTYC_BEL); - continue; - } - if (c->session->curw->window == w) { - status_message_set(c, "Bell in current window"); - continue; - } - status_message_set(c, "Bell in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - break; - case BELL_CURRENT: - if (s->flags & SESSION_UNATTACHED) - break; - visual = options_get_number(&s->options, "visual-bell"); - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - if (c->session->curw->window != w) - continue; - if (!visual) { - tty_putcode(&c->tty, TTYC_BEL); - continue; - } - status_message_set(c, "Bell in current window"); - } - break; - } - return (1); -} - -int -server_check_window_activity(struct session *s, struct window *w) -{ - struct client *c; - u_int i; - - if (!(w->flags & WINDOW_ACTIVITY)) - return (0); - - if (!options_get_number(&w->options, "monitor-activity")) - return (0); - - if (session_alert_has_window(s, w, WINDOW_ACTIVITY)) - return (0); - if (s->curw->window == w) - return (0); - - session_alert_add(s, w, WINDOW_ACTIVITY); - if (s->flags & SESSION_UNATTACHED) - return (0); - if (options_get_number(&s->options, "visual-activity")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - status_message_set(c, "Activity in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - } - - return (1); -} - -int -server_check_window_content( - struct session *s, struct window *w, struct window_pane *wp) -{ - struct client *c; - u_int i; - char *found, *ptr; - - if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */ - return (0); - - ptr = options_get_string(&w->options, "monitor-content"); - if (ptr == NULL || *ptr == '\0') - return (0); - - if (session_alert_has_window(s, w, WINDOW_CONTENT)) - return (0); - if (s->curw->window == w) - return (0); - - if ((found = window_pane_search(wp, ptr, NULL)) == NULL) - return (0); - xfree(found); - - session_alert_add(s, w, WINDOW_CONTENT); - if (s->flags & SESSION_UNATTACHED) - return (0); - if (options_get_number(&s->options, "visual-content")) { - for (i = 0; i < ARRAY_LENGTH(&clients); i++) { - c = ARRAY_ITEM(&clients, i); - if (c == NULL || c->session != s) - continue; - status_message_set(c, "Content in window %u", - winlink_find_by_window(&s->windows, w)->idx); - } - } - - return (1); -} - -/* Check if window still exists. */ -void -server_check_window(struct window *w) -{ - struct window_pane *wp, *wq; - struct options *oo = &w->options; - struct session *s; - struct winlink *wl; + struct window *w; + struct window_pane *wp; u_int i; - int destroyed; - destroyed = 1; + if (options_get_number(&global_s_options, "lock-server")) + server_lock_server(); + else + server_lock_sessions(); - wp = TAILQ_FIRST(&w->panes); - while (wp != NULL) { - wq = TAILQ_NEXT(wp, entry); - /* - * If the pane has died and the remain-on-exit flag is not set, - * remove the pane; otherwise, if the flag is set, don't allow - * the window to be destroyed (or it'll close when the last - * pane dies). - */ - if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) { - layout_close_pane(wp); - window_remove_pane(w, wp); - server_redraw_window(w); - } else - destroyed = 0; - wp = wq; - } - - if (!destroyed) - return; - - for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { - s = ARRAY_ITEM(&sessions, i); - if (s == NULL) - continue; - if (!session_has(s, w)) + for (i = 0; i < ARRAY_LENGTH(&windows); i++) { + w = ARRAY_ITEM(&windows, i); + if (w == NULL) continue; - restart: - /* Detach window and either redraw or kill clients. */ - RB_FOREACH(wl, winlinks, &s->windows) { - if (wl->window != w) - continue; - if (session_detach(s, wl)) { - server_destroy_session_group(s); - break; - } - server_redraw_session(s); - server_status_session_group(s); - goto restart; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (wp->mode != NULL && wp->mode->timer != NULL) + wp->mode->timer(wp); } } - - recalculate_sizes(); } /* Lock the server if ALL sessions have hit the time limit. */ @@ -1421,31 +690,6 @@ server_lock_sessions(void) } } -/* Call any once-per-second timers. */ -void -server_second_timers(void) -{ - struct window *w; - struct window_pane *wp; - u_int i; - - if (options_get_number(&global_s_options, "lock-server")) - server_lock_server(); - else - server_lock_sessions(); - - 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->mode != NULL && wp->mode->timer != NULL) - wp->mode->timer(wp); - } - } -} - /* Update socket execute permissions based on whether sessions are attached. */ int server_update_socket(void) diff --git a/tmux.h b/tmux.h index 03414ad7..a39001cb 100644 --- a/tmux.h +++ b/tmux.h @@ -1,4 +1,4 @@ -/* $Id: tmux.h,v 1.483 2009-10-23 17:32:26 tcunha Exp $ */ +/* $Id: tmux.h,v 1.484 2009-10-23 17:49:47 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -1515,8 +1515,19 @@ extern struct clients clients; extern struct clients dead_clients; int server_start(char *); -/* server-msg.c */ -int server_msg_dispatch(struct client *); +/* server-client.c */ +void server_client_create(int); +void server_client_lost(struct client *); +void server_client_callback(int, int, void *); +void server_client_loop(void); + +/* server-job.c */ +void server_job_callback(int, int, void *); +void server_job_loop(void); + +/* server-window.c */ +void server_window_callback(int, int, void *); +void server_window_loop(void); /* server-fn.c */ void server_fill_environ(struct session *, struct environ *); @@ -1848,7 +1859,7 @@ void buffer_write8(struct buffer *, uint8_t); uint8_t buffer_read8(struct buffer *); /* buffer-poll.c */ -int buffer_poll(struct pollfd *, struct buffer *, struct buffer *); +int buffer_poll(int, int, struct buffer *, struct buffer *); /* log.c */ void log_open_tty(int);