"Fossies" - the Fresh Open Source Software Archive

Member "tmux-3.2a/server.c" (10 Jun 2021, 11451 Bytes) of package /linux/misc/tmux-3.2a.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "server.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.2_vs_3.2a.

    1 /* $OpenBSD$ */
    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.h>
   27 #include <fcntl.h>
   28 #include <signal.h>
   29 #include <stdio.h>
   30 #include <stdlib.h>
   31 #include <string.h>
   32 #include <termios.h>
   33 #include <time.h>
   34 #include <unistd.h>
   35 
   36 #include "tmux.h"
   37 
   38 /*
   39  * Main server functions.
   40  */
   41 
   42 struct clients       clients;
   43 
   44 struct tmuxproc     *server_proc;
   45 static int       server_fd = -1;
   46 static uint64_t      server_client_flags;
   47 static int       server_exit;
   48 static struct event  server_ev_accept;
   49 static struct event  server_ev_tidy;
   50 
   51 struct cmd_find_state    marked_pane;
   52 
   53 static u_int         message_next;
   54 struct message_list  message_log;
   55 
   56 static int  server_loop(void);
   57 static void server_send_exit(void);
   58 static void server_accept(int, short, void *);
   59 static void server_signal(int);
   60 static void server_child_signal(void);
   61 static void server_child_exited(pid_t, int);
   62 static void server_child_stopped(pid_t, int);
   63 
   64 /* Set marked pane. */
   65 void
   66 server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
   67 {
   68     cmd_find_clear_state(&marked_pane, 0);
   69     marked_pane.s = s;
   70     marked_pane.wl = wl;
   71     marked_pane.w = wl->window;
   72     marked_pane.wp = wp;
   73 }
   74 
   75 /* Clear marked pane. */
   76 void
   77 server_clear_marked(void)
   78 {
   79     cmd_find_clear_state(&marked_pane, 0);
   80 }
   81 
   82 /* Is this the marked pane? */
   83 int
   84 server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
   85 {
   86     if (s == NULL || wl == NULL || wp == NULL)
   87         return (0);
   88     if (marked_pane.s != s || marked_pane.wl != wl)
   89         return (0);
   90     if (marked_pane.wp != wp)
   91         return (0);
   92     return (server_check_marked());
   93 }
   94 
   95 /* Check if the marked pane is still valid. */
   96 int
   97 server_check_marked(void)
   98 {
   99     return (cmd_find_valid_state(&marked_pane));
  100 }
  101 
  102 /* Create server socket. */
  103 static int
  104 server_create_socket(int flags, char **cause)
  105 {
  106     struct sockaddr_un  sa;
  107     size_t          size;
  108     mode_t          mask;
  109     int         fd, saved_errno;
  110 
  111     memset(&sa, 0, sizeof sa);
  112     sa.sun_family = AF_UNIX;
  113     size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
  114     if (size >= sizeof sa.sun_path) {
  115         errno = ENAMETOOLONG;
  116         goto fail;
  117     }
  118     unlink(sa.sun_path);
  119 
  120     if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
  121         goto fail;
  122 
  123     if (flags & CLIENT_DEFAULTSOCKET)
  124         mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
  125     else
  126         mask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
  127     if (bind(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
  128         saved_errno = errno;
  129         close(fd);
  130         errno = saved_errno;
  131         goto fail;
  132     }
  133     umask(mask);
  134 
  135     if (listen(fd, 128) == -1) {
  136         saved_errno = errno;
  137         close(fd);
  138         errno = saved_errno;
  139         goto fail;
  140     }
  141     setblocking(fd, 0);
  142 
  143     return (fd);
  144 
  145 fail:
  146     if (cause != NULL) {
  147         xasprintf(cause, "error creating %s (%s)", socket_path,
  148             strerror(errno));
  149     }
  150     return (-1);
  151 }
  152 
  153 /* Tidy up every hour. */
  154 static void
  155 server_tidy_event(__unused int fd, __unused short events, __unused void *data)
  156 {
  157     struct timeval  tv = { .tv_sec = 3600 };
  158     uint64_t        t = get_timer();
  159 
  160     format_tidy_jobs();
  161 
  162 #ifdef HAVE_MALLOC_TRIM
  163     malloc_trim(0);
  164 #endif
  165 
  166     log_debug("%s: took %llu milliseconds", __func__,
  167         (unsigned long long)(get_timer() - t));
  168     evtimer_add(&server_ev_tidy, &tv);
  169 }
  170 
  171 /* Fork new server. */
  172 int
  173 server_start(struct tmuxproc *client, int flags, struct event_base *base,
  174     int lockfd, char *lockfile)
  175 {
  176     int      fd;
  177     sigset_t     set, oldset;
  178     struct client   *c = NULL;
  179     char        *cause = NULL;
  180     struct timeval   tv = { .tv_sec = 3600 };
  181 
  182     sigfillset(&set);
  183     sigprocmask(SIG_BLOCK, &set, &oldset);
  184 
  185     if (~flags & CLIENT_NOFORK) {
  186         if (proc_fork_and_daemon(&fd) != 0) {
  187             sigprocmask(SIG_SETMASK, &oldset, NULL);
  188             return (fd);
  189         }
  190     }
  191     proc_clear_signals(client, 0);
  192     server_client_flags = flags;
  193 
  194     if (event_reinit(base) != 0)
  195         fatalx("event_reinit failed");
  196     server_proc = proc_start("server");
  197 
  198     proc_set_signals(server_proc, server_signal);
  199     sigprocmask(SIG_SETMASK, &oldset, NULL);
  200 
  201     if (log_get_level() > 1)
  202         tty_create_log();
  203     if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
  204         "tty ps", NULL) != 0)
  205         fatal("pledge failed");
  206 
  207     input_key_build();
  208     RB_INIT(&windows);
  209     RB_INIT(&all_window_panes);
  210     TAILQ_INIT(&clients);
  211     RB_INIT(&sessions);
  212     key_bindings_init();
  213     TAILQ_INIT(&message_log);
  214 
  215     gettimeofday(&start_time, NULL);
  216 
  217     server_fd = server_create_socket(flags, &cause);
  218     if (server_fd != -1)
  219         server_update_socket();
  220     if (~flags & CLIENT_NOFORK)
  221         c = server_client_create(fd);
  222     else
  223         options_set_number(global_options, "exit-empty", 0);
  224 
  225     if (lockfd >= 0) {
  226         unlink(lockfile);
  227         free(lockfile);
  228         close(lockfd);
  229     }
  230 
  231     if (cause != NULL) {
  232         if (c != NULL) {
  233             cmdq_append(c, cmdq_get_error(cause));
  234             c->flags |= CLIENT_EXIT;
  235         }
  236         free(cause);
  237     }
  238 
  239     evtimer_set(&server_ev_tidy, server_tidy_event, NULL);
  240     evtimer_add(&server_ev_tidy, &tv);
  241 
  242     server_add_accept(0);
  243     proc_loop(server_proc, server_loop);
  244 
  245     job_kill_all();
  246     status_prompt_save_history();
  247 
  248     exit(0);
  249 }
  250 
  251 /* Server loop callback. */
  252 static int
  253 server_loop(void)
  254 {
  255     struct client   *c;
  256     u_int        items;
  257 
  258     do {
  259         items = cmdq_next(NULL);
  260         TAILQ_FOREACH(c, &clients, entry) {
  261             if (c->flags & CLIENT_IDENTIFIED)
  262                 items += cmdq_next(c);
  263         }
  264     } while (items != 0);
  265 
  266     server_client_loop();
  267 
  268     if (!options_get_number(global_options, "exit-empty") && !server_exit)
  269         return (0);
  270 
  271     if (!options_get_number(global_options, "exit-unattached")) {
  272         if (!RB_EMPTY(&sessions))
  273             return (0);
  274     }
  275 
  276     TAILQ_FOREACH(c, &clients, entry) {
  277         if (c->session != NULL)
  278             return (0);
  279     }
  280 
  281     /*
  282      * No attached clients therefore want to exit - flush any waiting
  283      * clients but don't actually exit until they've gone.
  284      */
  285     cmd_wait_for_flush();
  286     if (!TAILQ_EMPTY(&clients))
  287         return (0);
  288 
  289     if (job_still_running())
  290         return (0);
  291 
  292     return (1);
  293 }
  294 
  295 /* Exit the server by killing all clients and windows. */
  296 static void
  297 server_send_exit(void)
  298 {
  299     struct client   *c, *c1;
  300     struct session  *s, *s1;
  301 
  302     cmd_wait_for_flush();
  303 
  304     TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
  305         if (c->flags & CLIENT_SUSPENDED)
  306             server_client_lost(c);
  307         else {
  308             c->flags |= CLIENT_EXIT;
  309             c->exit_type = CLIENT_EXIT_SHUTDOWN;
  310         }
  311         c->session = NULL;
  312     }
  313 
  314     RB_FOREACH_SAFE(s, sessions, &sessions, s1)
  315         session_destroy(s, 1, __func__);
  316 }
  317 
  318 /* Update socket execute permissions based on whether sessions are attached. */
  319 void
  320 server_update_socket(void)
  321 {
  322     struct session  *s;
  323     static int   last = -1;
  324     int      n, mode;
  325     struct stat      sb;
  326 
  327     n = 0;
  328     RB_FOREACH(s, sessions, &sessions) {
  329         if (s->attached != 0) {
  330             n++;
  331             break;
  332         }
  333     }
  334 
  335     if (n != last) {
  336         last = n;
  337 
  338         if (stat(socket_path, &sb) != 0)
  339             return;
  340         mode = sb.st_mode & ACCESSPERMS;
  341         if (n != 0) {
  342             if (mode & S_IRUSR)
  343                 mode |= S_IXUSR;
  344             if (mode & S_IRGRP)
  345                 mode |= S_IXGRP;
  346             if (mode & S_IROTH)
  347                 mode |= S_IXOTH;
  348         } else
  349             mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
  350         chmod(socket_path, mode);
  351     }
  352 }
  353 
  354 /* Callback for server socket. */
  355 static void
  356 server_accept(int fd, short events, __unused void *data)
  357 {
  358     struct sockaddr_storage sa;
  359     socklen_t       slen = sizeof sa;
  360     int         newfd;
  361 
  362     server_add_accept(0);
  363     if (!(events & EV_READ))
  364         return;
  365 
  366     newfd = accept(fd, (struct sockaddr *) &sa, &slen);
  367     if (newfd == -1) {
  368         if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
  369             return;
  370         if (errno == ENFILE || errno == EMFILE) {
  371             /* Delete and don't try again for 1 second. */
  372             server_add_accept(1);
  373             return;
  374         }
  375         fatal("accept failed");
  376     }
  377     if (server_exit) {
  378         close(newfd);
  379         return;
  380     }
  381     server_client_create(newfd);
  382 }
  383 
  384 /*
  385  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
  386  * event - used to backoff when running out of file descriptors.
  387  */
  388 void
  389 server_add_accept(int timeout)
  390 {
  391     struct timeval tv = { timeout, 0 };
  392 
  393     if (server_fd == -1)
  394         return;
  395 
  396     if (event_initialized(&server_ev_accept))
  397         event_del(&server_ev_accept);
  398 
  399     if (timeout == 0) {
  400         event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
  401             NULL);
  402         event_add(&server_ev_accept, NULL);
  403     } else {
  404         event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
  405             server_accept, NULL);
  406         event_add(&server_ev_accept, &tv);
  407     }
  408 }
  409 
  410 /* Signal handler. */
  411 static void
  412 server_signal(int sig)
  413 {
  414     int fd;
  415 
  416     log_debug("%s: %s", __func__, strsignal(sig));
  417     switch (sig) {
  418     case SIGINT:
  419     case SIGTERM:
  420         server_exit = 1;
  421         server_send_exit();
  422         break;
  423     case SIGCHLD:
  424         server_child_signal();
  425         break;
  426     case SIGUSR1:
  427         event_del(&server_ev_accept);
  428         fd = server_create_socket(server_client_flags, NULL);
  429         if (fd != -1) {
  430             close(server_fd);
  431             server_fd = fd;
  432             server_update_socket();
  433         }
  434         server_add_accept(0);
  435         break;
  436     case SIGUSR2:
  437         proc_toggle_log(server_proc);
  438         break;
  439     }
  440 }
  441 
  442 /* Handle SIGCHLD. */
  443 static void
  444 server_child_signal(void)
  445 {
  446     int  status;
  447     pid_t    pid;
  448 
  449     for (;;) {
  450         switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
  451         case -1:
  452             if (errno == ECHILD)
  453                 return;
  454             fatal("waitpid failed");
  455         case 0:
  456             return;
  457         }
  458         if (WIFSTOPPED(status))
  459             server_child_stopped(pid, status);
  460         else if (WIFEXITED(status) || WIFSIGNALED(status))
  461             server_child_exited(pid, status);
  462     }
  463 }
  464 
  465 /* Handle exited children. */
  466 static void
  467 server_child_exited(pid_t pid, int status)
  468 {
  469     struct window       *w, *w1;
  470     struct window_pane  *wp;
  471 
  472     RB_FOREACH_SAFE(w, windows, &windows, w1) {
  473         TAILQ_FOREACH(wp, &w->panes, entry) {
  474             if (wp->pid == pid) {
  475                 wp->status = status;
  476                 wp->flags |= PANE_STATUSREADY;
  477 
  478                 log_debug("%%%u exited", wp->id);
  479                 wp->flags |= PANE_EXITED;
  480 
  481                 if (window_pane_destroy_ready(wp))
  482                     server_destroy_pane(wp, 1);
  483                 break;
  484             }
  485         }
  486     }
  487     job_check_died(pid, status);
  488 }
  489 
  490 /* Handle stopped children. */
  491 static void
  492 server_child_stopped(pid_t pid, int status)
  493 {
  494     struct window       *w;
  495     struct window_pane  *wp;
  496 
  497     if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
  498         return;
  499 
  500     RB_FOREACH(w, windows, &windows) {
  501         TAILQ_FOREACH(wp, &w->panes, entry) {
  502             if (wp->pid == pid) {
  503                 if (killpg(pid, SIGCONT) != 0)
  504                     kill(pid, SIGCONT);
  505             }
  506         }
  507     }
  508     job_check_died(pid, status);
  509 }
  510 
  511 /* Add to message log. */
  512 void
  513 server_add_message(const char *fmt, ...)
  514 {
  515     struct message_entry    *msg, *msg1;
  516     char            *s;
  517     va_list          ap;
  518     u_int            limit;
  519 
  520     va_start(ap, fmt);
  521     xvasprintf(&s, fmt, ap);
  522     va_end(ap);
  523 
  524     log_debug("message: %s", s);
  525 
  526     msg = xcalloc(1, sizeof *msg);
  527     gettimeofday(&msg->msg_time, NULL);
  528     msg->msg_num = message_next++;
  529     msg->msg = s;
  530     TAILQ_INSERT_TAIL(&message_log, msg, entry);
  531 
  532     limit = options_get_number(global_options, "message-limit");
  533     TAILQ_FOREACH_SAFE(msg, &message_log, entry, msg1) {
  534         if (msg->msg_num + limit >= message_next)
  535             break;
  536         free(msg->msg);
  537         TAILQ_REMOVE(&message_log, msg, entry);
  538         free(msg);
  539     }
  540 }