15494e770Schristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4ed4e6cd4Schristos * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5698d5317Sjmmv * 6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 8698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 9698d5317Sjmmv * 10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17698d5317Sjmmv */ 18698d5317Sjmmv 19698d5317Sjmmv #include <sys/types.h> 20698d5317Sjmmv #include <sys/ioctl.h> 21698d5317Sjmmv #include <sys/socket.h> 22698d5317Sjmmv #include <sys/stat.h> 23698d5317Sjmmv #include <sys/un.h> 24698d5317Sjmmv #include <sys/wait.h> 25698d5317Sjmmv 26698d5317Sjmmv #include <errno.h> 27698d5317Sjmmv #include <fcntl.h> 28698d5317Sjmmv #include <signal.h> 29698d5317Sjmmv #include <stdio.h> 30698d5317Sjmmv #include <stdlib.h> 31698d5317Sjmmv #include <string.h> 32698d5317Sjmmv #include <termios.h> 33698d5317Sjmmv #include <time.h> 34698d5317Sjmmv #include <unistd.h> 35698d5317Sjmmv 36698d5317Sjmmv #include "tmux.h" 37698d5317Sjmmv 38698d5317Sjmmv /* 39698d5317Sjmmv * Main server functions. 40698d5317Sjmmv */ 41698d5317Sjmmv 42698d5317Sjmmv struct clients clients; 43698d5317Sjmmv 44ed4e6cd4Schristos struct tmuxproc *server_proc; 456483eba0Schristos static int server_fd = -1; 469fb66d81Schristos static uint64_t server_client_flags; 474e179ddaSchristos static int server_exit; 484e179ddaSchristos static struct event server_ev_accept; 499fb66d81Schristos static struct event server_ev_tidy; 50698d5317Sjmmv 51ed4e6cd4Schristos struct cmd_find_state marked_pane; 52d530c4d0Sjmmv 539fb66d81Schristos static u_int message_next; 549fb66d81Schristos struct message_list message_log; 559fb66d81Schristos 56f844e94eSwiz time_t current_time; 57f844e94eSwiz 584e179ddaSchristos static int server_loop(void); 594e179ddaSchristos static void server_send_exit(void); 604e179ddaSchristos static void server_accept(int, short, void *); 614e179ddaSchristos static void server_signal(int); 624e179ddaSchristos static void server_child_signal(void); 634e179ddaSchristos static void server_child_exited(pid_t, int); 644e179ddaSchristos static void server_child_stopped(pid_t, int); 655494e770Schristos 665494e770Schristos /* Set marked pane. */ 675494e770Schristos void 685494e770Schristos server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 695494e770Schristos { 70c9ad075bSchristos cmd_find_clear_state(&marked_pane, 0); 71ed4e6cd4Schristos marked_pane.s = s; 72ed4e6cd4Schristos marked_pane.wl = wl; 73ed4e6cd4Schristos marked_pane.w = wl->window; 74ed4e6cd4Schristos marked_pane.wp = wp; 755494e770Schristos } 765494e770Schristos 775494e770Schristos /* Clear marked pane. */ 785494e770Schristos void 795494e770Schristos server_clear_marked(void) 805494e770Schristos { 81c9ad075bSchristos cmd_find_clear_state(&marked_pane, 0); 825494e770Schristos } 835494e770Schristos 845494e770Schristos /* Is this the marked pane? */ 855494e770Schristos int 865494e770Schristos server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 875494e770Schristos { 885494e770Schristos if (s == NULL || wl == NULL || wp == NULL) 895494e770Schristos return (0); 90ed4e6cd4Schristos if (marked_pane.s != s || marked_pane.wl != wl) 915494e770Schristos return (0); 92ed4e6cd4Schristos if (marked_pane.wp != wp) 935494e770Schristos return (0); 945494e770Schristos return (server_check_marked()); 955494e770Schristos } 965494e770Schristos 975494e770Schristos /* Check if the marked pane is still valid. */ 985494e770Schristos int 995494e770Schristos server_check_marked(void) 1005494e770Schristos { 101ed4e6cd4Schristos return (cmd_find_valid_state(&marked_pane)); 1025494e770Schristos } 103698d5317Sjmmv 104698d5317Sjmmv /* Create server socket. */ 10546548964Swiz int 106*890b6d91Swiz server_create_socket(uint64_t flags, char **cause) 107698d5317Sjmmv { 108698d5317Sjmmv struct sockaddr_un sa; 109698d5317Sjmmv size_t size; 110698d5317Sjmmv mode_t mask; 1118f3b9483Schristos int fd, saved_errno; 112698d5317Sjmmv 113698d5317Sjmmv memset(&sa, 0, sizeof sa); 114698d5317Sjmmv sa.sun_family = AF_UNIX; 115698d5317Sjmmv size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 116698d5317Sjmmv if (size >= sizeof sa.sun_path) { 117698d5317Sjmmv errno = ENAMETOOLONG; 1188f3b9483Schristos goto fail; 119698d5317Sjmmv } 120698d5317Sjmmv unlink(sa.sun_path); 121698d5317Sjmmv 122698d5317Sjmmv if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 1238f3b9483Schristos goto fail; 124698d5317Sjmmv 1259fb66d81Schristos if (flags & CLIENT_DEFAULTSOCKET) 126698d5317Sjmmv mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); 1279fb66d81Schristos else 1289fb66d81Schristos mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 1298f3b9483Schristos if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { 1308f3b9483Schristos saved_errno = errno; 131c9ad075bSchristos close(fd); 1328f3b9483Schristos errno = saved_errno; 1338f3b9483Schristos goto fail; 134c9ad075bSchristos } 135698d5317Sjmmv umask(mask); 136698d5317Sjmmv 137c9ad075bSchristos if (listen(fd, 128) == -1) { 1388f3b9483Schristos saved_errno = errno; 139c9ad075bSchristos close(fd); 1408f3b9483Schristos errno = saved_errno; 1418f3b9483Schristos goto fail; 142c9ad075bSchristos } 143d530c4d0Sjmmv setblocking(fd, 0); 144698d5317Sjmmv 145698d5317Sjmmv return (fd); 1468f3b9483Schristos 1478f3b9483Schristos fail: 1488f3b9483Schristos if (cause != NULL) { 1498f3b9483Schristos xasprintf(cause, "error creating %s (%s)", socket_path, 1508f3b9483Schristos strerror(errno)); 1518f3b9483Schristos } 1528f3b9483Schristos return (-1); 1538f3b9483Schristos } 1548f3b9483Schristos 1559fb66d81Schristos /* Tidy up every hour. */ 1569fb66d81Schristos static void 1579fb66d81Schristos server_tidy_event(__unused int fd, __unused short events, __unused void *data) 1589fb66d81Schristos { 1599fb66d81Schristos struct timeval tv = { .tv_sec = 3600 }; 1609fb66d81Schristos uint64_t t = get_timer(); 1619fb66d81Schristos 1629fb66d81Schristos format_tidy_jobs(); 1639fb66d81Schristos 1649fb66d81Schristos #ifdef HAVE_MALLOC_TRIM 1659fb66d81Schristos malloc_trim(0); 1669fb66d81Schristos #endif 1679fb66d81Schristos 168e271dbb8Schristos log_debug("%s: took %ju milliseconds", __func__, (uintmax_t)get_timer() - t); 1699fb66d81Schristos evtimer_add(&server_ev_tidy, &tv); 1709fb66d81Schristos } 1719fb66d81Schristos 172698d5317Sjmmv /* Fork new server. */ 173698d5317Sjmmv int 174*890b6d91Swiz server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base, 1759fb66d81Schristos int lockfd, char *lockfile) 176698d5317Sjmmv { 1779fb66d81Schristos int fd; 178c9ad075bSchristos sigset_t set, oldset; 1799fb66d81Schristos struct client *c = NULL; 1808f3b9483Schristos char *cause = NULL; 1819fb66d81Schristos struct timeval tv = { .tv_sec = 3600 }; 182698d5317Sjmmv 183c9ad075bSchristos sigfillset(&set); 184c9ad075bSchristos sigprocmask(SIG_BLOCK, &set, &oldset); 1859fb66d81Schristos 1869fb66d81Schristos if (~flags & CLIENT_NOFORK) { 1879fb66d81Schristos if (proc_fork_and_daemon(&fd) != 0) { 188c9ad075bSchristos sigprocmask(SIG_SETMASK, &oldset, NULL); 1899fb66d81Schristos return (fd); 190698d5317Sjmmv } 1919fb66d81Schristos } 192c9ad075bSchristos proc_clear_signals(client, 0); 1939fb66d81Schristos server_client_flags = flags; 1949fb66d81Schristos 195c9ad075bSchristos if (event_reinit(base) != 0) 196c9ad075bSchristos fatalx("event_reinit failed"); 197c9ad075bSchristos server_proc = proc_start("server"); 1989fb66d81Schristos 199c9ad075bSchristos proc_set_signals(server_proc, server_signal); 200c9ad075bSchristos sigprocmask(SIG_SETMASK, &oldset, NULL); 201698d5317Sjmmv 202c9ad075bSchristos if (log_get_level() > 1) 203ed4e6cd4Schristos tty_create_log(); 204ed4e6cd4Schristos if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " 205ed4e6cd4Schristos "tty ps", NULL) != 0) 206ed4e6cd4Schristos fatal("pledge failed"); 207698d5317Sjmmv 2089fb66d81Schristos input_key_build(); 2095494e770Schristos RB_INIT(&windows); 210d530c4d0Sjmmv RB_INIT(&all_window_panes); 2115494e770Schristos TAILQ_INIT(&clients); 212698d5317Sjmmv RB_INIT(&sessions); 213698d5317Sjmmv key_bindings_init(); 2149fb66d81Schristos TAILQ_INIT(&message_log); 215ed4e6cd4Schristos gettimeofday(&start_time, NULL); 216698d5317Sjmmv 21746548964Swiz #ifdef HAVE_SYSTEMD 21846548964Swiz server_fd = systemd_create_socket(flags, &cause); 21946548964Swiz #else 2209fb66d81Schristos server_fd = server_create_socket(flags, &cause); 22146548964Swiz #endif 2228f3b9483Schristos if (server_fd != -1) 2235494e770Schristos server_update_socket(); 2249fb66d81Schristos if (~flags & CLIENT_NOFORK) 2259fb66d81Schristos c = server_client_create(fd); 2269fb66d81Schristos else 2279fb66d81Schristos options_set_number(global_options, "exit-empty", 0); 228698d5317Sjmmv 229ed4e6cd4Schristos if (lockfd >= 0) { 230928fc495Schristos unlink(lockfile); 231928fc495Schristos free(lockfile); 232928fc495Schristos close(lockfd); 233ed4e6cd4Schristos } 234698d5317Sjmmv 2358f3b9483Schristos if (cause != NULL) { 2369fb66d81Schristos if (c != NULL) { 23746548964Swiz c->exit_message = cause; 2388f3b9483Schristos c->flags |= CLIENT_EXIT; 23946548964Swiz } else { 24046548964Swiz fprintf(stderr, "%s\n", cause); 24146548964Swiz exit(1); 2428f3b9483Schristos } 2439fb66d81Schristos } 2449fb66d81Schristos 2459fb66d81Schristos evtimer_set(&server_ev_tidy, server_tidy_event, NULL); 2469fb66d81Schristos evtimer_add(&server_ev_tidy, &tv); 2478f3b9483Schristos 24846548964Swiz server_acl_init(); 24946548964Swiz 250928fc495Schristos server_add_accept(0); 251ed4e6cd4Schristos proc_loop(server_proc, server_loop); 252c9ad075bSchristos 253ef36e747Schristos job_kill_all(); 2545494e770Schristos status_prompt_save_history(); 255ef36e747Schristos 256698d5317Sjmmv exit(0); 257698d5317Sjmmv } 258698d5317Sjmmv 259ed4e6cd4Schristos /* Server loop callback. */ 2604e179ddaSchristos static int 261ed4e6cd4Schristos server_loop(void) 262698d5317Sjmmv { 2635494e770Schristos struct client *c; 2644e179ddaSchristos u_int items; 2654e179ddaSchristos 266f844e94eSwiz current_time = time(NULL); 267f844e94eSwiz 2684e179ddaSchristos do { 2694e179ddaSchristos items = cmdq_next(NULL); 2704e179ddaSchristos TAILQ_FOREACH(c, &clients, entry) { 2714e179ddaSchristos if (c->flags & CLIENT_IDENTIFIED) 2724e179ddaSchristos items += cmdq_next(c); 2734e179ddaSchristos } 2744e179ddaSchristos } while (items != 0); 275698d5317Sjmmv 276ed4e6cd4Schristos server_client_loop(); 277ed4e6cd4Schristos 2788f3b9483Schristos if (!options_get_number(global_options, "exit-empty") && !server_exit) 2798f3b9483Schristos return (0); 2808f3b9483Schristos 281ed4e6cd4Schristos if (!options_get_number(global_options, "exit-unattached")) { 282698d5317Sjmmv if (!RB_EMPTY(&sessions)) 283698d5317Sjmmv return (0); 284698d5317Sjmmv } 2855494e770Schristos 2865494e770Schristos TAILQ_FOREACH(c, &clients, entry) { 2875494e770Schristos if (c->session != NULL) 288698d5317Sjmmv return (0); 289698d5317Sjmmv } 2905494e770Schristos 2915494e770Schristos /* 2925494e770Schristos * No attached clients therefore want to exit - flush any waiting 2935494e770Schristos * clients but don't actually exit until they've gone. 2945494e770Schristos */ 2955494e770Schristos cmd_wait_for_flush(); 2965494e770Schristos if (!TAILQ_EMPTY(&clients)) 2975494e770Schristos return (0); 2985494e770Schristos 299ef36e747Schristos if (job_still_running()) 3008f3b9483Schristos return (0); 3018f3b9483Schristos 302698d5317Sjmmv return (1); 303698d5317Sjmmv } 304698d5317Sjmmv 305ed4e6cd4Schristos /* Exit the server by killing all clients and windows. */ 3064e179ddaSchristos static void 307ed4e6cd4Schristos server_send_exit(void) 308698d5317Sjmmv { 3095494e770Schristos struct client *c, *c1; 3105494e770Schristos struct session *s, *s1; 311698d5317Sjmmv 3125494e770Schristos cmd_wait_for_flush(); 3135494e770Schristos 3145494e770Schristos TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { 315ed4e6cd4Schristos if (c->flags & CLIENT_SUSPENDED) 316698d5317Sjmmv server_client_lost(c); 3178f3b9483Schristos else { 3189fb66d81Schristos c->flags |= CLIENT_EXIT; 3199fb66d81Schristos c->exit_type = CLIENT_EXIT_SHUTDOWN; 3208f3b9483Schristos } 321698d5317Sjmmv c->session = NULL; 322698d5317Sjmmv } 323698d5317Sjmmv 3245494e770Schristos RB_FOREACH_SAFE(s, sessions, &sessions, s1) 3256483eba0Schristos session_destroy(s, 1, __func__); 326698d5317Sjmmv } 327698d5317Sjmmv 328698d5317Sjmmv /* Update socket execute permissions based on whether sessions are attached. */ 329698d5317Sjmmv void 330698d5317Sjmmv server_update_socket(void) 331698d5317Sjmmv { 332698d5317Sjmmv struct session *s; 333698d5317Sjmmv static int last = -1; 334698d5317Sjmmv int n, mode; 335698d5317Sjmmv struct stat sb; 336698d5317Sjmmv 337698d5317Sjmmv n = 0; 338698d5317Sjmmv RB_FOREACH(s, sessions, &sessions) { 339ef36e747Schristos if (s->attached != 0) { 340698d5317Sjmmv n++; 341698d5317Sjmmv break; 342698d5317Sjmmv } 343698d5317Sjmmv } 344698d5317Sjmmv 345698d5317Sjmmv if (n != last) { 346698d5317Sjmmv last = n; 347698d5317Sjmmv 348698d5317Sjmmv if (stat(socket_path, &sb) != 0) 349698d5317Sjmmv return; 3504e179ddaSchristos mode = sb.st_mode & ACCESSPERMS; 351698d5317Sjmmv if (n != 0) { 352698d5317Sjmmv if (mode & S_IRUSR) 353698d5317Sjmmv mode |= S_IXUSR; 354698d5317Sjmmv if (mode & S_IRGRP) 355698d5317Sjmmv mode |= S_IXGRP; 356698d5317Sjmmv if (mode & S_IROTH) 357698d5317Sjmmv mode |= S_IXOTH; 358698d5317Sjmmv } else 359698d5317Sjmmv mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 360698d5317Sjmmv chmod(socket_path, mode); 361698d5317Sjmmv } 362698d5317Sjmmv } 363698d5317Sjmmv 364698d5317Sjmmv /* Callback for server socket. */ 3654e179ddaSchristos static void 366ed4e6cd4Schristos server_accept(int fd, short events, __unused void *data) 367698d5317Sjmmv { 368698d5317Sjmmv struct sockaddr_storage sa; 369698d5317Sjmmv socklen_t slen = sizeof sa; 370698d5317Sjmmv int newfd; 37146548964Swiz struct client *c; 372698d5317Sjmmv 373928fc495Schristos server_add_accept(0); 374698d5317Sjmmv if (!(events & EV_READ)) 375698d5317Sjmmv return; 376698d5317Sjmmv 377698d5317Sjmmv newfd = accept(fd, (struct sockaddr *) &sa, &slen); 378698d5317Sjmmv if (newfd == -1) { 379698d5317Sjmmv if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 380698d5317Sjmmv return; 381928fc495Schristos if (errno == ENFILE || errno == EMFILE) { 382928fc495Schristos /* Delete and don't try again for 1 second. */ 383928fc495Schristos server_add_accept(1); 384928fc495Schristos return; 385928fc495Schristos } 386698d5317Sjmmv fatal("accept failed"); 387698d5317Sjmmv } 38846548964Swiz 389ed4e6cd4Schristos if (server_exit) { 390698d5317Sjmmv close(newfd); 391698d5317Sjmmv return; 392698d5317Sjmmv } 39346548964Swiz c = server_client_create(newfd); 39446548964Swiz if (!server_acl_join(c)) { 39546548964Swiz c->exit_message = xstrdup("access not allowed"); 39646548964Swiz c->flags |= CLIENT_EXIT; 39746548964Swiz } 398698d5317Sjmmv } 399698d5317Sjmmv 400928fc495Schristos /* 401928fc495Schristos * Add accept event. If timeout is nonzero, add as a timeout instead of a read 402928fc495Schristos * event - used to backoff when running out of file descriptors. 403928fc495Schristos */ 404928fc495Schristos void 405928fc495Schristos server_add_accept(int timeout) 406928fc495Schristos { 407928fc495Schristos struct timeval tv = { timeout, 0 }; 408928fc495Schristos 4096483eba0Schristos if (server_fd == -1) 4106483eba0Schristos return; 4116483eba0Schristos 412928fc495Schristos if (event_initialized(&server_ev_accept)) 413928fc495Schristos event_del(&server_ev_accept); 414928fc495Schristos 415928fc495Schristos if (timeout == 0) { 416ed4e6cd4Schristos event_set(&server_ev_accept, server_fd, EV_READ, server_accept, 417ed4e6cd4Schristos NULL); 418928fc495Schristos event_add(&server_ev_accept, NULL); 419928fc495Schristos } else { 420ed4e6cd4Schristos event_set(&server_ev_accept, server_fd, EV_TIMEOUT, 421ed4e6cd4Schristos server_accept, NULL); 422928fc495Schristos event_add(&server_ev_accept, &tv); 423928fc495Schristos } 424928fc495Schristos } 425928fc495Schristos 426698d5317Sjmmv /* Signal handler. */ 4274e179ddaSchristos static void 428ed4e6cd4Schristos server_signal(int sig) 429698d5317Sjmmv { 4305494e770Schristos int fd; 4315494e770Schristos 432c9ad075bSchristos log_debug("%s: %s", __func__, strsignal(sig)); 433698d5317Sjmmv switch (sig) { 4349fb66d81Schristos case SIGINT: 435698d5317Sjmmv case SIGTERM: 436ed4e6cd4Schristos server_exit = 1; 437ed4e6cd4Schristos server_send_exit(); 438698d5317Sjmmv break; 439698d5317Sjmmv case SIGCHLD: 440698d5317Sjmmv server_child_signal(); 441698d5317Sjmmv break; 442698d5317Sjmmv case SIGUSR1: 443698d5317Sjmmv event_del(&server_ev_accept); 4449fb66d81Schristos fd = server_create_socket(server_client_flags, NULL); 4455494e770Schristos if (fd != -1) { 446698d5317Sjmmv close(server_fd); 4475494e770Schristos server_fd = fd; 4485494e770Schristos server_update_socket(); 4495494e770Schristos } 450928fc495Schristos server_add_accept(0); 451698d5317Sjmmv break; 452c9ad075bSchristos case SIGUSR2: 453c9ad075bSchristos proc_toggle_log(server_proc); 454c9ad075bSchristos break; 455698d5317Sjmmv } 456698d5317Sjmmv } 457698d5317Sjmmv 458698d5317Sjmmv /* Handle SIGCHLD. */ 4594e179ddaSchristos static void 460698d5317Sjmmv server_child_signal(void) 461698d5317Sjmmv { 462698d5317Sjmmv int status; 463698d5317Sjmmv pid_t pid; 464698d5317Sjmmv 465698d5317Sjmmv for (;;) { 466698d5317Sjmmv switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 467698d5317Sjmmv case -1: 468698d5317Sjmmv if (errno == ECHILD) 469698d5317Sjmmv return; 470698d5317Sjmmv fatal("waitpid failed"); 471698d5317Sjmmv case 0: 472698d5317Sjmmv return; 473698d5317Sjmmv } 474698d5317Sjmmv if (WIFSTOPPED(status)) 475698d5317Sjmmv server_child_stopped(pid, status); 476698d5317Sjmmv else if (WIFEXITED(status) || WIFSIGNALED(status)) 477698d5317Sjmmv server_child_exited(pid, status); 478698d5317Sjmmv } 479698d5317Sjmmv } 480698d5317Sjmmv 481698d5317Sjmmv /* Handle exited children. */ 4824e179ddaSchristos static void 483698d5317Sjmmv server_child_exited(pid_t pid, int status) 484698d5317Sjmmv { 4855494e770Schristos struct window *w, *w1; 486698d5317Sjmmv struct window_pane *wp; 487698d5317Sjmmv 4885494e770Schristos RB_FOREACH_SAFE(w, windows, &windows, w1) { 489698d5317Sjmmv TAILQ_FOREACH(wp, &w->panes, entry) { 490698d5317Sjmmv if (wp->pid == pid) { 4915494e770Schristos wp->status = status; 4928f3b9483Schristos wp->flags |= PANE_STATUSREADY; 493c9ad075bSchristos 494c9ad075bSchristos log_debug("%%%u exited", wp->id); 495c9ad075bSchristos wp->flags |= PANE_EXITED; 496c9ad075bSchristos 497c9ad075bSchristos if (window_pane_destroy_ready(wp)) 498ed4e6cd4Schristos server_destroy_pane(wp, 1); 499698d5317Sjmmv break; 500698d5317Sjmmv } 501698d5317Sjmmv } 502698d5317Sjmmv } 503ef36e747Schristos job_check_died(pid, status); 504698d5317Sjmmv } 505698d5317Sjmmv 506698d5317Sjmmv /* Handle stopped children. */ 5074e179ddaSchristos static void 508698d5317Sjmmv server_child_stopped(pid_t pid, int status) 509698d5317Sjmmv { 510698d5317Sjmmv struct window *w; 511698d5317Sjmmv struct window_pane *wp; 512698d5317Sjmmv 513698d5317Sjmmv if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 514698d5317Sjmmv return; 515698d5317Sjmmv 5165494e770Schristos RB_FOREACH(w, windows, &windows) { 517698d5317Sjmmv TAILQ_FOREACH(wp, &w->panes, entry) { 518698d5317Sjmmv if (wp->pid == pid) { 519698d5317Sjmmv if (killpg(pid, SIGCONT) != 0) 520698d5317Sjmmv kill(pid, SIGCONT); 521698d5317Sjmmv } 522698d5317Sjmmv } 523698d5317Sjmmv } 5249fb66d81Schristos job_check_died(pid, status); 5259fb66d81Schristos } 5269fb66d81Schristos 5279fb66d81Schristos /* Add to message log. */ 5289fb66d81Schristos void 5299fb66d81Schristos server_add_message(const char *fmt, ...) 5309fb66d81Schristos { 5319fb66d81Schristos struct message_entry *msg, *msg1; 5329fb66d81Schristos char *s; 5339fb66d81Schristos va_list ap; 5349fb66d81Schristos u_int limit; 5359fb66d81Schristos 5369fb66d81Schristos va_start(ap, fmt); 5379fb66d81Schristos xvasprintf(&s, fmt, ap); 5389fb66d81Schristos va_end(ap); 5399fb66d81Schristos 5409fb66d81Schristos log_debug("message: %s", s); 5419fb66d81Schristos 5429fb66d81Schristos msg = xcalloc(1, sizeof *msg); 5439fb66d81Schristos gettimeofday(&msg->msg_time, NULL); 5449fb66d81Schristos msg->msg_num = message_next++; 5459fb66d81Schristos msg->msg = s; 5469fb66d81Schristos TAILQ_INSERT_TAIL(&message_log, msg, entry); 5479fb66d81Schristos 5489fb66d81Schristos limit = options_get_number(global_options, "message-limit"); 5499fb66d81Schristos TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) { 5509fb66d81Schristos if (msg->msg_num + limit >= message_next) 5519fb66d81Schristos break; 5529fb66d81Schristos free(msg->msg); 5539fb66d81Schristos TAILQ_REMOVE(&message_log, msg, entry); 5549fb66d81Schristos free(msg); 5559fb66d81Schristos } 556698d5317Sjmmv } 557