Annotation of embedaddon/tmux/server.c, revision 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>