1*130af69dSnicm /* $OpenBSD: server.c,v 1.19 2009/08/11 19:32:25 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); 113311827fbSnicm } 114311827fbSnicm 115311827fbSnicm /* Find client index. */ 116311827fbSnicm int 117311827fbSnicm server_client_index(struct client *c) 118311827fbSnicm { 119311827fbSnicm u_int i; 120311827fbSnicm 121311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 122311827fbSnicm if (c == ARRAY_ITEM(&clients, i)) 123311827fbSnicm return (i); 124311827fbSnicm } 125311827fbSnicm return (-1); 126311827fbSnicm } 127311827fbSnicm 128311827fbSnicm /* Fork new server. */ 129311827fbSnicm int 130311827fbSnicm server_start(char *path) 131311827fbSnicm { 1320cd9638eSnicm struct client *c; 1330cd9638eSnicm int pair[2], srv_fd; 134311827fbSnicm char *cause; 135311827fbSnicm char rpathbuf[MAXPATHLEN]; 136311827fbSnicm 137311827fbSnicm /* The first client is special and gets a socketpair; create it. */ 138311827fbSnicm if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) 139311827fbSnicm fatal("socketpair failed"); 140311827fbSnicm 141311827fbSnicm switch (fork()) { 142311827fbSnicm case -1: 143311827fbSnicm fatal("fork failed"); 144311827fbSnicm case 0: 145311827fbSnicm break; 146311827fbSnicm default: 147311827fbSnicm close(pair[1]); 148311827fbSnicm return (pair[0]); 149311827fbSnicm } 150311827fbSnicm close(pair[0]); 151311827fbSnicm 152311827fbSnicm /* 153311827fbSnicm * Must daemonise before loading configuration as the PID changes so 154311827fbSnicm * $TMUX would be wrong for sessions created in the config file. 155311827fbSnicm */ 1560cd9638eSnicm if (daemon(1, 0) != 0) 157311827fbSnicm fatal("daemon failed"); 158311827fbSnicm 1590cd9638eSnicm logfile("server"); 1600cd9638eSnicm log_debug("server started, pid %ld", (long) getpid()); 1610cd9638eSnicm 162311827fbSnicm ARRAY_INIT(&windows); 163311827fbSnicm ARRAY_INIT(&clients); 164311827fbSnicm ARRAY_INIT(&sessions); 16568895571Snicm mode_key_init_trees(); 166311827fbSnicm key_bindings_init(); 167311827fbSnicm utf8_build(); 168311827fbSnicm 169311827fbSnicm server_locked = 0; 170311827fbSnicm server_password = NULL; 171311827fbSnicm server_activity = time(NULL); 172311827fbSnicm 173311827fbSnicm start_time = time(NULL); 174311827fbSnicm socket_path = path; 175311827fbSnicm 176311827fbSnicm if (realpath(socket_path, rpathbuf) == NULL) 177311827fbSnicm strlcpy(rpathbuf, socket_path, sizeof rpathbuf); 1780cd9638eSnicm log_debug("socket path %s", socket_path); 179311827fbSnicm setproctitle("server (%s)", rpathbuf); 180311827fbSnicm 181311827fbSnicm srv_fd = server_create_socket(); 182311827fbSnicm server_create_client(pair[1]); 183311827fbSnicm 1840cd9638eSnicm if (access(SYSTEM_CFG, R_OK) != 0) { 1850cd9638eSnicm if (errno != ENOENT) { 1860cd9638eSnicm xasprintf( 1870cd9638eSnicm &cause, "%s: %s", strerror(errno), SYSTEM_CFG); 1880cd9638eSnicm goto error; 1890cd9638eSnicm } 1900cd9638eSnicm } else if (load_cfg(SYSTEM_CFG, &cause) != 0) 1910cd9638eSnicm goto error; 1920cd9638eSnicm if (cfg_file != NULL && load_cfg(cfg_file, &cause) != 0) 1930cd9638eSnicm goto error; 1940cd9638eSnicm 1950cd9638eSnicm exit(server_main(srv_fd)); 1960cd9638eSnicm 1970cd9638eSnicm error: 1980cd9638eSnicm /* Write the error and shutdown the server. */ 1990cd9638eSnicm c = ARRAY_FIRST(&clients); 2000cd9638eSnicm 2010cd9638eSnicm server_write_error(c, cause); 2020cd9638eSnicm xfree(cause); 2030cd9638eSnicm 2040cd9638eSnicm server_shutdown(); 2050cd9638eSnicm c->flags |= CLIENT_BAD; 2060cd9638eSnicm 207311827fbSnicm exit(server_main(srv_fd)); 208311827fbSnicm } 209311827fbSnicm 210311827fbSnicm /* Create server socket. */ 211311827fbSnicm int 212311827fbSnicm server_create_socket(void) 213311827fbSnicm { 214311827fbSnicm struct sockaddr_un sa; 215311827fbSnicm size_t size; 216311827fbSnicm mode_t mask; 217311827fbSnicm int fd, mode; 218311827fbSnicm 219311827fbSnicm memset(&sa, 0, sizeof sa); 220311827fbSnicm sa.sun_family = AF_UNIX; 221311827fbSnicm size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 222311827fbSnicm if (size >= sizeof sa.sun_path) { 223311827fbSnicm errno = ENAMETOOLONG; 224311827fbSnicm fatal("socket failed"); 225311827fbSnicm } 226311827fbSnicm unlink(sa.sun_path); 227311827fbSnicm 228311827fbSnicm if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 229311827fbSnicm fatal("socket failed"); 230311827fbSnicm 231311827fbSnicm mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 232311827fbSnicm if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) 233311827fbSnicm fatal("bind failed"); 234311827fbSnicm umask(mask); 235311827fbSnicm 236311827fbSnicm if (listen(fd, 16) == -1) 237311827fbSnicm fatal("listen failed"); 238311827fbSnicm 239311827fbSnicm if ((mode = fcntl(fd, F_GETFL)) == -1) 240311827fbSnicm fatal("fcntl failed"); 241311827fbSnicm if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 242311827fbSnicm fatal("fcntl failed"); 243311827fbSnicm if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 244311827fbSnicm fatal("fcntl failed"); 245311827fbSnicm 246311827fbSnicm return (fd); 247311827fbSnicm } 248311827fbSnicm 249311827fbSnicm /* Main server loop. */ 250311827fbSnicm int 251311827fbSnicm server_main(int srv_fd) 252311827fbSnicm { 253311827fbSnicm struct window *w; 254311827fbSnicm struct pollfd *pfds, *pfd; 255311827fbSnicm int nfds, xtimeout; 256311827fbSnicm u_int i, n; 257311827fbSnicm time_t now, last; 258311827fbSnicm 259311827fbSnicm siginit(); 260311827fbSnicm 261311827fbSnicm last = time(NULL); 262311827fbSnicm 263311827fbSnicm pfds = NULL; 264311827fbSnicm for (;;) { 265311827fbSnicm /* If sigterm, kill all windows and clients. */ 266311827fbSnicm if (sigterm) 267311827fbSnicm server_shutdown(); 268311827fbSnicm 269311827fbSnicm /* Handle child exit. */ 270311827fbSnicm if (sigchld) { 271311827fbSnicm server_child_signal(); 272311827fbSnicm sigchld = 0; 273311827fbSnicm } 274311827fbSnicm 275311827fbSnicm /* Recreate socket on SIGUSR1. */ 276311827fbSnicm if (sigusr1) { 277311827fbSnicm close(srv_fd); 278311827fbSnicm srv_fd = server_create_socket(); 279311827fbSnicm sigusr1 = 0; 280311827fbSnicm } 281311827fbSnicm 282311827fbSnicm /* Initialise pollfd array. */ 283311827fbSnicm nfds = 1; 284311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 285311827fbSnicm w = ARRAY_ITEM(&windows, i); 286311827fbSnicm if (w != NULL) 287311827fbSnicm nfds += window_count_panes(w); 288311827fbSnicm } 289311827fbSnicm nfds += ARRAY_LENGTH(&clients) * 2; 290311827fbSnicm pfds = xrealloc(pfds, nfds, sizeof *pfds); 291311827fbSnicm memset(pfds, 0, nfds * sizeof *pfds); 292311827fbSnicm pfd = pfds; 293311827fbSnicm 294311827fbSnicm /* Fill server socket. */ 295311827fbSnicm pfd->fd = srv_fd; 296311827fbSnicm pfd->events = POLLIN; 297311827fbSnicm pfd++; 298311827fbSnicm 299311827fbSnicm /* Fill window and client sockets. */ 300311827fbSnicm server_fill_windows(&pfd); 301311827fbSnicm server_fill_clients(&pfd); 302311827fbSnicm 303311827fbSnicm /* Update socket permissions. */ 304311827fbSnicm xtimeout = INFTIM; 305311827fbSnicm if (sigterm || server_update_socket() != 0) 306311827fbSnicm xtimeout = POLL_TIMEOUT; 307311827fbSnicm 308311827fbSnicm /* Do the poll. */ 3099b1fc963Snicm if (poll(pfds, nfds, xtimeout) == -1) { 310311827fbSnicm if (errno == EAGAIN || errno == EINTR) 311311827fbSnicm continue; 312311827fbSnicm fatal("poll failed"); 313311827fbSnicm } 314311827fbSnicm pfd = pfds; 315311827fbSnicm 316311827fbSnicm /* Handle server socket. */ 317311827fbSnicm if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) 318311827fbSnicm fatalx("lost server socket"); 319311827fbSnicm if (pfd->revents & POLLIN) { 320311827fbSnicm server_accept_client(srv_fd); 321311827fbSnicm continue; 322311827fbSnicm } 323311827fbSnicm pfd++; 324311827fbSnicm 325311827fbSnicm /* Call second-based timers. */ 326311827fbSnicm now = time(NULL); 327311827fbSnicm if (now != last) { 328311827fbSnicm last = now; 329311827fbSnicm server_second_timers(); 330311827fbSnicm } 331311827fbSnicm 332311827fbSnicm /* Set window names. */ 333311827fbSnicm set_window_names(); 334311827fbSnicm 335311827fbSnicm /* 336311827fbSnicm * Handle window and client sockets. Clients can create 337311827fbSnicm * windows, so windows must come first to avoid messing up by 338311827fbSnicm * increasing the array size. 339311827fbSnicm */ 340311827fbSnicm server_handle_windows(&pfd); 341311827fbSnicm server_handle_clients(&pfd); 342311827fbSnicm 3430476b68eSnicm /* Collect any unset key bindings. */ 3440476b68eSnicm key_bindings_clean(); 3450476b68eSnicm 346311827fbSnicm /* 347311827fbSnicm * If we have no sessions and clients left, let's get out 348311827fbSnicm * of here... 349311827fbSnicm */ 350311827fbSnicm n = 0; 351311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 352311827fbSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 353311827fbSnicm n++; 354311827fbSnicm } 355311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 356311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 357311827fbSnicm n++; 358311827fbSnicm } 359311827fbSnicm if (n == 0) 360311827fbSnicm break; 361311827fbSnicm } 362311827fbSnicm if (pfds != NULL) 363311827fbSnicm xfree(pfds); 364311827fbSnicm 365311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 366311827fbSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 367311827fbSnicm session_destroy(ARRAY_ITEM(&sessions, i)); 368311827fbSnicm } 369311827fbSnicm ARRAY_FREE(&sessions); 370311827fbSnicm 371311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 372311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 373311827fbSnicm server_lost_client(ARRAY_ITEM(&clients, i)); 374311827fbSnicm } 375311827fbSnicm ARRAY_FREE(&clients); 376311827fbSnicm 37768895571Snicm mode_key_free_trees(); 378311827fbSnicm key_bindings_free(); 379311827fbSnicm 380311827fbSnicm close(srv_fd); 381311827fbSnicm 382311827fbSnicm unlink(socket_path); 383311827fbSnicm xfree(socket_path); 384311827fbSnicm 385eaecedb2Snicm options_free(&global_s_options); 386eaecedb2Snicm options_free(&global_w_options); 387311827fbSnicm if (server_password != NULL) 388311827fbSnicm xfree(server_password); 389311827fbSnicm 390311827fbSnicm return (0); 391311827fbSnicm } 392311827fbSnicm 393311827fbSnicm /* Kill all clients. */ 394311827fbSnicm void 395311827fbSnicm server_shutdown(void) 396311827fbSnicm { 397311827fbSnicm struct session *s; 398311827fbSnicm struct client *c; 399311827fbSnicm u_int i, j; 400311827fbSnicm 401311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 402311827fbSnicm s = ARRAY_ITEM(&sessions, i); 403311827fbSnicm for (j = 0; j < ARRAY_LENGTH(&clients); j++) { 404311827fbSnicm c = ARRAY_ITEM(&clients, j); 405311827fbSnicm if (c != NULL && c->session == s) { 406311827fbSnicm s = NULL; 407311827fbSnicm break; 408311827fbSnicm } 409311827fbSnicm } 410311827fbSnicm if (s != NULL) 411311827fbSnicm session_destroy(s); 412311827fbSnicm } 413311827fbSnicm 414311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 415311827fbSnicm c = ARRAY_ITEM(&clients, i); 4160cd9638eSnicm if (c != NULL) { 4170cd9638eSnicm if (c->flags & CLIENT_BAD) 4180cd9638eSnicm server_lost_client(c); 4190cd9638eSnicm else 420311827fbSnicm server_write_client(c, MSG_SHUTDOWN, NULL, 0); 4210cd9638eSnicm c->flags |= CLIENT_BAD; 4220cd9638eSnicm } 423311827fbSnicm } 424311827fbSnicm } 425311827fbSnicm 426311827fbSnicm /* Handle SIGCHLD. */ 427311827fbSnicm void 428311827fbSnicm server_child_signal(void) 429311827fbSnicm { 430311827fbSnicm struct window *w; 431311827fbSnicm struct window_pane *wp; 432311827fbSnicm int status; 433311827fbSnicm pid_t pid; 434311827fbSnicm u_int i; 435311827fbSnicm 436311827fbSnicm for (;;) { 437311827fbSnicm switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 438311827fbSnicm case -1: 439311827fbSnicm if (errno == ECHILD) 440311827fbSnicm return; 441311827fbSnicm fatal("waitpid"); 442311827fbSnicm case 0: 443311827fbSnicm return; 444311827fbSnicm } 445311827fbSnicm if (!WIFSTOPPED(status)) 446311827fbSnicm continue; 447311827fbSnicm if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 448311827fbSnicm continue; 449311827fbSnicm 450311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 451311827fbSnicm w = ARRAY_ITEM(&windows, i); 452311827fbSnicm if (w == NULL) 453311827fbSnicm continue; 454311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 455311827fbSnicm if (wp->pid == pid) { 456311827fbSnicm if (killpg(pid, SIGCONT) != 0) 457311827fbSnicm kill(pid, SIGCONT); 458311827fbSnicm } 459311827fbSnicm } 460311827fbSnicm } 461311827fbSnicm } 462311827fbSnicm } 463311827fbSnicm 464311827fbSnicm /* Fill window pollfds. */ 465311827fbSnicm void 466311827fbSnicm server_fill_windows(struct pollfd **pfd) 467311827fbSnicm { 468311827fbSnicm struct window *w; 469311827fbSnicm struct window_pane *wp; 470311827fbSnicm u_int i; 471311827fbSnicm 472311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 473311827fbSnicm w = ARRAY_ITEM(&windows, i); 474311827fbSnicm if (w == NULL) 475311827fbSnicm continue; 476311827fbSnicm 477311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 478311827fbSnicm (*pfd)->fd = wp->fd; 479311827fbSnicm if (wp->fd != -1) { 480311827fbSnicm (*pfd)->events = POLLIN; 481311827fbSnicm if (BUFFER_USED(wp->out) > 0) 482311827fbSnicm (*pfd)->events |= POLLOUT; 483311827fbSnicm } 484311827fbSnicm (*pfd)++; 485311827fbSnicm } 486311827fbSnicm } 487311827fbSnicm } 488311827fbSnicm 489311827fbSnicm /* Handle window pollfds. */ 490311827fbSnicm void 491311827fbSnicm server_handle_windows(struct pollfd **pfd) 492311827fbSnicm { 493311827fbSnicm struct window *w; 494311827fbSnicm struct window_pane *wp; 495311827fbSnicm u_int i; 496311827fbSnicm 497311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 498311827fbSnicm w = ARRAY_ITEM(&windows, i); 499311827fbSnicm if (w == NULL) 500311827fbSnicm continue; 501311827fbSnicm 502311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 503311827fbSnicm if (wp->fd != -1) { 504311827fbSnicm if (buffer_poll(*pfd, wp->in, wp->out) != 0) { 505311827fbSnicm close(wp->fd); 506311827fbSnicm wp->fd = -1; 507311827fbSnicm } else 508311827fbSnicm server_handle_window(w, wp); 509311827fbSnicm } 510311827fbSnicm (*pfd)++; 511311827fbSnicm } 512311827fbSnicm 513311827fbSnicm server_check_window(w); 514311827fbSnicm } 515311827fbSnicm } 516311827fbSnicm 517311827fbSnicm /* Check for general redraw on client. */ 518311827fbSnicm void 519311827fbSnicm server_check_redraw(struct client *c) 520311827fbSnicm { 521311827fbSnicm struct session *s; 522311827fbSnicm struct window_pane *wp; 523311827fbSnicm char title[512]; 524311827fbSnicm int flags, redraw; 525311827fbSnicm 526311827fbSnicm if (c == NULL || c->session == NULL) 527311827fbSnicm return; 528311827fbSnicm s = c->session; 529311827fbSnicm 530311827fbSnicm flags = c->tty.flags & TTY_FREEZE; 531311827fbSnicm c->tty.flags &= ~TTY_FREEZE; 532311827fbSnicm 533311827fbSnicm if (options_get_number(&s->options, "set-titles")) { 534311827fbSnicm xsnprintf(title, sizeof title, "%s:%u:%s - \"%s\"", 535311827fbSnicm s->name, s->curw->idx, s->curw->window->name, 536311827fbSnicm s->curw->window->active->screen->title); 537311827fbSnicm if (c->title == NULL || strcmp(title, c->title) != 0) { 538311827fbSnicm if (c->title != NULL) 539311827fbSnicm xfree(c->title); 540311827fbSnicm c->title = xstrdup(title); 541311827fbSnicm tty_set_title(&c->tty, c->title); 542311827fbSnicm } 543311827fbSnicm } 544311827fbSnicm 545311827fbSnicm if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { 546311827fbSnicm if (c->message_string != NULL) 547311827fbSnicm redraw = status_message_redraw(c); 548311827fbSnicm else if (c->prompt_string != NULL) 549311827fbSnicm redraw = status_prompt_redraw(c); 550311827fbSnicm else 551311827fbSnicm redraw = status_redraw(c); 552311827fbSnicm if (!redraw) 553311827fbSnicm c->flags &= ~CLIENT_STATUS; 554311827fbSnicm } 555311827fbSnicm 556311827fbSnicm if (c->flags & CLIENT_REDRAW) { 557311827fbSnicm if (server_locked) 558311827fbSnicm server_redraw_locked(c); 559311827fbSnicm else 56079fc95a8Snicm screen_redraw_screen(c, 0); 561311827fbSnicm c->flags &= ~CLIENT_STATUS; 562311827fbSnicm } else { 563311827fbSnicm TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { 564311827fbSnicm if (wp->flags & PANE_REDRAW) 565311827fbSnicm screen_redraw_pane(c, wp); 566311827fbSnicm } 567311827fbSnicm } 568311827fbSnicm 569311827fbSnicm if (c->flags & CLIENT_STATUS) 57079fc95a8Snicm screen_redraw_screen(c, 1); 571311827fbSnicm 572311827fbSnicm c->tty.flags |= flags; 573311827fbSnicm 574311827fbSnicm c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); 575311827fbSnicm } 576311827fbSnicm 577311827fbSnicm /* Redraw client when locked. */ 578311827fbSnicm void 579311827fbSnicm server_redraw_locked(struct client *c) 580311827fbSnicm { 581311827fbSnicm struct screen_write_ctx ctx; 582311827fbSnicm struct screen screen; 583d658fe3cSnicm struct grid_cell gc; 584311827fbSnicm u_int colour, xx, yy, i; 585311827fbSnicm int style; 586311827fbSnicm 587311827fbSnicm xx = c->tty.sx; 588311827fbSnicm yy = c->tty.sy - 1; 589311827fbSnicm if (xx == 0 || yy == 0) 590311827fbSnicm return; 591eaecedb2Snicm colour = options_get_number(&global_w_options, "clock-mode-colour"); 592eaecedb2Snicm style = options_get_number(&global_w_options, "clock-mode-style"); 593311827fbSnicm 594d658fe3cSnicm memcpy(&gc, &grid_default_cell, sizeof gc); 595d658fe3cSnicm gc.fg = colour; 596d658fe3cSnicm gc.attr |= GRID_ATTR_BRIGHT; 597d658fe3cSnicm 598311827fbSnicm screen_init(&screen, xx, yy, 0); 599311827fbSnicm 600311827fbSnicm screen_write_start(&ctx, NULL, &screen); 601311827fbSnicm clock_draw(&ctx, colour, style); 602d658fe3cSnicm 603d658fe3cSnicm if (password_failures != 0) { 604d658fe3cSnicm screen_write_cursormove(&ctx, 0, 0); 605d658fe3cSnicm screen_write_puts( 606d658fe3cSnicm &ctx, &gc, "%u failed attempts", password_failures); 607d658fe3cSnicm } 608d658fe3cSnicm 609311827fbSnicm screen_write_stop(&ctx); 610311827fbSnicm 611311827fbSnicm for (i = 0; i < screen_size_y(&screen); i++) 612311827fbSnicm tty_draw_line(&c->tty, &screen, i, 0, 0); 61379fc95a8Snicm screen_redraw_screen(c, 1); 614311827fbSnicm 615311827fbSnicm screen_free(&screen); 616311827fbSnicm } 617311827fbSnicm 618311827fbSnicm /* Check for timers on client. */ 619311827fbSnicm void 620311827fbSnicm server_check_timers(struct client *c) 621311827fbSnicm { 622311827fbSnicm struct session *s; 623311827fbSnicm struct timeval tv; 624311827fbSnicm u_int interval; 625311827fbSnicm 626311827fbSnicm if (c == NULL || c->session == NULL) 627311827fbSnicm return; 628311827fbSnicm s = c->session; 629311827fbSnicm 630311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 631311827fbSnicm fatal("gettimeofday"); 632311827fbSnicm 633311827fbSnicm if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >)) 634311827fbSnicm status_message_clear(c); 635311827fbSnicm 636311827fbSnicm if (c->message_string != NULL || c->prompt_string != NULL) { 637311827fbSnicm /* 638311827fbSnicm * Don't need timed redraw for messages/prompts so bail now. 639311827fbSnicm * The status timer isn't reset when they are redrawn anyway. 640311827fbSnicm */ 641311827fbSnicm return; 642311827fbSnicm } 643311827fbSnicm if (!options_get_number(&s->options, "status")) 644311827fbSnicm return; 645311827fbSnicm 646311827fbSnicm /* Check timer; resolution is only a second so don't be too clever. */ 647311827fbSnicm interval = options_get_number(&s->options, "status-interval"); 648311827fbSnicm if (interval == 0) 649311827fbSnicm return; 650311827fbSnicm if (tv.tv_sec < c->status_timer.tv_sec || 651311827fbSnicm ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) 652311827fbSnicm c->flags |= CLIENT_STATUS; 653311827fbSnicm } 654311827fbSnicm 655311827fbSnicm /* Fill client pollfds. */ 656311827fbSnicm void 657311827fbSnicm server_fill_clients(struct pollfd **pfd) 658311827fbSnicm { 659311827fbSnicm struct client *c; 660311827fbSnicm struct window *w; 661311827fbSnicm struct window_pane *wp; 662311827fbSnicm u_int i; 663311827fbSnicm 664311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 665311827fbSnicm c = ARRAY_ITEM(&clients, i); 666311827fbSnicm 667311827fbSnicm server_check_timers(c); 668311827fbSnicm server_check_redraw(c); 669311827fbSnicm 670311827fbSnicm if (c == NULL) 671311827fbSnicm (*pfd)->fd = -1; 672311827fbSnicm else { 673fd234c13Snicm (*pfd)->fd = c->ibuf.fd; 6740cd9638eSnicm if (!(c->flags & CLIENT_BAD)) 675fd234c13Snicm (*pfd)->events |= POLLIN; 676fd234c13Snicm if (c->ibuf.w.queued > 0) 677311827fbSnicm (*pfd)->events |= POLLOUT; 678311827fbSnicm } 679311827fbSnicm (*pfd)++; 680311827fbSnicm 681311827fbSnicm if (c == NULL || c->flags & CLIENT_SUSPENDED || 682311827fbSnicm c->tty.fd == -1 || c->session == NULL) 683311827fbSnicm (*pfd)->fd = -1; 684311827fbSnicm else { 685311827fbSnicm (*pfd)->fd = c->tty.fd; 686311827fbSnicm (*pfd)->events = POLLIN; 687311827fbSnicm if (BUFFER_USED(c->tty.out) > 0) 688311827fbSnicm (*pfd)->events |= POLLOUT; 689311827fbSnicm } 690311827fbSnicm (*pfd)++; 691311827fbSnicm } 692311827fbSnicm 693311827fbSnicm /* 694311827fbSnicm * Clear any window redraw flags (will have been redrawn as part of 695311827fbSnicm * client). 696311827fbSnicm */ 697311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 698311827fbSnicm w = ARRAY_ITEM(&windows, i); 699311827fbSnicm if (w == NULL) 700311827fbSnicm continue; 701311827fbSnicm 702311827fbSnicm w->flags &= ~WINDOW_REDRAW; 703311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) 704311827fbSnicm wp->flags &= ~PANE_REDRAW; 705311827fbSnicm } 706311827fbSnicm } 707311827fbSnicm 708311827fbSnicm /* Handle client pollfds. */ 709311827fbSnicm void 710311827fbSnicm server_handle_clients(struct pollfd **pfd) 711311827fbSnicm { 712311827fbSnicm struct client *c; 713311827fbSnicm u_int i; 714311827fbSnicm 715311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 716311827fbSnicm c = ARRAY_ITEM(&clients, i); 717311827fbSnicm 718311827fbSnicm if (c != NULL) { 719fd234c13Snicm if ((*pfd)->revents & (POLLERR|POLLNVAL|POLLHUP)) { 720311827fbSnicm server_lost_client(c); 721311827fbSnicm (*pfd) += 2; 722311827fbSnicm continue; 723fd234c13Snicm } 724fd234c13Snicm 725fd234c13Snicm if ((*pfd)->revents & POLLOUT) { 726fd234c13Snicm if (msgbuf_write(&c->ibuf.w) < 0) { 7270cd9638eSnicm server_lost_client(c); 7280cd9638eSnicm (*pfd) += 2; 7290cd9638eSnicm continue; 730fd234c13Snicm } 731fd234c13Snicm } 732fd234c13Snicm 733fd234c13Snicm if (c->flags & CLIENT_BAD) { 734fd234c13Snicm if (c->ibuf.w.queued == 0) 735fd234c13Snicm server_lost_client(c); 736fd234c13Snicm (*pfd) += 2; 737fd234c13Snicm continue; 738fd234c13Snicm } else if ((*pfd)->revents & POLLIN) { 739fd234c13Snicm if (server_msg_dispatch(c) != 0) { 740fd234c13Snicm server_lost_client(c); 741fd234c13Snicm (*pfd) += 2; 742fd234c13Snicm continue; 743fd234c13Snicm } 744fd234c13Snicm } 745311827fbSnicm } 746311827fbSnicm (*pfd)++; 747311827fbSnicm 748311827fbSnicm if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && 749311827fbSnicm c->tty.fd != -1 && c->session != NULL) { 750311827fbSnicm if (buffer_poll(*pfd, c->tty.in, c->tty.out) != 0) 751311827fbSnicm server_lost_client(c); 752311827fbSnicm else 753311827fbSnicm server_handle_client(c); 754311827fbSnicm } 755311827fbSnicm (*pfd)++; 756311827fbSnicm } 757311827fbSnicm } 758311827fbSnicm 759311827fbSnicm /* accept(2) and create new client. */ 760980b7663Snicm void 761311827fbSnicm server_accept_client(int srv_fd) 762311827fbSnicm { 763311827fbSnicm struct sockaddr_storage sa; 764311827fbSnicm socklen_t slen = sizeof sa; 765311827fbSnicm int fd; 766311827fbSnicm 767311827fbSnicm fd = accept(srv_fd, (struct sockaddr *) &sa, &slen); 768311827fbSnicm if (fd == -1) { 769311827fbSnicm if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 770980b7663Snicm return; 771311827fbSnicm fatal("accept failed"); 772311827fbSnicm } 773311827fbSnicm if (sigterm) { 774311827fbSnicm close(fd); 775980b7663Snicm return; 776311827fbSnicm } 777980b7663Snicm server_create_client(fd); 778311827fbSnicm } 779311827fbSnicm 780311827fbSnicm /* Input data from client. */ 781311827fbSnicm void 782311827fbSnicm server_handle_client(struct client *c) 783311827fbSnicm { 784311827fbSnicm struct window_pane *wp; 785311827fbSnicm struct screen *s; 786311827fbSnicm struct timeval tv; 787311827fbSnicm struct key_binding *bd; 788311827fbSnicm int key, prefix, status, xtimeout; 789311827fbSnicm int mode; 790311827fbSnicm u_char mouse[3]; 791311827fbSnicm 792311827fbSnicm xtimeout = options_get_number(&c->session->options, "repeat-time"); 793311827fbSnicm if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { 794311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 795311827fbSnicm fatal("gettimeofday"); 796311827fbSnicm if (timercmp(&tv, &c->repeat_timer, >)) 797311827fbSnicm c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); 798311827fbSnicm } 799311827fbSnicm 800311827fbSnicm /* Process keys. */ 801311827fbSnicm prefix = options_get_number(&c->session->options, "prefix"); 802311827fbSnicm while (tty_keys_next(&c->tty, &key, mouse) == 0) { 803311827fbSnicm server_activity = time(NULL); 804311827fbSnicm 805311827fbSnicm if (c->session == NULL) 806311827fbSnicm return; 807311827fbSnicm wp = c->session->curw->window->active; /* could die */ 808311827fbSnicm 809311827fbSnicm status_message_clear(c); 810311827fbSnicm if (c->prompt_string != NULL) { 811311827fbSnicm status_prompt_key(c, key); 812311827fbSnicm continue; 813311827fbSnicm } 814311827fbSnicm if (server_locked) 815311827fbSnicm continue; 816311827fbSnicm 817311827fbSnicm /* Check for mouse keys. */ 818311827fbSnicm if (key == KEYC_MOUSE) { 819311827fbSnicm window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]); 820311827fbSnicm continue; 821311827fbSnicm } 822311827fbSnicm 823311827fbSnicm /* No previous prefix key. */ 824311827fbSnicm if (!(c->flags & CLIENT_PREFIX)) { 825311827fbSnicm if (key == prefix) 826311827fbSnicm c->flags |= CLIENT_PREFIX; 827ad4696b5Snicm else { 828ad4696b5Snicm /* Try as a non-prefix key binding. */ 829ad4696b5Snicm if ((bd = key_bindings_lookup(key)) == NULL) 830311827fbSnicm window_pane_key(wp, c, key); 831ad4696b5Snicm else 832ad4696b5Snicm key_bindings_dispatch(bd, c); 833ad4696b5Snicm } 834311827fbSnicm continue; 835311827fbSnicm } 836311827fbSnicm 837311827fbSnicm /* Prefix key already pressed. Reset prefix and lookup key. */ 838311827fbSnicm c->flags &= ~CLIENT_PREFIX; 839ad4696b5Snicm if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { 840311827fbSnicm /* If repeating, treat this as a key, else ignore. */ 841311827fbSnicm if (c->flags & CLIENT_REPEAT) { 842311827fbSnicm c->flags &= ~CLIENT_REPEAT; 843311827fbSnicm if (key == prefix) 844311827fbSnicm c->flags |= CLIENT_PREFIX; 845311827fbSnicm else 846311827fbSnicm window_pane_key(wp, c, key); 847311827fbSnicm } 848311827fbSnicm continue; 849311827fbSnicm } 850311827fbSnicm 851311827fbSnicm /* If already repeating, but this key can't repeat, skip it. */ 852311827fbSnicm if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { 853311827fbSnicm c->flags &= ~CLIENT_REPEAT; 854311827fbSnicm if (key == prefix) 855311827fbSnicm c->flags |= CLIENT_PREFIX; 856311827fbSnicm else 857311827fbSnicm window_pane_key(wp, c, key); 858311827fbSnicm continue; 859311827fbSnicm } 860311827fbSnicm 861311827fbSnicm /* If this key can repeat, reset the repeat flags and timer. */ 862311827fbSnicm if (xtimeout != 0 && bd->can_repeat) { 863311827fbSnicm c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; 864311827fbSnicm 865311827fbSnicm tv.tv_sec = xtimeout / 1000; 866311827fbSnicm tv.tv_usec = (xtimeout % 1000) * 1000L; 867311827fbSnicm if (gettimeofday(&c->repeat_timer, NULL) != 0) 868311827fbSnicm fatal("gettimeofday"); 869311827fbSnicm timeradd(&c->repeat_timer, &tv, &c->repeat_timer); 870311827fbSnicm } 871311827fbSnicm 872311827fbSnicm /* Dispatch the command. */ 873311827fbSnicm key_bindings_dispatch(bd, c); 874311827fbSnicm } 875311827fbSnicm if (c->session == NULL) 876311827fbSnicm return; 877311827fbSnicm wp = c->session->curw->window->active; /* could die - do each loop */ 878311827fbSnicm s = wp->screen; 879311827fbSnicm 880311827fbSnicm /* Ensure cursor position and mode settings. */ 881311827fbSnicm status = options_get_number(&c->session->options, "status"); 882dde47eafSnicm tty_region(&c->tty, 0, c->tty.sy - 1, 0); 883af9e4c5dSnicm if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) 884af9e4c5dSnicm tty_cursor(&c->tty, 0, 0, 0, 0); 885af9e4c5dSnicm else 886311827fbSnicm tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff); 887311827fbSnicm 888311827fbSnicm mode = s->mode; 889311827fbSnicm if (server_locked) 890311827fbSnicm mode &= ~TTY_NOCURSOR; 891311827fbSnicm tty_update_mode(&c->tty, mode); 892311827fbSnicm } 893311827fbSnicm 894311827fbSnicm /* Lost a client. */ 895311827fbSnicm void 896311827fbSnicm server_lost_client(struct client *c) 897311827fbSnicm { 898311827fbSnicm u_int i; 899311827fbSnicm 900311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 901311827fbSnicm if (ARRAY_ITEM(&clients, i) == c) 902311827fbSnicm ARRAY_SET(&clients, i, NULL); 903311827fbSnicm } 904311827fbSnicm 905*130af69dSnicm tty_free(&c->tty); 906311827fbSnicm 907311827fbSnicm screen_free(&c->status); 908311827fbSnicm 909311827fbSnicm if (c->title != NULL) 910311827fbSnicm xfree(c->title); 911311827fbSnicm 912311827fbSnicm if (c->message_string != NULL) 913311827fbSnicm xfree(c->message_string); 914311827fbSnicm 915311827fbSnicm if (c->prompt_string != NULL) 916311827fbSnicm xfree(c->prompt_string); 917311827fbSnicm if (c->prompt_buffer != NULL) 918311827fbSnicm xfree(c->prompt_buffer); 919311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++) 920311827fbSnicm xfree(ARRAY_ITEM(&c->prompt_hdata, i)); 921311827fbSnicm ARRAY_FREE(&c->prompt_hdata); 922311827fbSnicm 923311827fbSnicm if (c->cwd != NULL) 924311827fbSnicm xfree(c->cwd); 925311827fbSnicm 926fd234c13Snicm close(c->ibuf.fd); 927fd234c13Snicm imsg_clear(&c->ibuf); 928311827fbSnicm xfree(c); 929311827fbSnicm 930311827fbSnicm recalculate_sizes(); 931311827fbSnicm } 932311827fbSnicm 933311827fbSnicm /* Handle window data. */ 934311827fbSnicm void 935311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp) 936311827fbSnicm { 937311827fbSnicm struct session *s; 938311827fbSnicm u_int i; 939311827fbSnicm int update; 940311827fbSnicm 941311827fbSnicm window_pane_parse(wp); 942311827fbSnicm 943311827fbSnicm if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0) 944311827fbSnicm return; 945311827fbSnicm 946311827fbSnicm update = 0; 947311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 948311827fbSnicm s = ARRAY_ITEM(&sessions, i); 949311827fbSnicm if (s == NULL || !session_has(s, w)) 950311827fbSnicm continue; 951311827fbSnicm 9520903c7b9Snicm update += server_check_window_bell(s, w); 953311827fbSnicm update += server_check_window_activity(s, w); 954311827fbSnicm update += server_check_window_content(s, w, wp); 955311827fbSnicm } 956311827fbSnicm if (update) 957311827fbSnicm server_status_window(w); 958311827fbSnicm 959311827fbSnicm w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT); 960311827fbSnicm } 961311827fbSnicm 962311827fbSnicm int 9630903c7b9Snicm server_check_window_bell(struct session *s, struct window *w) 964311827fbSnicm { 965311827fbSnicm struct client *c; 966311827fbSnicm u_int i; 9670903c7b9Snicm int action, visual; 968311827fbSnicm 969311827fbSnicm if (!(w->flags & WINDOW_BELL)) 970311827fbSnicm return (0); 971311827fbSnicm if (session_alert_has_window(s, w, WINDOW_BELL)) 972311827fbSnicm return (0); 973311827fbSnicm session_alert_add(s, w, WINDOW_BELL); 974311827fbSnicm 975311827fbSnicm action = options_get_number(&s->options, "bell-action"); 976311827fbSnicm switch (action) { 977311827fbSnicm case BELL_ANY: 978311827fbSnicm if (s->flags & SESSION_UNATTACHED) 979311827fbSnicm break; 9800903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 981311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 982311827fbSnicm c = ARRAY_ITEM(&clients, i); 9830903c7b9Snicm if (c == NULL || c->session != s) 9840903c7b9Snicm continue; 9850903c7b9Snicm if (!visual) { 986311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 9870903c7b9Snicm continue; 9880903c7b9Snicm } 9890903c7b9Snicm if (c->session->curw->window == w) { 9900903c7b9Snicm status_message_set(c, "Bell in current window"); 9910903c7b9Snicm continue; 9920903c7b9Snicm } 9930903c7b9Snicm status_message_set(c, "Bell in window %u", 9940903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 995311827fbSnicm } 996311827fbSnicm break; 997311827fbSnicm case BELL_CURRENT: 9980903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 999311827fbSnicm break; 10000903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 1001311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1002311827fbSnicm c = ARRAY_ITEM(&clients, i); 10030903c7b9Snicm if (c == NULL || c->session != s) 10040903c7b9Snicm continue; 10050903c7b9Snicm if (c->session->curw->window != w) 10060903c7b9Snicm continue; 10070903c7b9Snicm if (!visual) { 1008311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 10090903c7b9Snicm continue; 10100903c7b9Snicm } 10110903c7b9Snicm status_message_set(c, "Bell in current window"); 1012311827fbSnicm } 1013311827fbSnicm break; 1014311827fbSnicm } 1015311827fbSnicm return (1); 1016311827fbSnicm } 1017311827fbSnicm 1018311827fbSnicm int 1019311827fbSnicm server_check_window_activity(struct session *s, struct window *w) 1020311827fbSnicm { 10210903c7b9Snicm struct client *c; 10220903c7b9Snicm u_int i; 10230903c7b9Snicm 1024311827fbSnicm if (!(w->flags & WINDOW_ACTIVITY)) 1025311827fbSnicm return (0); 10260903c7b9Snicm 1027311827fbSnicm if (!options_get_number(&w->options, "monitor-activity")) 1028311827fbSnicm return (0); 10290903c7b9Snicm 1030311827fbSnicm if (session_alert_has_window(s, w, WINDOW_ACTIVITY)) 1031311827fbSnicm return (0); 10320903c7b9Snicm if (s->curw->window == w) 10330903c7b9Snicm return (0); 10340903c7b9Snicm 1035311827fbSnicm session_alert_add(s, w, WINDOW_ACTIVITY); 10360903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 10370903c7b9Snicm return (0); 10380903c7b9Snicm if (options_get_number(&s->options, "visual-activity")) { 10390903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 10400903c7b9Snicm c = ARRAY_ITEM(&clients, i); 10410903c7b9Snicm if (c == NULL || c->session != s) 10420903c7b9Snicm continue; 10430903c7b9Snicm status_message_set(c, "Activity in window %u", 10440903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 10450903c7b9Snicm } 10460903c7b9Snicm } 10470903c7b9Snicm 1048311827fbSnicm return (1); 1049311827fbSnicm } 1050311827fbSnicm 1051311827fbSnicm int 1052311827fbSnicm server_check_window_content( 1053311827fbSnicm struct session *s, struct window *w, struct window_pane *wp) 1054311827fbSnicm { 10550903c7b9Snicm struct client *c; 10560903c7b9Snicm u_int i; 1057311827fbSnicm char *found, *ptr; 1058311827fbSnicm 10590903c7b9Snicm if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */ 1060311827fbSnicm return (0); 10610903c7b9Snicm 10620903c7b9Snicm ptr = options_get_string(&w->options, "monitor-content"); 10630903c7b9Snicm if (ptr == NULL || *ptr == '\0') 1064311827fbSnicm return (0); 10650903c7b9Snicm 1066311827fbSnicm if (session_alert_has_window(s, w, WINDOW_CONTENT)) 1067311827fbSnicm return (0); 10680903c7b9Snicm if (s->curw->window == w) 10690903c7b9Snicm return (0); 10700903c7b9Snicm 1071570a3589Snicm if ((found = window_pane_search(wp, ptr, NULL)) == NULL) 1072311827fbSnicm return (0); 1073311827fbSnicm xfree(found); 10740903c7b9Snicm 10750903c7b9Snicm session_alert_add(s, w, WINDOW_CONTENT); 10760903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 10770903c7b9Snicm return (0); 10780903c7b9Snicm if (options_get_number(&s->options, "visual-content")) { 10790903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 10800903c7b9Snicm c = ARRAY_ITEM(&clients, i); 10810903c7b9Snicm if (c == NULL || c->session != s) 10820903c7b9Snicm continue; 10830903c7b9Snicm status_message_set(c, "Content in window %u", 10840903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 10850903c7b9Snicm } 10860903c7b9Snicm } 10870903c7b9Snicm 1088311827fbSnicm return (1); 1089311827fbSnicm } 1090311827fbSnicm 1091c752cdacSnicm /* Check if window still exists. */ 1092311827fbSnicm void 1093311827fbSnicm server_check_window(struct window *w) 1094311827fbSnicm { 1095311827fbSnicm struct window_pane *wp, *wq; 1096311827fbSnicm struct client *c; 1097311827fbSnicm struct session *s; 1098311827fbSnicm struct winlink *wl; 1099311827fbSnicm u_int i, j; 1100311827fbSnicm int destroyed, flag; 1101311827fbSnicm 1102311827fbSnicm flag = options_get_number(&w->options, "remain-on-exit"); 1103311827fbSnicm 1104311827fbSnicm destroyed = 1; 1105311827fbSnicm 1106311827fbSnicm wp = TAILQ_FIRST(&w->panes); 1107311827fbSnicm while (wp != NULL) { 1108311827fbSnicm wq = TAILQ_NEXT(wp, entry); 1109c752cdacSnicm /* 1110c752cdacSnicm * If the pane has died and the remain-on-exit flag is not set, 1111c752cdacSnicm * remove the pane; otherwise, if the flag is set, don't allow 1112c752cdacSnicm * the window to be destroyed (or it'll close when the last 1113c752cdacSnicm * pane dies). 1114c752cdacSnicm */ 1115c752cdacSnicm if (wp->fd == -1 && !flag) { 1116af9e4c5dSnicm layout_close_pane(wp); 1117311827fbSnicm window_remove_pane(w, wp); 1118311827fbSnicm server_redraw_window(w); 1119c752cdacSnicm } else 1120c752cdacSnicm destroyed = 0; 1121311827fbSnicm wp = wq; 1122311827fbSnicm } 1123311827fbSnicm 1124311827fbSnicm if (!destroyed) 1125311827fbSnicm return; 1126311827fbSnicm 1127311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1128311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1129311827fbSnicm if (s == NULL) 1130311827fbSnicm continue; 1131311827fbSnicm if (!session_has(s, w)) 1132311827fbSnicm continue; 1133311827fbSnicm 1134311827fbSnicm restart: 1135311827fbSnicm /* Detach window and either redraw or kill clients. */ 1136311827fbSnicm RB_FOREACH(wl, winlinks, &s->windows) { 1137311827fbSnicm if (wl->window != w) 1138311827fbSnicm continue; 1139311827fbSnicm destroyed = session_detach(s, wl); 1140311827fbSnicm for (j = 0; j < ARRAY_LENGTH(&clients); j++) { 1141311827fbSnicm c = ARRAY_ITEM(&clients, j); 1142311827fbSnicm if (c == NULL || c->session != s) 1143311827fbSnicm continue; 1144311827fbSnicm if (!destroyed) { 1145311827fbSnicm server_redraw_client(c); 1146311827fbSnicm continue; 1147311827fbSnicm } 1148311827fbSnicm c->session = NULL; 1149311827fbSnicm server_write_client(c, MSG_EXIT, NULL, 0); 1150311827fbSnicm } 1151311827fbSnicm /* If the session was destroyed, bail now. */ 1152311827fbSnicm if (destroyed) 1153311827fbSnicm break; 1154311827fbSnicm goto restart; 1155311827fbSnicm } 1156311827fbSnicm } 1157311827fbSnicm 1158311827fbSnicm recalculate_sizes(); 1159311827fbSnicm } 1160311827fbSnicm 1161311827fbSnicm /* Call any once-per-second timers. */ 1162311827fbSnicm void 1163311827fbSnicm server_second_timers(void) 1164311827fbSnicm { 1165311827fbSnicm struct window *w; 1166311827fbSnicm struct window_pane *wp; 1167311827fbSnicm u_int i; 1168311827fbSnicm int xtimeout; 1169311827fbSnicm struct tm now, then; 1170311827fbSnicm static time_t last_t = 0; 1171311827fbSnicm time_t t; 1172311827fbSnicm 1173311827fbSnicm t = time(NULL); 1174eaecedb2Snicm xtimeout = options_get_number(&global_s_options, "lock-after-time"); 1175311827fbSnicm if (xtimeout > 0 && t > server_activity + xtimeout) 1176311827fbSnicm server_lock(); 1177311827fbSnicm 1178311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 1179311827fbSnicm w = ARRAY_ITEM(&windows, i); 1180311827fbSnicm if (w == NULL) 1181311827fbSnicm continue; 1182311827fbSnicm 1183311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 1184311827fbSnicm if (wp->mode != NULL && wp->mode->timer != NULL) 1185311827fbSnicm wp->mode->timer(wp); 1186311827fbSnicm } 1187311827fbSnicm } 1188311827fbSnicm 1189311827fbSnicm /* Check for a minute having passed. */ 1190311827fbSnicm gmtime_r(&t, &now); 1191311827fbSnicm gmtime_r(&last_t, &then); 1192311827fbSnicm if (now.tm_min == then.tm_min) 1193311827fbSnicm return; 1194311827fbSnicm last_t = t; 1195311827fbSnicm 1196311827fbSnicm /* If locked, redraw all clients. */ 1197311827fbSnicm if (server_locked) { 1198311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1199311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 1200311827fbSnicm server_redraw_client(ARRAY_ITEM(&clients, i)); 1201311827fbSnicm } 1202311827fbSnicm } 1203311827fbSnicm } 1204311827fbSnicm 1205311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */ 1206311827fbSnicm int 1207311827fbSnicm server_update_socket(void) 1208311827fbSnicm { 1209311827fbSnicm struct session *s; 1210311827fbSnicm u_int i; 1211311827fbSnicm static int last = -1; 1212311827fbSnicm int n; 1213311827fbSnicm 1214311827fbSnicm n = 0; 1215311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1216311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1217311827fbSnicm if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { 1218311827fbSnicm n++; 1219311827fbSnicm break; 1220311827fbSnicm } 1221311827fbSnicm } 1222311827fbSnicm 1223311827fbSnicm if (n != last) { 1224311827fbSnicm last = n; 1225311827fbSnicm if (n != 0) 1226311827fbSnicm chmod(socket_path, S_IRWXU); 1227311827fbSnicm else 1228311827fbSnicm chmod(socket_path, S_IRUSR|S_IWUSR); 1229311827fbSnicm } 1230311827fbSnicm 1231311827fbSnicm return (n); 1232311827fbSnicm } 1233