1*b7fa064bSnicm /* $OpenBSD: server.c,v 1.45 2009/10/10 09:31:39 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; 467255ff90Snicm struct clients dead_clients; 47311827fbSnicm 48*b7fa064bSnicm /* Mapping of a pollfd to an fd independent of its position in the array. */ 49*b7fa064bSnicm struct poll_item { 50*b7fa064bSnicm struct pollfd pfd; 51*b7fa064bSnicm 52*b7fa064bSnicm RB_ENTRY(poll_item) entry; 53*b7fa064bSnicm }; 54*b7fa064bSnicm RB_HEAD(poll_items, poll_item) poll_items; 55*b7fa064bSnicm 56*b7fa064bSnicm int server_poll_cmp(struct poll_item *, struct poll_item *); 57*b7fa064bSnicm struct pollfd *server_poll_lookup(int); 58*b7fa064bSnicm void server_poll_add(int, int); 59*b7fa064bSnicm struct pollfd *server_poll_flatten(int *); 60*b7fa064bSnicm void server_poll_parse(struct pollfd *); 61*b7fa064bSnicm void server_poll_reset(void); 62*b7fa064bSnicm RB_PROTOTYPE(poll_items, poll_item, entry, server_poll_cmp); 63*b7fa064bSnicm RB_GENERATE(poll_items, poll_item, entry, server_poll_cmp); 64*b7fa064bSnicm 65980b7663Snicm void server_create_client(int); 66311827fbSnicm int server_create_socket(void); 67311827fbSnicm int server_main(int); 68311827fbSnicm void server_shutdown(void); 6930da262fSnicm int server_should_shutdown(void); 70311827fbSnicm void server_child_signal(void); 71*b7fa064bSnicm void server_fill_windows(void); 72*b7fa064bSnicm void server_handle_windows(void); 73*b7fa064bSnicm void server_fill_clients(void); 74*b7fa064bSnicm void server_handle_clients(void); 75980b7663Snicm void server_accept_client(int); 76311827fbSnicm void server_handle_client(struct client *); 77311827fbSnicm void server_handle_window(struct window *, struct window_pane *); 780903c7b9Snicm int server_check_window_bell(struct session *, struct window *); 79311827fbSnicm int server_check_window_activity(struct session *, 80311827fbSnicm struct window *); 81311827fbSnicm int server_check_window_content(struct session *, struct window *, 82311827fbSnicm struct window_pane *); 837255ff90Snicm void server_clean_dead(void); 84311827fbSnicm void server_lost_client(struct client *); 85311827fbSnicm void server_check_window(struct window *); 86311827fbSnicm void server_check_redraw(struct client *); 8795b31741Snicm void server_set_title(struct client *); 88311827fbSnicm void server_check_timers(struct client *); 89311827fbSnicm void server_second_timers(void); 90311827fbSnicm int server_update_socket(void); 91311827fbSnicm 92*b7fa064bSnicm int 93*b7fa064bSnicm server_poll_cmp(struct poll_item *pitem1, struct poll_item *pitem2) 94*b7fa064bSnicm { 95*b7fa064bSnicm return (pitem1->pfd.fd - pitem2->pfd.fd); 96*b7fa064bSnicm } 97*b7fa064bSnicm 98*b7fa064bSnicm struct pollfd * 99*b7fa064bSnicm server_poll_lookup(int fd) 100*b7fa064bSnicm { 101*b7fa064bSnicm struct poll_item pitem; 102*b7fa064bSnicm 103*b7fa064bSnicm pitem.pfd.fd = fd; 104*b7fa064bSnicm return (&RB_FIND(poll_items, &poll_items, &pitem)->pfd); 105*b7fa064bSnicm } 106*b7fa064bSnicm 107*b7fa064bSnicm void 108*b7fa064bSnicm server_poll_add(int fd, int events) 109*b7fa064bSnicm { 110*b7fa064bSnicm struct poll_item *pitem; 111*b7fa064bSnicm 112*b7fa064bSnicm pitem = xmalloc(sizeof *pitem); 113*b7fa064bSnicm pitem->pfd.fd = fd; 114*b7fa064bSnicm pitem->pfd.events = events; 115*b7fa064bSnicm RB_INSERT(poll_items, &poll_items, pitem); 116*b7fa064bSnicm } 117*b7fa064bSnicm 118*b7fa064bSnicm struct pollfd * 119*b7fa064bSnicm server_poll_flatten(int *nfds) 120*b7fa064bSnicm { 121*b7fa064bSnicm struct poll_item *pitem; 122*b7fa064bSnicm struct pollfd *pfds; 123*b7fa064bSnicm 124*b7fa064bSnicm pfds = NULL; 125*b7fa064bSnicm *nfds = 0; 126*b7fa064bSnicm RB_FOREACH(pitem, poll_items, &poll_items) { 127*b7fa064bSnicm pfds = xrealloc(pfds, (*nfds) + 1, sizeof *pfds); 128*b7fa064bSnicm pfds[*nfds].fd = pitem->pfd.fd; 129*b7fa064bSnicm pfds[*nfds].events = pitem->pfd.events; 130*b7fa064bSnicm (*nfds)++; 131*b7fa064bSnicm } 132*b7fa064bSnicm return (pfds); 133*b7fa064bSnicm } 134*b7fa064bSnicm 135*b7fa064bSnicm void 136*b7fa064bSnicm server_poll_parse(struct pollfd *pfds) 137*b7fa064bSnicm { 138*b7fa064bSnicm struct poll_item *pitem; 139*b7fa064bSnicm int nfds; 140*b7fa064bSnicm 141*b7fa064bSnicm nfds = 0; 142*b7fa064bSnicm RB_FOREACH(pitem, poll_items, &poll_items) { 143*b7fa064bSnicm pitem->pfd.revents = pfds[nfds].revents; 144*b7fa064bSnicm nfds++; 145*b7fa064bSnicm } 146*b7fa064bSnicm xfree(pfds); 147*b7fa064bSnicm } 148*b7fa064bSnicm 149*b7fa064bSnicm void 150*b7fa064bSnicm server_poll_reset(void) 151*b7fa064bSnicm { 152*b7fa064bSnicm struct poll_item *pitem; 153*b7fa064bSnicm 154*b7fa064bSnicm while (!RB_EMPTY(&poll_items)) { 155*b7fa064bSnicm pitem = RB_ROOT(&poll_items); 156*b7fa064bSnicm RB_REMOVE(poll_items, &poll_items, pitem); 157*b7fa064bSnicm xfree(pitem); 158*b7fa064bSnicm } 159*b7fa064bSnicm } 160*b7fa064bSnicm 161311827fbSnicm /* Create a new client. */ 162980b7663Snicm void 163311827fbSnicm server_create_client(int fd) 164311827fbSnicm { 165311827fbSnicm struct client *c; 166311827fbSnicm int mode; 167311827fbSnicm u_int i; 168311827fbSnicm 169311827fbSnicm if ((mode = fcntl(fd, F_GETFL)) == -1) 170311827fbSnicm fatal("fcntl failed"); 171311827fbSnicm if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 172311827fbSnicm fatal("fcntl failed"); 173311827fbSnicm if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 174311827fbSnicm fatal("fcntl failed"); 175311827fbSnicm 176311827fbSnicm c = xcalloc(1, sizeof *c); 1777255ff90Snicm c->references = 0; 178fd234c13Snicm imsg_init(&c->ibuf, fd); 179311827fbSnicm 18060ea6095Snicm if (gettimeofday(&c->tv, NULL) != 0) 18160ea6095Snicm fatal("gettimeofday failed"); 18260ea6095Snicm 183311827fbSnicm ARRAY_INIT(&c->prompt_hdata); 184311827fbSnicm 185311827fbSnicm c->tty.fd = -1; 186311827fbSnicm c->title = NULL; 187311827fbSnicm 188311827fbSnicm c->session = NULL; 189311827fbSnicm c->tty.sx = 80; 19088081898Snicm c->tty.sy = 24; 191311827fbSnicm screen_init(&c->status, c->tty.sx, 1, 0); 192311827fbSnicm 193311827fbSnicm c->message_string = NULL; 194311827fbSnicm 195311827fbSnicm c->prompt_string = NULL; 196311827fbSnicm c->prompt_buffer = NULL; 197311827fbSnicm c->prompt_index = 0; 198311827fbSnicm 199311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 200311827fbSnicm if (ARRAY_ITEM(&clients, i) == NULL) { 201311827fbSnicm ARRAY_SET(&clients, i, c); 202980b7663Snicm return; 203311827fbSnicm } 204311827fbSnicm } 205311827fbSnicm ARRAY_ADD(&clients, c); 2060bb39cd5Snicm log_debug("new client %d", fd); 207311827fbSnicm } 208311827fbSnicm 209311827fbSnicm /* Fork new server. */ 210311827fbSnicm int 211311827fbSnicm server_start(char *path) 212311827fbSnicm { 2130cd9638eSnicm struct client *c; 2140cd9638eSnicm int pair[2], srv_fd; 215311827fbSnicm char *cause; 216311827fbSnicm char rpathbuf[MAXPATHLEN]; 217311827fbSnicm 218311827fbSnicm /* The first client is special and gets a socketpair; create it. */ 219311827fbSnicm if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) 220311827fbSnicm fatal("socketpair failed"); 221311827fbSnicm 222311827fbSnicm switch (fork()) { 223311827fbSnicm case -1: 224311827fbSnicm fatal("fork failed"); 225311827fbSnicm case 0: 226311827fbSnicm break; 227311827fbSnicm default: 228311827fbSnicm close(pair[1]); 229311827fbSnicm return (pair[0]); 230311827fbSnicm } 231311827fbSnicm close(pair[0]); 232311827fbSnicm 233311827fbSnicm /* 234311827fbSnicm * Must daemonise before loading configuration as the PID changes so 235311827fbSnicm * $TMUX would be wrong for sessions created in the config file. 236311827fbSnicm */ 2370cd9638eSnicm if (daemon(1, 0) != 0) 238311827fbSnicm fatal("daemon failed"); 239311827fbSnicm 2400cd9638eSnicm logfile("server"); 2410cd9638eSnicm log_debug("server started, pid %ld", (long) getpid()); 2420cd9638eSnicm 243311827fbSnicm ARRAY_INIT(&windows); 244311827fbSnicm ARRAY_INIT(&clients); 2457255ff90Snicm ARRAY_INIT(&dead_clients); 246311827fbSnicm ARRAY_INIT(&sessions); 2477255ff90Snicm ARRAY_INIT(&dead_sessions); 24868895571Snicm mode_key_init_trees(); 249311827fbSnicm key_bindings_init(); 250311827fbSnicm utf8_build(); 251311827fbSnicm 252311827fbSnicm server_activity = time(NULL); 253311827fbSnicm 254311827fbSnicm start_time = time(NULL); 255311827fbSnicm socket_path = path; 256311827fbSnicm 257311827fbSnicm if (realpath(socket_path, rpathbuf) == NULL) 258311827fbSnicm strlcpy(rpathbuf, socket_path, sizeof rpathbuf); 2590cd9638eSnicm log_debug("socket path %s", socket_path); 260311827fbSnicm setproctitle("server (%s)", rpathbuf); 261311827fbSnicm 262311827fbSnicm srv_fd = server_create_socket(); 263311827fbSnicm server_create_client(pair[1]); 264311827fbSnicm 2650cd9638eSnicm if (access(SYSTEM_CFG, R_OK) != 0) { 2660cd9638eSnicm if (errno != ENOENT) { 2670cd9638eSnicm xasprintf( 2680cd9638eSnicm &cause, "%s: %s", strerror(errno), SYSTEM_CFG); 2690cd9638eSnicm goto error; 2700cd9638eSnicm } 271c0a52a07Snicm } else if (load_cfg(SYSTEM_CFG, NULL, &cause) != 0) 2720cd9638eSnicm goto error; 273c0a52a07Snicm if (cfg_file != NULL && load_cfg(cfg_file, NULL, &cause) != 0) 2740cd9638eSnicm goto error; 2750cd9638eSnicm 2760cd9638eSnicm exit(server_main(srv_fd)); 2770cd9638eSnicm 2780cd9638eSnicm error: 2790cd9638eSnicm /* Write the error and shutdown the server. */ 2800cd9638eSnicm c = ARRAY_FIRST(&clients); 2810cd9638eSnicm 2820cd9638eSnicm server_write_error(c, cause); 2830cd9638eSnicm xfree(cause); 2840cd9638eSnicm 2855879d031Snicm sigterm = 1; 2860cd9638eSnicm server_shutdown(); 2870cd9638eSnicm 288311827fbSnicm exit(server_main(srv_fd)); 289311827fbSnicm } 290311827fbSnicm 291311827fbSnicm /* Create server socket. */ 292311827fbSnicm int 293311827fbSnicm server_create_socket(void) 294311827fbSnicm { 295311827fbSnicm struct sockaddr_un sa; 296311827fbSnicm size_t size; 297311827fbSnicm mode_t mask; 298311827fbSnicm int fd, mode; 299311827fbSnicm 300311827fbSnicm memset(&sa, 0, sizeof sa); 301311827fbSnicm sa.sun_family = AF_UNIX; 302311827fbSnicm size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 303311827fbSnicm if (size >= sizeof sa.sun_path) { 304311827fbSnicm errno = ENAMETOOLONG; 305311827fbSnicm fatal("socket failed"); 306311827fbSnicm } 307311827fbSnicm unlink(sa.sun_path); 308311827fbSnicm 309311827fbSnicm if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 310311827fbSnicm fatal("socket failed"); 311311827fbSnicm 312311827fbSnicm mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 313311827fbSnicm if (bind(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) 314311827fbSnicm fatal("bind failed"); 315311827fbSnicm umask(mask); 316311827fbSnicm 317311827fbSnicm if (listen(fd, 16) == -1) 318311827fbSnicm fatal("listen failed"); 319311827fbSnicm 320311827fbSnicm if ((mode = fcntl(fd, F_GETFL)) == -1) 321311827fbSnicm fatal("fcntl failed"); 322311827fbSnicm if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1) 323311827fbSnicm fatal("fcntl failed"); 324311827fbSnicm if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) 325311827fbSnicm fatal("fcntl failed"); 326311827fbSnicm 327311827fbSnicm return (fd); 328311827fbSnicm } 329311827fbSnicm 330311827fbSnicm /* Main server loop. */ 331311827fbSnicm int 332311827fbSnicm server_main(int srv_fd) 333311827fbSnicm { 334311827fbSnicm struct pollfd *pfds, *pfd; 335311827fbSnicm int nfds, xtimeout; 33630da262fSnicm u_int i; 337311827fbSnicm time_t now, last; 338311827fbSnicm 339311827fbSnicm siginit(); 3400bb39cd5Snicm log_debug("server socket is %d", srv_fd); 341311827fbSnicm 342311827fbSnicm last = time(NULL); 343311827fbSnicm 344311827fbSnicm pfds = NULL; 345311827fbSnicm for (;;) { 346311827fbSnicm /* If sigterm, kill all windows and clients. */ 347311827fbSnicm if (sigterm) 348311827fbSnicm server_shutdown(); 349311827fbSnicm 35030da262fSnicm /* Stop if no sessions or clients left. */ 35130da262fSnicm if (server_should_shutdown()) 35230da262fSnicm break; 35330da262fSnicm 354311827fbSnicm /* Handle child exit. */ 355311827fbSnicm if (sigchld) { 356311827fbSnicm server_child_signal(); 357311827fbSnicm sigchld = 0; 358311827fbSnicm } 359311827fbSnicm 360311827fbSnicm /* Recreate socket on SIGUSR1. */ 361311827fbSnicm if (sigusr1) { 362311827fbSnicm close(srv_fd); 363311827fbSnicm srv_fd = server_create_socket(); 364311827fbSnicm sigusr1 = 0; 365311827fbSnicm } 366311827fbSnicm 367*b7fa064bSnicm /* Initialise pollfd array and add server socket. */ 368*b7fa064bSnicm server_poll_reset(); 369*b7fa064bSnicm server_poll_add(srv_fd, POLLIN); 370311827fbSnicm 371311827fbSnicm /* Fill window and client sockets. */ 372*b7fa064bSnicm server_fill_windows(); 373*b7fa064bSnicm server_fill_clients(); 374311827fbSnicm 375311827fbSnicm /* Update socket permissions. */ 376311827fbSnicm xtimeout = INFTIM; 3775879d031Snicm if (server_update_socket() != 0) 378311827fbSnicm xtimeout = POLL_TIMEOUT; 379311827fbSnicm 380311827fbSnicm /* Do the poll. */ 381*b7fa064bSnicm pfds = server_poll_flatten(&nfds); 382*b7fa064bSnicm log_debug("polling %d", nfds); 3839b1fc963Snicm if (poll(pfds, nfds, xtimeout) == -1) { 384311827fbSnicm if (errno == EAGAIN || errno == EINTR) 385311827fbSnicm continue; 386311827fbSnicm fatal("poll failed"); 387311827fbSnicm } 388*b7fa064bSnicm server_poll_parse(pfds); 389311827fbSnicm 390311827fbSnicm /* Handle server socket. */ 391*b7fa064bSnicm pfd = server_poll_lookup(srv_fd); 392311827fbSnicm if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) 393311827fbSnicm fatalx("lost server socket"); 394311827fbSnicm if (pfd->revents & POLLIN) { 395311827fbSnicm server_accept_client(srv_fd); 396311827fbSnicm continue; 397311827fbSnicm } 398311827fbSnicm 399311827fbSnicm /* Call second-based timers. */ 400311827fbSnicm now = time(NULL); 401311827fbSnicm if (now != last) { 402311827fbSnicm last = now; 403311827fbSnicm server_second_timers(); 404311827fbSnicm } 405311827fbSnicm 406311827fbSnicm /* Set window names. */ 407311827fbSnicm set_window_names(); 408311827fbSnicm 409311827fbSnicm /* 410311827fbSnicm * Handle window and client sockets. Clients can create 411311827fbSnicm * windows, so windows must come first to avoid messing up by 412311827fbSnicm * increasing the array size. 413311827fbSnicm */ 414*b7fa064bSnicm server_handle_windows(); 415*b7fa064bSnicm server_handle_clients(); 416311827fbSnicm 4170476b68eSnicm /* Collect any unset key bindings. */ 4180476b68eSnicm key_bindings_clean(); 4190476b68eSnicm 4207255ff90Snicm /* Collect dead clients and sessions. */ 4217255ff90Snicm server_clean_dead(); 422311827fbSnicm } 423*b7fa064bSnicm server_poll_reset(); 424311827fbSnicm 425311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 426311827fbSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 427311827fbSnicm session_destroy(ARRAY_ITEM(&sessions, i)); 428311827fbSnicm } 429311827fbSnicm ARRAY_FREE(&sessions); 430311827fbSnicm 431311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 432311827fbSnicm if (ARRAY_ITEM(&clients, i) != NULL) 433311827fbSnicm server_lost_client(ARRAY_ITEM(&clients, i)); 434311827fbSnicm } 435311827fbSnicm ARRAY_FREE(&clients); 436311827fbSnicm 43768895571Snicm mode_key_free_trees(); 438311827fbSnicm key_bindings_free(); 439311827fbSnicm 440311827fbSnicm close(srv_fd); 441311827fbSnicm 442311827fbSnicm unlink(socket_path); 443311827fbSnicm xfree(socket_path); 444311827fbSnicm 445eaecedb2Snicm options_free(&global_s_options); 446eaecedb2Snicm options_free(&global_w_options); 447311827fbSnicm 448311827fbSnicm return (0); 449311827fbSnicm } 450311827fbSnicm 451311827fbSnicm /* Kill all clients. */ 452311827fbSnicm void 453311827fbSnicm server_shutdown(void) 454311827fbSnicm { 455311827fbSnicm struct session *s; 456311827fbSnicm struct client *c; 457311827fbSnicm u_int i, j; 458311827fbSnicm 45930da262fSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 46030da262fSnicm c = ARRAY_ITEM(&clients, i); 46130da262fSnicm if (c != NULL) { 46230da262fSnicm if (c->flags & (CLIENT_BAD|CLIENT_SUSPENDED)) 46330da262fSnicm server_lost_client(c); 46430da262fSnicm else 46530da262fSnicm server_write_client(c, MSG_SHUTDOWN, NULL, 0); 46630da262fSnicm } 46730da262fSnicm } 46830da262fSnicm 469311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 470311827fbSnicm s = ARRAY_ITEM(&sessions, i); 471311827fbSnicm for (j = 0; j < ARRAY_LENGTH(&clients); j++) { 472311827fbSnicm c = ARRAY_ITEM(&clients, j); 473311827fbSnicm if (c != NULL && c->session == s) { 474311827fbSnicm s = NULL; 475311827fbSnicm break; 476311827fbSnicm } 477311827fbSnicm } 478311827fbSnicm if (s != NULL) 479311827fbSnicm session_destroy(s); 480311827fbSnicm } 48130da262fSnicm } 482311827fbSnicm 48330da262fSnicm /* Check if the server should be shutting down (no more clients or windows). */ 48430da262fSnicm int 48530da262fSnicm server_should_shutdown(void) 48630da262fSnicm { 48730da262fSnicm u_int i; 48830da262fSnicm 48930da262fSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 49030da262fSnicm if (ARRAY_ITEM(&sessions, i) != NULL) 49130da262fSnicm return (0); 49230da262fSnicm } 493311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 49430da262fSnicm if (ARRAY_ITEM(&clients, i) != NULL) 49530da262fSnicm return (0); 4960cd9638eSnicm } 49730da262fSnicm return (1); 498311827fbSnicm } 499311827fbSnicm 500311827fbSnicm /* Handle SIGCHLD. */ 501311827fbSnicm void 502311827fbSnicm server_child_signal(void) 503311827fbSnicm { 504311827fbSnicm struct window *w; 505311827fbSnicm struct window_pane *wp; 506311827fbSnicm int status; 507311827fbSnicm pid_t pid; 508311827fbSnicm u_int i; 509311827fbSnicm 510311827fbSnicm for (;;) { 511311827fbSnicm switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 512311827fbSnicm case -1: 513311827fbSnicm if (errno == ECHILD) 514311827fbSnicm return; 51585d997dcSnicm fatal("waitpid failed"); 516311827fbSnicm case 0: 517311827fbSnicm return; 518311827fbSnicm } 519311827fbSnicm if (!WIFSTOPPED(status)) 520311827fbSnicm continue; 521311827fbSnicm if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 522311827fbSnicm continue; 523311827fbSnicm 524311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 525311827fbSnicm w = ARRAY_ITEM(&windows, i); 526311827fbSnicm if (w == NULL) 527311827fbSnicm continue; 528311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 529311827fbSnicm if (wp->pid == pid) { 530311827fbSnicm if (killpg(pid, SIGCONT) != 0) 531311827fbSnicm kill(pid, SIGCONT); 532311827fbSnicm } 533311827fbSnicm } 534311827fbSnicm } 535311827fbSnicm } 536311827fbSnicm } 537311827fbSnicm 538311827fbSnicm /* Fill window pollfds. */ 539311827fbSnicm void 540*b7fa064bSnicm server_fill_windows(void) 541311827fbSnicm { 542311827fbSnicm struct window *w; 543311827fbSnicm struct window_pane *wp; 544311827fbSnicm u_int i; 545*b7fa064bSnicm int events; 546311827fbSnicm 547311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 548311827fbSnicm w = ARRAY_ITEM(&windows, i); 549311827fbSnicm if (w == NULL) 550311827fbSnicm continue; 551311827fbSnicm 552311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 553*b7fa064bSnicm if (wp->fd == -1) 554*b7fa064bSnicm continue; 555*b7fa064bSnicm events = POLLIN; 556311827fbSnicm if (BUFFER_USED(wp->out) > 0) 557*b7fa064bSnicm events |= POLLOUT; 558*b7fa064bSnicm server_poll_add(wp->fd, events); 559311827fbSnicm } 560311827fbSnicm } 561311827fbSnicm } 562311827fbSnicm 563311827fbSnicm /* Handle window pollfds. */ 564311827fbSnicm void 565*b7fa064bSnicm server_handle_windows(void) 566311827fbSnicm { 567311827fbSnicm struct window *w; 568311827fbSnicm struct window_pane *wp; 569*b7fa064bSnicm struct pollfd *pfd; 570311827fbSnicm u_int i; 571311827fbSnicm 572311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 573311827fbSnicm w = ARRAY_ITEM(&windows, i); 574311827fbSnicm if (w == NULL) 575311827fbSnicm continue; 576311827fbSnicm 577311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 578*b7fa064bSnicm if (wp->fd == -1) 579*b7fa064bSnicm continue; 580*b7fa064bSnicm if ((pfd = server_poll_lookup(wp->fd)) == NULL) 581*b7fa064bSnicm continue; 582*b7fa064bSnicm if (buffer_poll(pfd, wp->in, wp->out) != 0) { 583311827fbSnicm close(wp->fd); 584311827fbSnicm wp->fd = -1; 585311827fbSnicm } else 586311827fbSnicm server_handle_window(w, wp); 587311827fbSnicm } 588311827fbSnicm 589311827fbSnicm server_check_window(w); 590311827fbSnicm } 591311827fbSnicm } 592311827fbSnicm 593311827fbSnicm /* Check for general redraw on client. */ 594311827fbSnicm void 595311827fbSnicm server_check_redraw(struct client *c) 596311827fbSnicm { 597311827fbSnicm struct session *s; 598311827fbSnicm struct window_pane *wp; 599311827fbSnicm int flags, redraw; 600311827fbSnicm 601311827fbSnicm if (c == NULL || c->session == NULL) 602311827fbSnicm return; 603311827fbSnicm s = c->session; 604311827fbSnicm 605311827fbSnicm flags = c->tty.flags & TTY_FREEZE; 606311827fbSnicm c->tty.flags &= ~TTY_FREEZE; 607311827fbSnicm 608311827fbSnicm if (c->flags & (CLIENT_REDRAW|CLIENT_STATUS)) { 60995b31741Snicm if (options_get_number(&s->options, "set-titles")) 61095b31741Snicm server_set_title(c); 61195b31741Snicm 612311827fbSnicm if (c->message_string != NULL) 613311827fbSnicm redraw = status_message_redraw(c); 614311827fbSnicm else if (c->prompt_string != NULL) 615311827fbSnicm redraw = status_prompt_redraw(c); 616311827fbSnicm else 617311827fbSnicm redraw = status_redraw(c); 618311827fbSnicm if (!redraw) 619311827fbSnicm c->flags &= ~CLIENT_STATUS; 620311827fbSnicm } 621311827fbSnicm 622311827fbSnicm if (c->flags & CLIENT_REDRAW) { 62379fc95a8Snicm screen_redraw_screen(c, 0); 624311827fbSnicm c->flags &= ~CLIENT_STATUS; 625311827fbSnicm } else { 626311827fbSnicm TAILQ_FOREACH(wp, &c->session->curw->window->panes, entry) { 627311827fbSnicm if (wp->flags & PANE_REDRAW) 628311827fbSnicm screen_redraw_pane(c, wp); 629311827fbSnicm } 630311827fbSnicm } 631311827fbSnicm 632311827fbSnicm if (c->flags & CLIENT_STATUS) 63379fc95a8Snicm screen_redraw_screen(c, 1); 634311827fbSnicm 635311827fbSnicm c->tty.flags |= flags; 636311827fbSnicm 637311827fbSnicm c->flags &= ~(CLIENT_REDRAW|CLIENT_STATUS); 638311827fbSnicm } 639311827fbSnicm 64095b31741Snicm /* Set client title. */ 64195b31741Snicm void 64295b31741Snicm server_set_title(struct client *c) 64395b31741Snicm { 64495b31741Snicm struct session *s = c->session; 64595b31741Snicm const char *template; 64695b31741Snicm char *title; 64795b31741Snicm 64895b31741Snicm template = options_get_string(&s->options, "set-titles-string"); 64995b31741Snicm 65095b31741Snicm title = status_replace(c->session, template, time(NULL)); 65195b31741Snicm if (c->title == NULL || strcmp(title, c->title) != 0) { 65295b31741Snicm if (c->title != NULL) 65395b31741Snicm xfree(c->title); 65495b31741Snicm c->title = xstrdup(title); 65595b31741Snicm tty_set_title(&c->tty, c->title); 65695b31741Snicm } 65795b31741Snicm xfree(title); 65895b31741Snicm } 65995b31741Snicm 660311827fbSnicm /* Check for timers on client. */ 661311827fbSnicm void 662311827fbSnicm server_check_timers(struct client *c) 663311827fbSnicm { 664311827fbSnicm struct session *s; 665311827fbSnicm struct timeval tv; 666311827fbSnicm u_int interval; 667311827fbSnicm 668311827fbSnicm if (c == NULL || c->session == NULL) 669311827fbSnicm return; 670311827fbSnicm s = c->session; 671311827fbSnicm 672311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 67385d997dcSnicm fatal("gettimeofday failed"); 674311827fbSnicm 675669c539cSnicm if (c->flags & CLIENT_IDENTIFY && timercmp(&tv, &c->identify_timer, >)) 676669c539cSnicm server_clear_identify(c); 677669c539cSnicm 678311827fbSnicm if (c->message_string != NULL && timercmp(&tv, &c->message_timer, >)) 679311827fbSnicm status_message_clear(c); 680311827fbSnicm 681311827fbSnicm if (c->message_string != NULL || c->prompt_string != NULL) { 682311827fbSnicm /* 683311827fbSnicm * Don't need timed redraw for messages/prompts so bail now. 684311827fbSnicm * The status timer isn't reset when they are redrawn anyway. 685311827fbSnicm */ 686311827fbSnicm return; 687311827fbSnicm } 688311827fbSnicm if (!options_get_number(&s->options, "status")) 689311827fbSnicm return; 690311827fbSnicm 691311827fbSnicm /* Check timer; resolution is only a second so don't be too clever. */ 692311827fbSnicm interval = options_get_number(&s->options, "status-interval"); 693311827fbSnicm if (interval == 0) 694311827fbSnicm return; 695311827fbSnicm if (tv.tv_sec < c->status_timer.tv_sec || 696311827fbSnicm ((u_int) tv.tv_sec) - c->status_timer.tv_sec >= interval) 697311827fbSnicm c->flags |= CLIENT_STATUS; 698311827fbSnicm } 699311827fbSnicm 700311827fbSnicm /* Fill client pollfds. */ 701311827fbSnicm void 702*b7fa064bSnicm server_fill_clients(void) 703311827fbSnicm { 704311827fbSnicm struct client *c; 705311827fbSnicm struct window *w; 706311827fbSnicm struct window_pane *wp; 707311827fbSnicm u_int i; 708*b7fa064bSnicm int events; 709311827fbSnicm 710311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 711311827fbSnicm c = ARRAY_ITEM(&clients, i); 712311827fbSnicm 713311827fbSnicm server_check_timers(c); 714311827fbSnicm server_check_redraw(c); 715311827fbSnicm 716*b7fa064bSnicm if (c != NULL) { 717*b7fa064bSnicm events = 0; 7180cd9638eSnicm if (!(c->flags & CLIENT_BAD)) 719*b7fa064bSnicm events |= POLLIN; 720fd234c13Snicm if (c->ibuf.w.queued > 0) 721*b7fa064bSnicm events |= POLLOUT; 722*b7fa064bSnicm server_poll_add(c->ibuf.fd, events); 723311827fbSnicm } 724311827fbSnicm 725*b7fa064bSnicm if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && 726*b7fa064bSnicm c->tty.fd != -1 && c->session != NULL) { 727*b7fa064bSnicm events = POLLIN; 728311827fbSnicm if (BUFFER_USED(c->tty.out) > 0) 729*b7fa064bSnicm events |= POLLOUT; 730*b7fa064bSnicm server_poll_add(c->tty.fd, events); 731311827fbSnicm } 732311827fbSnicm } 733311827fbSnicm 734311827fbSnicm /* 735311827fbSnicm * Clear any window redraw flags (will have been redrawn as part of 736311827fbSnicm * client). 737311827fbSnicm */ 738311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 739311827fbSnicm w = ARRAY_ITEM(&windows, i); 740311827fbSnicm if (w == NULL) 741311827fbSnicm continue; 742311827fbSnicm 743311827fbSnicm w->flags &= ~WINDOW_REDRAW; 744311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) 745311827fbSnicm wp->flags &= ~PANE_REDRAW; 746311827fbSnicm } 747311827fbSnicm } 748311827fbSnicm 749311827fbSnicm /* Handle client pollfds. */ 750311827fbSnicm void 751*b7fa064bSnicm server_handle_clients(void) 752311827fbSnicm { 753311827fbSnicm struct client *c; 754*b7fa064bSnicm struct pollfd *pfd; 755311827fbSnicm u_int i; 756311827fbSnicm 757311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 758311827fbSnicm c = ARRAY_ITEM(&clients, i); 759311827fbSnicm 760311827fbSnicm if (c != NULL) { 761*b7fa064bSnicm if ((pfd = server_poll_lookup(c->ibuf.fd)) == NULL) 762*b7fa064bSnicm continue; 763*b7fa064bSnicm if (pfd->revents & (POLLERR|POLLNVAL|POLLHUP)) { 764311827fbSnicm server_lost_client(c); 765311827fbSnicm continue; 766fd234c13Snicm } 767fd234c13Snicm 768*b7fa064bSnicm if (pfd->revents & POLLOUT) { 769fd234c13Snicm if (msgbuf_write(&c->ibuf.w) < 0) { 7700cd9638eSnicm server_lost_client(c); 7710cd9638eSnicm continue; 772fd234c13Snicm } 773fd234c13Snicm } 774fd234c13Snicm 775fd234c13Snicm if (c->flags & CLIENT_BAD) { 776fd234c13Snicm if (c->ibuf.w.queued == 0) 777fd234c13Snicm server_lost_client(c); 778fd234c13Snicm continue; 779*b7fa064bSnicm } else if (pfd->revents & POLLIN) { 780fd234c13Snicm if (server_msg_dispatch(c) != 0) { 781fd234c13Snicm server_lost_client(c); 782fd234c13Snicm continue; 783fd234c13Snicm } 784fd234c13Snicm } 785311827fbSnicm } 786311827fbSnicm 787311827fbSnicm if (c != NULL && !(c->flags & CLIENT_SUSPENDED) && 788311827fbSnicm c->tty.fd != -1 && c->session != NULL) { 789*b7fa064bSnicm if ((pfd = server_poll_lookup(c->tty.fd)) == NULL) 790*b7fa064bSnicm continue; 791*b7fa064bSnicm if (buffer_poll(pfd, c->tty.in, c->tty.out) != 0) 792311827fbSnicm server_lost_client(c); 793311827fbSnicm else 794311827fbSnicm server_handle_client(c); 795311827fbSnicm } 796311827fbSnicm } 797311827fbSnicm } 798311827fbSnicm 799311827fbSnicm /* accept(2) and create new client. */ 800980b7663Snicm void 801311827fbSnicm server_accept_client(int srv_fd) 802311827fbSnicm { 803311827fbSnicm struct sockaddr_storage sa; 804311827fbSnicm socklen_t slen = sizeof sa; 805311827fbSnicm int fd; 806311827fbSnicm 807311827fbSnicm fd = accept(srv_fd, (struct sockaddr *) &sa, &slen); 808311827fbSnicm if (fd == -1) { 809311827fbSnicm if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 810980b7663Snicm return; 811311827fbSnicm fatal("accept failed"); 812311827fbSnicm } 813311827fbSnicm if (sigterm) { 814311827fbSnicm close(fd); 815980b7663Snicm return; 816311827fbSnicm } 817980b7663Snicm server_create_client(fd); 818311827fbSnicm } 819311827fbSnicm 820311827fbSnicm /* Input data from client. */ 821311827fbSnicm void 822311827fbSnicm server_handle_client(struct client *c) 823311827fbSnicm { 824e086383eSnicm struct window *w; 825311827fbSnicm struct window_pane *wp; 826311827fbSnicm struct screen *s; 827311827fbSnicm struct timeval tv; 828311827fbSnicm struct key_binding *bd; 829dfab4feaSnicm struct keylist *keylist; 830dfab4feaSnicm int key, status, xtimeout, mode, isprefix; 831dfab4feaSnicm u_int i; 832311827fbSnicm u_char mouse[3]; 833311827fbSnicm 834311827fbSnicm xtimeout = options_get_number(&c->session->options, "repeat-time"); 835311827fbSnicm if (xtimeout != 0 && c->flags & CLIENT_REPEAT) { 836311827fbSnicm if (gettimeofday(&tv, NULL) != 0) 83785d997dcSnicm fatal("gettimeofday failed"); 838311827fbSnicm if (timercmp(&tv, &c->repeat_timer, >)) 839311827fbSnicm c->flags &= ~(CLIENT_PREFIX|CLIENT_REPEAT); 840311827fbSnicm } 841311827fbSnicm 842311827fbSnicm /* Process keys. */ 843dfab4feaSnicm keylist = options_get_data(&c->session->options, "prefix"); 844311827fbSnicm while (tty_keys_next(&c->tty, &key, mouse) == 0) { 845311827fbSnicm server_activity = time(NULL); 846311827fbSnicm 847311827fbSnicm if (c->session == NULL) 848311827fbSnicm return; 849e086383eSnicm w = c->session->curw->window; 850e086383eSnicm wp = w->active; /* could die */ 851e086383eSnicm 852e086383eSnicm /* Special case: number keys jump to pane in identify mode. */ 853e086383eSnicm if (c->flags & CLIENT_IDENTIFY && key >= '0' && key <= '9') { 854e086383eSnicm wp = window_pane_at_index(w, key - '0'); 855e086383eSnicm if (wp != NULL && window_pane_visible(wp)) 856e086383eSnicm window_set_active_pane(w, wp); 857e086383eSnicm server_clear_identify(c); 858e086383eSnicm continue; 859e086383eSnicm } 860311827fbSnicm 861311827fbSnicm status_message_clear(c); 862669c539cSnicm server_clear_identify(c); 863311827fbSnicm if (c->prompt_string != NULL) { 864311827fbSnicm status_prompt_key(c, key); 865311827fbSnicm continue; 866311827fbSnicm } 867311827fbSnicm 868311827fbSnicm /* Check for mouse keys. */ 869311827fbSnicm if (key == KEYC_MOUSE) { 870311827fbSnicm window_pane_mouse(wp, c, mouse[0], mouse[1], mouse[2]); 871311827fbSnicm continue; 872311827fbSnicm } 873311827fbSnicm 874dfab4feaSnicm /* Is this a prefix key? */ 875dfab4feaSnicm isprefix = 0; 876dfab4feaSnicm for (i = 0; i < ARRAY_LENGTH(keylist); i++) { 877dfab4feaSnicm if (key == ARRAY_ITEM(keylist, i)) { 878dfab4feaSnicm isprefix = 1; 879dfab4feaSnicm break; 880dfab4feaSnicm } 881dfab4feaSnicm } 882dfab4feaSnicm 883311827fbSnicm /* No previous prefix key. */ 884311827fbSnicm if (!(c->flags & CLIENT_PREFIX)) { 885dfab4feaSnicm if (isprefix) 886311827fbSnicm c->flags |= CLIENT_PREFIX; 887ad4696b5Snicm else { 888ad4696b5Snicm /* Try as a non-prefix key binding. */ 889ad4696b5Snicm if ((bd = key_bindings_lookup(key)) == NULL) 890311827fbSnicm window_pane_key(wp, c, key); 891ad4696b5Snicm else 892ad4696b5Snicm key_bindings_dispatch(bd, c); 893ad4696b5Snicm } 894311827fbSnicm continue; 895311827fbSnicm } 896311827fbSnicm 897311827fbSnicm /* Prefix key already pressed. Reset prefix and lookup key. */ 898311827fbSnicm c->flags &= ~CLIENT_PREFIX; 899ad4696b5Snicm if ((bd = key_bindings_lookup(key | KEYC_PREFIX)) == NULL) { 900311827fbSnicm /* If repeating, treat this as a key, else ignore. */ 901311827fbSnicm if (c->flags & CLIENT_REPEAT) { 902311827fbSnicm c->flags &= ~CLIENT_REPEAT; 903dfab4feaSnicm if (isprefix) 904311827fbSnicm c->flags |= CLIENT_PREFIX; 905311827fbSnicm else 906311827fbSnicm window_pane_key(wp, c, key); 907311827fbSnicm } 908311827fbSnicm continue; 909311827fbSnicm } 910311827fbSnicm 911311827fbSnicm /* If already repeating, but this key can't repeat, skip it. */ 912311827fbSnicm if (c->flags & CLIENT_REPEAT && !bd->can_repeat) { 913311827fbSnicm c->flags &= ~CLIENT_REPEAT; 914dfab4feaSnicm if (isprefix) 915311827fbSnicm c->flags |= CLIENT_PREFIX; 916311827fbSnicm else 917311827fbSnicm window_pane_key(wp, c, key); 918311827fbSnicm continue; 919311827fbSnicm } 920311827fbSnicm 921311827fbSnicm /* If this key can repeat, reset the repeat flags and timer. */ 922311827fbSnicm if (xtimeout != 0 && bd->can_repeat) { 923311827fbSnicm c->flags |= CLIENT_PREFIX|CLIENT_REPEAT; 924311827fbSnicm 925311827fbSnicm tv.tv_sec = xtimeout / 1000; 926311827fbSnicm tv.tv_usec = (xtimeout % 1000) * 1000L; 927311827fbSnicm if (gettimeofday(&c->repeat_timer, NULL) != 0) 92885d997dcSnicm fatal("gettimeofday failed"); 929311827fbSnicm timeradd(&c->repeat_timer, &tv, &c->repeat_timer); 930311827fbSnicm } 931311827fbSnicm 932311827fbSnicm /* Dispatch the command. */ 933311827fbSnicm key_bindings_dispatch(bd, c); 934311827fbSnicm } 935311827fbSnicm if (c->session == NULL) 936311827fbSnicm return; 937311827fbSnicm wp = c->session->curw->window->active; /* could die - do each loop */ 938311827fbSnicm s = wp->screen; 939311827fbSnicm 94045400b51Snicm /* 94145400b51Snicm * Update cursor position and mode settings. The scroll region and 94245400b51Snicm * attributes are cleared across poll(2) as this is the most likely 94345400b51Snicm * time a user may interrupt tmux, for example with ~^Z in ssh(1). This 94445400b51Snicm * is a compromise between excessive resets and likelihood of an 94545400b51Snicm * interrupt. 94645400b51Snicm * 94745400b51Snicm * tty_region/tty_reset/tty_update_mode already take care of not 94845400b51Snicm * resetting things that are already in their default state. 94945400b51Snicm */ 950311827fbSnicm status = options_get_number(&c->session->options, "status"); 951dde47eafSnicm tty_region(&c->tty, 0, c->tty.sy - 1, 0); 952af9e4c5dSnicm if (!window_pane_visible(wp) || wp->yoff + s->cy >= c->tty.sy - status) 953af9e4c5dSnicm tty_cursor(&c->tty, 0, 0, 0, 0); 954af9e4c5dSnicm else 955311827fbSnicm tty_cursor(&c->tty, s->cx, s->cy, wp->xoff, wp->yoff); 956311827fbSnicm 957311827fbSnicm mode = s->mode; 958311827fbSnicm tty_update_mode(&c->tty, mode); 95945400b51Snicm tty_reset(&c->tty); 960311827fbSnicm } 961311827fbSnicm 962311827fbSnicm /* Lost a client. */ 963311827fbSnicm void 964311827fbSnicm server_lost_client(struct client *c) 965311827fbSnicm { 966311827fbSnicm u_int i; 967311827fbSnicm 968311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 969311827fbSnicm if (ARRAY_ITEM(&clients, i) == c) 970311827fbSnicm ARRAY_SET(&clients, i, NULL); 971311827fbSnicm } 9720bb39cd5Snicm log_debug("lost client %d", c->ibuf.fd); 973311827fbSnicm 974f369cd7fSnicm /* 975f369cd7fSnicm * If CLIENT_TERMINAL hasn't been set, then tty_init hasn't been called 976f369cd7fSnicm * and tty_free might close an unrelated fd. 977f369cd7fSnicm */ 978f369cd7fSnicm if (c->flags & CLIENT_TERMINAL) 979130af69dSnicm tty_free(&c->tty); 980311827fbSnicm 981311827fbSnicm screen_free(&c->status); 982311827fbSnicm 983311827fbSnicm if (c->title != NULL) 984311827fbSnicm xfree(c->title); 985311827fbSnicm 986311827fbSnicm if (c->message_string != NULL) 987311827fbSnicm xfree(c->message_string); 988311827fbSnicm 989311827fbSnicm if (c->prompt_string != NULL) 990311827fbSnicm xfree(c->prompt_string); 991311827fbSnicm if (c->prompt_buffer != NULL) 992311827fbSnicm xfree(c->prompt_buffer); 993311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&c->prompt_hdata); i++) 994311827fbSnicm xfree(ARRAY_ITEM(&c->prompt_hdata, i)); 995311827fbSnicm ARRAY_FREE(&c->prompt_hdata); 996311827fbSnicm 997311827fbSnicm if (c->cwd != NULL) 998311827fbSnicm xfree(c->cwd); 999311827fbSnicm 1000fd234c13Snicm close(c->ibuf.fd); 1001fd234c13Snicm imsg_clear(&c->ibuf); 10027255ff90Snicm 10037255ff90Snicm for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { 10047255ff90Snicm if (ARRAY_ITEM(&dead_clients, i) == NULL) { 10057255ff90Snicm ARRAY_SET(&dead_clients, i, c); 10067255ff90Snicm break; 10077255ff90Snicm } 10087255ff90Snicm } 10097255ff90Snicm if (i == ARRAY_LENGTH(&dead_clients)) 10107255ff90Snicm ARRAY_ADD(&dead_clients, c); 10117255ff90Snicm c->flags |= CLIENT_DEAD; 1012311827fbSnicm 1013311827fbSnicm recalculate_sizes(); 1014311827fbSnicm } 1015311827fbSnicm 10167255ff90Snicm /* Free dead, unreferenced clients and sessions. */ 10177255ff90Snicm void 10187255ff90Snicm server_clean_dead(void) 10197255ff90Snicm { 10207255ff90Snicm struct session *s; 10217255ff90Snicm struct client *c; 10227255ff90Snicm u_int i; 10237255ff90Snicm 10247255ff90Snicm for (i = 0; i < ARRAY_LENGTH(&dead_sessions); i++) { 10257255ff90Snicm s = ARRAY_ITEM(&dead_sessions, i); 10267255ff90Snicm if (s == NULL || s->references != 0) 10277255ff90Snicm continue; 10287255ff90Snicm ARRAY_SET(&dead_sessions, i, NULL); 10297255ff90Snicm xfree(s); 10307255ff90Snicm } 10317255ff90Snicm 10327255ff90Snicm for (i = 0; i < ARRAY_LENGTH(&dead_clients); i++) { 10337255ff90Snicm c = ARRAY_ITEM(&dead_clients, i); 10347255ff90Snicm if (c == NULL || c->references != 0) 10357255ff90Snicm continue; 10367255ff90Snicm ARRAY_SET(&dead_clients, i, NULL); 10377255ff90Snicm xfree(c); 10387255ff90Snicm } 10397255ff90Snicm } 10407255ff90Snicm 1041311827fbSnicm /* Handle window data. */ 1042311827fbSnicm void 1043311827fbSnicm server_handle_window(struct window *w, struct window_pane *wp) 1044311827fbSnicm { 1045311827fbSnicm struct session *s; 1046311827fbSnicm u_int i; 1047311827fbSnicm int update; 1048311827fbSnicm 1049311827fbSnicm window_pane_parse(wp); 1050311827fbSnicm 1051311827fbSnicm if ((w->flags & (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT)) == 0) 1052311827fbSnicm return; 1053311827fbSnicm 1054311827fbSnicm update = 0; 1055311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1056311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1057311827fbSnicm if (s == NULL || !session_has(s, w)) 1058311827fbSnicm continue; 1059311827fbSnicm 10600903c7b9Snicm update += server_check_window_bell(s, w); 1061311827fbSnicm update += server_check_window_activity(s, w); 1062311827fbSnicm update += server_check_window_content(s, w, wp); 1063311827fbSnicm } 1064311827fbSnicm if (update) 1065311827fbSnicm server_status_window(w); 1066311827fbSnicm 1067311827fbSnicm w->flags &= ~(WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_CONTENT); 1068311827fbSnicm } 1069311827fbSnicm 1070311827fbSnicm int 10710903c7b9Snicm server_check_window_bell(struct session *s, struct window *w) 1072311827fbSnicm { 1073311827fbSnicm struct client *c; 1074311827fbSnicm u_int i; 10750903c7b9Snicm int action, visual; 1076311827fbSnicm 1077311827fbSnicm if (!(w->flags & WINDOW_BELL)) 1078311827fbSnicm return (0); 1079311827fbSnicm if (session_alert_has_window(s, w, WINDOW_BELL)) 1080311827fbSnicm return (0); 1081311827fbSnicm session_alert_add(s, w, WINDOW_BELL); 1082311827fbSnicm 1083311827fbSnicm action = options_get_number(&s->options, "bell-action"); 1084311827fbSnicm switch (action) { 1085311827fbSnicm case BELL_ANY: 1086311827fbSnicm if (s->flags & SESSION_UNATTACHED) 1087311827fbSnicm break; 10880903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 1089311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1090311827fbSnicm c = ARRAY_ITEM(&clients, i); 10910903c7b9Snicm if (c == NULL || c->session != s) 10920903c7b9Snicm continue; 10930903c7b9Snicm if (!visual) { 1094311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 10950903c7b9Snicm continue; 10960903c7b9Snicm } 10970903c7b9Snicm if (c->session->curw->window == w) { 10980903c7b9Snicm status_message_set(c, "Bell in current window"); 10990903c7b9Snicm continue; 11000903c7b9Snicm } 11010903c7b9Snicm status_message_set(c, "Bell in window %u", 11020903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 1103311827fbSnicm } 1104311827fbSnicm break; 1105311827fbSnicm case BELL_CURRENT: 11060903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 1107311827fbSnicm break; 11080903c7b9Snicm visual = options_get_number(&s->options, "visual-bell"); 1109311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 1110311827fbSnicm c = ARRAY_ITEM(&clients, i); 11110903c7b9Snicm if (c == NULL || c->session != s) 11120903c7b9Snicm continue; 11130903c7b9Snicm if (c->session->curw->window != w) 11140903c7b9Snicm continue; 11150903c7b9Snicm if (!visual) { 1116311827fbSnicm tty_putcode(&c->tty, TTYC_BEL); 11170903c7b9Snicm continue; 11180903c7b9Snicm } 11190903c7b9Snicm status_message_set(c, "Bell in current window"); 1120311827fbSnicm } 1121311827fbSnicm break; 1122311827fbSnicm } 1123311827fbSnicm return (1); 1124311827fbSnicm } 1125311827fbSnicm 1126311827fbSnicm int 1127311827fbSnicm server_check_window_activity(struct session *s, struct window *w) 1128311827fbSnicm { 11290903c7b9Snicm struct client *c; 11300903c7b9Snicm u_int i; 11310903c7b9Snicm 1132311827fbSnicm if (!(w->flags & WINDOW_ACTIVITY)) 1133311827fbSnicm return (0); 11340903c7b9Snicm 1135311827fbSnicm if (!options_get_number(&w->options, "monitor-activity")) 1136311827fbSnicm return (0); 11370903c7b9Snicm 1138311827fbSnicm if (session_alert_has_window(s, w, WINDOW_ACTIVITY)) 1139311827fbSnicm return (0); 11400903c7b9Snicm if (s->curw->window == w) 11410903c7b9Snicm return (0); 11420903c7b9Snicm 1143311827fbSnicm session_alert_add(s, w, WINDOW_ACTIVITY); 11440903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 11450903c7b9Snicm return (0); 11460903c7b9Snicm if (options_get_number(&s->options, "visual-activity")) { 11470903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 11480903c7b9Snicm c = ARRAY_ITEM(&clients, i); 11490903c7b9Snicm if (c == NULL || c->session != s) 11500903c7b9Snicm continue; 11510903c7b9Snicm status_message_set(c, "Activity in window %u", 11520903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 11530903c7b9Snicm } 11540903c7b9Snicm } 11550903c7b9Snicm 1156311827fbSnicm return (1); 1157311827fbSnicm } 1158311827fbSnicm 1159311827fbSnicm int 1160311827fbSnicm server_check_window_content( 1161311827fbSnicm struct session *s, struct window *w, struct window_pane *wp) 1162311827fbSnicm { 11630903c7b9Snicm struct client *c; 11640903c7b9Snicm u_int i; 1165311827fbSnicm char *found, *ptr; 1166311827fbSnicm 11670903c7b9Snicm if (!(w->flags & WINDOW_ACTIVITY)) /* activity for new content */ 1168311827fbSnicm return (0); 11690903c7b9Snicm 11700903c7b9Snicm ptr = options_get_string(&w->options, "monitor-content"); 11710903c7b9Snicm if (ptr == NULL || *ptr == '\0') 1172311827fbSnicm return (0); 11730903c7b9Snicm 1174311827fbSnicm if (session_alert_has_window(s, w, WINDOW_CONTENT)) 1175311827fbSnicm return (0); 11760903c7b9Snicm if (s->curw->window == w) 11770903c7b9Snicm return (0); 11780903c7b9Snicm 1179570a3589Snicm if ((found = window_pane_search(wp, ptr, NULL)) == NULL) 1180311827fbSnicm return (0); 1181311827fbSnicm xfree(found); 11820903c7b9Snicm 11830903c7b9Snicm session_alert_add(s, w, WINDOW_CONTENT); 11840903c7b9Snicm if (s->flags & SESSION_UNATTACHED) 11850903c7b9Snicm return (0); 11860903c7b9Snicm if (options_get_number(&s->options, "visual-content")) { 11870903c7b9Snicm for (i = 0; i < ARRAY_LENGTH(&clients); i++) { 11880903c7b9Snicm c = ARRAY_ITEM(&clients, i); 11890903c7b9Snicm if (c == NULL || c->session != s) 11900903c7b9Snicm continue; 11910903c7b9Snicm status_message_set(c, "Content in window %u", 11920903c7b9Snicm winlink_find_by_window(&s->windows, w)->idx); 11930903c7b9Snicm } 11940903c7b9Snicm } 11950903c7b9Snicm 1196311827fbSnicm return (1); 1197311827fbSnicm } 1198311827fbSnicm 1199c752cdacSnicm /* Check if window still exists. */ 1200311827fbSnicm void 1201311827fbSnicm server_check_window(struct window *w) 1202311827fbSnicm { 1203311827fbSnicm struct window_pane *wp, *wq; 1204e0b5b8e5Snicm struct options *oo = &w->options; 1205311827fbSnicm struct session *s; 1206311827fbSnicm struct winlink *wl; 1207fafcfb1dSnicm u_int i; 1208e0b5b8e5Snicm int destroyed; 1209311827fbSnicm 1210311827fbSnicm destroyed = 1; 1211311827fbSnicm 1212311827fbSnicm wp = TAILQ_FIRST(&w->panes); 1213311827fbSnicm while (wp != NULL) { 1214311827fbSnicm wq = TAILQ_NEXT(wp, entry); 1215c752cdacSnicm /* 1216c752cdacSnicm * If the pane has died and the remain-on-exit flag is not set, 1217c752cdacSnicm * remove the pane; otherwise, if the flag is set, don't allow 1218c752cdacSnicm * the window to be destroyed (or it'll close when the last 1219c752cdacSnicm * pane dies). 1220c752cdacSnicm */ 12212be3d294Snicm if (wp->fd == -1 && !options_get_number(oo, "remain-on-exit")) { 1222af9e4c5dSnicm layout_close_pane(wp); 1223311827fbSnicm window_remove_pane(w, wp); 1224311827fbSnicm server_redraw_window(w); 1225c752cdacSnicm } else 1226c752cdacSnicm destroyed = 0; 1227311827fbSnicm wp = wq; 1228311827fbSnicm } 1229311827fbSnicm 1230311827fbSnicm if (!destroyed) 1231311827fbSnicm return; 1232311827fbSnicm 1233311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1234311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1235311827fbSnicm if (s == NULL) 1236311827fbSnicm continue; 1237311827fbSnicm if (!session_has(s, w)) 1238311827fbSnicm continue; 1239311827fbSnicm 1240311827fbSnicm restart: 1241311827fbSnicm /* Detach window and either redraw or kill clients. */ 1242311827fbSnicm RB_FOREACH(wl, winlinks, &s->windows) { 1243311827fbSnicm if (wl->window != w) 1244311827fbSnicm continue; 12452d9a0fc7Snicm if (session_detach(s, wl)) { 12462d9a0fc7Snicm server_destroy_session(s); 1247311827fbSnicm break; 12482d9a0fc7Snicm } 12492d9a0fc7Snicm server_redraw_session(s); 1250311827fbSnicm goto restart; 1251311827fbSnicm } 1252311827fbSnicm } 1253311827fbSnicm 1254311827fbSnicm recalculate_sizes(); 1255311827fbSnicm } 1256311827fbSnicm 1257311827fbSnicm /* Call any once-per-second timers. */ 1258311827fbSnicm void 1259311827fbSnicm server_second_timers(void) 1260311827fbSnicm { 1261311827fbSnicm struct window *w; 1262311827fbSnicm struct window_pane *wp; 1263311827fbSnicm u_int i; 1264311827fbSnicm int xtimeout; 1265311827fbSnicm time_t t; 1266311827fbSnicm 1267311827fbSnicm t = time(NULL); 12681b552934Snicm 1269eaecedb2Snicm xtimeout = options_get_number(&global_s_options, "lock-after-time"); 1270fd523813Snicm if (xtimeout > 0 && t > server_activity + xtimeout) { 1271311827fbSnicm server_lock(); 1272fd523813Snicm recalculate_sizes(); 1273fd523813Snicm } 1274311827fbSnicm 1275311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&windows); i++) { 1276311827fbSnicm w = ARRAY_ITEM(&windows, i); 1277311827fbSnicm if (w == NULL) 1278311827fbSnicm continue; 1279311827fbSnicm 1280311827fbSnicm TAILQ_FOREACH(wp, &w->panes, entry) { 1281311827fbSnicm if (wp->mode != NULL && wp->mode->timer != NULL) 1282311827fbSnicm wp->mode->timer(wp); 1283311827fbSnicm } 1284311827fbSnicm } 1285311827fbSnicm } 1286311827fbSnicm 1287311827fbSnicm /* Update socket execute permissions based on whether sessions are attached. */ 1288311827fbSnicm int 1289311827fbSnicm server_update_socket(void) 1290311827fbSnicm { 1291311827fbSnicm struct session *s; 1292311827fbSnicm u_int i; 1293311827fbSnicm static int last = -1; 1294311827fbSnicm int n; 1295311827fbSnicm 1296311827fbSnicm n = 0; 1297311827fbSnicm for (i = 0; i < ARRAY_LENGTH(&sessions); i++) { 1298311827fbSnicm s = ARRAY_ITEM(&sessions, i); 1299311827fbSnicm if (s != NULL && !(s->flags & SESSION_UNATTACHED)) { 1300311827fbSnicm n++; 1301311827fbSnicm break; 1302311827fbSnicm } 1303311827fbSnicm } 1304311827fbSnicm 1305311827fbSnicm if (n != last) { 1306311827fbSnicm last = n; 1307311827fbSnicm if (n != 0) 1308311827fbSnicm chmod(socket_path, S_IRWXU); 1309311827fbSnicm else 1310311827fbSnicm chmod(socket_path, S_IRUSR|S_IWUSR); 1311311827fbSnicm } 1312311827fbSnicm 1313311827fbSnicm return (n); 1314311827fbSnicm } 1315