1*7519eda3Snicm /* $OpenBSD: server.c,v 1.155 2015/12/15 00:00:01 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> 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; 477724d0b0Snicm int server_fd; 484d9325ceSnicm int server_exit; 497724d0b0Snicm struct event server_ev_accept; 50b7fa064bSnicm 51*7519eda3Snicm struct cmd_find_state marked_pane; 522986c025Snicm 53311827fbSnicm int server_create_socket(void); 545b8ac713Snicm int server_loop(void); 554d9325ceSnicm int server_should_exit(void); 564d9325ceSnicm void server_send_exit(void); 575b8ac713Snicm void server_accept(int, short, void *); 585b8ac713Snicm void server_signal(int); 597724d0b0Snicm void server_child_signal(void); 607724d0b0Snicm void server_child_exited(pid_t, int); 617724d0b0Snicm void server_child_stopped(pid_t, int); 62b7fa064bSnicm 632986c025Snicm /* Set marked pane. */ 642986c025Snicm void 652986c025Snicm server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 662986c025Snicm { 67*7519eda3Snicm cmd_find_clear_state(&marked_pane, NULL, 0); 68*7519eda3Snicm marked_pane.s = s; 69*7519eda3Snicm marked_pane.wl = wl; 70*7519eda3Snicm marked_pane.w = wl->window; 71*7519eda3Snicm marked_pane.wp = wp; 722986c025Snicm } 732986c025Snicm 742986c025Snicm /* Clear marked pane. */ 752986c025Snicm void 762986c025Snicm server_clear_marked(void) 772986c025Snicm { 78*7519eda3Snicm cmd_find_clear_state(&marked_pane, NULL, 0); 792986c025Snicm } 802986c025Snicm 812986c025Snicm /* Is this the marked pane? */ 822986c025Snicm int 832986c025Snicm server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) 842986c025Snicm { 852986c025Snicm if (s == NULL || wl == NULL || wp == NULL) 862986c025Snicm return (0); 87*7519eda3Snicm if (marked_pane.s != s || marked_pane.wl != wl) 882986c025Snicm return (0); 89*7519eda3Snicm if (marked_pane.wp != wp) 902986c025Snicm return (0); 912986c025Snicm return (server_check_marked()); 922986c025Snicm } 932986c025Snicm 942986c025Snicm /* Check if the marked pane is still valid. */ 952986c025Snicm int 962986c025Snicm server_check_marked(void) 972986c025Snicm { 98*7519eda3Snicm return (cmd_find_valid_state(&marked_pane)); 992986c025Snicm } 1002986c025Snicm 10189654e7cSnicm /* Create server socket. */ 10289654e7cSnicm int 10389654e7cSnicm server_create_socket(void) 104311827fbSnicm { 10589654e7cSnicm struct sockaddr_un sa; 10689654e7cSnicm size_t size; 10789654e7cSnicm mode_t mask; 108a0ee4254Snicm int fd; 10989654e7cSnicm 11089654e7cSnicm memset(&sa, 0, sizeof sa); 11189654e7cSnicm sa.sun_family = AF_UNIX; 11289654e7cSnicm size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path); 11389654e7cSnicm if (size >= sizeof sa.sun_path) { 11489654e7cSnicm errno = ENAMETOOLONG; 115d53cf33dSnicm return (-1); 11689654e7cSnicm } 11789654e7cSnicm unlink(sa.sun_path); 11889654e7cSnicm 11989654e7cSnicm if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 120d53cf33dSnicm return (-1); 12189654e7cSnicm 1227394227cSnicm mask = umask(S_IXUSR|S_IXGRP|S_IRWXO); 123fbc54f53Sguenther if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) 124d53cf33dSnicm return (-1); 12589654e7cSnicm umask(mask); 12689654e7cSnicm 12789654e7cSnicm if (listen(fd, 16) == -1) 128d53cf33dSnicm return (-1); 129a0ee4254Snicm setblocking(fd, 0); 130311827fbSnicm 13189654e7cSnicm return (fd); 13289654e7cSnicm } 133311827fbSnicm 134311827fbSnicm /* Fork new server. */ 135311827fbSnicm int 1367b560ed5Snicm server_start(struct event_base *base, int lockfd, char *lockfile) 137311827fbSnicm { 1384e7bd783Snicm int pair[2]; 139311827fbSnicm 140311827fbSnicm if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0) 141311827fbSnicm fatal("socketpair failed"); 142311827fbSnicm 1435b8ac713Snicm server_proc = proc_start("server", base, 1, server_signal); 1445b8ac713Snicm if (server_proc == NULL) { 145311827fbSnicm close(pair[1]); 146311827fbSnicm return (pair[0]); 147311827fbSnicm } 148311827fbSnicm close(pair[0]); 149311827fbSnicm 1500a607e68Snicm if (log_get_level() > 3) 1516d2e9b46Snicm tty_create_log(); 152cb19d99cSnicm if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec " 153cb19d99cSnicm "tty ps", NULL) != 0) 1544e7a7adeSnicm fatal("pledge failed"); 1554e7a7adeSnicm 156ae69181dSnicm RB_INIT(&windows); 1573e0f2533Snicm RB_INIT(&all_window_panes); 15882873134Snicm TAILQ_INIT(&clients); 15959996dc3Snicm RB_INIT(&sessions); 16001b2421eSnicm TAILQ_INIT(&session_groups); 16168895571Snicm mode_key_init_trees(); 162311827fbSnicm key_bindings_init(); 163311827fbSnicm 1648c1ade38Snicm gettimeofday(&start_time, NULL); 165311827fbSnicm 1667724d0b0Snicm server_fd = server_create_socket(); 167d53cf33dSnicm if (server_fd == -1) 168d53cf33dSnicm fatal("couldn't create socket"); 169d53cf33dSnicm server_update_socket(); 17089654e7cSnicm server_client_create(pair[1]); 171311827fbSnicm 1725c705c36Snicm if (lockfd >= 0) { 173549634dfSnicm unlink(lockfile); 1747d053cf9Snicm free(lockfile); 175549634dfSnicm close(lockfd); 1765c705c36Snicm } 177549634dfSnicm 17884cce0efSnicm start_cfg(); 179175d36ccSnicm 180179ef399Snicm status_prompt_load_history(); 1810cd9638eSnicm 182a06e463cSnicm server_add_accept(0); 1837724d0b0Snicm 1845b8ac713Snicm proc_loop(server_proc, server_loop); 185179ef399Snicm status_prompt_save_history(); 1867724d0b0Snicm exit(0); 187311827fbSnicm } 188311827fbSnicm 1895b8ac713Snicm /* Server loop callback. */ 1905b8ac713Snicm int 1917724d0b0Snicm server_loop(void) 192311827fbSnicm { 1935b8ac713Snicm struct client *c; 194311827fbSnicm 19589654e7cSnicm server_client_loop(); 19630da262fSnicm 197d89252e5Snicm if (!options_get_number(global_options, "exit-unattached")) { 19859996dc3Snicm if (!RB_EMPTY(&sessions)) 19930da262fSnicm return (0); 20030da262fSnicm } 2017d5939e2Snicm 20282873134Snicm TAILQ_FOREACH(c, &clients, entry) { 20382873134Snicm if (c->session != NULL) 2047d5939e2Snicm return (0); 2057d5939e2Snicm } 2067d5939e2Snicm 2077d5939e2Snicm /* 2087d5939e2Snicm * No attached clients therefore want to exit - flush any waiting 2097d5939e2Snicm * clients but don't actually exit until they've gone. 2107d5939e2Snicm */ 2117d5939e2Snicm cmd_wait_for_flush(); 21282873134Snicm if (!TAILQ_EMPTY(&clients)) 21330da262fSnicm return (0); 2147d5939e2Snicm 21530da262fSnicm return (1); 216311827fbSnicm } 217311827fbSnicm 2184d9325ceSnicm /* Exit the server by killing all clients and windows. */ 219311827fbSnicm void 2204d9325ceSnicm server_send_exit(void) 221311827fbSnicm { 22282873134Snicm struct client *c, *c1; 22382873134Snicm struct session *s, *s1; 224311827fbSnicm 2257d5939e2Snicm cmd_wait_for_flush(); 2267d5939e2Snicm 22782873134Snicm TAILQ_FOREACH_SAFE(c, &clients, entry, c1) { 2285b8ac713Snicm if (c->flags & CLIENT_SUSPENDED) 2297724d0b0Snicm server_client_lost(c); 2307724d0b0Snicm else 2315b8ac713Snicm proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0); 2327724d0b0Snicm c->session = NULL; 2338148c7feSnicm } 234311827fbSnicm 23582873134Snicm RB_FOREACH_SAFE(s, sessions, &sessions, s1) 2367724d0b0Snicm session_destroy(s); 237311827fbSnicm } 238311827fbSnicm 2397724d0b0Snicm /* Update socket execute permissions based on whether sessions are attached. */ 24037dfd443Snicm void 2417724d0b0Snicm server_update_socket(void) 2427724d0b0Snicm { 2437724d0b0Snicm struct session *s; 2447724d0b0Snicm static int last = -1; 2456d9987f5Snicm int n, mode; 2466d9987f5Snicm struct stat sb; 2477724d0b0Snicm 2487724d0b0Snicm n = 0; 24959996dc3Snicm RB_FOREACH(s, sessions, &sessions) { 25059996dc3Snicm if (!(s->flags & SESSION_UNATTACHED)) { 2517724d0b0Snicm n++; 2527724d0b0Snicm break; 2537724d0b0Snicm } 2547724d0b0Snicm } 2557724d0b0Snicm 2567724d0b0Snicm if (n != last) { 2577724d0b0Snicm last = n; 2586d9987f5Snicm 2596d9987f5Snicm if (stat(socket_path, &sb) != 0) 2606d9987f5Snicm return; 2616d9987f5Snicm mode = sb.st_mode; 2626d9987f5Snicm if (n != 0) { 2636d9987f5Snicm if (mode & S_IRUSR) 2646d9987f5Snicm mode |= S_IXUSR; 2656d9987f5Snicm if (mode & S_IRGRP) 2666d9987f5Snicm mode |= S_IXGRP; 2676d9987f5Snicm if (mode & S_IROTH) 2686d9987f5Snicm mode |= S_IXOTH; 2696d9987f5Snicm } else 2706d9987f5Snicm mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH); 2716d9987f5Snicm chmod(socket_path, mode); 2727724d0b0Snicm } 2737724d0b0Snicm } 2747724d0b0Snicm 2757724d0b0Snicm /* Callback for server socket. */ 276311827fbSnicm void 277d0e2e7f1Snicm server_accept(int fd, short events, __unused void *data) 2787724d0b0Snicm { 2797724d0b0Snicm struct sockaddr_storage sa; 2807724d0b0Snicm socklen_t slen = sizeof sa; 2817724d0b0Snicm int newfd; 2827724d0b0Snicm 283a06e463cSnicm server_add_accept(0); 2847724d0b0Snicm if (!(events & EV_READ)) 2857724d0b0Snicm return; 2867724d0b0Snicm 2877724d0b0Snicm newfd = accept(fd, (struct sockaddr *) &sa, &slen); 2887724d0b0Snicm if (newfd == -1) { 2897724d0b0Snicm if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED) 2907724d0b0Snicm return; 291a06e463cSnicm if (errno == ENFILE || errno == EMFILE) { 292a06e463cSnicm /* Delete and don't try again for 1 second. */ 293a06e463cSnicm server_add_accept(1); 294a06e463cSnicm return; 295a06e463cSnicm } 2967724d0b0Snicm fatal("accept failed"); 2977724d0b0Snicm } 2984d9325ceSnicm if (server_exit) { 2997724d0b0Snicm close(newfd); 3007724d0b0Snicm return; 3017724d0b0Snicm } 3027724d0b0Snicm server_client_create(newfd); 3037724d0b0Snicm } 3047724d0b0Snicm 305a06e463cSnicm /* 306a06e463cSnicm * Add accept event. If timeout is nonzero, add as a timeout instead of a read 307a06e463cSnicm * event - used to backoff when running out of file descriptors. 308a06e463cSnicm */ 309a06e463cSnicm void 310a06e463cSnicm server_add_accept(int timeout) 311a06e463cSnicm { 312a06e463cSnicm struct timeval tv = { timeout, 0 }; 313a06e463cSnicm 314a06e463cSnicm if (event_initialized(&server_ev_accept)) 315a06e463cSnicm event_del(&server_ev_accept); 316a06e463cSnicm 317a06e463cSnicm if (timeout == 0) { 3185b8ac713Snicm event_set(&server_ev_accept, server_fd, EV_READ, server_accept, 3195b8ac713Snicm NULL); 320a06e463cSnicm event_add(&server_ev_accept, NULL); 321a06e463cSnicm } else { 3225b8ac713Snicm event_set(&server_ev_accept, server_fd, EV_TIMEOUT, 3235b8ac713Snicm server_accept, NULL); 324a06e463cSnicm event_add(&server_ev_accept, &tv); 325a06e463cSnicm } 326a06e463cSnicm } 327a06e463cSnicm 3287724d0b0Snicm /* Signal handler. */ 3297724d0b0Snicm void 3305b8ac713Snicm server_signal(int sig) 3317724d0b0Snicm { 332d53cf33dSnicm int fd; 3335235cd7fSnicm 3347724d0b0Snicm switch (sig) { 3357724d0b0Snicm case SIGTERM: 3364d9325ceSnicm server_exit = 1; 3374d9325ceSnicm server_send_exit(); 3387724d0b0Snicm break; 3397724d0b0Snicm case SIGCHLD: 3407724d0b0Snicm server_child_signal(); 3417724d0b0Snicm break; 3427724d0b0Snicm case SIGUSR1: 3437724d0b0Snicm event_del(&server_ev_accept); 344d53cf33dSnicm fd = server_create_socket(); 345d53cf33dSnicm if (fd != -1) { 3467724d0b0Snicm close(server_fd); 347d53cf33dSnicm server_fd = fd; 348d53cf33dSnicm server_update_socket(); 349d53cf33dSnicm } 350a06e463cSnicm server_add_accept(0); 3517724d0b0Snicm break; 3527724d0b0Snicm } 3537724d0b0Snicm } 3547724d0b0Snicm 3557724d0b0Snicm /* Handle SIGCHLD. */ 3567724d0b0Snicm void 3577724d0b0Snicm server_child_signal(void) 3587724d0b0Snicm { 3597724d0b0Snicm int status; 3607724d0b0Snicm pid_t pid; 3617724d0b0Snicm 3627724d0b0Snicm for (;;) { 3637724d0b0Snicm switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) { 3647724d0b0Snicm case -1: 3657724d0b0Snicm if (errno == ECHILD) 3667724d0b0Snicm return; 3677724d0b0Snicm fatal("waitpid failed"); 3687724d0b0Snicm case 0: 3697724d0b0Snicm return; 3707724d0b0Snicm } 3717724d0b0Snicm if (WIFSTOPPED(status)) 3727724d0b0Snicm server_child_stopped(pid, status); 373a236b623Snicm else if (WIFEXITED(status) || WIFSIGNALED(status)) 3747724d0b0Snicm server_child_exited(pid, status); 3757724d0b0Snicm } 3767724d0b0Snicm } 3777724d0b0Snicm 3787724d0b0Snicm /* Handle exited children. */ 3797724d0b0Snicm void 3807724d0b0Snicm server_child_exited(pid_t pid, int status) 381311827fbSnicm { 382ae69181dSnicm struct window *w, *w1; 38389654e7cSnicm struct window_pane *wp; 3847724d0b0Snicm struct job *job; 3857724d0b0Snicm 386ae69181dSnicm RB_FOREACH_SAFE(w, windows, &windows, w1) { 3877724d0b0Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 3887724d0b0Snicm if (wp->pid == pid) { 3893e459475Snicm wp->status = status; 3907c6e570cSnicm server_destroy_pane(wp); 3917c6e570cSnicm break; 3927724d0b0Snicm } 3937724d0b0Snicm } 3947724d0b0Snicm } 3957724d0b0Snicm 3960a271d7eSnicm LIST_FOREACH(job, &all_jobs, lentry) { 3977724d0b0Snicm if (pid == job->pid) { 39824abd014Snicm job_died(job, status); /* might free job */ 39924abd014Snicm break; 4007724d0b0Snicm } 4017724d0b0Snicm } 4027724d0b0Snicm } 4037724d0b0Snicm 4047724d0b0Snicm /* Handle stopped children. */ 4057724d0b0Snicm void 4067724d0b0Snicm server_child_stopped(pid_t pid, int status) 4077724d0b0Snicm { 4087724d0b0Snicm struct window *w; 4097724d0b0Snicm struct window_pane *wp; 4107724d0b0Snicm 4117724d0b0Snicm if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 4127724d0b0Snicm return; 4137724d0b0Snicm 414ae69181dSnicm RB_FOREACH(w, windows, &windows) { 4157724d0b0Snicm TAILQ_FOREACH(wp, &w->panes, entry) { 4167724d0b0Snicm if (wp->pid == pid) { 4177724d0b0Snicm if (killpg(pid, SIGCONT) != 0) 4187724d0b0Snicm kill(pid, SIGCONT); 4197724d0b0Snicm } 4207724d0b0Snicm } 4217724d0b0Snicm } 4227724d0b0Snicm } 423