1*3d40d63aSnicm /* $OpenBSD: server.c,v 1.208 2025/01/01 15:17:36 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 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> 277724d0b0Snicm #include <event.h> 28311827fbSnicm #include <fcntl.h> 29a575a2dfSnicm #include <paths.h> 30311827fbSnicm #include <signal.h> 31311827fbSnicm #include <stdio.h> 32311827fbSnicm #include <stdlib.h> 33311827fbSnicm #include <string.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 struct clients clients; 45311827fbSnicm 465b8ac713Snicm struct tmuxproc *server_proc; 47432eaf59Snicm static int server_fd = -1; 48b3e4eeb8Snicm static uint64_t server_client_flags; 499883b791Snicm static int server_exit; 509883b791Snicm static struct event server_ev_accept; 5197887b4cSnicm static struct event server_ev_tidy; 52b7fa064bSnicm 537519eda3Snicm struct cmd_find_state marked_pane; 542986c025Snicm 550687354cSnicm static u_int message_next; 560687354cSnicm struct message_list message_log; 570687354cSnicm 58a711f92aSnicm time_t current_time; 59a711f92aSnicm 609883b791Snicm static int server_loop(void); 619883b791Snicm static void server_send_exit(void); 629883b791Snicm static void server_accept(int, short, void *); 639883b791Snicm static void server_signal(int); 649883b791Snicm static void server_child_signal(void); 659883b791Snicm static void server_child_exited(pid_t, int); 669883b791Snicm static void server_child_stopped(pid_t, int); 67b7fa064bSnicm 682986c025Snicm /* Set marked pane. */ 692986c025Snicm void 702986c025Snicm server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 712986c025Snicm { 7293e732aaSnicm cmd_find_clear_state(&marked_pane, 0); 737519eda3Snicm marked_pane.s = s; 747519eda3Snicm marked_pane.wl = wl; 757519eda3Snicm marked_pane.w = wl->window; 767519eda3Snicm marked_pane.wp = wp; 772986c025Snicm } 782986c025Snicm 792986c025Snicm /* Clear marked pane. */ 802986c025Snicm void 812986c025Snicm server_clear_marked(void) 822986c025Snicm { 8393e732aaSnicm cmd_find_clear_state(&marked_pane, 0); 842986c025Snicm } 852986c025Snicm 862986c025Snicm /* Is this the marked pane? */ 872986c025Snicm int 882986c025Snicm server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 892986c025Snicm { 902986c025Snicm if (s == NULL || wl == NULL || wp == NULL) 912986c025Snicm return (0); 927519eda3Snicm if (marked_pane.s != s || marked_pane.wl != wl) 932986c025Snicm return (0); 947519eda3Snicm if (marked_pane.wp != wp) 952986c025Snicm return (0); 962986c025Snicm return (server_check_marked()); 972986c025Snicm } 982986c025Snicm 992986c025Snicm /* Check if the marked pane is still valid. */ 1002986c025Snicm int 1012986c025Snicm server_check_marked(void) 1022986c025Snicm { 1037519eda3Snicm return (cmd_find_valid_state(&marked_pane)); 1042986c025Snicm } 1052986c025Snicm 10689654e7cSnicm /* Create server socket. */ 107ab0a70a7Snicm int 108ab0a70a7Snicm server_create_socket(uint64_t flags, char **cause) 109311827fbSnicm { 11089654e7cSnicm struct sockaddr_un sa; 11189654e7cSnicm size_t size; 11289654e7cSnicm mode_t mask; 113039206c6Snicm int fd, saved_errno; 11489654e7cSnicm 11589654e7cSnicm memset(&sa, 0, sizeof sa); 11689654e7cSnicm sa.sun_family = AF_UNIX; 11789654e7cSnicm size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 11889654e7cSnicm if (size >= sizeof sa.sun_path) { 11989654e7cSnicm errno = ENAMETOOLONG; 120039206c6Snicm goto fail; 12189654e7cSnicm } 12289654e7cSnicm unlink(sa.sun_path); 12389654e7cSnicm 12489654e7cSnicm if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 125039206c6Snicm goto fail; 12689654e7cSnicm 127b6d7e0c7Snicm if (flags & CLIENT_DEFAULTSOCKET) 1287394227cSnicm mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); 129b6d7e0c7Snicm else 130b6d7e0c7Snicm mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 131039206c6Snicm if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { 132039206c6Snicm saved_errno = errno; 133fb807b18Snicm close(fd); 134039206c6Snicm errno = saved_errno; 135039206c6Snicm goto fail; 136fb807b18Snicm } 13789654e7cSnicm umask(mask); 13889654e7cSnicm 139fb807b18Snicm if (listen(fd, 128) == -1) { 140039206c6Snicm saved_errno = errno; 141fb807b18Snicm close(fd); 142039206c6Snicm errno = saved_errno; 143039206c6Snicm goto fail; 144fb807b18Snicm } 145a0ee4254Snicm setblocking(fd, 0); 146311827fbSnicm 14789654e7cSnicm return (fd); 148039206c6Snicm 149039206c6Snicm fail: 150039206c6Snicm if (cause != NULL) { 151039206c6Snicm xasprintf(cause, "error creating %s (%s)", socket_path, 152039206c6Snicm strerror(errno)); 153039206c6Snicm } 154039206c6Snicm return (-1); 155039206c6Snicm } 156039206c6Snicm 15797887b4cSnicm /* Tidy up every hour. */ 15897887b4cSnicm static void 15997887b4cSnicm server_tidy_event(__unused int fd, __unused short events, __unused void *data) 16097887b4cSnicm { 16197887b4cSnicm struct timeval tv = { .tv_sec = 3600 }; 16297887b4cSnicm uint64_t t = get_timer(); 16397887b4cSnicm 16497887b4cSnicm format_tidy_jobs(); 16597887b4cSnicm 1662bd728e0Snicm log_debug("%s: took %llu milliseconds", __func__, 1672bd728e0Snicm (unsigned long long)(get_timer() - t)); 16897887b4cSnicm evtimer_add(&server_ev_tidy, &tv); 16997887b4cSnicm } 17097887b4cSnicm 171311827fbSnicm /* Fork new server. */ 172311827fbSnicm int 173ab0a70a7Snicm server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base, 174b6d7e0c7Snicm int lockfd, char *lockfile) 175311827fbSnicm { 176ab26eabfSnicm int fd; 177189d1393Snicm sigset_t set, oldset; 17810e1651aSnicm struct client *c = NULL; 179039206c6Snicm char *cause = NULL; 18097887b4cSnicm struct timeval tv = { .tv_sec = 3600 }; 181311827fbSnicm 182189d1393Snicm sigfillset(&set); 183189d1393Snicm sigprocmask(SIG_BLOCK, &set, &oldset); 1849e0637eaSnicm 1859e0637eaSnicm if (~flags & CLIENT_NOFORK) { 186ab26eabfSnicm if (proc_fork_and_daemon(&fd) != 0) { 187189d1393Snicm sigprocmask(SIG_SETMASK, &oldset, NULL); 188ab26eabfSnicm return (fd); 189311827fbSnicm } 1909e0637eaSnicm } 19189b52a5bSnicm proc_clear_signals(client, 0); 192ab26eabfSnicm server_client_flags = flags; 1939e0637eaSnicm 194c37a9299Snicm if (event_reinit(base) != 0) 195c37a9299Snicm fatalx("event_reinit failed"); 196c37a9299Snicm server_proc = proc_start("server"); 1979e0637eaSnicm 198c37a9299Snicm proc_set_signals(server_proc, server_signal); 199189d1393Snicm sigprocmask(SIG_SETMASK, &oldset, NULL); 200c37a9299Snicm 20179c9b201Snicm if (log_get_level() > 1) 2026d2e9b46Snicm tty_create_log(); 203cb19d99cSnicm if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " 204cb19d99cSnicm "tty ps", NULL) != 0) 2054e7a7adeSnicm fatal("pledge failed"); 2064e7a7adeSnicm 2079265d1acSnicm input_key_build(); 208*3d40d63aSnicm utf8_update_width_cache(); 209ae69181dSnicm RB_INIT(&windows); 2103e0f2533Snicm RB_INIT(&all_window_panes); 21182873134Snicm TAILQ_INIT(&clients); 21259996dc3Snicm RB_INIT(&sessions); 213311827fbSnicm key_bindings_init(); 2140687354cSnicm TAILQ_INIT(&message_log); 2158c1ade38Snicm gettimeofday(&start_time, NULL); 216311827fbSnicm 217b6d7e0c7Snicm server_fd = server_create_socket(flags, &cause); 218039206c6Snicm if (server_fd != -1) 219d53cf33dSnicm server_update_socket(); 2209e0637eaSnicm if (~flags & CLIENT_NOFORK) 221ab26eabfSnicm c = server_client_create(fd); 2229e0637eaSnicm else 2239e0637eaSnicm options_set_number(global_options, "exit-empty", 0); 224311827fbSnicm 2255c705c36Snicm if (lockfd >= 0) { 226549634dfSnicm unlink(lockfile); 2277d053cf9Snicm free(lockfile); 228549634dfSnicm close(lockfd); 2295c705c36Snicm } 230549634dfSnicm 231039206c6Snicm if (cause != NULL) { 23210e1651aSnicm if (c != NULL) { 23387d557e0Snicm c->exit_message = cause; 234039206c6Snicm c->flags |= CLIENT_EXIT; 235b43857e5Snicm } else { 236b43857e5Snicm fprintf(stderr, "%s\n", cause); 237b43857e5Snicm exit(1); 238b43857e5Snicm } 23910e1651aSnicm } 2407724d0b0Snicm 24197887b4cSnicm evtimer_set(&server_ev_tidy, server_tidy_event, NULL); 24297887b4cSnicm evtimer_add(&server_ev_tidy, &tv); 24397887b4cSnicm 2448ab000fcSnicm server_acl_init(); 2458ab000fcSnicm 246432eaf59Snicm server_add_accept(0); 2475b8ac713Snicm proc_loop(server_proc, server_loop); 2481a432fefSnicm 2499430fe9eSnicm job_kill_all(); 250179ef399Snicm status_prompt_save_history(); 2519430fe9eSnicm 2527724d0b0Snicm exit(0); 253311827fbSnicm } 254311827fbSnicm 2555b8ac713Snicm /* Server loop callback. */ 2569883b791Snicm static int 2577724d0b0Snicm server_loop(void) 258311827fbSnicm { 2595b8ac713Snicm struct client *c; 260765b9a58Snicm u_int items; 261765b9a58Snicm 262a711f92aSnicm current_time = time(NULL); 263a711f92aSnicm 264765b9a58Snicm do { 265765b9a58Snicm items = cmdq_next(NULL); 2666a2dc005Snicm TAILQ_FOREACH(c, &clients, entry) { 2676a2dc005Snicm if (c->flags & CLIENT_IDENTIFIED) 268765b9a58Snicm items += cmdq_next(c); 2696a2dc005Snicm } 270765b9a58Snicm } while (items != 0); 271311827fbSnicm 27289654e7cSnicm server_client_loop(); 27330da262fSnicm 2740d4a1e1fSnicm if (!options_get_number(global_options, "exit-empty") && !server_exit) 2750d4a1e1fSnicm return (0); 2760d4a1e1fSnicm 277d89252e5Snicm if (!options_get_number(global_options, "exit-unattached")) { 27859996dc3Snicm if (!RB_EMPTY(&sessions)) 27930da262fSnicm return (0); 28030da262fSnicm } 2817d5939e2Snicm 28282873134Snicm TAILQ_FOREACH(c, &clients, entry) { 28382873134Snicm if (c->session != NULL) 2847d5939e2Snicm return (0); 2857d5939e2Snicm } 2867d5939e2Snicm 2877d5939e2Snicm /* 2887d5939e2Snicm * No attached clients therefore want to exit - flush any waiting 2897d5939e2Snicm * clients but don't actually exit until they've gone. 2907d5939e2Snicm */ 2917d5939e2Snicm cmd_wait_for_flush(); 29282873134Snicm if (!TAILQ_EMPTY(&clients)) 29330da262fSnicm return (0); 2947d5939e2Snicm 2959430fe9eSnicm if (job_still_running()) 29649505a58Snicm return (0); 29749505a58Snicm 29830da262fSnicm return (1); 299311827fbSnicm } 300311827fbSnicm 3014d9325ceSnicm /* Exit the server by killing all clients and windows. */ 3029883b791Snicm static void 3034d9325ceSnicm server_send_exit(void) 304311827fbSnicm { 30582873134Snicm struct client *c, *c1; 30682873134Snicm struct session *s, *s1; 307311827fbSnicm 3087d5939e2Snicm cmd_wait_for_flush(); 3097d5939e2Snicm 31082873134Snicm TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { 3115b8ac713Snicm if (c->flags & CLIENT_SUSPENDED) 3127724d0b0Snicm server_client_lost(c); 31349505a58Snicm else { 314a34cf9c8Snicm c->flags |= CLIENT_EXIT; 315a34cf9c8Snicm c->exit_type = CLIENT_EXIT_SHUTDOWN; 31649505a58Snicm } 3177724d0b0Snicm c->session = NULL; 3188148c7feSnicm } 319311827fbSnicm 32082873134Snicm RB_FOREACH_SAFE(s, sessions, &sessions, s1) 321c26c4f79Snicm session_destroy(s, 1, __func__); 322311827fbSnicm } 323311827fbSnicm 3247724d0b0Snicm /* Update socket execute permissions based on whether sessions are attached. */ 32537dfd443Snicm void 3267724d0b0Snicm server_update_socket(void) 3277724d0b0Snicm { 3287724d0b0Snicm struct session *s; 3297724d0b0Snicm static int last = -1; 3306d9987f5Snicm int n, mode; 3316d9987f5Snicm struct stat sb; 3327724d0b0Snicm 3337724d0b0Snicm n = 0; 33459996dc3Snicm RB_FOREACH(s, sessions, &sessions) { 335647c5c18Snicm if (s->attached != 0) { 3367724d0b0Snicm n++; 3377724d0b0Snicm break; 3387724d0b0Snicm } 3397724d0b0Snicm } 3407724d0b0Snicm 3417724d0b0Snicm if (n != last) { 3427724d0b0Snicm last = n; 3436d9987f5Snicm 3446d9987f5Snicm if (stat(socket_path, &sb) != 0) 3456d9987f5Snicm return; 346d61e0664Ssemarie mode = sb.st_mode & ACCESSPERMS; 3476d9987f5Snicm if (n != 0) { 3486d9987f5Snicm if (mode & S_IRUSR) 3496d9987f5Snicm mode |= S_IXUSR; 3506d9987f5Snicm if (mode & S_IRGRP) 3516d9987f5Snicm mode |= S_IXGRP; 3526d9987f5Snicm if (mode & S_IROTH) 3536d9987f5Snicm mode |= S_IXOTH; 3546d9987f5Snicm } else 3556d9987f5Snicm mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 3566d9987f5Snicm chmod(socket_path, mode); 3577724d0b0Snicm } 3587724d0b0Snicm } 3597724d0b0Snicm 3607724d0b0Snicm /* Callback for server socket. */ 3619883b791Snicm static void 362d0e2e7f1Snicm server_accept(int fd, short events, __unused void *data) 3637724d0b0Snicm { 3647724d0b0Snicm struct sockaddr_storage sa; 3657724d0b0Snicm socklen_t slen = sizeof sa; 3667724d0b0Snicm int newfd; 3678ab000fcSnicm struct client *c; 3687724d0b0Snicm 369a06e463cSnicm server_add_accept(0); 3707724d0b0Snicm if (!(events & EV_READ)) 3717724d0b0Snicm return; 3727724d0b0Snicm 3737724d0b0Snicm newfd = accept(fd, (struct sockaddr *) &sa, &slen); 3747724d0b0Snicm if (newfd == -1) { 3757724d0b0Snicm if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 3767724d0b0Snicm return; 377a06e463cSnicm if (errno == ENFILE || errno == EMFILE) { 378a06e463cSnicm /* Delete and don't try again for 1 second. */ 379a06e463cSnicm server_add_accept(1); 380a06e463cSnicm return; 381a06e463cSnicm } 3827724d0b0Snicm fatal("accept failed"); 3837724d0b0Snicm } 3848ab000fcSnicm 3854d9325ceSnicm if (server_exit) { 3867724d0b0Snicm close(newfd); 3877724d0b0Snicm return; 3887724d0b0Snicm } 3898ab000fcSnicm c = server_client_create(newfd); 3908ab000fcSnicm if (!server_acl_join(c)) { 3918ab000fcSnicm c->exit_message = xstrdup("access not allowed"); 3928ab000fcSnicm c->flags |= CLIENT_EXIT; 3938ab000fcSnicm } 3947724d0b0Snicm } 3957724d0b0Snicm 396a06e463cSnicm /* 397a06e463cSnicm * Add accept event. If timeout is nonzero, add as a timeout instead of a read 398a06e463cSnicm * event - used to backoff when running out of file descriptors. 399a06e463cSnicm */ 400a06e463cSnicm void 401a06e463cSnicm server_add_accept(int timeout) 402a06e463cSnicm { 403a06e463cSnicm struct timeval tv = { timeout, 0 }; 404a06e463cSnicm 405432eaf59Snicm if (server_fd == -1) 406432eaf59Snicm return; 407432eaf59Snicm 408a06e463cSnicm if (event_initialized(&server_ev_accept)) 409a06e463cSnicm event_del(&server_ev_accept); 410a06e463cSnicm 411a06e463cSnicm if (timeout == 0) { 4125b8ac713Snicm event_set(&server_ev_accept, server_fd, EV_READ, server_accept, 4135b8ac713Snicm NULL); 414a06e463cSnicm event_add(&server_ev_accept, NULL); 415a06e463cSnicm } else { 4165b8ac713Snicm event_set(&server_ev_accept, server_fd, EV_TIMEOUT, 4175b8ac713Snicm server_accept, NULL); 418a06e463cSnicm event_add(&server_ev_accept, &tv); 419a06e463cSnicm } 420a06e463cSnicm } 421a06e463cSnicm 4227724d0b0Snicm /* Signal handler. */ 4239883b791Snicm static void 4245b8ac713Snicm server_signal(int sig) 4257724d0b0Snicm { 426d53cf33dSnicm int fd; 4275235cd7fSnicm 428bc4816c6Snicm log_debug("%s: %s", __func__, strsignal(sig)); 4297724d0b0Snicm switch (sig) { 4309e0637eaSnicm case SIGINT: 4317724d0b0Snicm case SIGTERM: 4324d9325ceSnicm server_exit = 1; 4334d9325ceSnicm server_send_exit(); 4347724d0b0Snicm break; 4357724d0b0Snicm case SIGCHLD: 4367724d0b0Snicm server_child_signal(); 4377724d0b0Snicm break; 4387724d0b0Snicm case SIGUSR1: 4397724d0b0Snicm event_del(&server_ev_accept); 440b6d7e0c7Snicm fd = server_create_socket(server_client_flags, NULL); 441d53cf33dSnicm if (fd != -1) { 4427724d0b0Snicm close(server_fd); 443d53cf33dSnicm server_fd = fd; 444d53cf33dSnicm server_update_socket(); 445d53cf33dSnicm } 446a06e463cSnicm server_add_accept(0); 4477724d0b0Snicm break; 44879c9b201Snicm case SIGUSR2: 44979c9b201Snicm proc_toggle_log(server_proc); 45079c9b201Snicm break; 4517724d0b0Snicm } 4527724d0b0Snicm } 4537724d0b0Snicm 4547724d0b0Snicm /* Handle SIGCHLD. */ 4559883b791Snicm static void 4567724d0b0Snicm server_child_signal(void) 4577724d0b0Snicm { 4587724d0b0Snicm int status; 4597724d0b0Snicm pid_t pid; 4607724d0b0Snicm 4617724d0b0Snicm for (;;) { 4627724d0b0Snicm switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 4637724d0b0Snicm case -1: 4647724d0b0Snicm if (errno == ECHILD) 4657724d0b0Snicm return; 4667724d0b0Snicm fatal("waitpid failed"); 4677724d0b0Snicm case 0: 4687724d0b0Snicm return; 4697724d0b0Snicm } 4707724d0b0Snicm if (WIFSTOPPED(status)) 4717724d0b0Snicm server_child_stopped(pid, status); 472a236b623Snicm else if (WIFEXITED(status) || WIFSIGNALED(status)) 4737724d0b0Snicm server_child_exited(pid, status); 4747724d0b0Snicm } 4757724d0b0Snicm } 4767724d0b0Snicm 4777724d0b0Snicm /* Handle exited children. */ 4789883b791Snicm static void 4797724d0b0Snicm server_child_exited(pid_t pid, int status) 480311827fbSnicm { 481ae69181dSnicm struct window *w, *w1; 48289654e7cSnicm struct window_pane *wp; 4837724d0b0Snicm 484ae69181dSnicm RB_FOREACH_SAFE(w, windows, &windows, w1) { 4857724d0b0Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 4867724d0b0Snicm if (wp->pid == pid) { 4873e459475Snicm wp->status = status; 488514fd64dSnicm wp->flags |= PANE_STATUSREADY; 489f35e5f52Snicm 490f35e5f52Snicm log_debug("%%%u exited", wp->id); 491f35e5f52Snicm wp->flags |= PANE_EXITED; 492f35e5f52Snicm 493f35e5f52Snicm if (window_pane_destroy_ready(wp)) 4944fc586aaSnicm server_destroy_pane(wp, 1); 4957c6e570cSnicm break; 4967724d0b0Snicm } 4977724d0b0Snicm } 4987724d0b0Snicm } 4999430fe9eSnicm job_check_died(pid, status); 5007724d0b0Snicm } 5017724d0b0Snicm 5027724d0b0Snicm /* Handle stopped children. */ 5039883b791Snicm static void 5047724d0b0Snicm server_child_stopped(pid_t pid, int status) 5057724d0b0Snicm { 5067724d0b0Snicm struct window *w; 5077724d0b0Snicm struct window_pane *wp; 5087724d0b0Snicm 5097724d0b0Snicm if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 5107724d0b0Snicm return; 5117724d0b0Snicm 512ae69181dSnicm RB_FOREACH(w, windows, &windows) { 5137724d0b0Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 5147724d0b0Snicm if (wp->pid == pid) { 5157724d0b0Snicm if (killpg(pid, SIGCONT) != 0) 5167724d0b0Snicm kill(pid, SIGCONT); 5177724d0b0Snicm } 5187724d0b0Snicm } 5197724d0b0Snicm } 520a6c9106fSnicm job_check_died(pid, status); 5217724d0b0Snicm } 5220687354cSnicm 5230687354cSnicm /* Add to message log. */ 5240687354cSnicm void 5250687354cSnicm server_add_message(const char *fmt, ...) 5260687354cSnicm { 5270687354cSnicm struct message_entry *msg, *msg1; 5280687354cSnicm char *s; 5290687354cSnicm va_list ap; 5300687354cSnicm u_int limit; 5310687354cSnicm 5320687354cSnicm va_start(ap, fmt); 5330687354cSnicm xvasprintf(&s, fmt, ap); 5340687354cSnicm va_end(ap); 5350687354cSnicm 5360687354cSnicm log_debug("message: %s", s); 5370687354cSnicm 5380687354cSnicm msg = xcalloc(1, sizeof *msg); 5390687354cSnicm gettimeofday(&msg->msg_time, NULL); 5400687354cSnicm msg->msg_num = message_next++; 5410687354cSnicm msg->msg = s; 5420687354cSnicm TAILQ_INSERT_TAIL(&message_log, msg, entry); 5430687354cSnicm 5440687354cSnicm limit = options_get_number(global_options, "message-limit"); 5450687354cSnicm TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) { 5460687354cSnicm if (msg->msg_num + limit >= message_next) 5470687354cSnicm break; 5480687354cSnicm free(msg->msg); 5490687354cSnicm TAILQ_REMOVE(&message_log, msg, entry); 5500687354cSnicm free(msg); 5510687354cSnicm } 5520687354cSnicm } 553