1*f369cd7fSnicm /* $OpenBSD: server.c,v 1.25 2009/08/31 11:37:27 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 4311827fbSnicm * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm #include <sys/ioctl.h> 21311827fbSnicm #include <sys/socket.h> 22311827fbSnicm #include <sys/stat.h> 23311827fbSnicm #include <sys/un.h> 24311827fbSnicm #include <sys/wait.h> 25311827fbSnicm 26311827fbSnicm #include <errno.h> 27311827fbSnicm #include <fcntl.h> 28a575a2dfSnicm #include <paths.h> 29311827fbSnicm #include <signal.h> 30311827fbSnicm #include <stdio.h> 31311827fbSnicm #include <stdlib.h> 32311827fbSnicm #include <string.h> 33311827fbSnicm #include <syslog.h> 34311827fbSnicm #include <termios.h> 35311827fbSnicm #include <time.h> 36311827fbSnicm #include <unistd.h> 37311827fbSnicm 38311827fbSnicm #include "tmux.h" 39311827fbSnicm 40311827fbSnicm /* 41311827fbSnicm * Main server functions. 42311827fbSnicm */ 43311827fbSnicm 44311827fbSnicm /* Client list. */ 45311827fbSnicm struct clients clients; 46311827fbSnicm 47980b7663Snicm void server_create_client(int); 48311827fbSnicm int server_create_socket(void); 49311827fbSnicm int server_main(int); 50311827fbSnicm void server_shutdown(void); 51311827fbSnicm void server_child_signal(void); 52311827fbSnicm void server_fill_windows(struct pollfd **); 53311827fbSnicm void server_handle_windows(struct pollfd **); 54311827fbSnicm void server_fill_clients(struct pollfd **); 55311827fbSnicm void server_handle_clients(struct pollfd **); 56980b7663Snicm void server_accept_client(int); 57311827fbSnicm void server_handle_client(struct client *); 58311827fbSnicm void server_handle_window(struct window *, struct window_pane *); 590903c7b9Snicm int server_check_window_bell(struct session *, struct window *); 60311827fbSnicm int server_check_window_activity(struct session *, 61311827fbSnicm struct window *); 62311827fbSnicm int server_check_window_content(struct session *, struct window *, 63311827fbSnicm struct window_pane *); 64311827fbSnicm void server_lost_client(struct client *); 65311827fbSnicm void server_check_window(struct window *); 66311827fbSnicm void server_check_redraw(struct client *); 67311827fbSnicm void server_redraw_locked(struct client *); 68311827fbSnicm void server_check_timers(struct client *); 69311827fbSnicm void server_second_timers(void); 70311827fbSnicm int server_update_socket(void); 71311827fbSnicm 72311827fbSnicm /* Create a new client. */ 73980b7663Snicm void 74311827fbSnicm server_create_client(int fd) 75311827fbSnicm { 76311827fbSnicm struct client *c; 77311827fbSnicm int mode; 78311827fbSnicm u_int i; 79311827fbSnicm 80311827fbSnicm if ((mode = fcntl(fd, F_GETFL)) == -1) 81311827fbSnicm fatal("fcntl failed"); 82311827fbSnicm if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 83311827fbSnicm fatal("fcntl failed"); 84311827fbSnicm if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 85311827fbSnicm fatal("fcntl failed"); 86311827fbSnicm 87311827fbSnicm c = xcalloc(1, sizeof *c); 88fd234c13Snicm imsg_init(&c->ibuf, fd); 89311827fbSnicm 90311827fbSnicm ARRAY_INIT(&c->prompt_hdata); 91311827fbSnicm 92311827fbSnicm c->tty.fd = -1; 93311827fbSnicm c->title = NULL; 94311827fbSnicm 95311827fbSnicm c->session = NULL; 96311827fbSnicm c->tty.sx = 80; 97311827fbSnicm c->tty.sy = 25; 98311827fbSnicm screen_init(&c->status, c->tty.sx, 1, 0); 99311827fbSnicm 100311827fbSnicm c->message_string = NULL; 101311827fbSnicm 102311827fbSnicm c->prompt_string = NULL; 103311827fbSnicm c->prompt_buffer = NULL; 104311827fbSnicm c->prompt_index = 0; 105311827fbSnicm 106311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 107311827fbSnicm if (ARRAY_ITEM(&clients, i) == NULL) { 108311827fbSnicm ARRAY_SET(&clients, i, c); 109980b7663Snicm return; 110311827fbSnicm } 111311827fbSnicm } 112311827fbSnicm ARRAY_ADD(&clients, c); 1130bb39cd5Snicm log_debug("new client %d", fd); 114311827fbSnicm } 115311827fbSnicm 116311827fbSnicm /* Find client index. */ 117311827fbSnicm int 118311827fbSnicm server_client_index(struct client *c) 119311827fbSnicm { 120311827fbSnicm u_int i; 121311827fbSnicm 122311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 123311827fbSnicm if (c == ARRAY_ITEM(&clients, i)) 124311827fbSnicm return (i); 125311827fbSnicm } 126311827fbSnicm return (-1); 127311827fbSnicm } 128311827fbSnicm 129311827fbSnicm /* Fork new server. */ 130311827fbSnicm int 131311827fbSnicm server_start(char *path) 132311827fbSnicm { 1330cd9638eSnicm struct client *c; 1340cd9638eSnicm int pair[2], srv_fd; 135311827fbSnicm char *cause; 136311827fbSnicm char rpathbuf[MAXPATHLEN]; 137311827fbSnicm 138311827fbSnicm /* The first client is special and gets a socketpair; create it. */ 139311827fbSnicm if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) 140311827fbSnicm fatal("socketpair failed"); 141311827fbSnicm 142311827fbSnicm switch (fork()) { 143311827fbSnicm case -1: 144311827fbSnicm fatal("fork failed"); 145311827fbSnicm case 0: 146311827fbSnicm break; 147311827fbSnicm default: 148311827fbSnicm close(pair[1]); 149311827fbSnicm return (pair[0]); 150311827fbSnicm } 151311827fbSnicm close(pair[0]); 152311827fbSnicm 153311827fbSnicm /* 154311827fbSnicm * Must daemonise before loading configuration as the PID changes so 155311827fbSnicm * $TMUX would be wrong for sessions created in the config file. 156311827fbSnicm */ 1570cd9638eSnicm if (daemon(1, 0) != 0) 158311827fbSnicm fatal("daemon failed"); 159311827fbSnicm 1600cd9638eSnicm logfile("server"); 1610cd9638eSnicm log_debug("server started, pid %ld", (long) getpid()); 1620cd9638eSnicm 163311827fbSnicm ARRAY_INIT(&windows); 164311827fbSnicm ARRAY_INIT(&clients); 165311827fbSnicm ARRAY_INIT(&sessions); 16668895571Snicm mode_key_init_trees(); 167311827fbSnicm key_bindings_init(); 168311827fbSnicm utf8_build(); 169311827fbSnicm 170311827fbSnicm server_locked = 0; 171311827fbSnicm server_password = NULL; 172311827fbSnicm server_activity = time(NULL); 173311827fbSnicm 174311827fbSnicm start_time = time(NULL); 175311827fbSnicm socket_path = path; 176311827fbSnicm 177311827fbSnicm if (realpath(socket_path, rpathbuf) == NULL) 178311827fbSnicm strlcpy(rpathbuf, socket_path, sizeof rpathbuf); 1790cd9638eSnicm log_debug("socket path %s", socket_path); 180311827fbSnicm setproctitle("server (%s)", rpathbuf); 181311827fbSnicm 182311827fbSnicm srv_fd = server_create_socket(); 183311827fbSnicm server_create_client(pair[1]); 184311827fbSnicm 1850cd9638eSnicm if (access(SYSTEM_CFG, R_OK) != 0) { 1860cd9638eSnicm if (errno != ENOENT) { 1870cd9638eSnicm xasprintf( 1880cd9638eSnicm &cause, "%s: %s", strerror(errno), SYSTEM_CFG); 1890cd9638eSnicm goto error; 1900cd9638eSnicm } 191c0a52a07Snicm } else if (load_cfg(SYSTEM_CFG, NULL, &cause) != 0) 1920cd9638eSnicm goto error; 193c0a52a07Snicm if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0) 1940cd9638eSnicm goto error; 1950cd9638eSnicm 1960cd9638eSnicm exit(server_main(srv_fd)); 1970cd9638eSnicm 1980cd9638eSnicm error: 1990cd9638eSnicm /* Write the error and shutdown the server. */ 2000cd9638eSnicm c = ARRAY_FIRST(&clients); 2010cd9638eSnicm 2020cd9638eSnicm server_write_error(c, cause); 2030cd9638eSnicm xfree(cause); 2040cd9638eSnicm 2050cd9638eSnicm server_shutdown(); 2060cd9638eSnicm c->flags |= CLIENT_BAD; 2070cd9638eSnicm 208311827fbSnicm exit(server_main(srv_fd)); 209311827fbSnicm } 210311827fbSnicm 211311827fbSnicm /* Create server socket. */ 212311827fbSnicm int 213311827fbSnicm server_create_socket(void) 214311827fbSnicm { 215311827fbSnicm struct sockaddr_un sa; 216311827fbSnicm size_t size; 217311827fbSnicm mode_t mask; 218311827fbSnicm int fd, mode; 219311827fbSnicm 220311827fbSnicm memset(&sa, 0, sizeof sa); 221311827fbSnicm sa.sun_family = AF_UNIX; 222311827fbSnicm size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 223311827fbSnicm if (size >= sizeof sa.sun_path) { 224311827fbSnicm errno = ENAMETOOLONG; 225311827fbSnicm fatal("socket failed"); 226311827fbSnicm } 227311827fbSnicm unlink(sa.sun_path); 228311827fbSnicm 229311827fbSnicm if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 230311827fbSnicm fatal("socket failed"); 231311827fbSnicm 232311827fbSnicm mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 233311827fbSnicm if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) 234311827fbSnicm fatal("bind failed"); 235311827fbSnicm umask(mask); 236311827fbSnicm 237311827fbSnicm if (listen(fd, 16) == -1) 238311827fbSnicm fatal("listen failed"); 239311827fbSnicm 240311827fbSnicm if ((mode = fcntl(fd, F_GETFL)) == -1) 241311827fbSnicm fatal("fcntl failed"); 242311827fbSnicm if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 243311827fbSnicm fatal("fcntl failed"); 244311827fbSnicm if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 245311827fbSnicm fatal("fcntl failed"); 246311827fbSnicm 247311827fbSnicm return (fd); 248311827fbSnicm } 249311827fbSnicm 250311827fbSnicm /* Main server loop. */ 251311827fbSnicm int 252311827fbSnicm server_main(int srv_fd) 253311827fbSnicm { 254311827fbSnicm struct window *w; 255311827fbSnicm struct pollfd *pfds, *pfd; 256311827fbSnicm int nfds, xtimeout; 257311827fbSnicm u_int i, n; 258311827fbSnicm time_t now, last; 259311827fbSnicm 260311827fbSnicm siginit(); 2610bb39cd5Snicm log_debug("server socket is %d", srv_fd); 262311827fbSnicm 263311827fbSnicm last = time(NULL); 264311827fbSnicm 265311827fbSnicm pfds = NULL; 266311827fbSnicm for (;;) { 267311827fbSnicm /* If sigterm, kill all windows and clients. */ 268311827fbSnicm if (sigterm) 269311827fbSnicm server_shutdown(); 270311827fbSnicm 271311827fbSnicm /* Handle child exit. */ 272311827fbSnicm if (sigchld) { 273311827fbSnicm server_child_signal(); 274311827fbSnicm sigchld = 0; 275311827fbSnicm } 276311827fbSnicm 277311827fbSnicm /* Recreate socket on SIGUSR1. */ 278311827fbSnicm if (sigusr1) { 279311827fbSnicm close(srv_fd); 280311827fbSnicm srv_fd = server_create_socket(); 281311827fbSnicm sigusr1 = 0; 282311827fbSnicm } 283311827fbSnicm 284311827fbSnicm /* Initialise pollfd array. */ 285311827fbSnicm nfds = 1; 286311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 287311827fbSnicm w = ARRAY_ITEM(&windows, i); 288311827fbSnicm if (w != NULL) 289311827fbSnicm nfds += window_count_panes(w); 290311827fbSnicm } 291311827fbSnicm nfds += ARRAY_LENGTH(&clients) * 2; 292311827fbSnicm pfds = xrealloc(pfds, nfds, sizeof *pfds); 293311827fbSnicm memset(pfds, 0, nfds * sizeof *pfds); 294311827fbSnicm pfd = pfds; 295311827fbSnicm 296311827fbSnicm /* Fill server socket. */ 297311827fbSnicm pfd->fd = srv_fd; 298311827fbSnicm pfd->events = POLLIN; 299311827fbSnicm pfd++; 300311827fbSnicm 301311827fbSnicm /* Fill window and client sockets. */ 302311827fbSnicm server_fill_windows(&pfd); 303311827fbSnicm server_fill_clients(&pfd); 304311827fbSnicm 305311827fbSnicm /* Update socket permissions. */ 306311827fbSnicm xtimeout = INFTIM; 307311827fbSnicm if (sigterm || server_update_socket() != 0) 308311827fbSnicm xtimeout = POLL_TIMEOUT; 309311827fbSnicm 310311827fbSnicm /* Do the poll. */ 3119b1fc963Snicm if (poll(pfds, nfds, xtimeout) == -1) { 312311827fbSnicm if (errno == EAGAIN || errno == EINTR) 313311827fbSnicm continue; 314311827fbSnicm fatal("poll failed"); 315311827fbSnicm } 316311827fbSnicm pfd = pfds; 317311827fbSnicm 318311827fbSnicm /* Handle server socket. */ 319311827fbSnicm if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) 320311827fbSnicm fatalx("lost server socket"); 321311827fbSnicm if (pfd->revents & POLLIN) { 322311827fbSnicm server_accept_client(srv_fd); 323311827fbSnicm continue; 324311827fbSnicm } 325311827fbSnicm pfd++; 326311827fbSnicm 327311827fbSnicm /* Call second-based timers. */ 328311827fbSnicm now = time(NULL); 329311827fbSnicm if (now != last) { 330311827fbSnicm last = now; 331311827fbSnicm server_second_timers(); 332311827fbSnicm } 333311827fbSnicm 334311827fbSnicm /* Set window names. */ 335311827fbSnicm set_window_names(); 336311827fbSnicm 337311827fbSnicm /* 338311827fbSnicm * Handle window and client sockets. Clients can create 339311827fbSnicm * windows, so windows must come first to avoid messing up by 340311827fbSnicm * increasing the array size. 341311827fbSnicm */ 342311827fbSnicm server_handle_windows(&pfd); 343311827fbSnicm server_handle_clients(&pfd); 344311827fbSnicm 3450476b68eSnicm /* Collect any unset key bindings. */ 3460476b68eSnicm key_bindings_clean(); 3470476b68eSnicm 348311827fbSnicm /* 349311827fbSnicm * If we have no sessions and clients left, let's get out 350311827fbSnicm * of here... 351311827fbSnicm */ 352311827fbSnicm n = 0; 353311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 354311827fbSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 355311827fbSnicm n++; 356311827fbSnicm } 357311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 358311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 359311827fbSnicm n++; 360311827fbSnicm } 361311827fbSnicm if (n == 0) 362311827fbSnicm break; 363311827fbSnicm } 364311827fbSnicm if (pfds != NULL) 365311827fbSnicm xfree(pfds); 366311827fbSnicm 367311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 368311827fbSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 369311827fbSnicm session_destroy(ARRAY_ITEM(&sessions, i)); 370311827fbSnicm } 371311827fbSnicm ARRAY_FREE(&sessions); 372311827fbSnicm 373311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 374311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 375311827fbSnicm server_lost_client(ARRAY_ITEM(&clients, i)); 376311827fbSnicm } 377311827fbSnicm ARRAY_FREE(&clients); 378311827fbSnicm 37968895571Snicm mode_key_free_trees(); 380311827fbSnicm key_bindings_free(); 381311827fbSnicm 382311827fbSnicm close(srv_fd); 383311827fbSnicm 384311827fbSnicm unlink(socket_path); 385311827fbSnicm xfree(socket_path); 386311827fbSnicm 387eaecedb2Snicm options_free(&global_s_options); 388eaecedb2Snicm options_free(&global_w_options); 389311827fbSnicm if (server_password != NULL) 390311827fbSnicm xfree(server_password); 391311827fbSnicm 392311827fbSnicm return (0); 393311827fbSnicm } 394311827fbSnicm 395311827fbSnicm /* Kill all clients. */ 396311827fbSnicm void 397311827fbSnicm server_shutdown(void) 398311827fbSnicm { 399311827fbSnicm struct session *s; 400311827fbSnicm struct client *c; 401311827fbSnicm u_int i, j; 402311827fbSnicm 403311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 404311827fbSnicm s = ARRAY_ITEM(&sessions, i); 405311827fbSnicm for (j = 0; j < ARRAY_LENGTH(&clients); j++) { 406311827fbSnicm c = ARRAY_ITEM(&clients, j); 407311827fbSnicm if (c != NULL && c->session == s) { 408311827fbSnicm s = NULL; 409311827fbSnicm break; 410311827fbSnicm } 411311827fbSnicm } 412311827fbSnicm if (s != NULL) 413311827fbSnicm session_destroy(s); 414311827fbSnicm } 415311827fbSnicm 416311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 417311827fbSnicm c = ARRAY_ITEM(&clients, i); 4180cd9638eSnicm if (c != NULL) { 4190cd9638eSnicm if (c->flags & CLIENT_BAD) 4200cd9638eSnicm server_lost_client(c); 4210cd9638eSnicm else 422311827fbSnicm server_write_client(c, MSG_SHUTDOWN, NULL, 0); 4230cd9638eSnicm c->flags |= CLIENT_BAD; 4240cd9638eSnicm } 425311827fbSnicm } 426311827fbSnicm } 427311827fbSnicm 428311827fbSnicm /* Handle SIGCHLD. */ 429311827fbSnicm void 430311827fbSnicm server_child_signal(void) 431311827fbSnicm { 432311827fbSnicm struct window *w; 433311827fbSnicm struct window_pane *wp; 434311827fbSnicm int status; 435311827fbSnicm pid_t pid; 436311827fbSnicm u_int i; 437311827fbSnicm 438311827fbSnicm for (;;) { 439311827fbSnicm switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 440311827fbSnicm case -1: 441311827fbSnicm if (errno == ECHILD) 442311827fbSnicm return; 443311827fbSnicm fatal("waitpid"); 444311827fbSnicm case 0: 445311827fbSnicm return; 446311827fbSnicm } 447311827fbSnicm if (!WIFSTOPPED(status)) 448311827fbSnicm continue; 449311827fbSnicm if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 450311827fbSnicm continue; 451311827fbSnicm 452311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 453311827fbSnicm w = ARRAY_ITEM(&windows, i); 454311827fbSnicm if (w == NULL) 455311827fbSnicm continue; 456311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 457311827fbSnicm if (wp->pid == pid) { 458311827fbSnicm if (killpg(pid, SIGCONT) != 0) 459311827fbSnicm kill(pid, SIGCONT); 460311827fbSnicm } 461311827fbSnicm } 462311827fbSnicm } 463311827fbSnicm } 464311827fbSnicm } 465311827fbSnicm 466311827fbSnicm /* Fill window pollfds. */ 467311827fbSnicm void 468311827fbSnicm server_fill_windows(struct pollfd **pfd) 469311827fbSnicm { 470311827fbSnicm struct window *w; 471311827fbSnicm struct window_pane *wp; 472311827fbSnicm u_int i; 473311827fbSnicm 474311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 475311827fbSnicm w = ARRAY_ITEM(&windows, i); 476311827fbSnicm if (w == NULL) 477311827fbSnicm continue; 478311827fbSnicm 479311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 480311827fbSnicm (*pfd)->fd = wp->fd; 481311827fbSnicm if (wp->fd != -1) { 482311827fbSnicm (*pfd)->events = POLLIN; 483311827fbSnicm if (BUFFER_USED(wp->out) > 0) 484311827fbSnicm (*pfd)->events |= POLLOUT; 485311827fbSnicm } 486311827fbSnicm (*pfd)++; 487311827fbSnicm } 488311827fbSnicm } 489311827fbSnicm } 490311827fbSnicm 491311827fbSnicm /* Handle window pollfds. */ 492311827fbSnicm void 493311827fbSnicm server_handle_windows(struct pollfd **pfd) 494311827fbSnicm { 495311827fbSnicm struct window *w; 496311827fbSnicm struct window_pane *wp; 497311827fbSnicm u_int i; 498311827fbSnicm 499311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 500311827fbSnicm w = ARRAY_ITEM(&windows, i); 501311827fbSnicm if (w == NULL) 502311827fbSnicm continue; 503311827fbSnicm 504311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 505311827fbSnicm if (wp->fd != -1) { 506311827fbSnicm if (buffer_poll(*pfd, wp->in, wp->out) != 0) { 507311827fbSnicm close(wp->fd); 508311827fbSnicm wp->fd = -1; 509311827fbSnicm } else 510311827fbSnicm server_handle_window(w, wp); 511311827fbSnicm } 512311827fbSnicm (*pfd)++; 513311827fbSnicm } 514311827fbSnicm 515311827fbSnicm server_check_window(w); 516311827fbSnicm } 517311827fbSnicm } 518311827fbSnicm 519311827fbSnicm /* Check for general redraw on client. */ 520311827fbSnicm void 521311827fbSnicm server_check_redraw(struct client *c) 522311827fbSnicm { 523311827fbSnicm struct session *s; 524311827fbSnicm struct window_pane *wp; 525311827fbSnicm char title[512]; 526311827fbSnicm int flags, redraw; 527311827fbSnicm 528311827fbSnicm if (c == NULL || c->session == NULL) 529311827fbSnicm return; 530311827fbSnicm s = c->session; 531311827fbSnicm 532311827fbSnicm flags = c->tty.flags & TTY_FREEZE; 533311827fbSnicm c->tty.flags &= ~TTY_FREEZE; 534311827fbSnicm 535311827fbSnicm if (options_get_number(&s->options, "set-titles")) { 536311827fbSnicm xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"", 537311827fbSnicm s->name, s->curw->idx, s->curw->window->name, 538311827fbSnicm s->curw->window->active->screen->title); 539311827fbSnicm if (c->title == NULL || strcmp(title, c->title) != 0) { 540311827fbSnicm if (c->title != NULL) 541311827fbSnicm xfree(c->title); 542311827fbSnicm c->title = xstrdup(title); 543311827fbSnicm tty_set_title(&c->tty, c->title); 544311827fbSnicm } 545311827fbSnicm } 546311827fbSnicm 547311827fbSnicm if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { 548311827fbSnicm if (c->message_string != NULL) 549311827fbSnicm redraw = status_message_redraw(c); 550311827fbSnicm else if (c->prompt_string != NULL) 551311827fbSnicm redraw = status_prompt_redraw(c); 552311827fbSnicm else 553311827fbSnicm redraw = status_redraw(c); 554311827fbSnicm if (!redraw) 555311827fbSnicm c->flags &= ~CLIENT_STATUS; 556311827fbSnicm } 557311827fbSnicm 558311827fbSnicm if (c->flags & CLIENT_REDRAW) { 559311827fbSnicm if (server_locked) 560311827fbSnicm server_redraw_locked(c); 561311827fbSnicm else 56279fc95a8Snicm screen_redraw_screen(c, 0); 563311827fbSnicm c->flags &= ~CLIENT_STATUS; 564311827fbSnicm } else { 565311827fbSnicm TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { 566311827fbSnicm if (wp->flags & PANE_REDRAW) 567311827fbSnicm screen_redraw_pane(c, wp); 568311827fbSnicm } 569311827fbSnicm } 570311827fbSnicm 571311827fbSnicm if (c->flags & CLIENT_STATUS) 57279fc95a8Snicm screen_redraw_screen(c, 1); 573311827fbSnicm 574311827fbSnicm c->tty.flags |= flags; 575311827fbSnicm 576311827fbSnicm c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); 577311827fbSnicm } 578311827fbSnicm 579311827fbSnicm /* Redraw client when locked. */ 580311827fbSnicm void 581311827fbSnicm server_redraw_locked(struct client *c) 582311827fbSnicm { 583311827fbSnicm struct screen_write_ctx ctx; 584311827fbSnicm struct screen screen; 585d658fe3cSnicm struct grid_cell gc; 586311827fbSnicm u_int colour, xx, yy, i; 587311827fbSnicm int style; 588311827fbSnicm 589311827fbSnicm xx = c->tty.sx; 590311827fbSnicm yy = c->tty.sy - 1; 591311827fbSnicm if (xx == 0 || yy == 0) 592311827fbSnicm return; 593eaecedb2Snicm colour = options_get_number(&global_w_options, "clock-mode-colour"); 594eaecedb2Snicm style = options_get_number(&global_w_options, "clock-mode-style"); 595311827fbSnicm 596d658fe3cSnicm memcpy(&gc, &grid_default_cell, sizeof gc); 597d658fe3cSnicm gc.fg = colour; 598d658fe3cSnicm gc.attr |= GRID_ATTR_BRIGHT; 599d658fe3cSnicm 600311827fbSnicm screen_init(&screen, xx, yy, 0); 601311827fbSnicm 602311827fbSnicm screen_write_start(&ctx, NULL, &screen); 603311827fbSnicm clock_draw(&ctx, colour, style); 604d658fe3cSnicm 605d658fe3cSnicm if (password_failures != 0) { 606d658fe3cSnicm screen_write_cursormove(&ctx, 0, 0); 607d658fe3cSnicm screen_write_puts( 608d658fe3cSnicm &ctx, &gc, "%u failed attempts", password_failures); 609d658fe3cSnicm } 610d658fe3cSnicm 611311827fbSnicm screen_write_stop(&ctx); 612311827fbSnicm 613311827fbSnicm for (i = 0; i < screen_size_y(&screen); i++) 614311827fbSnicm tty_draw_line(&c->tty, &screen, i, 0, 0); 61579fc95a8Snicm screen_redraw_screen(c, 1); 616311827fbSnicm 617311827fbSnicm screen_free(&screen); 618311827fbSnicm } 619311827fbSnicm 620311827fbSnicm /* Check for timers on client. */ 621311827fbSnicm void 622311827fbSnicm server_check_timers(struct client *c) 623311827fbSnicm { 624311827fbSnicm struct session *s; 625311827fbSnicm struct timeval tv; 626311827fbSnicm u_int interval; 627311827fbSnicm 628311827fbSnicm if (c == NULL || c->session == NULL) 629311827fbSnicm return; 630311827fbSnicm s = c->session; 631311827fbSnicm 632311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 633311827fbSnicm fatal("gettimeofday"); 634311827fbSnicm 635311827fbSnicm if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >)) 636311827fbSnicm status_message_clear(c); 637311827fbSnicm 638311827fbSnicm if (c->message_string != NULL || c->prompt_string != NULL) { 639311827fbSnicm /* 640311827fbSnicm * Don't need timed redraw for messages/prompts so bail now. 641311827fbSnicm * The status timer isn't reset when they are redrawn anyway. 642311827fbSnicm */ 643311827fbSnicm return; 644311827fbSnicm } 645311827fbSnicm if (!options_get_number(&s->options, "status")) 646311827fbSnicm return; 647311827fbSnicm 648311827fbSnicm /* Check timer; resolution is only a second so don't be too clever. */ 649311827fbSnicm interval = options_get_number(&s->options, "status-interval"); 650311827fbSnicm if (interval == 0) 651311827fbSnicm return; 652311827fbSnicm if (tv.tv_sec < c->status_timer.tv_sec || 653311827fbSnicm ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) 654311827fbSnicm c->flags |= CLIENT_STATUS; 655311827fbSnicm } 656311827fbSnicm 657311827fbSnicm /* Fill client pollfds. */ 658311827fbSnicm void 659311827fbSnicm server_fill_clients(struct pollfd **pfd) 660311827fbSnicm { 661311827fbSnicm struct client *c; 662311827fbSnicm struct window *w; 663311827fbSnicm struct window_pane *wp; 664311827fbSnicm u_int i; 665311827fbSnicm 666311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 667311827fbSnicm c = ARRAY_ITEM(&clients, i); 668311827fbSnicm 669311827fbSnicm server_check_timers(c); 670311827fbSnicm server_check_redraw(c); 671311827fbSnicm 672311827fbSnicm if (c == NULL) 673311827fbSnicm (*pfd)->fd = -1; 674311827fbSnicm else { 675fd234c13Snicm (*pfd)->fd = c->ibuf.fd; 6760cd9638eSnicm if (!(c->flags & CLIENT_BAD)) 677fd234c13Snicm (*pfd)->events |= POLLIN; 678fd234c13Snicm if (c->ibuf.w.queued > 0) 679311827fbSnicm (*pfd)->events |= POLLOUT; 680311827fbSnicm } 681311827fbSnicm (*pfd)++; 682311827fbSnicm 683311827fbSnicm if (c == NULL || c->flags & CLIENT_SUSPENDED || 684311827fbSnicm c->tty.fd == -1 || c->session == NULL) 685311827fbSnicm (*pfd)->fd = -1; 686311827fbSnicm else { 687311827fbSnicm (*pfd)->fd = c->tty.fd; 688311827fbSnicm (*pfd)->events = POLLIN; 689311827fbSnicm if (BUFFER_USED(c->tty.out) > 0) 690311827fbSnicm (*pfd)->events |= POLLOUT; 691311827fbSnicm } 692311827fbSnicm (*pfd)++; 693311827fbSnicm } 694311827fbSnicm 695311827fbSnicm /* 696311827fbSnicm * Clear any window redraw flags (will have been redrawn as part of 697311827fbSnicm * client). 698311827fbSnicm */ 699311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 700311827fbSnicm w = ARRAY_ITEM(&windows, i); 701311827fbSnicm if (w == NULL) 702311827fbSnicm continue; 703311827fbSnicm 704311827fbSnicm w->flags &= ~WINDOW_REDRAW; 705311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) 706311827fbSnicm wp->flags &= ~PANE_REDRAW; 707311827fbSnicm } 708311827fbSnicm } 709311827fbSnicm 710311827fbSnicm /* Handle client pollfds. */ 711311827fbSnicm void 712311827fbSnicm server_handle_clients(struct pollfd **pfd) 713311827fbSnicm { 714311827fbSnicm struct client *c; 715311827fbSnicm u_int i; 716311827fbSnicm 717311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 718311827fbSnicm c = ARRAY_ITEM(&clients, i); 719311827fbSnicm 720311827fbSnicm if (c != NULL) { 721fd234c13Snicm if ((*pfd)->revents & (POLLERR|POLLNVAL|POLLHUP)) { 722311827fbSnicm server_lost_client(c); 723311827fbSnicm (*pfd) += 2; 724311827fbSnicm continue; 725fd234c13Snicm } 726fd234c13Snicm 727fd234c13Snicm if ((*pfd)->revents & POLLOUT) { 728fd234c13Snicm if (msgbuf_write(&c->ibuf.w) < 0) { 7290cd9638eSnicm server_lost_client(c); 7300cd9638eSnicm (*pfd) += 2; 7310cd9638eSnicm continue; 732fd234c13Snicm } 733fd234c13Snicm } 734fd234c13Snicm 735fd234c13Snicm if (c->flags & CLIENT_BAD) { 736fd234c13Snicm if (c->ibuf.w.queued == 0) 737fd234c13Snicm server_lost_client(c); 738fd234c13Snicm (*pfd) += 2; 739fd234c13Snicm continue; 740fd234c13Snicm } else if ((*pfd)->revents & POLLIN) { 741fd234c13Snicm if (server_msg_dispatch(c) != 0) { 742fd234c13Snicm server_lost_client(c); 743fd234c13Snicm (*pfd) += 2; 744fd234c13Snicm continue; 745fd234c13Snicm } 746fd234c13Snicm } 747311827fbSnicm } 748311827fbSnicm (*pfd)++; 749311827fbSnicm 750311827fbSnicm if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && 751311827fbSnicm c->tty.fd != -1 && c->session != NULL) { 752311827fbSnicm if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0) 753311827fbSnicm server_lost_client(c); 754311827fbSnicm else 755311827fbSnicm server_handle_client(c); 756311827fbSnicm } 757311827fbSnicm (*pfd)++; 758311827fbSnicm } 759311827fbSnicm } 760311827fbSnicm 761311827fbSnicm /* accept(2) and create new client. */ 762980b7663Snicm void 763311827fbSnicm server_accept_client(int srv_fd) 764311827fbSnicm { 765311827fbSnicm struct sockaddr_storage sa; 766311827fbSnicm socklen_t slen = sizeof sa; 767311827fbSnicm int fd; 768311827fbSnicm 769311827fbSnicm fd = accept(srv_fd, (struct sockaddr *) &sa, &slen); 770311827fbSnicm if (fd == -1) { 771311827fbSnicm if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 772980b7663Snicm return; 773311827fbSnicm fatal("accept failed"); 774311827fbSnicm } 775311827fbSnicm if (sigterm) { 776311827fbSnicm close(fd); 777980b7663Snicm return; 778311827fbSnicm } 779980b7663Snicm server_create_client(fd); 780311827fbSnicm } 781311827fbSnicm 782311827fbSnicm /* Input data from client. */ 783311827fbSnicm void 784311827fbSnicm server_handle_client(struct client *c) 785311827fbSnicm { 786311827fbSnicm struct window_pane *wp; 787311827fbSnicm struct screen *s; 788311827fbSnicm struct timeval tv; 789311827fbSnicm struct key_binding *bd; 790311827fbSnicm int key, prefix, status, xtimeout; 791311827fbSnicm int mode; 792311827fbSnicm u_char mouse[3]; 793311827fbSnicm 794311827fbSnicm xtimeout = options_get_number(&c->session->options, "repeat-time"); 795311827fbSnicm if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { 796311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 797311827fbSnicm fatal("gettimeofday"); 798311827fbSnicm if (timercmp(&tv, &c->repeat_timer, >)) 799311827fbSnicm c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); 800311827fbSnicm } 801311827fbSnicm 802311827fbSnicm /* Process keys. */ 803311827fbSnicm prefix = options_get_number(&c->session->options, "prefix"); 804311827fbSnicm while (tty_keys_next(&c->tty, &key, mouse) == 0) { 805311827fbSnicm server_activity = time(NULL); 806311827fbSnicm 807311827fbSnicm if (c->session == NULL) 808311827fbSnicm return; 809311827fbSnicm wp = c->session->curw->window->active; /* could die */ 810311827fbSnicm 811311827fbSnicm status_message_clear(c); 812311827fbSnicm if (c->prompt_string != NULL) { 813311827fbSnicm status_prompt_key(c, key); 814311827fbSnicm continue; 815311827fbSnicm } 816311827fbSnicm if (server_locked) 817311827fbSnicm continue; 818311827fbSnicm 819311827fbSnicm /* Check for mouse keys. */ 820311827fbSnicm if (key == KEYC_MOUSE) { 821311827fbSnicm window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]); 822311827fbSnicm continue; 823311827fbSnicm } 824311827fbSnicm 825311827fbSnicm /* No previous prefix key. */ 826311827fbSnicm if (!(c->flags & CLIENT_PREFIX)) { 827311827fbSnicm if (key == prefix) 828311827fbSnicm c->flags |= CLIENT_PREFIX; 829ad4696b5Snicm else { 830ad4696b5Snicm /* Try as a non-prefix key binding. */ 831ad4696b5Snicm if ((bd = key_bindings_lookup(key)) == NULL) 832311827fbSnicm window_pane_key(wp, c, key); 833ad4696b5Snicm else 834ad4696b5Snicm key_bindings_dispatch(bd, c); 835ad4696b5Snicm } 836311827fbSnicm continue; 837311827fbSnicm } 838311827fbSnicm 839311827fbSnicm /* Prefix key already pressed. Reset prefix and lookup key. */ 840311827fbSnicm c->flags &= ~CLIENT_PREFIX; 841ad4696b5Snicm if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { 842311827fbSnicm /* If repeating, treat this as a key, else ignore. */ 843311827fbSnicm if (c->flags & CLIENT_REPEAT) { 844311827fbSnicm c->flags &= ~CLIENT_REPEAT; 845311827fbSnicm if (key == prefix) 846311827fbSnicm c->flags |= CLIENT_PREFIX; 847311827fbSnicm else 848311827fbSnicm window_pane_key(wp, c, key); 849311827fbSnicm } 850311827fbSnicm continue; 851311827fbSnicm } 852311827fbSnicm 853311827fbSnicm /* If already repeating, but this key can't repeat, skip it. */ 854311827fbSnicm if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { 855311827fbSnicm c->flags &= ~CLIENT_REPEAT; 856311827fbSnicm if (key == prefix) 857311827fbSnicm c->flags |= CLIENT_PREFIX; 858311827fbSnicm else 859311827fbSnicm window_pane_key(wp, c, key); 860311827fbSnicm continue; 861311827fbSnicm } 862311827fbSnicm 863311827fbSnicm /* If this key can repeat, reset the repeat flags and timer. */ 864311827fbSnicm if (xtimeout != 0 && bd->can_repeat) { 865311827fbSnicm c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; 866311827fbSnicm 867311827fbSnicm tv.tv_sec = xtimeout / 1000; 868311827fbSnicm tv.tv_usec = (xtimeout % 1000) * 1000L; 869311827fbSnicm if (gettimeofday(&c->repeat_timer, NULL) != 0) 870311827fbSnicm fatal("gettimeofday"); 871311827fbSnicm timeradd(&c->repeat_timer, &tv, &c->repeat_timer); 872311827fbSnicm } 873311827fbSnicm 874311827fbSnicm /* Dispatch the command. */ 875311827fbSnicm key_bindings_dispatch(bd, c); 876311827fbSnicm } 877311827fbSnicm if (c->session == NULL) 878311827fbSnicm return; 879311827fbSnicm wp = c->session->curw->window->active; /* could die - do each loop */ 880311827fbSnicm s = wp->screen; 881311827fbSnicm 88245400b51Snicm /* 88345400b51Snicm * Update cursor position and mode settings. The scroll region and 88445400b51Snicm * attributes are cleared across poll(2) as this is the most likely 88545400b51Snicm * time a user may interrupt tmux, for example with ~^Z in ssh(1). This 88645400b51Snicm * is a compromise between excessive resets and likelihood of an 88745400b51Snicm * interrupt. 88845400b51Snicm * 88945400b51Snicm * tty_region/tty_reset/tty_update_mode already take care of not 89045400b51Snicm * resetting things that are already in their default state. 89145400b51Snicm */ 892311827fbSnicm status = options_get_number(&c->session->options, "status"); 893dde47eafSnicm tty_region(&c->tty, 0, c->tty.sy - 1, 0); 894af9e4c5dSnicm if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) 895af9e4c5dSnicm tty_cursor(&c->tty, 0, 0, 0, 0); 896af9e4c5dSnicm else 897311827fbSnicm tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff); 898311827fbSnicm 899311827fbSnicm mode = s->mode; 900311827fbSnicm if (server_locked) 901311827fbSnicm mode &= ~TTY_NOCURSOR; 902311827fbSnicm tty_update_mode(&c->tty, mode); 90345400b51Snicm tty_reset(&c->tty); 904311827fbSnicm } 905311827fbSnicm 906311827fbSnicm /* Lost a client. */ 907311827fbSnicm void 908311827fbSnicm server_lost_client(struct client *c) 909311827fbSnicm { 910311827fbSnicm u_int i; 911311827fbSnicm 912311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 913311827fbSnicm if (ARRAY_ITEM(&clients, i) == c) 914311827fbSnicm ARRAY_SET(&clients, i, NULL); 915311827fbSnicm } 9160bb39cd5Snicm log_debug("lost client %d", c->ibuf.fd); 917311827fbSnicm 918*f369cd7fSnicm /* 919*f369cd7fSnicm * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called 920*f369cd7fSnicm * and tty_free might close an unrelated fd. 921*f369cd7fSnicm */ 922*f369cd7fSnicm if (c->flags & CLIENT_TERMINAL) 923130af69dSnicm tty_free(&c->tty); 924311827fbSnicm 925311827fbSnicm screen_free(&c->status); 926311827fbSnicm 927311827fbSnicm if (c->title != NULL) 928311827fbSnicm xfree(c->title); 929311827fbSnicm 930311827fbSnicm if (c->message_string != NULL) 931311827fbSnicm xfree(c->message_string); 932311827fbSnicm 933311827fbSnicm if (c->prompt_string != NULL) 934311827fbSnicm xfree(c->prompt_string); 935311827fbSnicm if (c->prompt_buffer != NULL) 936311827fbSnicm xfree(c->prompt_buffer); 937311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++) 938311827fbSnicm xfree(ARRAY_ITEM(&c->prompt_hdata, i)); 939311827fbSnicm ARRAY_FREE(&c->prompt_hdata); 940311827fbSnicm 941311827fbSnicm if (c->cwd != NULL) 942311827fbSnicm xfree(c->cwd); 943311827fbSnicm 944fd234c13Snicm close(c->ibuf.fd); 945fd234c13Snicm imsg_clear(&c->ibuf); 946311827fbSnicm xfree(c); 947311827fbSnicm 948311827fbSnicm recalculate_sizes(); 949311827fbSnicm } 950311827fbSnicm 951311827fbSnicm /* Handle window data. */ 952311827fbSnicm void 953311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp) 954311827fbSnicm { 955311827fbSnicm struct session *s; 956311827fbSnicm u_int i; 957311827fbSnicm int update; 958311827fbSnicm 959311827fbSnicm window_pane_parse(wp); 960311827fbSnicm 961311827fbSnicm if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0) 962311827fbSnicm return; 963311827fbSnicm 964311827fbSnicm update = 0; 965311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 966311827fbSnicm s = ARRAY_ITEM(&sessions, i); 967311827fbSnicm if (s == NULL || !session_has(s, w)) 968311827fbSnicm continue; 969311827fbSnicm 9700903c7b9Snicm update += server_check_window_bell(s, w); 971311827fbSnicm update += server_check_window_activity(s, w); 972311827fbSnicm update += server_check_window_content(s, w, wp); 973311827fbSnicm } 974311827fbSnicm if (update) 975311827fbSnicm server_status_window(w); 976311827fbSnicm 977311827fbSnicm w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT); 978311827fbSnicm } 979311827fbSnicm 980311827fbSnicm int 9810903c7b9Snicm server_check_window_bell(struct session *s, struct window *w) 982311827fbSnicm { 983311827fbSnicm struct client *c; 984311827fbSnicm u_int i; 9850903c7b9Snicm int action, visual; 986311827fbSnicm 987311827fbSnicm if (!(w->flags & WINDOW_BELL)) 988311827fbSnicm return (0); 989311827fbSnicm if (session_alert_has_window(s, w, WINDOW_BELL)) 990311827fbSnicm return (0); 991311827fbSnicm session_alert_add(s, w, WINDOW_BELL); 992311827fbSnicm 993311827fbSnicm action = options_get_number(&s->options, "bell-action"); 994311827fbSnicm switch (action) { 995311827fbSnicm case BELL_ANY: 996311827fbSnicm if (s->flags & SESSION_UNATTACHED) 997311827fbSnicm break; 9980903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 999311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1000311827fbSnicm c = ARRAY_ITEM(&clients, i); 10010903c7b9Snicm if (c == NULL || c->session != s) 10020903c7b9Snicm continue; 10030903c7b9Snicm if (!visual) { 1004311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 10050903c7b9Snicm continue; 10060903c7b9Snicm } 10070903c7b9Snicm if (c->session->curw->window == w) { 10080903c7b9Snicm status_message_set(c, "Bell in current window"); 10090903c7b9Snicm continue; 10100903c7b9Snicm } 10110903c7b9Snicm status_message_set(c, "Bell in window %u", 10120903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 1013311827fbSnicm } 1014311827fbSnicm break; 1015311827fbSnicm case BELL_CURRENT: 10160903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 1017311827fbSnicm break; 10180903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 1019311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1020311827fbSnicm c = ARRAY_ITEM(&clients, i); 10210903c7b9Snicm if (c == NULL || c->session != s) 10220903c7b9Snicm continue; 10230903c7b9Snicm if (c->session->curw->window != w) 10240903c7b9Snicm continue; 10250903c7b9Snicm if (!visual) { 1026311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 10270903c7b9Snicm continue; 10280903c7b9Snicm } 10290903c7b9Snicm status_message_set(c, "Bell in current window"); 1030311827fbSnicm } 1031311827fbSnicm break; 1032311827fbSnicm } 1033311827fbSnicm return (1); 1034311827fbSnicm } 1035311827fbSnicm 1036311827fbSnicm int 1037311827fbSnicm server_check_window_activity(struct session *s, struct window *w) 1038311827fbSnicm { 10390903c7b9Snicm struct client *c; 10400903c7b9Snicm u_int i; 10410903c7b9Snicm 1042311827fbSnicm if (!(w->flags & WINDOW_ACTIVITY)) 1043311827fbSnicm return (0); 10440903c7b9Snicm 1045311827fbSnicm if (!options_get_number(&w->options, "monitor-activity")) 1046311827fbSnicm return (0); 10470903c7b9Snicm 1048311827fbSnicm if (session_alert_has_window(s, w, WINDOW_ACTIVITY)) 1049311827fbSnicm return (0); 10500903c7b9Snicm if (s->curw->window == w) 10510903c7b9Snicm return (0); 10520903c7b9Snicm 1053311827fbSnicm session_alert_add(s, w, WINDOW_ACTIVITY); 10540903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 10550903c7b9Snicm return (0); 10560903c7b9Snicm if (options_get_number(&s->options, "visual-activity")) { 10570903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 10580903c7b9Snicm c = ARRAY_ITEM(&clients, i); 10590903c7b9Snicm if (c == NULL || c->session != s) 10600903c7b9Snicm continue; 10610903c7b9Snicm status_message_set(c, "Activity in window %u", 10620903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 10630903c7b9Snicm } 10640903c7b9Snicm } 10650903c7b9Snicm 1066311827fbSnicm return (1); 1067311827fbSnicm } 1068311827fbSnicm 1069311827fbSnicm int 1070311827fbSnicm server_check_window_content( 1071311827fbSnicm struct session *s, struct window *w, struct window_pane *wp) 1072311827fbSnicm { 10730903c7b9Snicm struct client *c; 10740903c7b9Snicm u_int i; 1075311827fbSnicm char *found, *ptr; 1076311827fbSnicm 10770903c7b9Snicm if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */ 1078311827fbSnicm return (0); 10790903c7b9Snicm 10800903c7b9Snicm ptr = options_get_string(&w->options, "monitor-content"); 10810903c7b9Snicm if (ptr == NULL || *ptr == '\0') 1082311827fbSnicm return (0); 10830903c7b9Snicm 1084311827fbSnicm if (session_alert_has_window(s, w, WINDOW_CONTENT)) 1085311827fbSnicm return (0); 10860903c7b9Snicm if (s->curw->window == w) 10870903c7b9Snicm return (0); 10880903c7b9Snicm 1089570a3589Snicm if ((found = window_pane_search(wp, ptr, NULL)) == NULL) 1090311827fbSnicm return (0); 1091311827fbSnicm xfree(found); 10920903c7b9Snicm 10930903c7b9Snicm session_alert_add(s, w, WINDOW_CONTENT); 10940903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 10950903c7b9Snicm return (0); 10960903c7b9Snicm if (options_get_number(&s->options, "visual-content")) { 10970903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 10980903c7b9Snicm c = ARRAY_ITEM(&clients, i); 10990903c7b9Snicm if (c == NULL || c->session != s) 11000903c7b9Snicm continue; 11010903c7b9Snicm status_message_set(c, "Content in window %u", 11020903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 11030903c7b9Snicm } 11040903c7b9Snicm } 11050903c7b9Snicm 1106311827fbSnicm return (1); 1107311827fbSnicm } 1108311827fbSnicm 1109c752cdacSnicm /* Check if window still exists. */ 1110311827fbSnicm void 1111311827fbSnicm server_check_window(struct window *w) 1112311827fbSnicm { 1113311827fbSnicm struct window_pane *wp, *wq; 1114e0b5b8e5Snicm struct options *oo = &w->options; 1115311827fbSnicm struct client *c; 1116311827fbSnicm struct session *s; 1117311827fbSnicm struct winlink *wl; 1118311827fbSnicm u_int i, j; 1119e0b5b8e5Snicm int destroyed; 1120311827fbSnicm 1121311827fbSnicm destroyed = 1; 1122311827fbSnicm 1123311827fbSnicm wp = TAILQ_FIRST(&w->panes); 1124311827fbSnicm while (wp != NULL) { 1125311827fbSnicm wq = TAILQ_NEXT(wp, entry); 1126c752cdacSnicm /* 1127c752cdacSnicm * If the pane has died and the remain-on-exit flag is not set, 1128c752cdacSnicm * remove the pane; otherwise, if the flag is set, don't allow 1129c752cdacSnicm * the window to be destroyed (or it'll close when the last 1130c752cdacSnicm * pane dies). 1131c752cdacSnicm */ 11322be3d294Snicm if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) { 1133af9e4c5dSnicm layout_close_pane(wp); 1134311827fbSnicm window_remove_pane(w, wp); 1135311827fbSnicm server_redraw_window(w); 1136c752cdacSnicm } else 1137c752cdacSnicm destroyed = 0; 1138311827fbSnicm wp = wq; 1139311827fbSnicm } 1140311827fbSnicm 1141311827fbSnicm if (!destroyed) 1142311827fbSnicm return; 1143311827fbSnicm 1144311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1145311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1146311827fbSnicm if (s == NULL) 1147311827fbSnicm continue; 1148311827fbSnicm if (!session_has(s, w)) 1149311827fbSnicm continue; 1150311827fbSnicm 1151311827fbSnicm restart: 1152311827fbSnicm /* Detach window and either redraw or kill clients. */ 1153311827fbSnicm RB_FOREACH(wl, winlinks, &s->windows) { 1154311827fbSnicm if (wl->window != w) 1155311827fbSnicm continue; 1156311827fbSnicm destroyed = session_detach(s, wl); 1157311827fbSnicm for (j = 0; j < ARRAY_LENGTH(&clients); j++) { 1158311827fbSnicm c = ARRAY_ITEM(&clients, j); 1159311827fbSnicm if (c == NULL || c->session != s) 1160311827fbSnicm continue; 1161311827fbSnicm if (!destroyed) { 1162311827fbSnicm server_redraw_client(c); 1163311827fbSnicm continue; 1164311827fbSnicm } 1165311827fbSnicm c->session = NULL; 1166311827fbSnicm server_write_client(c, MSG_EXIT, NULL, 0); 1167311827fbSnicm } 1168311827fbSnicm /* If the session was destroyed, bail now. */ 1169311827fbSnicm if (destroyed) 1170311827fbSnicm break; 1171311827fbSnicm goto restart; 1172311827fbSnicm } 1173311827fbSnicm } 1174311827fbSnicm 1175311827fbSnicm recalculate_sizes(); 1176311827fbSnicm } 1177311827fbSnicm 1178311827fbSnicm /* Call any once-per-second timers. */ 1179311827fbSnicm void 1180311827fbSnicm server_second_timers(void) 1181311827fbSnicm { 1182311827fbSnicm struct window *w; 1183311827fbSnicm struct window_pane *wp; 1184311827fbSnicm u_int i; 1185311827fbSnicm int xtimeout; 1186311827fbSnicm struct tm now, then; 1187311827fbSnicm static time_t last_t = 0; 1188311827fbSnicm time_t t; 1189311827fbSnicm 1190311827fbSnicm t = time(NULL); 1191eaecedb2Snicm xtimeout = options_get_number(&global_s_options, "lock-after-time"); 1192311827fbSnicm if (xtimeout > 0 && t > server_activity + xtimeout) 1193311827fbSnicm server_lock(); 1194311827fbSnicm 1195311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 1196311827fbSnicm w = ARRAY_ITEM(&windows, i); 1197311827fbSnicm if (w == NULL) 1198311827fbSnicm continue; 1199311827fbSnicm 1200311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 1201311827fbSnicm if (wp->mode != NULL && wp->mode->timer != NULL) 1202311827fbSnicm wp->mode->timer(wp); 1203311827fbSnicm } 1204311827fbSnicm } 1205311827fbSnicm 1206311827fbSnicm /* Check for a minute having passed. */ 1207311827fbSnicm gmtime_r(&t, &now); 1208311827fbSnicm gmtime_r(&last_t, &then); 1209311827fbSnicm if (now.tm_min == then.tm_min) 1210311827fbSnicm return; 1211311827fbSnicm last_t = t; 1212311827fbSnicm 1213311827fbSnicm /* If locked, redraw all clients. */ 1214311827fbSnicm if (server_locked) { 1215311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1216311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 1217311827fbSnicm server_redraw_client(ARRAY_ITEM(&clients, i)); 1218311827fbSnicm } 1219311827fbSnicm } 1220311827fbSnicm } 1221311827fbSnicm 1222311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */ 1223311827fbSnicm int 1224311827fbSnicm server_update_socket(void) 1225311827fbSnicm { 1226311827fbSnicm struct session *s; 1227311827fbSnicm u_int i; 1228311827fbSnicm static int last = -1; 1229311827fbSnicm int n; 1230311827fbSnicm 1231311827fbSnicm n = 0; 1232311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1233311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1234311827fbSnicm if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { 1235311827fbSnicm n++; 1236311827fbSnicm break; 1237311827fbSnicm } 1238311827fbSnicm } 1239311827fbSnicm 1240311827fbSnicm if (n != last) { 1241311827fbSnicm last = n; 1242311827fbSnicm if (n != 0) 1243311827fbSnicm chmod(socket_path, S_IRWXU); 1244311827fbSnicm else 1245311827fbSnicm chmod(socket_path, S_IRUSR|S_IWUSR); 1246311827fbSnicm } 1247311827fbSnicm 1248311827fbSnicm return (n); 1249311827fbSnicm } 1250