Annotation of src/usr.bin/tmux/server.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
! 15: * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
! 16: * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/types.h>
! 20: #include <sys/ioctl.h>
! 21: #include <sys/socket.h>
! 22: #include <sys/stat.h>
! 23: #include <sys/un.h>
! 24: #include <sys/wait.h>
! 25:
! 26: #include <errno.h>
! 27: #include <fcntl.h>
! 28: #include <signal.h>
! 29: #include <stdio.h>
! 30: #include <stdlib.h>
! 31: #include <string.h>
! 32: #include <syslog.h>
! 33: #include <termios.h>
! 34: #include <time.h>
! 35: #include <unistd.h>
! 36:
! 37: #include "tmux.h"
! 38:
! 39: /*
! 40: * Main server functions.
! 41: */
! 42:
! 43: /* Client list. */
! 44: struct clients clients;
! 45:
! 46: int server_create_socket(void);
! 47: int server_main(int);
! 48: void server_shutdown(void);
! 49: void server_child_signal(void);
! 50: void server_fill_windows(struct pollfd **);
! 51: void server_handle_windows(struct pollfd **);
! 52: void server_fill_clients(struct pollfd **);
! 53: void server_handle_clients(struct pollfd **);
! 54: struct client *server_accept_client(int);
! 55: void server_handle_client(struct client *);
! 56: void server_handle_window(struct window *, struct window_pane *);
! 57: int server_check_window_bell(struct session *, struct window *,
! 58: struct window_pane *);
! 59: int server_check_window_activity(struct session *,
! 60: struct window *);
! 61: int server_check_window_content(struct session *, struct window *,
! 62: struct window_pane *);
! 63: void server_lost_client(struct client *);
! 64: void server_check_window(struct window *);
! 65: void server_check_redraw(struct client *);
! 66: void server_redraw_locked(struct client *);
! 67: void server_check_timers(struct client *);
! 68: void server_second_timers(void);
! 69: int server_update_socket(void);
! 70:
! 71: /* Create a new client. */
! 72: struct client *
! 73: server_create_client(int fd)
! 74: {
! 75: struct client *c;
! 76: int mode;
! 77: u_int i;
! 78:
! 79: if ((mode = fcntl(fd, F_GETFL)) == -1)
! 80: fatal("fcntl failed");
! 81: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
! 82: fatal("fcntl failed");
! 83: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
! 84: fatal("fcntl failed");
! 85:
! 86: c = xcalloc(1, sizeof *c);
! 87: c->fd = fd;
! 88: c->in = buffer_create(BUFSIZ);
! 89: c->out = buffer_create(BUFSIZ);
! 90:
! 91: ARRAY_INIT(&c->prompt_hdata);
! 92:
! 93: c->tty.fd = -1;
! 94: c->title = NULL;
! 95:
! 96: c->session = NULL;
! 97: c->tty.sx = 80;
! 98: c->tty.sy = 25;
! 99: screen_init(&c->status, c->tty.sx, 1, 0);
! 100:
! 101: c->message_string = NULL;
! 102:
! 103: c->prompt_string = NULL;
! 104: c->prompt_buffer = NULL;
! 105: c->prompt_index = 0;
! 106:
! 107: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 108: if (ARRAY_ITEM(&clients, i) == NULL) {
! 109: ARRAY_SET(&clients, i, c);
! 110: return (c);
! 111: }
! 112: }
! 113: ARRAY_ADD(&clients, c);
! 114: return (c);
! 115: }
! 116:
! 117: /* Find client index. */
! 118: int
! 119: server_client_index(struct client *c)
! 120: {
! 121: u_int i;
! 122:
! 123: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 124: if (c == ARRAY_ITEM(&clients, i))
! 125: return (i);
! 126: }
! 127: return (-1);
! 128: }
! 129:
! 130: /* Fork new server. */
! 131: int
! 132: server_start(char *path)
! 133: {
! 134: int pair[2], srv_fd;
! 135: char *cause;
! 136: char rpathbuf[MAXPATHLEN];
! 137:
! 138: /* The first client is special and gets a socketpair; create it. */
! 139: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
! 140: fatal("socketpair failed");
! 141:
! 142: switch (fork()) {
! 143: case -1:
! 144: fatal("fork failed");
! 145: case 0:
! 146: break;
! 147: default:
! 148: close(pair[1]);
! 149: return (pair[0]);
! 150: }
! 151: close(pair[0]);
! 152:
! 153: /*
! 154: * Must daemonise before loading configuration as the PID changes so
! 155: * $TMUX would be wrong for sessions created in the config file.
! 156: */
! 157: if (daemon(1, 1) != 0)
! 158: fatal("daemon failed");
! 159:
! 160: ARRAY_INIT(&windows);
! 161: ARRAY_INIT(&clients);
! 162: ARRAY_INIT(&sessions);
! 163: key_bindings_init();
! 164: utf8_build();
! 165:
! 166: server_locked = 0;
! 167: server_password = NULL;
! 168: server_activity = time(NULL);
! 169:
! 170: start_time = time(NULL);
! 171: socket_path = path;
! 172:
! 173: if (cfg_file != NULL && load_cfg(cfg_file, &cause) != 0) {
! 174: log_warnx("%s", cause);
! 175: exit(1);
! 176: }
! 177: logfile("server");
! 178:
! 179: log_debug("server started, pid %ld", (long) getpid());
! 180: log_debug("socket path %s", socket_path);
! 181:
! 182: if (realpath(socket_path, rpathbuf) == NULL)
! 183: strlcpy(rpathbuf, socket_path, sizeof rpathbuf);
! 184: setproctitle("server (%s)", rpathbuf);
! 185:
! 186: srv_fd = server_create_socket();
! 187: server_create_client(pair[1]);
! 188:
! 189: exit(server_main(srv_fd));
! 190: }
! 191:
! 192: /* Create server socket. */
! 193: int
! 194: server_create_socket(void)
! 195: {
! 196: struct sockaddr_un sa;
! 197: size_t size;
! 198: mode_t mask;
! 199: int fd, mode;
! 200:
! 201: memset(&sa, 0, sizeof sa);
! 202: sa.sun_family = AF_UNIX;
! 203: size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
! 204: if (size >= sizeof sa.sun_path) {
! 205: errno = ENAMETOOLONG;
! 206: fatal("socket failed");
! 207: }
! 208: unlink(sa.sun_path);
! 209:
! 210: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
! 211: fatal("socket failed");
! 212:
! 213: mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
! 214: if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1)
! 215: fatal("bind failed");
! 216: umask(mask);
! 217:
! 218: if (listen(fd, 16) == -1)
! 219: fatal("listen failed");
! 220:
! 221: if ((mode = fcntl(fd, F_GETFL)) == -1)
! 222: fatal("fcntl failed");
! 223: if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
! 224: fatal("fcntl failed");
! 225: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
! 226: fatal("fcntl failed");
! 227:
! 228: return (fd);
! 229: }
! 230:
! 231: /* Main server loop. */
! 232: int
! 233: server_main(int srv_fd)
! 234: {
! 235: struct window *w;
! 236: struct pollfd *pfds, *pfd;
! 237: int nfds, xtimeout;
! 238: u_int i, n;
! 239: time_t now, last;
! 240:
! 241: siginit();
! 242:
! 243: last = time(NULL);
! 244:
! 245: pfds = NULL;
! 246: for (;;) {
! 247: /* If sigterm, kill all windows and clients. */
! 248: if (sigterm)
! 249: server_shutdown();
! 250:
! 251: /* Handle child exit. */
! 252: if (sigchld) {
! 253: server_child_signal();
! 254: sigchld = 0;
! 255: }
! 256:
! 257: /* Recreate socket on SIGUSR1. */
! 258: if (sigusr1) {
! 259: close(srv_fd);
! 260: srv_fd = server_create_socket();
! 261: sigusr1 = 0;
! 262: }
! 263:
! 264: /* Initialise pollfd array. */
! 265: nfds = 1;
! 266: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 267: w = ARRAY_ITEM(&windows, i);
! 268: if (w != NULL)
! 269: nfds += window_count_panes(w);
! 270: }
! 271: nfds += ARRAY_LENGTH(&clients) * 2;
! 272: pfds = xrealloc(pfds, nfds, sizeof *pfds);
! 273: memset(pfds, 0, nfds * sizeof *pfds);
! 274: pfd = pfds;
! 275:
! 276: /* Fill server socket. */
! 277: pfd->fd = srv_fd;
! 278: pfd->events = POLLIN;
! 279: pfd++;
! 280:
! 281: /* Fill window and client sockets. */
! 282: server_fill_windows(&pfd);
! 283: server_fill_clients(&pfd);
! 284:
! 285: /* Update socket permissions. */
! 286: xtimeout = INFTIM;
! 287: if (sigterm || server_update_socket() != 0)
! 288: xtimeout = POLL_TIMEOUT;
! 289:
! 290: /* Do the poll. */
! 291: if ((nfds = poll(pfds, nfds, xtimeout)) == -1) {
! 292: if (errno == EAGAIN || errno == EINTR)
! 293: continue;
! 294: fatal("poll failed");
! 295: }
! 296: pfd = pfds;
! 297:
! 298: /* Handle server socket. */
! 299: if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP))
! 300: fatalx("lost server socket");
! 301: if (pfd->revents & POLLIN) {
! 302: server_accept_client(srv_fd);
! 303: continue;
! 304: }
! 305: pfd++;
! 306:
! 307: /* Call second-based timers. */
! 308: now = time(NULL);
! 309: if (now != last) {
! 310: last = now;
! 311: server_second_timers();
! 312: }
! 313:
! 314: /* Set window names. */
! 315: set_window_names();
! 316:
! 317: /*
! 318: * Handle window and client sockets. Clients can create
! 319: * windows, so windows must come first to avoid messing up by
! 320: * increasing the array size.
! 321: */
! 322: server_handle_windows(&pfd);
! 323: server_handle_clients(&pfd);
! 324:
! 325: /*
! 326: * If we have no sessions and clients left, let's get out
! 327: * of here...
! 328: */
! 329: n = 0;
! 330: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 331: if (ARRAY_ITEM(&sessions, i) != NULL)
! 332: n++;
! 333: }
! 334: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 335: if (ARRAY_ITEM(&clients, i) != NULL)
! 336: n++;
! 337: }
! 338: if (n == 0)
! 339: break;
! 340: }
! 341: if (pfds != NULL)
! 342: xfree(pfds);
! 343:
! 344: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 345: if (ARRAY_ITEM(&sessions, i) != NULL)
! 346: session_destroy(ARRAY_ITEM(&sessions, i));
! 347: }
! 348: ARRAY_FREE(&sessions);
! 349:
! 350: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 351: if (ARRAY_ITEM(&clients, i) != NULL)
! 352: server_lost_client(ARRAY_ITEM(&clients, i));
! 353: }
! 354: ARRAY_FREE(&clients);
! 355:
! 356: key_bindings_free();
! 357:
! 358: close(srv_fd);
! 359:
! 360: unlink(socket_path);
! 361: xfree(socket_path);
! 362:
! 363: options_free(&global_options);
! 364: options_free(&global_window_options);
! 365: if (server_password != NULL)
! 366: xfree(server_password);
! 367:
! 368: return (0);
! 369: }
! 370:
! 371: /* Kill all clients. */
! 372: void
! 373: server_shutdown(void)
! 374: {
! 375: struct session *s;
! 376: struct client *c;
! 377: u_int i, j;
! 378:
! 379: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 380: s = ARRAY_ITEM(&sessions, i);
! 381: for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
! 382: c = ARRAY_ITEM(&clients, j);
! 383: if (c != NULL && c->session == s) {
! 384: s = NULL;
! 385: break;
! 386: }
! 387: }
! 388: if (s != NULL)
! 389: session_destroy(s);
! 390: }
! 391:
! 392: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 393: c = ARRAY_ITEM(&clients, i);
! 394: if (c != NULL)
! 395: server_write_client(c, MSG_SHUTDOWN, NULL, 0);
! 396: }
! 397: }
! 398:
! 399: /* Handle SIGCHLD. */
! 400: void
! 401: server_child_signal(void)
! 402: {
! 403: struct window *w;
! 404: struct window_pane *wp;
! 405: int status;
! 406: pid_t pid;
! 407: u_int i;
! 408:
! 409: for (;;) {
! 410: switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
! 411: case -1:
! 412: if (errno == ECHILD)
! 413: return;
! 414: fatal("waitpid");
! 415: case 0:
! 416: return;
! 417: }
! 418: if (!WIFSTOPPED(status))
! 419: continue;
! 420: if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
! 421: continue;
! 422:
! 423: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 424: w = ARRAY_ITEM(&windows, i);
! 425: if (w == NULL)
! 426: continue;
! 427: TAILQ_FOREACH(wp, &w->panes, entry) {
! 428: if (wp->pid == pid) {
! 429: if (killpg(pid, SIGCONT) != 0)
! 430: kill(pid, SIGCONT);
! 431: }
! 432: }
! 433: }
! 434: }
! 435: }
! 436:
! 437: /* Fill window pollfds. */
! 438: void
! 439: server_fill_windows(struct pollfd **pfd)
! 440: {
! 441: struct window *w;
! 442: struct window_pane *wp;
! 443: u_int i;
! 444:
! 445: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 446: w = ARRAY_ITEM(&windows, i);
! 447: if (w == NULL)
! 448: continue;
! 449:
! 450: TAILQ_FOREACH(wp, &w->panes, entry) {
! 451: (*pfd)->fd = wp->fd;
! 452: if (wp->fd != -1) {
! 453: (*pfd)->events = POLLIN;
! 454: if (BUFFER_USED(wp->out) > 0)
! 455: (*pfd)->events |= POLLOUT;
! 456: }
! 457: (*pfd)++;
! 458: }
! 459: }
! 460: }
! 461:
! 462: /* Handle window pollfds. */
! 463: void
! 464: server_handle_windows(struct pollfd **pfd)
! 465: {
! 466: struct window *w;
! 467: struct window_pane *wp;
! 468: u_int i;
! 469:
! 470: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 471: w = ARRAY_ITEM(&windows, i);
! 472: if (w == NULL)
! 473: continue;
! 474:
! 475: TAILQ_FOREACH(wp, &w->panes, entry) {
! 476: if (wp->fd != -1) {
! 477: if (buffer_poll(*pfd, wp->in, wp->out) != 0) {
! 478: close(wp->fd);
! 479: wp->fd = -1;
! 480: } else
! 481: server_handle_window(w, wp);
! 482: }
! 483: (*pfd)++;
! 484: }
! 485:
! 486: server_check_window(w);
! 487: }
! 488: }
! 489:
! 490: /* Check for general redraw on client. */
! 491: void
! 492: server_check_redraw(struct client *c)
! 493: {
! 494: struct session *s;
! 495: struct window_pane *wp;
! 496: char title[512];
! 497: int flags, redraw;
! 498:
! 499: if (c == NULL || c->session == NULL)
! 500: return;
! 501: s = c->session;
! 502:
! 503: flags = c->tty.flags & TTY_FREEZE;
! 504: c->tty.flags &= ~TTY_FREEZE;
! 505:
! 506: if (options_get_number(&s->options, "set-titles")) {
! 507: xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"",
! 508: s->name, s->curw->idx, s->curw->window->name,
! 509: s->curw->window->active->screen->title);
! 510: if (c->title == NULL || strcmp(title, c->title) != 0) {
! 511: if (c->title != NULL)
! 512: xfree(c->title);
! 513: c->title = xstrdup(title);
! 514: tty_set_title(&c->tty, c->title);
! 515: }
! 516: }
! 517:
! 518: if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) {
! 519: if (c->message_string != NULL)
! 520: redraw = status_message_redraw(c);
! 521: else if (c->prompt_string != NULL)
! 522: redraw = status_prompt_redraw(c);
! 523: else
! 524: redraw = status_redraw(c);
! 525: if (!redraw)
! 526: c->flags &= ~CLIENT_STATUS;
! 527: }
! 528:
! 529: if (c->flags & CLIENT_REDRAW) {
! 530: if (server_locked)
! 531: server_redraw_locked(c);
! 532: else
! 533: screen_redraw_screen(c);
! 534: c->flags &= ~CLIENT_STATUS;
! 535: } else {
! 536: TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) {
! 537: if (wp->flags & PANE_REDRAW)
! 538: screen_redraw_pane(c, wp);
! 539: }
! 540: }
! 541:
! 542: if (c->flags & CLIENT_STATUS)
! 543: screen_redraw_status(c);
! 544:
! 545: c->tty.flags |= flags;
! 546:
! 547: c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS);
! 548: }
! 549:
! 550: /* Redraw client when locked. */
! 551: void
! 552: server_redraw_locked(struct client *c)
! 553: {
! 554: struct screen_write_ctx ctx;
! 555: struct screen screen;
! 556: u_int colour, xx, yy, i;
! 557: int style;
! 558:
! 559: xx = c->tty.sx;
! 560: yy = c->tty.sy - 1;
! 561: if (xx == 0 || yy == 0)
! 562: return;
! 563: colour = options_get_number(
! 564: &global_window_options, "clock-mode-colour");
! 565: style = options_get_number(
! 566: &global_window_options, "clock-mode-style");
! 567:
! 568: screen_init(&screen, xx, yy, 0);
! 569:
! 570: screen_write_start(&ctx, NULL, &screen);
! 571: clock_draw(&ctx, colour, style);
! 572: screen_write_stop(&ctx);
! 573:
! 574: for (i = 0; i < screen_size_y(&screen); i++)
! 575: tty_draw_line(&c->tty, &screen, i, 0, 0);
! 576: screen_redraw_status(c);
! 577:
! 578: screen_free(&screen);
! 579: }
! 580:
! 581: /* Check for timers on client. */
! 582: void
! 583: server_check_timers(struct client *c)
! 584: {
! 585: struct session *s;
! 586: struct timeval tv;
! 587: u_int interval;
! 588:
! 589: if (c == NULL || c->session == NULL)
! 590: return;
! 591: s = c->session;
! 592:
! 593: if (gettimeofday(&tv, NULL) != 0)
! 594: fatal("gettimeofday");
! 595:
! 596: if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >))
! 597: status_message_clear(c);
! 598:
! 599: if (c->message_string != NULL || c->prompt_string != NULL) {
! 600: /*
! 601: * Don't need timed redraw for messages/prompts so bail now.
! 602: * The status timer isn't reset when they are redrawn anyway.
! 603: */
! 604: return;
! 605: }
! 606: if (!options_get_number(&s->options, "status"))
! 607: return;
! 608:
! 609: /* Check timer; resolution is only a second so don't be too clever. */
! 610: interval = options_get_number(&s->options, "status-interval");
! 611: if (interval == 0)
! 612: return;
! 613: if (tv.tv_sec < c->status_timer.tv_sec ||
! 614: ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval)
! 615: c->flags |= CLIENT_STATUS;
! 616: }
! 617:
! 618: /* Fill client pollfds. */
! 619: void
! 620: server_fill_clients(struct pollfd **pfd)
! 621: {
! 622: struct client *c;
! 623: struct window *w;
! 624: struct window_pane *wp;
! 625: u_int i;
! 626:
! 627: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 628: c = ARRAY_ITEM(&clients, i);
! 629:
! 630: server_check_timers(c);
! 631: server_check_redraw(c);
! 632:
! 633: if (c == NULL)
! 634: (*pfd)->fd = -1;
! 635: else {
! 636: (*pfd)->fd = c->fd;
! 637: (*pfd)->events = POLLIN;
! 638: if (BUFFER_USED(c->out) > 0)
! 639: (*pfd)->events |= POLLOUT;
! 640: }
! 641: (*pfd)++;
! 642:
! 643: if (c == NULL || c->flags & CLIENT_SUSPENDED ||
! 644: c->tty.fd == -1 || c->session == NULL)
! 645: (*pfd)->fd = -1;
! 646: else {
! 647: (*pfd)->fd = c->tty.fd;
! 648: (*pfd)->events = POLLIN;
! 649: if (BUFFER_USED(c->tty.out) > 0)
! 650: (*pfd)->events |= POLLOUT;
! 651: }
! 652: (*pfd)++;
! 653: }
! 654:
! 655: /*
! 656: * Clear any window redraw flags (will have been redrawn as part of
! 657: * client).
! 658: */
! 659: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 660: w = ARRAY_ITEM(&windows, i);
! 661: if (w == NULL)
! 662: continue;
! 663:
! 664: w->flags &= ~WINDOW_REDRAW;
! 665: TAILQ_FOREACH(wp, &w->panes, entry)
! 666: wp->flags &= ~PANE_REDRAW;
! 667: }
! 668: }
! 669:
! 670: /* Handle client pollfds. */
! 671: void
! 672: server_handle_clients(struct pollfd **pfd)
! 673: {
! 674: struct client *c;
! 675: u_int i;
! 676:
! 677: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 678: c = ARRAY_ITEM(&clients, i);
! 679:
! 680: if (c != NULL) {
! 681: if (buffer_poll(*pfd, c->in, c->out) != 0) {
! 682: server_lost_client(c);
! 683: (*pfd) += 2;
! 684: continue;
! 685: } else
! 686: server_msg_dispatch(c);
! 687: }
! 688: (*pfd)++;
! 689:
! 690: if (c != NULL && !(c->flags & CLIENT_SUSPENDED) &&
! 691: c->tty.fd != -1 && c->session != NULL) {
! 692: if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0)
! 693: server_lost_client(c);
! 694: else
! 695: server_handle_client(c);
! 696: }
! 697: (*pfd)++;
! 698: }
! 699: }
! 700:
! 701: /* accept(2) and create new client. */
! 702: struct client *
! 703: server_accept_client(int srv_fd)
! 704: {
! 705: struct sockaddr_storage sa;
! 706: socklen_t slen = sizeof sa;
! 707: int fd;
! 708:
! 709: fd = accept(srv_fd, (struct sockaddr *) &sa, &slen);
! 710: if (fd == -1) {
! 711: if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
! 712: return (NULL);
! 713: fatal("accept failed");
! 714: }
! 715: if (sigterm) {
! 716: close(fd);
! 717: return (NULL);
! 718: }
! 719: return (server_create_client(fd));
! 720: }
! 721:
! 722: /* Input data from client. */
! 723: void
! 724: server_handle_client(struct client *c)
! 725: {
! 726: struct window_pane *wp;
! 727: struct screen *s;
! 728: struct timeval tv;
! 729: struct key_binding *bd;
! 730: int key, prefix, status, xtimeout;
! 731: int mode;
! 732: u_char mouse[3];
! 733:
! 734: xtimeout = options_get_number(&c->session->options, "repeat-time");
! 735: if (xtimeout != 0 && c->flags & CLIENT_REPEAT) {
! 736: if (gettimeofday(&tv, NULL) != 0)
! 737: fatal("gettimeofday");
! 738: if (timercmp(&tv, &c->repeat_timer, >))
! 739: c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT);
! 740: }
! 741:
! 742: /* Process keys. */
! 743: prefix = options_get_number(&c->session->options, "prefix");
! 744: while (tty_keys_next(&c->tty, &key, mouse) == 0) {
! 745: server_activity = time(NULL);
! 746:
! 747: if (c->session == NULL)
! 748: return;
! 749: wp = c->session->curw->window->active; /* could die */
! 750:
! 751: status_message_clear(c);
! 752: if (c->prompt_string != NULL) {
! 753: status_prompt_key(c, key);
! 754: continue;
! 755: }
! 756: if (server_locked)
! 757: continue;
! 758:
! 759: /* Check for mouse keys. */
! 760: if (key == KEYC_MOUSE) {
! 761: window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]);
! 762: continue;
! 763: }
! 764:
! 765: /* No previous prefix key. */
! 766: if (!(c->flags & CLIENT_PREFIX)) {
! 767: if (key == prefix)
! 768: c->flags |= CLIENT_PREFIX;
! 769: else
! 770: window_pane_key(wp, c, key);
! 771: continue;
! 772: }
! 773:
! 774: /* Prefix key already pressed. Reset prefix and lookup key. */
! 775: c->flags &= ~CLIENT_PREFIX;
! 776: if ((bd = key_bindings_lookup(key)) == NULL) {
! 777: /* If repeating, treat this as a key, else ignore. */
! 778: if (c->flags & CLIENT_REPEAT) {
! 779: c->flags &= ~CLIENT_REPEAT;
! 780: if (key == prefix)
! 781: c->flags |= CLIENT_PREFIX;
! 782: else
! 783: window_pane_key(wp, c, key);
! 784: }
! 785: continue;
! 786: }
! 787:
! 788: /* If already repeating, but this key can't repeat, skip it. */
! 789: if (c->flags & CLIENT_REPEAT && !bd->can_repeat) {
! 790: c->flags &= ~CLIENT_REPEAT;
! 791: if (key == prefix)
! 792: c->flags |= CLIENT_PREFIX;
! 793: else
! 794: window_pane_key(wp, c, key);
! 795: continue;
! 796: }
! 797:
! 798: /* If this key can repeat, reset the repeat flags and timer. */
! 799: if (xtimeout != 0 && bd->can_repeat) {
! 800: c->flags |= CLIENT_PREFIX|CLIENT_REPEAT;
! 801:
! 802: tv.tv_sec = xtimeout / 1000;
! 803: tv.tv_usec = (xtimeout % 1000) * 1000L;
! 804: if (gettimeofday(&c->repeat_timer, NULL) != 0)
! 805: fatal("gettimeofday");
! 806: timeradd(&c->repeat_timer, &tv, &c->repeat_timer);
! 807: }
! 808:
! 809: /* Dispatch the command. */
! 810: key_bindings_dispatch(bd, c);
! 811: }
! 812: if (c->session == NULL)
! 813: return;
! 814: wp = c->session->curw->window->active; /* could die - do each loop */
! 815: s = wp->screen;
! 816:
! 817: /* Ensure cursor position and mode settings. */
! 818: status = options_get_number(&c->session->options, "status");
! 819: if (wp->yoff + s->cy < c->tty.sy - status)
! 820: tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff);
! 821:
! 822: mode = s->mode;
! 823: if (server_locked)
! 824: mode &= ~TTY_NOCURSOR;
! 825: tty_update_mode(&c->tty, mode);
! 826: }
! 827:
! 828: /* Lost a client. */
! 829: void
! 830: server_lost_client(struct client *c)
! 831: {
! 832: u_int i;
! 833:
! 834: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 835: if (ARRAY_ITEM(&clients, i) == c)
! 836: ARRAY_SET(&clients, i, NULL);
! 837: }
! 838:
! 839: tty_free(&c->tty, c->flags & CLIENT_SUSPENDED);
! 840:
! 841: screen_free(&c->status);
! 842:
! 843: if (c->title != NULL)
! 844: xfree(c->title);
! 845:
! 846: if (c->message_string != NULL)
! 847: xfree(c->message_string);
! 848:
! 849: if (c->prompt_string != NULL)
! 850: xfree(c->prompt_string);
! 851: if (c->prompt_buffer != NULL)
! 852: xfree(c->prompt_buffer);
! 853: for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++)
! 854: xfree(ARRAY_ITEM(&c->prompt_hdata, i));
! 855: ARRAY_FREE(&c->prompt_hdata);
! 856:
! 857: if (c->cwd != NULL)
! 858: xfree(c->cwd);
! 859:
! 860: close(c->fd);
! 861: buffer_destroy(c->in);
! 862: buffer_destroy(c->out);
! 863: xfree(c);
! 864:
! 865: recalculate_sizes();
! 866: }
! 867:
! 868: /* Handle window data. */
! 869: void
! 870: server_handle_window(struct window *w, struct window_pane *wp)
! 871: {
! 872: struct session *s;
! 873: u_int i;
! 874: int update;
! 875:
! 876: window_pane_parse(wp);
! 877:
! 878: if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0)
! 879: return;
! 880:
! 881: update = 0;
! 882: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 883: s = ARRAY_ITEM(&sessions, i);
! 884: if (s == NULL || !session_has(s, w))
! 885: continue;
! 886:
! 887: update += server_check_window_bell(s, w, wp);
! 888: update += server_check_window_activity(s, w);
! 889: update += server_check_window_content(s, w, wp);
! 890: }
! 891: if (update)
! 892: server_status_window(w);
! 893:
! 894: w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT);
! 895: }
! 896:
! 897: int
! 898: server_check_window_bell(
! 899: struct session *s, struct window *w, struct window_pane *wp)
! 900: {
! 901: struct client *c;
! 902: u_int i;
! 903: int action;
! 904:
! 905: if (!(w->flags & WINDOW_BELL))
! 906: return (0);
! 907: if (session_alert_has_window(s, w, WINDOW_BELL))
! 908: return (0);
! 909: session_alert_add(s, w, WINDOW_BELL);
! 910:
! 911: action = options_get_number(&s->options, "bell-action");
! 912: switch (action) {
! 913: case BELL_ANY:
! 914: if (s->flags & SESSION_UNATTACHED)
! 915: break;
! 916: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 917: c = ARRAY_ITEM(&clients, i);
! 918: if (c != NULL && c->session == s)
! 919: tty_putcode(&c->tty, TTYC_BEL);
! 920: }
! 921: break;
! 922: case BELL_CURRENT:
! 923: if (w->active != wp)
! 924: break;
! 925: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 926: c = ARRAY_ITEM(&clients, i);
! 927: if (c != NULL && c->session == s)
! 928: tty_putcode(&c->tty, TTYC_BEL);
! 929: }
! 930: break;
! 931: }
! 932: return (1);
! 933: }
! 934:
! 935: int
! 936: server_check_window_activity(struct session *s, struct window *w)
! 937: {
! 938: if (!(w->flags & WINDOW_ACTIVITY))
! 939: return (0);
! 940: if (!options_get_number(&w->options, "monitor-activity"))
! 941: return (0);
! 942: if (session_alert_has_window(s, w, WINDOW_ACTIVITY))
! 943: return (0);
! 944: session_alert_add(s, w, WINDOW_ACTIVITY);
! 945: return (1);
! 946: }
! 947:
! 948: int
! 949: server_check_window_content(
! 950: struct session *s, struct window *w, struct window_pane *wp)
! 951: {
! 952: char *found, *ptr;
! 953:
! 954: if (!(w->flags & WINDOW_CONTENT))
! 955: return (0);
! 956: if ((ptr = options_get_string(&w->options, "monitor-content")) == NULL)
! 957: return (0);
! 958: if (*ptr == '\0')
! 959: return (0);
! 960: if (session_alert_has_window(s, w, WINDOW_CONTENT))
! 961: return (0);
! 962: if ((found = window_pane_search(wp, ptr)) == NULL)
! 963: return (0);
! 964: session_alert_add(s, w, WINDOW_CONTENT);
! 965: xfree(found);
! 966: return (1);
! 967: }
! 968:
! 969: /* Check if window still exists.. */
! 970: void
! 971: server_check_window(struct window *w)
! 972: {
! 973: struct window_pane *wp, *wq;
! 974: struct client *c;
! 975: struct session *s;
! 976: struct winlink *wl;
! 977: u_int i, j;
! 978: int destroyed, flag;
! 979:
! 980: flag = options_get_number(&w->options, "remain-on-exit");
! 981:
! 982: destroyed = 1;
! 983:
! 984: wp = TAILQ_FIRST(&w->panes);
! 985: while (wp != NULL) {
! 986: wq = TAILQ_NEXT(wp, entry);
! 987: if (wp->fd != -1)
! 988: destroyed = 0;
! 989: else if (!flag) {
! 990: window_remove_pane(w, wp);
! 991: server_redraw_window(w);
! 992: layout_refresh(w, 0);
! 993: }
! 994: wp = wq;
! 995: }
! 996:
! 997: if (!destroyed)
! 998: return;
! 999:
! 1000: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 1001: s = ARRAY_ITEM(&sessions, i);
! 1002: if (s == NULL)
! 1003: continue;
! 1004: if (!session_has(s, w))
! 1005: continue;
! 1006:
! 1007: restart:
! 1008: /* Detach window and either redraw or kill clients. */
! 1009: RB_FOREACH(wl, winlinks, &s->windows) {
! 1010: if (wl->window != w)
! 1011: continue;
! 1012: destroyed = session_detach(s, wl);
! 1013: for (j = 0; j < ARRAY_LENGTH(&clients); j++) {
! 1014: c = ARRAY_ITEM(&clients, j);
! 1015: if (c == NULL || c->session != s)
! 1016: continue;
! 1017: if (!destroyed) {
! 1018: server_redraw_client(c);
! 1019: continue;
! 1020: }
! 1021: c->session = NULL;
! 1022: server_write_client(c, MSG_EXIT, NULL, 0);
! 1023: }
! 1024: /* If the session was destroyed, bail now. */
! 1025: if (destroyed)
! 1026: break;
! 1027: goto restart;
! 1028: }
! 1029: }
! 1030:
! 1031: recalculate_sizes();
! 1032: }
! 1033:
! 1034: /* Call any once-per-second timers. */
! 1035: void
! 1036: server_second_timers(void)
! 1037: {
! 1038: struct window *w;
! 1039: struct window_pane *wp;
! 1040: u_int i;
! 1041: int xtimeout;
! 1042: struct tm now, then;
! 1043: static time_t last_t = 0;
! 1044: time_t t;
! 1045:
! 1046: t = time(NULL);
! 1047: xtimeout = options_get_number(&global_options, "lock-after-time");
! 1048: if (xtimeout > 0 && t > server_activity + xtimeout)
! 1049: server_lock();
! 1050:
! 1051: for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
! 1052: w = ARRAY_ITEM(&windows, i);
! 1053: if (w == NULL)
! 1054: continue;
! 1055:
! 1056: TAILQ_FOREACH(wp, &w->panes, entry) {
! 1057: if (wp->mode != NULL && wp->mode->timer != NULL)
! 1058: wp->mode->timer(wp);
! 1059: }
! 1060: }
! 1061:
! 1062: /* Check for a minute having passed. */
! 1063: gmtime_r(&t, &now);
! 1064: gmtime_r(&last_t, &then);
! 1065: if (now.tm_min == then.tm_min)
! 1066: return;
! 1067: last_t = t;
! 1068:
! 1069: /* If locked, redraw all clients. */
! 1070: if (server_locked) {
! 1071: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 1072: if (ARRAY_ITEM(&clients, i) != NULL)
! 1073: server_redraw_client(ARRAY_ITEM(&clients, i));
! 1074: }
! 1075: }
! 1076: }
! 1077:
! 1078: /* Update socket execute permissions based on whether sessions are attached. */
! 1079: int
! 1080: server_update_socket(void)
! 1081: {
! 1082: struct session *s;
! 1083: u_int i;
! 1084: static int last = -1;
! 1085: int n;
! 1086:
! 1087: n = 0;
! 1088: for (i = 0; i < ARRAY_LENGTH(&sessions); i++) {
! 1089: s = ARRAY_ITEM(&sessions, i);
! 1090: if (s != NULL && !(s->flags & SESSION_UNATTACHED)) {
! 1091: n++;
! 1092: break;
! 1093: }
! 1094: }
! 1095:
! 1096: if (n != last) {
! 1097: last = n;
! 1098: if (n != 0)
! 1099: chmod(socket_path, S_IRWXU);
! 1100: else
! 1101: chmod(socket_path, S_IRUSR|S_IWUSR);
! 1102: }
! 1103:
! 1104: return (n);
! 1105: }