| File: | src/usr.bin/tmux/server.c |
| Warning: | line 219, column 7 1st function call argument is an uninitialized value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: server.c,v 1.198 2021/06/10 07:45:43 nicm Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> | |||
| 5 | * | |||
| 6 | * Permission to use, copy, modify, and distribute this software for any | |||
| 7 | * purpose with or without fee is hereby granted, provided that the above | |||
| 8 | * copyright notice and this permission notice appear in all copies. | |||
| 9 | * | |||
| 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER | |||
| 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING | |||
| 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 17 | */ | |||
| 18 | ||||
| 19 | #include <sys/types.h> | |||
| 20 | #include <sys/ioctl.h> | |||
| 21 | #include <sys/socket.h> | |||
| 22 | #include <sys/stat.h> | |||
| 23 | #include <sys/un.h> | |||
| 24 | #include <sys/wait.h> | |||
| 25 | ||||
| 26 | #include <errno(*__errno()).h> | |||
| 27 | #include <event.h> | |||
| 28 | #include <fcntl.h> | |||
| 29 | #include <paths.h> | |||
| 30 | #include <signal.h> | |||
| 31 | #include <stdio.h> | |||
| 32 | #include <stdlib.h> | |||
| 33 | #include <string.h> | |||
| 34 | #include <termios.h> | |||
| 35 | #include <time.h> | |||
| 36 | #include <unistd.h> | |||
| 37 | ||||
| 38 | #include "tmux.h" | |||
| 39 | ||||
| 40 | /* | |||
| 41 | * Main server functions. | |||
| 42 | */ | |||
| 43 | ||||
| 44 | struct clients clients; | |||
| 45 | ||||
| 46 | struct tmuxproc *server_proc; | |||
| 47 | static int server_fd = -1; | |||
| 48 | static uint64_t server_client_flags; | |||
| 49 | static int server_exit; | |||
| 50 | static struct event server_ev_accept; | |||
| 51 | static struct event server_ev_tidy; | |||
| 52 | ||||
| 53 | struct cmd_find_state marked_pane; | |||
| 54 | ||||
| 55 | static u_int message_next; | |||
| 56 | struct message_list message_log; | |||
| 57 | ||||
| 58 | static int server_loop(void); | |||
| 59 | static void server_send_exit(void); | |||
| 60 | static void server_accept(int, short, void *); | |||
| 61 | static void server_signal(int); | |||
| 62 | static void server_child_signal(void); | |||
| 63 | static void server_child_exited(pid_t, int); | |||
| 64 | static void server_child_stopped(pid_t, int); | |||
| 65 | ||||
| 66 | /* Set marked pane. */ | |||
| 67 | void | |||
| 68 | server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) | |||
| 69 | { | |||
| 70 | cmd_find_clear_state(&marked_pane, 0); | |||
| 71 | marked_pane.s = s; | |||
| 72 | marked_pane.wl = wl; | |||
| 73 | marked_pane.w = wl->window; | |||
| 74 | marked_pane.wp = wp; | |||
| 75 | } | |||
| 76 | ||||
| 77 | /* Clear marked pane. */ | |||
| 78 | void | |||
| 79 | server_clear_marked(void) | |||
| 80 | { | |||
| 81 | cmd_find_clear_state(&marked_pane, 0); | |||
| 82 | } | |||
| 83 | ||||
| 84 | /* Is this the marked pane? */ | |||
| 85 | int | |||
| 86 | server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) | |||
| 87 | { | |||
| 88 | if (s == NULL((void *)0) || wl == NULL((void *)0) || wp == NULL((void *)0)) | |||
| 89 | return (0); | |||
| 90 | if (marked_pane.s != s || marked_pane.wl != wl) | |||
| 91 | return (0); | |||
| 92 | if (marked_pane.wp != wp) | |||
| 93 | return (0); | |||
| 94 | return (server_check_marked()); | |||
| 95 | } | |||
| 96 | ||||
| 97 | /* Check if the marked pane is still valid. */ | |||
| 98 | int | |||
| 99 | server_check_marked(void) | |||
| 100 | { | |||
| 101 | return (cmd_find_valid_state(&marked_pane)); | |||
| 102 | } | |||
| 103 | ||||
| 104 | /* Create server socket. */ | |||
| 105 | static int | |||
| 106 | server_create_socket(int flags, char **cause) | |||
| 107 | { | |||
| 108 | struct sockaddr_un sa; | |||
| 109 | size_t size; | |||
| 110 | mode_t mask; | |||
| 111 | int fd, saved_errno; | |||
| 112 | ||||
| 113 | memset(&sa, 0, sizeof sa); | |||
| 114 | sa.sun_family = AF_UNIX1; | |||
| 115 | size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); | |||
| 116 | if (size >= sizeof sa.sun_path) { | |||
| 117 | errno(*__errno()) = ENAMETOOLONG63; | |||
| 118 | goto fail; | |||
| 119 | } | |||
| 120 | unlink(sa.sun_path); | |||
| 121 | ||||
| 122 | if ((fd = socket(AF_UNIX1, SOCK_STREAM1, 0)) == -1) | |||
| 123 | goto fail; | |||
| 124 | ||||
| 125 | if (flags & CLIENT_DEFAULTSOCKET0x8000000) | |||
| 126 | mask = umask(S_IXUSR0000100|S_IXGRP0000010|S_IRWXO0000007); | |||
| 127 | else | |||
| 128 | mask = umask(S_IXUSR0000100|S_IRWXG0000070|S_IRWXO0000007); | |||
| 129 | if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) { | |||
| 130 | saved_errno = errno(*__errno()); | |||
| 131 | close(fd); | |||
| 132 | errno(*__errno()) = saved_errno; | |||
| 133 | goto fail; | |||
| 134 | } | |||
| 135 | umask(mask); | |||
| 136 | ||||
| 137 | if (listen(fd, 128) == -1) { | |||
| 138 | saved_errno = errno(*__errno()); | |||
| 139 | close(fd); | |||
| 140 | errno(*__errno()) = saved_errno; | |||
| 141 | goto fail; | |||
| 142 | } | |||
| 143 | setblocking(fd, 0); | |||
| 144 | ||||
| 145 | return (fd); | |||
| 146 | ||||
| 147 | fail: | |||
| 148 | if (cause != NULL((void *)0)) { | |||
| 149 | xasprintf(cause, "error creating %s (%s)", socket_path, | |||
| 150 | strerror(errno(*__errno()))); | |||
| 151 | } | |||
| 152 | return (-1); | |||
| 153 | } | |||
| 154 | ||||
| 155 | /* Tidy up every hour. */ | |||
| 156 | static void | |||
| 157 | server_tidy_event(__unused__attribute__((__unused__)) int fd, __unused__attribute__((__unused__)) short events, __unused__attribute__((__unused__)) void *data) | |||
| 158 | { | |||
| 159 | struct timeval tv = { .tv_sec = 3600 }; | |||
| 160 | uint64_t t = get_timer(); | |||
| 161 | ||||
| 162 | format_tidy_jobs(); | |||
| 163 | ||||
| 164 | log_debug("%s: took %llu milliseconds", __func__, | |||
| 165 | (unsigned long long)(get_timer() - t)); | |||
| 166 | evtimer_add(&server_ev_tidy, &tv)event_add(&server_ev_tidy, &tv); | |||
| 167 | } | |||
| 168 | ||||
| 169 | /* Fork new server. */ | |||
| 170 | int | |||
| 171 | server_start(struct tmuxproc *client, int flags, struct event_base *base, | |||
| 172 | int lockfd, char *lockfile) | |||
| 173 | { | |||
| 174 | int fd; | |||
| ||||
| 175 | sigset_t set, oldset; | |||
| 176 | struct client *c = NULL((void *)0); | |||
| 177 | char *cause = NULL((void *)0); | |||
| 178 | struct timeval tv = { .tv_sec = 3600 }; | |||
| 179 | ||||
| 180 | sigfillset(&set); | |||
| 181 | sigprocmask(SIG_BLOCK1, &set, &oldset); | |||
| 182 | ||||
| 183 | if (~flags & CLIENT_NOFORK0x40000000) { | |||
| 184 | if (proc_fork_and_daemon(&fd) != 0) { | |||
| 185 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
| 186 | return (fd); | |||
| 187 | } | |||
| 188 | } | |||
| 189 | proc_clear_signals(client, 0); | |||
| 190 | server_client_flags = flags; | |||
| 191 | ||||
| 192 | if (event_reinit(base) != 0) | |||
| 193 | fatalx("event_reinit failed"); | |||
| 194 | server_proc = proc_start("server"); | |||
| 195 | ||||
| 196 | proc_set_signals(server_proc, server_signal); | |||
| 197 | sigprocmask(SIG_SETMASK3, &oldset, NULL((void *)0)); | |||
| 198 | ||||
| 199 | if (log_get_level() > 1) | |||
| 200 | tty_create_log(); | |||
| 201 | if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " | |||
| 202 | "tty ps", NULL((void *)0)) != 0) | |||
| 203 | fatal("pledge failed"); | |||
| 204 | ||||
| 205 | input_key_build(); | |||
| 206 | RB_INIT(&windows)do { (&windows)->rbh_root = ((void *)0); } while (0); | |||
| 207 | RB_INIT(&all_window_panes)do { (&all_window_panes)->rbh_root = ((void *)0); } while (0); | |||
| 208 | TAILQ_INIT(&clients)do { (&clients)->tqh_first = ((void *)0); (&clients )->tqh_last = &(&clients)->tqh_first; } while ( 0); | |||
| 209 | RB_INIT(&sessions)do { (&sessions)->rbh_root = ((void *)0); } while (0); | |||
| 210 | key_bindings_init(); | |||
| 211 | TAILQ_INIT(&message_log)do { (&message_log)->tqh_first = ((void *)0); (&message_log )->tqh_last = &(&message_log)->tqh_first; } while (0); | |||
| 212 | ||||
| 213 | gettimeofday(&start_time, NULL((void *)0)); | |||
| 214 | ||||
| 215 | server_fd = server_create_socket(flags, &cause); | |||
| 216 | if (server_fd != -1) | |||
| 217 | server_update_socket(); | |||
| 218 | if (~flags & CLIENT_NOFORK0x40000000) | |||
| 219 | c = server_client_create(fd); | |||
| ||||
| 220 | else | |||
| 221 | options_set_number(global_options, "exit-empty", 0); | |||
| 222 | ||||
| 223 | if (lockfd >= 0) { | |||
| 224 | unlink(lockfile); | |||
| 225 | free(lockfile); | |||
| 226 | close(lockfd); | |||
| 227 | } | |||
| 228 | ||||
| 229 | if (cause != NULL((void *)0)) { | |||
| 230 | if (c != NULL((void *)0)) { | |||
| 231 | cmdq_append(c, cmdq_get_error(cause)); | |||
| 232 | c->flags |= CLIENT_EXIT0x4; | |||
| 233 | } | |||
| 234 | free(cause); | |||
| 235 | } | |||
| 236 | ||||
| 237 | evtimer_set(&server_ev_tidy, server_tidy_event, NULL)event_set(&server_ev_tidy, -1, 0, server_tidy_event, ((void *)0)); | |||
| 238 | evtimer_add(&server_ev_tidy, &tv)event_add(&server_ev_tidy, &tv); | |||
| 239 | ||||
| 240 | server_add_accept(0); | |||
| 241 | proc_loop(server_proc, server_loop); | |||
| 242 | ||||
| 243 | job_kill_all(); | |||
| 244 | status_prompt_save_history(); | |||
| 245 | ||||
| 246 | exit(0); | |||
| 247 | } | |||
| 248 | ||||
| 249 | /* Server loop callback. */ | |||
| 250 | static int | |||
| 251 | server_loop(void) | |||
| 252 | { | |||
| 253 | struct client *c; | |||
| 254 | u_int items; | |||
| 255 | ||||
| 256 | do { | |||
| 257 | items = cmdq_next(NULL((void *)0)); | |||
| 258 | TAILQ_FOREACH(c, &clients, entry)for((c) = ((&clients)->tqh_first); (c) != ((void *)0); (c) = ((c)->entry.tqe_next)) { | |||
| 259 | if (c->flags & CLIENT_IDENTIFIED0x40000) | |||
| 260 | items += cmdq_next(c); | |||
| 261 | } | |||
| 262 | } while (items != 0); | |||
| 263 | ||||
| 264 | server_client_loop(); | |||
| 265 | ||||
| 266 | if (!options_get_number(global_options, "exit-empty") && !server_exit) | |||
| 267 | return (0); | |||
| 268 | ||||
| 269 | if (!options_get_number(global_options, "exit-unattached")) { | |||
| 270 | if (!RB_EMPTY(&sessions)((&sessions)->rbh_root == ((void *)0))) | |||
| 271 | return (0); | |||
| 272 | } | |||
| 273 | ||||
| 274 | TAILQ_FOREACH(c, &clients, entry)for((c) = ((&clients)->tqh_first); (c) != ((void *)0); (c) = ((c)->entry.tqe_next)) { | |||
| 275 | if (c->session != NULL((void *)0)) | |||
| 276 | return (0); | |||
| 277 | } | |||
| 278 | ||||
| 279 | /* | |||
| 280 | * No attached clients therefore want to exit - flush any waiting | |||
| 281 | * clients but don't actually exit until they've gone. | |||
| 282 | */ | |||
| 283 | cmd_wait_for_flush(); | |||
| 284 | if (!TAILQ_EMPTY(&clients)(((&clients)->tqh_first) == ((void *)0))) | |||
| 285 | return (0); | |||
| 286 | ||||
| 287 | if (job_still_running()) | |||
| 288 | return (0); | |||
| 289 | ||||
| 290 | return (1); | |||
| 291 | } | |||
| 292 | ||||
| 293 | /* Exit the server by killing all clients and windows. */ | |||
| 294 | static void | |||
| 295 | server_send_exit(void) | |||
| 296 | { | |||
| 297 | struct client *c, *c1; | |||
| 298 | struct session *s, *s1; | |||
| 299 | ||||
| 300 | cmd_wait_for_flush(); | |||
| 301 | ||||
| 302 | TAILQ_FOREACH_SAFE(c, &clients, entry, c1)for ((c) = ((&clients)->tqh_first); (c) != ((void *)0) && ((c1) = ((c)->entry.tqe_next), 1); (c) = (c1)) { | |||
| 303 | if (c->flags & CLIENT_SUSPENDED0x40) | |||
| 304 | server_client_lost(c); | |||
| 305 | else { | |||
| 306 | c->flags |= CLIENT_EXIT0x4; | |||
| 307 | c->exit_type = CLIENT_EXIT_SHUTDOWN; | |||
| 308 | } | |||
| 309 | c->session = NULL((void *)0); | |||
| 310 | } | |||
| 311 | ||||
| 312 | RB_FOREACH_SAFE(s, sessions, &sessions, s1)for ((s) = sessions_RB_MINMAX(&sessions, -1); ((s) != ((void *)0)) && ((s1) = sessions_RB_NEXT(s), 1); (s) = (s1) ) | |||
| 313 | session_destroy(s, 1, __func__); | |||
| 314 | } | |||
| 315 | ||||
| 316 | /* Update socket execute permissions based on whether sessions are attached. */ | |||
| 317 | void | |||
| 318 | server_update_socket(void) | |||
| 319 | { | |||
| 320 | struct session *s; | |||
| 321 | static int last = -1; | |||
| 322 | int n, mode; | |||
| 323 | struct stat sb; | |||
| 324 | ||||
| 325 | n = 0; | |||
| 326 | RB_FOREACH(s, sessions, &sessions)for ((s) = sessions_RB_MINMAX(&sessions, -1); (s) != ((void *)0); (s) = sessions_RB_NEXT(s)) { | |||
| 327 | if (s->attached != 0) { | |||
| 328 | n++; | |||
| 329 | break; | |||
| 330 | } | |||
| 331 | } | |||
| 332 | ||||
| 333 | if (n != last) { | |||
| 334 | last = n; | |||
| 335 | ||||
| 336 | if (stat(socket_path, &sb) != 0) | |||
| 337 | return; | |||
| 338 | mode = sb.st_mode & ACCESSPERMS(0000700|0000070|0000007); | |||
| 339 | if (n != 0) { | |||
| 340 | if (mode & S_IRUSR0000400) | |||
| 341 | mode |= S_IXUSR0000100; | |||
| 342 | if (mode & S_IRGRP0000040) | |||
| 343 | mode |= S_IXGRP0000010; | |||
| 344 | if (mode & S_IROTH0000004) | |||
| 345 | mode |= S_IXOTH0000001; | |||
| 346 | } else | |||
| 347 | mode &= ~(S_IXUSR0000100|S_IXGRP0000010|S_IXOTH0000001); | |||
| 348 | chmod(socket_path, mode); | |||
| 349 | } | |||
| 350 | } | |||
| 351 | ||||
| 352 | /* Callback for server socket. */ | |||
| 353 | static void | |||
| 354 | server_accept(int fd, short events, __unused__attribute__((__unused__)) void *data) | |||
| 355 | { | |||
| 356 | struct sockaddr_storage sa; | |||
| 357 | socklen_t slen = sizeof sa; | |||
| 358 | int newfd; | |||
| 359 | ||||
| 360 | server_add_accept(0); | |||
| 361 | if (!(events & EV_READ0x02)) | |||
| 362 | return; | |||
| 363 | ||||
| 364 | newfd = accept(fd, (struct sockaddr *) &sa, &slen); | |||
| 365 | if (newfd == -1) { | |||
| 366 | if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == EINTR4 || errno(*__errno()) == ECONNABORTED53) | |||
| 367 | return; | |||
| 368 | if (errno(*__errno()) == ENFILE23 || errno(*__errno()) == EMFILE24) { | |||
| 369 | /* Delete and don't try again for 1 second. */ | |||
| 370 | server_add_accept(1); | |||
| 371 | return; | |||
| 372 | } | |||
| 373 | fatal("accept failed"); | |||
| 374 | } | |||
| 375 | if (server_exit) { | |||
| 376 | close(newfd); | |||
| 377 | return; | |||
| 378 | } | |||
| 379 | server_client_create(newfd); | |||
| 380 | } | |||
| 381 | ||||
| 382 | /* | |||
| 383 | * Add accept event. If timeout is nonzero, add as a timeout instead of a read | |||
| 384 | * event - used to backoff when running out of file descriptors. | |||
| 385 | */ | |||
| 386 | void | |||
| 387 | server_add_accept(int timeout) | |||
| 388 | { | |||
| 389 | struct timeval tv = { timeout, 0 }; | |||
| 390 | ||||
| 391 | if (server_fd == -1) | |||
| 392 | return; | |||
| 393 | ||||
| 394 | if (event_initialized(&server_ev_accept)((&server_ev_accept)->ev_flags & 0x80)) | |||
| 395 | event_del(&server_ev_accept); | |||
| 396 | ||||
| 397 | if (timeout == 0) { | |||
| 398 | event_set(&server_ev_accept, server_fd, EV_READ0x02, server_accept, | |||
| 399 | NULL((void *)0)); | |||
| 400 | event_add(&server_ev_accept, NULL((void *)0)); | |||
| 401 | } else { | |||
| 402 | event_set(&server_ev_accept, server_fd, EV_TIMEOUT0x01, | |||
| 403 | server_accept, NULL((void *)0)); | |||
| 404 | event_add(&server_ev_accept, &tv); | |||
| 405 | } | |||
| 406 | } | |||
| 407 | ||||
| 408 | /* Signal handler. */ | |||
| 409 | static void | |||
| 410 | server_signal(int sig) | |||
| 411 | { | |||
| 412 | int fd; | |||
| 413 | ||||
| 414 | log_debug("%s: %s", __func__, strsignal(sig)); | |||
| 415 | switch (sig) { | |||
| 416 | case SIGINT2: | |||
| 417 | case SIGTERM15: | |||
| 418 | server_exit = 1; | |||
| 419 | server_send_exit(); | |||
| 420 | break; | |||
| 421 | case SIGCHLD20: | |||
| 422 | server_child_signal(); | |||
| 423 | break; | |||
| 424 | case SIGUSR130: | |||
| 425 | event_del(&server_ev_accept); | |||
| 426 | fd = server_create_socket(server_client_flags, NULL((void *)0)); | |||
| 427 | if (fd != -1) { | |||
| 428 | close(server_fd); | |||
| 429 | server_fd = fd; | |||
| 430 | server_update_socket(); | |||
| 431 | } | |||
| 432 | server_add_accept(0); | |||
| 433 | break; | |||
| 434 | case SIGUSR231: | |||
| 435 | proc_toggle_log(server_proc); | |||
| 436 | break; | |||
| 437 | } | |||
| 438 | } | |||
| 439 | ||||
| 440 | /* Handle SIGCHLD. */ | |||
| 441 | static void | |||
| 442 | server_child_signal(void) | |||
| 443 | { | |||
| 444 | int status; | |||
| 445 | pid_t pid; | |||
| 446 | ||||
| 447 | for (;;) { | |||
| 448 | switch (pid = waitpid(WAIT_ANY(-1), &status, WNOHANG1|WUNTRACED2)) { | |||
| 449 | case -1: | |||
| 450 | if (errno(*__errno()) == ECHILD10) | |||
| 451 | return; | |||
| 452 | fatal("waitpid failed"); | |||
| 453 | case 0: | |||
| 454 | return; | |||
| 455 | } | |||
| 456 | if (WIFSTOPPED(status)(((status) & 0xff) == 0177)) | |||
| 457 | server_child_stopped(pid, status); | |||
| 458 | else if (WIFEXITED(status)(((status) & 0177) == 0) || WIFSIGNALED(status)(((status) & 0177) != 0177 && ((status) & 0177 ) != 0)) | |||
| 459 | server_child_exited(pid, status); | |||
| 460 | } | |||
| 461 | } | |||
| 462 | ||||
| 463 | /* Handle exited children. */ | |||
| 464 | static void | |||
| 465 | server_child_exited(pid_t pid, int status) | |||
| 466 | { | |||
| 467 | struct window *w, *w1; | |||
| 468 | struct window_pane *wp; | |||
| 469 | ||||
| 470 | RB_FOREACH_SAFE(w, windows, &windows, w1)for ((w) = windows_RB_MINMAX(&windows, -1); ((w) != ((void *)0)) && ((w1) = windows_RB_NEXT(w), 1); (w) = (w1)) { | |||
| 471 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
| 472 | if (wp->pid == pid) { | |||
| 473 | wp->status = status; | |||
| 474 | wp->flags |= PANE_STATUSREADY0x200; | |||
| 475 | ||||
| 476 | log_debug("%%%u exited", wp->id); | |||
| 477 | wp->flags |= PANE_EXITED0x100; | |||
| 478 | ||||
| 479 | if (window_pane_destroy_ready(wp)) | |||
| 480 | server_destroy_pane(wp, 1); | |||
| 481 | break; | |||
| 482 | } | |||
| 483 | } | |||
| 484 | } | |||
| 485 | job_check_died(pid, status); | |||
| 486 | } | |||
| 487 | ||||
| 488 | /* Handle stopped children. */ | |||
| 489 | static void | |||
| 490 | server_child_stopped(pid_t pid, int status) | |||
| 491 | { | |||
| 492 | struct window *w; | |||
| 493 | struct window_pane *wp; | |||
| 494 | ||||
| 495 | if (WSTOPSIG(status)(int)(((unsigned)(status) >> 8) & 0xff) == SIGTTIN21 || WSTOPSIG(status)(int)(((unsigned)(status) >> 8) & 0xff) == SIGTTOU22) | |||
| 496 | return; | |||
| 497 | ||||
| 498 | RB_FOREACH(w, windows, &windows)for ((w) = windows_RB_MINMAX(&windows, -1); (w) != ((void *)0); (w) = windows_RB_NEXT(w)) { | |||
| 499 | TAILQ_FOREACH(wp, &w->panes, entry)for((wp) = ((&w->panes)->tqh_first); (wp) != ((void *)0); (wp) = ((wp)->entry.tqe_next)) { | |||
| 500 | if (wp->pid == pid) { | |||
| 501 | if (killpg(pid, SIGCONT19) != 0) | |||
| 502 | kill(pid, SIGCONT19); | |||
| 503 | } | |||
| 504 | } | |||
| 505 | } | |||
| 506 | job_check_died(pid, status); | |||
| 507 | } | |||
| 508 | ||||
| 509 | /* Add to message log. */ | |||
| 510 | void | |||
| 511 | server_add_message(const char *fmt, ...) | |||
| 512 | { | |||
| 513 | struct message_entry *msg, *msg1; | |||
| 514 | char *s; | |||
| 515 | va_list ap; | |||
| 516 | u_int limit; | |||
| 517 | ||||
| 518 | va_start(ap, fmt)__builtin_va_start(ap, fmt); | |||
| 519 | xvasprintf(&s, fmt, ap); | |||
| 520 | va_end(ap)__builtin_va_end(ap); | |||
| 521 | ||||
| 522 | log_debug("message: %s", s); | |||
| 523 | ||||
| 524 | msg = xcalloc(1, sizeof *msg); | |||
| 525 | gettimeofday(&msg->msg_time, NULL((void *)0)); | |||
| 526 | msg->msg_num = message_next++; | |||
| 527 | msg->msg = s; | |||
| 528 | TAILQ_INSERT_TAIL(&message_log, msg, entry)do { (msg)->entry.tqe_next = ((void *)0); (msg)->entry. tqe_prev = (&message_log)->tqh_last; *(&message_log )->tqh_last = (msg); (&message_log)->tqh_last = & (msg)->entry.tqe_next; } while (0); | |||
| 529 | ||||
| 530 | limit = options_get_number(global_options, "message-limit"); | |||
| 531 | TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1)for ((msg) = ((&message_log)->tqh_first); (msg) != ((void *)0) && ((msg1) = ((msg)->entry.tqe_next), 1); (msg ) = (msg1)) { | |||
| 532 | if (msg->msg_num + limit >= message_next) | |||
| 533 | break; | |||
| 534 | free(msg->msg); | |||
| 535 | TAILQ_REMOVE(&message_log, msg, entry)do { if (((msg)->entry.tqe_next) != ((void *)0)) (msg)-> entry.tqe_next->entry.tqe_prev = (msg)->entry.tqe_prev; else (&message_log)->tqh_last = (msg)->entry.tqe_prev ; *(msg)->entry.tqe_prev = (msg)->entry.tqe_next; ; ; } while (0); | |||
| 536 | free(msg); | |||
| 537 | } | |||
| 538 | } |