Annotation of embedaddon/tmux/server.c, revision 1.1.1.1

1.1       misho       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 <event.h>
                     28: #include <fcntl.h>
                     29: #include <signal.h>
                     30: #include <stdio.h>
                     31: #include <stdlib.h>
                     32: #include <string.h>
                     33: #include <termios.h>
                     34: #include <time.h>
                     35: #include <unistd.h>
                     36: 
                     37: #include "tmux.h"
                     38: 
                     39: /*
                     40:  * Main server functions.
                     41:  */
                     42: 
                     43: struct clients          clients;
                     44: 
                     45: struct tmuxproc                *server_proc;
                     46: static int              server_fd;
                     47: static int              server_exit;
                     48: static struct event     server_ev_accept;
                     49: 
                     50: struct cmd_find_state   marked_pane;
                     51: 
                     52: static int     server_create_socket(void);
                     53: static int     server_loop(void);
                     54: static void    server_send_exit(void);
                     55: static void    server_accept(int, short, void *);
                     56: static void    server_signal(int);
                     57: static void    server_child_signal(void);
                     58: static void    server_child_exited(pid_t, int);
                     59: static void    server_child_stopped(pid_t, int);
                     60: 
                     61: /* Set marked pane. */
                     62: void
                     63: server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
                     64: {
                     65:        cmd_find_clear_state(&marked_pane, NULL, 0);
                     66:        marked_pane.s = s;
                     67:        marked_pane.wl = wl;
                     68:        marked_pane.w = wl->window;
                     69:        marked_pane.wp = wp;
                     70: }
                     71: 
                     72: /* Clear marked pane. */
                     73: void
                     74: server_clear_marked(void)
                     75: {
                     76:        cmd_find_clear_state(&marked_pane, NULL, 0);
                     77: }
                     78: 
                     79: /* Is this the marked pane? */
                     80: int
                     81: server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp)
                     82: {
                     83:        if (s == NULL || wl == NULL || wp == NULL)
                     84:                return (0);
                     85:        if (marked_pane.s != s || marked_pane.wl != wl)
                     86:                return (0);
                     87:        if (marked_pane.wp != wp)
                     88:                return (0);
                     89:        return (server_check_marked());
                     90: }
                     91: 
                     92: /* Check if the marked pane is still valid. */
                     93: int
                     94: server_check_marked(void)
                     95: {
                     96:        return (cmd_find_valid_state(&marked_pane));
                     97: }
                     98: 
                     99: /* Create server socket. */
                    100: static int
                    101: server_create_socket(void)
                    102: {
                    103:        struct sockaddr_un      sa;
                    104:        size_t                  size;
                    105:        mode_t                  mask;
                    106:        int                     fd;
                    107: 
                    108:        memset(&sa, 0, sizeof sa);
                    109:        sa.sun_family = AF_UNIX;
                    110:        size = strlcpy(sa.sun_path, socket_path, sizeof sa.sun_path);
                    111:        if (size >= sizeof sa.sun_path) {
                    112:                errno = ENAMETOOLONG;
                    113:                return (-1);
                    114:        }
                    115:        unlink(sa.sun_path);
                    116: 
                    117:        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
                    118:                return (-1);
                    119: 
                    120:        mask = umask(S_IXUSR|S_IXGRP|S_IRWXO);
                    121:        if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1)
                    122:                return (-1);
                    123:        umask(mask);
                    124: 
                    125:        if (listen(fd, 128) == -1)
                    126:                return (-1);
                    127:        setblocking(fd, 0);
                    128: 
                    129:        return (fd);
                    130: }
                    131: 
                    132: /* Fork new server. */
                    133: int
                    134: server_start(struct event_base *base, int lockfd, char *lockfile)
                    135: {
                    136:        int     pair[2];
                    137: 
                    138:        if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
                    139:                fatal("socketpair failed");
                    140: 
                    141:        server_proc = proc_start("server", base, 1, server_signal);
                    142:        if (server_proc == NULL) {
                    143:                close(pair[1]);
                    144:                return (pair[0]);
                    145:        }
                    146:        close(pair[0]);
                    147: 
                    148:        if (log_get_level() > 3)
                    149:                tty_create_log();
                    150:        if (pledge("stdio rpath wpath cpath fattr unix getpw recvfd proc exec "
                    151:            "tty ps", NULL) != 0)
                    152:                fatal("pledge failed");
                    153: 
                    154:        RB_INIT(&windows);
                    155:        RB_INIT(&all_window_panes);
                    156:        TAILQ_INIT(&clients);
                    157:        RB_INIT(&sessions);
                    158:        RB_INIT(&session_groups);
                    159:        key_bindings_init();
                    160: 
                    161:        gettimeofday(&start_time, NULL);
                    162: 
                    163:        server_fd = server_create_socket();
                    164:        if (server_fd == -1)
                    165:                fatal("couldn't create socket");
                    166:        server_update_socket();
                    167:        server_client_create(pair[1]);
                    168: 
                    169:        if (lockfd >= 0) {
                    170:                unlink(lockfile);
                    171:                free(lockfile);
                    172:                close(lockfd);
                    173:        }
                    174: 
                    175:        start_cfg();
                    176: 
                    177:        status_prompt_load_history();
                    178: 
                    179:        server_add_accept(0);
                    180: 
                    181:        proc_loop(server_proc, server_loop);
                    182:        status_prompt_save_history();
                    183:        exit(0);
                    184: }
                    185: 
                    186: /* Server loop callback. */
                    187: static int
                    188: server_loop(void)
                    189: {
                    190:        struct client   *c;
                    191:        u_int            items;
                    192: 
                    193:        do {
                    194:                items = cmdq_next(NULL);
                    195:                TAILQ_FOREACH(c, &clients, entry) {
                    196:                        if (c->flags & CLIENT_IDENTIFIED)
                    197:                                items += cmdq_next(c);
                    198:                }
                    199:        } while (items != 0);
                    200: 
                    201:        server_client_loop();
                    202: 
                    203:        if (!options_get_number(global_options, "exit-unattached")) {
                    204:                if (!RB_EMPTY(&sessions))
                    205:                        return (0);
                    206:        }
                    207: 
                    208:        TAILQ_FOREACH(c, &clients, entry) {
                    209:                if (c->session != NULL)
                    210:                        return (0);
                    211:        }
                    212: 
                    213:        /*
                    214:         * No attached clients therefore want to exit - flush any waiting
                    215:         * clients but don't actually exit until they've gone.
                    216:         */
                    217:        cmd_wait_for_flush();
                    218:        if (!TAILQ_EMPTY(&clients))
                    219:                return (0);
                    220: 
                    221:        return (1);
                    222: }
                    223: 
                    224: /* Exit the server by killing all clients and windows. */
                    225: static void
                    226: server_send_exit(void)
                    227: {
                    228:        struct client   *c, *c1;
                    229:        struct session  *s, *s1;
                    230: 
                    231:        cmd_wait_for_flush();
                    232: 
                    233:        TAILQ_FOREACH_SAFE(c, &clients, entry, c1) {
                    234:                if (c->flags & CLIENT_SUSPENDED)
                    235:                        server_client_lost(c);
                    236:                else
                    237:                        proc_send(c->peer, MSG_SHUTDOWN, -1, NULL, 0);
                    238:                c->session = NULL;
                    239:        }
                    240: 
                    241:        RB_FOREACH_SAFE(s, sessions, &sessions, s1)
                    242:                session_destroy(s);
                    243: }
                    244: 
                    245: /* Update socket execute permissions based on whether sessions are attached. */
                    246: void
                    247: server_update_socket(void)
                    248: {
                    249:        struct session  *s;
                    250:        static int       last = -1;
                    251:        int              n, mode;
                    252:        struct stat      sb;
                    253: 
                    254:        n = 0;
                    255:        RB_FOREACH(s, sessions, &sessions) {
                    256:                if (!(s->flags & SESSION_UNATTACHED)) {
                    257:                        n++;
                    258:                        break;
                    259:                }
                    260:        }
                    261: 
                    262:        if (n != last) {
                    263:                last = n;
                    264: 
                    265:                if (stat(socket_path, &sb) != 0)
                    266:                        return;
                    267:                mode = sb.st_mode & ACCESSPERMS;
                    268:                if (n != 0) {
                    269:                        if (mode & S_IRUSR)
                    270:                                mode |= S_IXUSR;
                    271:                        if (mode & S_IRGRP)
                    272:                                mode |= S_IXGRP;
                    273:                        if (mode & S_IROTH)
                    274:                                mode |= S_IXOTH;
                    275:                } else
                    276:                        mode &= ~(S_IXUSR|S_IXGRP|S_IXOTH);
                    277:                chmod(socket_path, mode);
                    278:        }
                    279: }
                    280: 
                    281: /* Callback for server socket. */
                    282: static void
                    283: server_accept(int fd, short events, __unused void *data)
                    284: {
                    285:        struct sockaddr_storage sa;
                    286:        socklen_t               slen = sizeof sa;
                    287:        int                     newfd;
                    288: 
                    289:        server_add_accept(0);
                    290:        if (!(events & EV_READ))
                    291:                return;
                    292: 
                    293:        newfd = accept(fd, (struct sockaddr *) &sa, &slen);
                    294:        if (newfd == -1) {
                    295:                if (errno == EAGAIN || errno == EINTR || errno == ECONNABORTED)
                    296:                        return;
                    297:                if (errno == ENFILE || errno == EMFILE) {
                    298:                        /* Delete and don't try again for 1 second. */
                    299:                        server_add_accept(1);
                    300:                        return;
                    301:                }
                    302:                fatal("accept failed");
                    303:        }
                    304:        if (server_exit) {
                    305:                close(newfd);
                    306:                return;
                    307:        }
                    308:        server_client_create(newfd);
                    309: }
                    310: 
                    311: /*
                    312:  * Add accept event. If timeout is nonzero, add as a timeout instead of a read
                    313:  * event - used to backoff when running out of file descriptors.
                    314:  */
                    315: void
                    316: server_add_accept(int timeout)
                    317: {
                    318:        struct timeval tv = { timeout, 0 };
                    319: 
                    320:        if (event_initialized(&server_ev_accept))
                    321:                event_del(&server_ev_accept);
                    322: 
                    323:        if (timeout == 0) {
                    324:                event_set(&server_ev_accept, server_fd, EV_READ, server_accept,
                    325:                    NULL);
                    326:                event_add(&server_ev_accept, NULL);
                    327:        } else {
                    328:                event_set(&server_ev_accept, server_fd, EV_TIMEOUT,
                    329:                    server_accept, NULL);
                    330:                event_add(&server_ev_accept, &tv);
                    331:        }
                    332: }
                    333: 
                    334: /* Signal handler. */
                    335: static void
                    336: server_signal(int sig)
                    337: {
                    338:        int     fd;
                    339: 
                    340:        switch (sig) {
                    341:        case SIGTERM:
                    342:                server_exit = 1;
                    343:                server_send_exit();
                    344:                break;
                    345:        case SIGCHLD:
                    346:                server_child_signal();
                    347:                break;
                    348:        case SIGUSR1:
                    349:                event_del(&server_ev_accept);
                    350:                fd = server_create_socket();
                    351:                if (fd != -1) {
                    352:                        close(server_fd);
                    353:                        server_fd = fd;
                    354:                        server_update_socket();
                    355:                }
                    356:                server_add_accept(0);
                    357:                break;
                    358:        }
                    359: }
                    360: 
                    361: /* Handle SIGCHLD. */
                    362: static void
                    363: server_child_signal(void)
                    364: {
                    365:        int      status;
                    366:        pid_t    pid;
                    367: 
                    368:        for (;;) {
                    369:                switch (pid = waitpid(WAIT_ANY, &status, WNOHANG|WUNTRACED)) {
                    370:                case -1:
                    371:                        if (errno == ECHILD)
                    372:                                return;
                    373:                        fatal("waitpid failed");
                    374:                case 0:
                    375:                        return;
                    376:                }
                    377:                if (WIFSTOPPED(status))
                    378:                        server_child_stopped(pid, status);
                    379:                else if (WIFEXITED(status) || WIFSIGNALED(status))
                    380:                        server_child_exited(pid, status);
                    381:        }
                    382: }
                    383: 
                    384: /* Handle exited children. */
                    385: static void
                    386: server_child_exited(pid_t pid, int status)
                    387: {
                    388:        struct window           *w, *w1;
                    389:        struct window_pane      *wp;
                    390:        struct job              *job;
                    391: 
                    392:        RB_FOREACH_SAFE(w, windows, &windows, w1) {
                    393:                TAILQ_FOREACH(wp, &w->panes, entry) {
                    394:                        if (wp->pid == pid) {
                    395:                                wp->status = status;
                    396:                                server_destroy_pane(wp, 1);
                    397:                                break;
                    398:                        }
                    399:                }
                    400:        }
                    401: 
                    402:        LIST_FOREACH(job, &all_jobs, lentry) {
                    403:                if (pid == job->pid) {
                    404:                        job_died(job, status);  /* might free job */
                    405:                        break;
                    406:                }
                    407:        }
                    408: }
                    409: 
                    410: /* Handle stopped children. */
                    411: static void
                    412: server_child_stopped(pid_t pid, int status)
                    413: {
                    414:        struct window           *w;
                    415:        struct window_pane      *wp;
                    416: 
                    417:        if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
                    418:                return;
                    419: 
                    420:        RB_FOREACH(w, windows, &windows) {
                    421:                TAILQ_FOREACH(wp, &w->panes, entry) {
                    422:                        if (wp->pid == pid) {
                    423:                                if (killpg(pid, SIGCONT) != 0)
                    424:                                        kill(pid, SIGCONT);
                    425:                        }
                    426:                }
                    427:        }
                    428: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>