[BACK]Return to client.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

Annotation of src/usr.bin/tmux/client.c, Revision 1.46

1.46    ! nicm        1: /* $OpenBSD: client.c,v 1.45 2010/10/16 08:31:55 nicm Exp $ */
1.1       nicm        2:
                      3: /*
                      4:  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
                      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/socket.h>
                     21: #include <sys/stat.h>
                     22: #include <sys/un.h>
                     23: #include <sys/wait.h>
                     24:
                     25: #include <errno.h>
1.30      nicm       26: #include <event.h>
1.1       nicm       27: #include <fcntl.h>
                     28: #include <pwd.h>
                     29: #include <stdlib.h>
                     30: #include <string.h>
                     31: #include <unistd.h>
                     32:
                     33: #include "tmux.h"
                     34:
1.25      nicm       35: struct imsgbuf client_ibuf;
1.30      nicm       36: struct event   client_event;
1.25      nicm       37: const char     *client_exitmsg;
1.32      nicm       38: int            client_exitval;
1.46    ! nicm       39: int            client_attached;
1.1       nicm       40:
1.46    ! nicm       41: int            client_connect(char *, int);
1.30      nicm       42: void           client_send_identify(int);
                     43: void           client_send_environ(void);
                     44: void           client_write_server(enum msgtype, void *, size_t);
1.31      nicm       45: void           client_update_event(void);
1.30      nicm       46: void           client_signal(int, short, void *);
                     47: void           client_callback(int, short, void *);
1.46    ! nicm       48: int            client_dispatch_attached(void);
        !            49: int            client_dispatch_wait(void *);
1.25      nicm       50:
1.46    ! nicm       51: /* Connect client to server. */
        !            52: int
        !            53: client_connect(char *path, int start_server)
1.1       nicm       54: {
1.26      nicm       55:        struct sockaddr_un      sa;
                     56:        size_t                  size;
                     57:        int                     fd, mode;
1.1       nicm       58:
                     59:        memset(&sa, 0, sizeof sa);
                     60:        sa.sun_family = AF_UNIX;
                     61:        size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
                     62:        if (size >= sizeof sa.sun_path) {
                     63:                errno = ENAMETOOLONG;
1.46    ! nicm       64:                return (-1);
1.1       nicm       65:        }
                     66:
1.10      nicm       67:        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
1.18      nicm       68:                fatal("socket failed");
1.1       nicm       69:
1.10      nicm       70:        if (connect(fd, (struct sockaddr *) &sa, SUN_LEN(&sa)) == -1) {
1.46    ! nicm       71:                if (!start_server)
        !            72:                        goto failed;
1.28      nicm       73:                switch (errno) {
                     74:                case ECONNREFUSED:
                     75:                        if (unlink(path) != 0)
1.46    ! nicm       76:                                goto failed;
1.28      nicm       77:                        /* FALLTHROUGH */
                     78:                case ENOENT:
1.46    ! nicm       79:                        if ((fd = server_start()) == -1)
        !            80:                                goto failed;
        !            81:                        break;
        !            82:                default:
        !            83:                        goto failed;
1.1       nicm       84:                }
                     85:        }
                     86:
1.10      nicm       87:        if ((mode = fcntl(fd, F_GETFL)) == -1)
1.1       nicm       88:                fatal("fcntl failed");
1.10      nicm       89:        if (fcntl(fd, F_SETFL, mode|O_NONBLOCK) == -1)
1.1       nicm       90:                fatal("fcntl failed");
1.46    ! nicm       91:        return (fd);
        !            92:
        !            93: failed:
        !            94:        close(fd);
        !            95:        return (-1);
        !            96: }
        !            97:
        !            98: /* Client main loop. */
        !            99: int
        !           100: client_main(int argc, char **argv, int flags)
        !           101: {
        !           102:        struct cmd              *cmd;
        !           103:        struct cmd_list         *cmdlist;
        !           104:        struct msg_command_data  cmddata;
        !           105:        int                      cmdflags, fd;
        !           106:        enum msgtype             msg;
        !           107:        char                    *cause;
        !           108:
        !           109:        /* Set up the initial command. */
        !           110:        cmdflags = 0;
        !           111:        if (shell_cmd != NULL) {
        !           112:                msg = MSG_SHELL;
        !           113:                cmdflags = CMD_STARTSERVER;
        !           114:        } else if (argc == 0) {
        !           115:                msg = MSG_COMMAND;
        !           116:                cmdflags = CMD_STARTSERVER|CMD_SENDENVIRON|CMD_CANTNEST;
        !           117:        } else {
        !           118:                msg = MSG_COMMAND;
        !           119:
        !           120:                /*
        !           121:                 * It sucks parsing the command string twice (in client and
        !           122:                 * later in server) but it is necessary to get the start server
        !           123:                 * flag.
        !           124:                 */
        !           125:                if ((cmdlist = cmd_list_parse(argc, argv, &cause)) == NULL) {
        !           126:                        log_warnx("%s", cause);
        !           127:                        return (1);
        !           128:                }
        !           129:                cmdflags &= ~CMD_STARTSERVER;
        !           130:                TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
        !           131:                        if (cmd->entry->flags & CMD_STARTSERVER)
        !           132:                                cmdflags |= CMD_STARTSERVER;
        !           133:                        if (cmd->entry->flags & CMD_SENDENVIRON)
        !           134:                                cmdflags |= CMD_SENDENVIRON;
        !           135:                        if (cmd->entry->flags & CMD_CANTNEST)
        !           136:                                cmdflags |= CMD_CANTNEST;
        !           137:                }
        !           138:                cmd_list_free(cmdlist);
        !           139:        }
        !           140:
        !           141:        /*
        !           142:         * Check if this could be a nested session, if the command can't nest:
        !           143:         * if the socket path matches $TMUX, this is probably the same server.
        !           144:         */
        !           145:        if (shell_cmd == NULL && environ_path != NULL &&
        !           146:            cmdflags & CMD_CANTNEST && strcmp(socket_path, environ_path) == 0) {
        !           147:                log_warnx("sessions should be nested with care. "
        !           148:                    "unset $TMUX to force.");
        !           149:                return (1);
        !           150:        }
        !           151:
        !           152:        /* Initialise the client socket and start the server. */
        !           153:        fd = client_connect(socket_path, cmdflags & CMD_STARTSERVER);
        !           154:        if (fd == -1) {
        !           155:                log_warn("failed to connect to server");
        !           156:                return (1);
        !           157:        }
        !           158:
        !           159:        /* Set process title, log and signals now this is the client. */
        !           160:        setproctitle("client (%s)", socket_path);
        !           161:        logfile("client");
        !           162:
        !           163:        /* Create imsg. */
1.25      nicm      164:        imsg_init(&client_ibuf, fd);
1.46    ! nicm      165:        event_set(&client_event, fd, EV_READ, client_callback, shell_cmd);
        !           166:
        !           167:        /* Establish signal handlers. */
        !           168:        set_signals(client_signal);
1.1       nicm      169:
1.46    ! nicm      170:        /* Send initial environment. */
1.11      nicm      171:        if (cmdflags & CMD_SENDENVIRON)
1.25      nicm      172:                client_send_environ();
1.42      nicm      173:        client_send_identify(flags);
1.1       nicm      174:
1.46    ! nicm      175:        /* Send first command. */
        !           176:        if (msg == MSG_COMMAND) {
        !           177:                /* Fill in command line arguments. */
        !           178:                cmddata.pid = environ_pid;
        !           179:                cmddata.idx = environ_idx;
        !           180:
        !           181:                /* Prepare command for server. */
        !           182:                cmddata.argc = argc;
        !           183:                if (cmd_pack_argv(
        !           184:                    argc, argv, cmddata.argv, sizeof cmddata.argv) != 0) {
        !           185:                        log_warnx("command too long");
        !           186:                        return (1);
        !           187:                }
        !           188:
        !           189:                client_write_server(msg, &cmddata, sizeof cmddata);
        !           190:        } else if (msg == MSG_SHELL)
        !           191:                client_write_server(msg, NULL, 0);
1.1       nicm      192:
1.46    ! nicm      193:        /* Set the event and dispatch. */
        !           194:        client_update_event();
        !           195:        event_dispatch();
        !           196:
        !           197:        /* Print the exit message, if any, and exit. */
        !           198:        if (client_attached && client_exitmsg != NULL && !login_shell)
        !           199:                printf("[%s]\n", client_exitmsg);
        !           200:        return (client_exitval);
1.1       nicm      201: }
                    202:
1.46    ! nicm      203: /* Send identify message to server with the file descriptors. */
1.11      nicm      204: void
1.26      nicm      205: client_send_identify(int flags)
                    206: {
                    207:        struct msg_identify_data        data;
                    208:        char                           *term;
                    209:        int                             fd;
                    210:
                    211:        data.flags = flags;
                    212:
                    213:        if (getcwd(data.cwd, sizeof data.cwd) == NULL)
                    214:                *data.cwd = '\0';
1.35      nicm      215:
1.26      nicm      216:        term = getenv("TERM");
                    217:        if (term == NULL ||
                    218:            strlcpy(data.term, term, sizeof data.term) >= sizeof data.term)
                    219:                *data.term = '\0';
                    220:
                    221:        if ((fd = dup(STDIN_FILENO)) == -1)
                    222:                fatal("dup failed");
                    223:        imsg_compose(&client_ibuf,
                    224:            MSG_IDENTIFY, PROTOCOL_VERSION, -1, fd, &data, sizeof data);
1.42      nicm      225:
                    226:        if ((fd = dup(STDOUT_FILENO)) == -1)
                    227:                fatal("dup failed");
1.46    ! nicm      228:        imsg_compose(&client_ibuf,
        !           229:            MSG_STDOUT, PROTOCOL_VERSION, -1, fd, NULL, 0);
1.42      nicm      230:
                    231:        if ((fd = dup(STDERR_FILENO)) == -1)
                    232:                fatal("dup failed");
1.46    ! nicm      233:        imsg_compose(&client_ibuf,
        !           234:            MSG_STDERR, PROTOCOL_VERSION, -1, fd, NULL, 0);
1.26      nicm      235: }
                    236:
1.46    ! nicm      237: /* Forward entire environment to server. */
1.26      nicm      238: void
1.25      nicm      239: client_send_environ(void)
1.11      nicm      240: {
1.26      nicm      241:        struct msg_environ_data data;
1.11      nicm      242:        char                  **var;
                    243:
1.35      nicm      244:        for (var = environ; *var != NULL; var++) {
1.11      nicm      245:                if (strlcpy(data.var, *var, sizeof data.var) >= sizeof data.var)
                    246:                        continue;
1.25      nicm      247:                client_write_server(MSG_ENVIRON, &data, sizeof data);
1.11      nicm      248:        }
                    249: }
                    250:
1.46    ! nicm      251: /* Write a message to the server without a file descriptor. */
1.25      nicm      252: void
                    253: client_write_server(enum msgtype type, void *buf, size_t len)
                    254: {
1.35      nicm      255:        imsg_compose(&client_ibuf, type, PROTOCOL_VERSION, -1, -1, buf, len);
1.25      nicm      256: }
                    257:
1.46    ! nicm      258: /* Update client event based on whether it needs to read or read and write. */
1.31      nicm      259: void
                    260: client_update_event(void)
                    261: {
                    262:        short   events;
                    263:
                    264:        event_del(&client_event);
                    265:        events = EV_READ;
                    266:        if (client_ibuf.w.queued > 0)
                    267:                events |= EV_WRITE;
1.46    ! nicm      268:        event_set(
        !           269:            &client_event, client_ibuf.fd, events, client_callback, shell_cmd);
1.31      nicm      270:        event_add(&client_event, NULL);
                    271: }
                    272:
1.46    ! nicm      273: /* Callback to handle signals in the client. */
1.34      nicm      274: /* ARGSUSED */
1.30      nicm      275: void
1.31      nicm      276: client_signal(int sig, unused short events, unused void *data)
1.30      nicm      277: {
1.46    ! nicm      278:        struct sigaction sigact;
        !           279:        int              status;
1.30      nicm      280:
1.46    ! nicm      281:        if (!client_attached) {
        !           282:                switch (sig) {
        !           283:                case SIGCHLD:
        !           284:                        waitpid(WAIT_ANY, &status, WNOHANG);
        !           285:                        break;
        !           286:                case SIGTERM:
        !           287:                        event_loopexit(NULL);
        !           288:                        break;
        !           289:                }
        !           290:        } else {
        !           291:                switch (sig) {
        !           292:                case SIGHUP:
        !           293:                        client_exitmsg = "lost tty";
        !           294:                        client_exitval = 1;
        !           295:                        client_write_server(MSG_EXITING, NULL, 0);
        !           296:                        break;
        !           297:                case SIGTERM:
        !           298:                        client_exitmsg = "terminated";
        !           299:                        client_exitval = 1;
        !           300:                        client_write_server(MSG_EXITING, NULL, 0);
        !           301:                        break;
        !           302:                case SIGWINCH:
        !           303:                        client_write_server(MSG_RESIZE, NULL, 0);
        !           304:                        break;
        !           305:                case SIGCONT:
        !           306:                        memset(&sigact, 0, sizeof sigact);
        !           307:                        sigemptyset(&sigact.sa_mask);
        !           308:                        sigact.sa_flags = SA_RESTART;
        !           309:                        sigact.sa_handler = SIG_IGN;
        !           310:                        if (sigaction(SIGTSTP, &sigact, NULL) != 0)
        !           311:                                fatal("sigaction failed");
        !           312:                        client_write_server(MSG_WAKEUP, NULL, 0);
        !           313:                        break;
        !           314:                }
1.30      nicm      315:        }
                    316:
1.31      nicm      317:        client_update_event();
1.30      nicm      318: }
                    319:
1.46    ! nicm      320: /* Callback for client imsg read events. */
1.34      nicm      321: /* ARGSUSED */
1.30      nicm      322: void
1.46    ! nicm      323: client_callback(unused int fd, short events, void *data)
1.30      nicm      324: {
1.33      nicm      325:        ssize_t n;
1.46    ! nicm      326:        int     retval;
1.30      nicm      327:
                    328:        if (events & EV_READ) {
                    329:                if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
                    330:                        goto lost_server;
1.46    ! nicm      331:                if (client_attached)
        !           332:                        retval = client_dispatch_attached();
        !           333:                else
        !           334:                        retval = client_dispatch_wait(data);
        !           335:                if (retval != 0) {
1.30      nicm      336:                        event_loopexit(NULL);
                    337:                        return;
1.35      nicm      338:                }
1.30      nicm      339:        }
1.35      nicm      340:
1.30      nicm      341:        if (events & EV_WRITE) {
                    342:                if (msgbuf_write(&client_ibuf.w) < 0)
                    343:                        goto lost_server;
                    344:        }
                    345:
1.31      nicm      346:        client_update_event();
1.30      nicm      347:        return;
                    348:
                    349: lost_server:
                    350:        client_exitmsg = "lost server";
1.32      nicm      351:        client_exitval = 1;
1.30      nicm      352:        event_loopexit(NULL);
                    353: }
                    354:
1.46    ! nicm      355: /* Dispatch imsgs when in wait state (before MSG_READY). */
        !           356: int
        !           357: client_dispatch_wait(void *data)
        !           358: {
        !           359:        struct imsg             imsg;
        !           360:        ssize_t                 n, datalen;
        !           361:        struct msg_shell_data   shelldata;
        !           362:        struct msg_exit_data    exitdata;
        !           363:        const char             *shellcmd = data;
        !           364:
        !           365:        if ((n = imsg_read(&client_ibuf)) == -1 || n == 0)
        !           366:                fatalx("imsg_read failed");
        !           367:
        !           368:        for (;;) {
        !           369:                if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
        !           370:                        fatalx("imsg_get failed");
        !           371:                if (n == 0)
        !           372:                        return (0);
        !           373:                datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
        !           374:
        !           375:                switch (imsg.hdr.type) {
        !           376:                case MSG_EXIT:
        !           377:                case MSG_SHUTDOWN:
        !           378:                        if (datalen != sizeof exitdata) {
        !           379:                                if (datalen != 0)
        !           380:                                        fatalx("bad MSG_EXIT size");
        !           381:                        } else {
        !           382:                                memcpy(&exitdata, imsg.data, sizeof exitdata);
        !           383:                                client_exitval = exitdata.retcode;
        !           384:                        }
        !           385:                        imsg_free(&imsg);
        !           386:                        return (-1);
        !           387:                case MSG_READY:
        !           388:                        if (datalen != 0)
        !           389:                                fatalx("bad MSG_READY size");
        !           390:
        !           391:                        client_attached = 1;
        !           392:                        break;
        !           393:                case MSG_VERSION:
        !           394:                        if (datalen != 0)
        !           395:                                fatalx("bad MSG_VERSION size");
        !           396:
        !           397:                        log_warnx("protocol version mismatch (client %u, "
        !           398:                            "server %u)", PROTOCOL_VERSION, imsg.hdr.peerid);
        !           399:                        client_exitval = 1;
        !           400:
        !           401:                        imsg_free(&imsg);
        !           402:                        return (-1);
        !           403:                case MSG_SHELL:
        !           404:                        if (datalen != sizeof shelldata)
        !           405:                                fatalx("bad MSG_SHELL size");
        !           406:                        memcpy(&shelldata, imsg.data, sizeof shelldata);
        !           407:                        shelldata.shell[(sizeof shelldata.shell) - 1] = '\0';
        !           408:
        !           409:                        clear_signals(0);
        !           410:
        !           411:                        shell_exec(shelldata.shell, shellcmd);
        !           412:                        /* NOTREACHED */
        !           413:                default:
        !           414:                        fatalx("unexpected message");
        !           415:                }
        !           416:
        !           417:                imsg_free(&imsg);
        !           418:        }
        !           419: }
        !           420:
        !           421: /* Dispatch imsgs in attached state (after MSG_READY). */
        !           422: /* ARGSUSED */
1.9       nicm      423: int
1.46    ! nicm      424: client_dispatch_attached(void)
1.9       nicm      425: {
1.30      nicm      426:        struct imsg             imsg;
                    427:        struct msg_lock_data    lockdata;
                    428:        struct sigaction        sigact;
                    429:        ssize_t                 n, datalen;
1.9       nicm      430:
                    431:        for (;;) {
1.25      nicm      432:                if ((n = imsg_get(&client_ibuf, &imsg)) == -1)
1.12      nicm      433:                        fatalx("imsg_get failed");
                    434:                if (n == 0)
1.9       nicm      435:                        return (0);
1.12      nicm      436:                datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1.9       nicm      437:
1.30      nicm      438:                log_debug("client got %d", imsg.hdr.type);
1.12      nicm      439:                switch (imsg.hdr.type) {
1.9       nicm      440:                case MSG_DETACH:
1.12      nicm      441:                        if (datalen != 0)
1.9       nicm      442:                                fatalx("bad MSG_DETACH size");
                    443:
1.25      nicm      444:                        client_write_server(MSG_EXITING, NULL, 0);
                    445:                        client_exitmsg = "detached";
1.9       nicm      446:                        break;
                    447:                case MSG_EXIT:
1.43      nicm      448:                        if (datalen != 0 &&
                    449:                            datalen != sizeof (struct msg_exit_data))
1.9       nicm      450:                                fatalx("bad MSG_EXIT size");
1.12      nicm      451:
1.25      nicm      452:                        client_write_server(MSG_EXITING, NULL, 0);
                    453:                        client_exitmsg = "exited";
1.9       nicm      454:                        break;
                    455:                case MSG_EXITED:
1.12      nicm      456:                        if (datalen != 0)
1.9       nicm      457:                                fatalx("bad MSG_EXITED size");
                    458:
1.12      nicm      459:                        imsg_free(&imsg);
1.9       nicm      460:                        return (-1);
                    461:                case MSG_SHUTDOWN:
1.12      nicm      462:                        if (datalen != 0)
1.9       nicm      463:                                fatalx("bad MSG_SHUTDOWN size");
                    464:
1.25      nicm      465:                        client_write_server(MSG_EXITING, NULL, 0);
                    466:                        client_exitmsg = "server exited";
1.32      nicm      467:                        client_exitval = 1;
1.9       nicm      468:                        break;
                    469:                case MSG_SUSPEND:
1.12      nicm      470:                        if (datalen != 0)
1.9       nicm      471:                                fatalx("bad MSG_SUSPEND size");
                    472:
1.30      nicm      473:                        memset(&sigact, 0, sizeof sigact);
                    474:                        sigemptyset(&sigact.sa_mask);
                    475:                        sigact.sa_flags = SA_RESTART;
                    476:                        sigact.sa_handler = SIG_DFL;
                    477:                        if (sigaction(SIGTSTP, &sigact, NULL) != 0)
                    478:                                fatal("sigaction failed");
                    479:                        kill(getpid(), SIGTSTP);
1.21      nicm      480:                        break;
                    481:                case MSG_LOCK:
                    482:                        if (datalen != sizeof lockdata)
                    483:                                fatalx("bad MSG_LOCK size");
                    484:                        memcpy(&lockdata, imsg.data, sizeof lockdata);
1.35      nicm      485:
1.21      nicm      486:                        lockdata.cmd[(sizeof lockdata.cmd) - 1] = '\0';
                    487:                        system(lockdata.cmd);
1.25      nicm      488:                        client_write_server(MSG_UNLOCK, NULL, 0);
1.9       nicm      489:                        break;
                    490:                default:
                    491:                        fatalx("unexpected message");
                    492:                }
1.12      nicm      493:
                    494:                imsg_free(&imsg);
1.9       nicm      495:        }
1.1       nicm      496: }