Annotation of embedaddon/tmux/client.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/file.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 <stdlib.h>
! 31: #include <string.h>
! 32: #include <unistd.h>
! 33:
! 34: #include "tmux.h"
! 35:
! 36: static struct tmuxproc *client_proc;
! 37: static struct tmuxpeer *client_peer;
! 38: static int client_flags;
! 39: static struct event client_stdin;
! 40: static enum {
! 41: CLIENT_EXIT_NONE,
! 42: CLIENT_EXIT_DETACHED,
! 43: CLIENT_EXIT_DETACHED_HUP,
! 44: CLIENT_EXIT_LOST_TTY,
! 45: CLIENT_EXIT_TERMINATED,
! 46: CLIENT_EXIT_LOST_SERVER,
! 47: CLIENT_EXIT_EXITED,
! 48: CLIENT_EXIT_SERVER_EXITED,
! 49: } client_exitreason = CLIENT_EXIT_NONE;
! 50: static int client_exitval;
! 51: static enum msgtype client_exittype;
! 52: static const char *client_exitsession;
! 53: static const char *client_execshell;
! 54: static const char *client_execcmd;
! 55: static int client_attached;
! 56:
! 57: static __dead void client_exec(const char *,const char *);
! 58: static int client_get_lock(char *);
! 59: static int client_connect(struct event_base *, const char *, int);
! 60: static void client_send_identify(const char *, const char *);
! 61: static void client_stdin_callback(int, short, void *);
! 62: static void client_write(int, const char *, size_t);
! 63: static void client_signal(int);
! 64: static void client_dispatch(struct imsg *, void *);
! 65: static void client_dispatch_attached(struct imsg *);
! 66: static void client_dispatch_wait(struct imsg *, const char *);
! 67: static const char *client_exit_message(void);
! 68:
! 69: /*
! 70: * Get server create lock. If already held then server start is happening in
! 71: * another client, so block until the lock is released and return -2 to
! 72: * retry. Return -1 on failure to continue and start the server anyway.
! 73: */
! 74: static int
! 75: client_get_lock(char *lockfile)
! 76: {
! 77: int lockfd;
! 78:
! 79: log_debug("lock file is %s", lockfile);
! 80:
! 81: if ((lockfd = open(lockfile, O_WRONLY|O_CREAT, 0600)) == -1) {
! 82: log_debug("open failed: %s", strerror(errno));
! 83: return (-1);
! 84: }
! 85:
! 86: if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) {
! 87: log_debug("flock failed: %s", strerror(errno));
! 88: if (errno != EAGAIN)
! 89: return (lockfd);
! 90: while (flock(lockfd, LOCK_EX) == -1 && errno == EINTR)
! 91: /* nothing */;
! 92: close(lockfd);
! 93: return (-2);
! 94: }
! 95: log_debug("flock succeeded");
! 96:
! 97: return (lockfd);
! 98: }
! 99:
! 100: /* Connect client to server. */
! 101: static int
! 102: client_connect(struct event_base *base, const char *path, int start_server)
! 103: {
! 104: struct sockaddr_un sa;
! 105: size_t size;
! 106: int fd, lockfd = -1, locked = 0;
! 107: char *lockfile = NULL;
! 108:
! 109: memset(&sa, 0, sizeof sa);
! 110: sa.sun_family = AF_UNIX;
! 111: size = strlcpy(sa.sun_path, path, sizeof sa.sun_path);
! 112: if (size >= sizeof sa.sun_path) {
! 113: errno = ENAMETOOLONG;
! 114: return (-1);
! 115: }
! 116: log_debug("socket is %s", path);
! 117:
! 118: retry:
! 119: if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
! 120: return (-1);
! 121:
! 122: log_debug("trying connect");
! 123: if (connect(fd, (struct sockaddr *)&sa, sizeof sa) == -1) {
! 124: log_debug("connect failed: %s", strerror(errno));
! 125: if (errno != ECONNREFUSED && errno != ENOENT)
! 126: goto failed;
! 127: if (!start_server)
! 128: goto failed;
! 129: close(fd);
! 130:
! 131: if (!locked) {
! 132: xasprintf(&lockfile, "%s.lock", path);
! 133: if ((lockfd = client_get_lock(lockfile)) < 0) {
! 134: log_debug("didn't get lock (%d)", lockfd);
! 135:
! 136: free(lockfile);
! 137: lockfile = NULL;
! 138:
! 139: if (lockfd == -2)
! 140: goto retry;
! 141: }
! 142: log_debug("got lock (%d)", lockfd);
! 143:
! 144: /*
! 145: * Always retry at least once, even if we got the lock,
! 146: * because another client could have taken the lock,
! 147: * started the server and released the lock between our
! 148: * connect() and flock().
! 149: */
! 150: locked = 1;
! 151: goto retry;
! 152: }
! 153:
! 154: if (lockfd >= 0 && unlink(path) != 0 && errno != ENOENT) {
! 155: free(lockfile);
! 156: close(lockfd);
! 157: return (-1);
! 158: }
! 159: fd = server_start(base, lockfd, lockfile);
! 160: }
! 161:
! 162: if (locked && lockfd >= 0) {
! 163: free(lockfile);
! 164: close(lockfd);
! 165: }
! 166: setblocking(fd, 0);
! 167: return (fd);
! 168:
! 169: failed:
! 170: if (locked) {
! 171: free(lockfile);
! 172: close(lockfd);
! 173: }
! 174: close(fd);
! 175: return (-1);
! 176: }
! 177:
! 178: /* Get exit string from reason number. */
! 179: const char *
! 180: client_exit_message(void)
! 181: {
! 182: static char msg[256];
! 183:
! 184: switch (client_exitreason) {
! 185: case CLIENT_EXIT_NONE:
! 186: break;
! 187: case CLIENT_EXIT_DETACHED:
! 188: if (client_exitsession != NULL) {
! 189: xsnprintf(msg, sizeof msg, "detached "
! 190: "(from session %s)", client_exitsession);
! 191: return (msg);
! 192: }
! 193: return ("detached");
! 194: case CLIENT_EXIT_DETACHED_HUP:
! 195: if (client_exitsession != NULL) {
! 196: xsnprintf(msg, sizeof msg, "detached and SIGHUP "
! 197: "(from session %s)", client_exitsession);
! 198: return (msg);
! 199: }
! 200: return ("detached and SIGHUP");
! 201: case CLIENT_EXIT_LOST_TTY:
! 202: return ("lost tty");
! 203: case CLIENT_EXIT_TERMINATED:
! 204: return ("terminated");
! 205: case CLIENT_EXIT_LOST_SERVER:
! 206: return ("lost server");
! 207: case CLIENT_EXIT_EXITED:
! 208: return ("exited");
! 209: case CLIENT_EXIT_SERVER_EXITED:
! 210: return ("server exited");
! 211: }
! 212: return ("unknown reason");
! 213: }
! 214:
! 215: /* Client main loop. */
! 216: int
! 217: client_main(struct event_base *base, int argc, char **argv, int flags,
! 218: const char *shellcmd)
! 219: {
! 220: struct cmd *cmd;
! 221: struct cmd_list *cmdlist;
! 222: struct msg_command_data *data;
! 223: int cmdflags, fd, i;
! 224: const char *ttynam, *cwd;
! 225: pid_t ppid;
! 226: enum msgtype msg;
! 227: char *cause, path[PATH_MAX];
! 228: struct termios tio, saved_tio;
! 229: size_t size;
! 230:
! 231: /* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
! 232: signal(SIGCHLD, SIG_IGN);
! 233:
! 234: /* Save the flags. */
! 235: client_flags = flags;
! 236:
! 237: /* Set up the initial command. */
! 238: cmdflags = 0;
! 239: if (shellcmd != NULL) {
! 240: msg = MSG_SHELL;
! 241: cmdflags = CMD_STARTSERVER;
! 242: } else if (argc == 0) {
! 243: msg = MSG_COMMAND;
! 244: cmdflags = CMD_STARTSERVER;
! 245: } else {
! 246: msg = MSG_COMMAND;
! 247:
! 248: /*
! 249: * It sucks parsing the command string twice (in client and
! 250: * later in server) but it is necessary to get the start server
! 251: * flag.
! 252: */
! 253: cmdlist = cmd_list_parse(argc, argv, NULL, 0, &cause);
! 254: if (cmdlist != NULL) {
! 255: TAILQ_FOREACH(cmd, &cmdlist->list, qentry) {
! 256: if (cmd->entry->flags & CMD_STARTSERVER)
! 257: cmdflags |= CMD_STARTSERVER;
! 258: }
! 259: cmd_list_free(cmdlist);
! 260: }
! 261: }
! 262:
! 263: /* Create client process structure (starts logging). */
! 264: client_proc = proc_start("client", base, 0, client_signal);
! 265:
! 266: /* Initialize the client socket and start the server. */
! 267: fd = client_connect(base, socket_path, cmdflags & CMD_STARTSERVER);
! 268: if (fd == -1) {
! 269: if (errno == ECONNREFUSED) {
! 270: fprintf(stderr, "no server running on %s\n",
! 271: socket_path);
! 272: } else {
! 273: fprintf(stderr, "error connecting to %s (%s)\n",
! 274: socket_path, strerror(errno));
! 275: }
! 276: return (1);
! 277: }
! 278: client_peer = proc_add_peer(client_proc, fd, client_dispatch,
! 279: (void *)shellcmd);
! 280:
! 281: /* Save these before pledge(). */
! 282: if ((cwd = getcwd(path, sizeof path)) == NULL) {
! 283: if ((cwd = find_home()) == NULL)
! 284: cwd = "/";
! 285: }
! 286: if ((ttynam = ttyname(STDIN_FILENO)) == NULL)
! 287: ttynam = "";
! 288:
! 289: /*
! 290: * Drop privileges for client. "proc exec" is needed for -c and for
! 291: * locking (which uses system(3)).
! 292: *
! 293: * "tty" is needed to restore termios(4) and also for some reason -CC
! 294: * does not work properly without it (input is not recognised).
! 295: *
! 296: * "sendfd" is dropped later in client_dispatch_wait().
! 297: */
! 298: if (pledge("stdio unix sendfd proc exec tty", NULL) != 0)
! 299: fatal("pledge failed");
! 300:
! 301: /* Free stuff that is not used in the client. */
! 302: if (ptm_fd != -1)
! 303: close(ptm_fd);
! 304: options_free(global_options);
! 305: options_free(global_s_options);
! 306: options_free(global_w_options);
! 307: environ_free(global_environ);
! 308:
! 309: /* Create stdin handler. */
! 310: setblocking(STDIN_FILENO, 0);
! 311: event_set(&client_stdin, STDIN_FILENO, EV_READ|EV_PERSIST,
! 312: client_stdin_callback, NULL);
! 313: if (client_flags & CLIENT_CONTROLCONTROL) {
! 314: if (tcgetattr(STDIN_FILENO, &saved_tio) != 0) {
! 315: fprintf(stderr, "tcgetattr failed: %s\n",
! 316: strerror(errno));
! 317: return (1);
! 318: }
! 319: cfmakeraw(&tio);
! 320: tio.c_iflag = ICRNL|IXANY;
! 321: tio.c_oflag = OPOST|ONLCR;
! 322: #ifdef NOKERNINFO
! 323: tio.c_lflag = NOKERNINFO;
! 324: #endif
! 325: tio.c_cflag = CREAD|CS8|HUPCL;
! 326: tio.c_cc[VMIN] = 1;
! 327: tio.c_cc[VTIME] = 0;
! 328: cfsetispeed(&tio, cfgetispeed(&saved_tio));
! 329: cfsetospeed(&tio, cfgetospeed(&saved_tio));
! 330: tcsetattr(STDIN_FILENO, TCSANOW, &tio);
! 331: }
! 332:
! 333: /* Send identify messages. */
! 334: client_send_identify(ttynam, cwd);
! 335:
! 336: /* Send first command. */
! 337: if (msg == MSG_COMMAND) {
! 338: /* How big is the command? */
! 339: size = 0;
! 340: for (i = 0; i < argc; i++)
! 341: size += strlen(argv[i]) + 1;
! 342: data = xmalloc((sizeof *data) + size);
! 343:
! 344: /* Prepare command for server. */
! 345: data->argc = argc;
! 346: if (cmd_pack_argv(argc, argv, (char *)(data + 1), size) != 0) {
! 347: fprintf(stderr, "command too long\n");
! 348: free(data);
! 349: return (1);
! 350: }
! 351: size += sizeof *data;
! 352:
! 353: /* Send the command. */
! 354: if (proc_send(client_peer, msg, -1, data, size) != 0) {
! 355: fprintf(stderr, "failed to send command\n");
! 356: free(data);
! 357: return (1);
! 358: }
! 359: free(data);
! 360: } else if (msg == MSG_SHELL)
! 361: proc_send(client_peer, msg, -1, NULL, 0);
! 362:
! 363: /* Start main loop. */
! 364: proc_loop(client_proc, NULL);
! 365:
! 366: /* Run command if user requested exec, instead of exiting. */
! 367: if (client_exittype == MSG_EXEC) {
! 368: if (client_flags & CLIENT_CONTROLCONTROL)
! 369: tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
! 370: clear_signals(0);
! 371: client_exec(client_execshell, client_execcmd);
! 372: }
! 373:
! 374: /* Print the exit message, if any, and exit. */
! 375: if (client_attached) {
! 376: if (client_exitreason != CLIENT_EXIT_NONE)
! 377: printf("[%s]\n", client_exit_message());
! 378:
! 379: ppid = getppid();
! 380: if (client_exittype == MSG_DETACHKILL && ppid > 1)
! 381: kill(ppid, SIGHUP);
! 382: } else if (client_flags & CLIENT_CONTROLCONTROL) {
! 383: if (client_exitreason != CLIENT_EXIT_NONE)
! 384: printf("%%exit %s\n", client_exit_message());
! 385: else
! 386: printf("%%exit\n");
! 387: printf("\033\\");
! 388: tcsetattr(STDOUT_FILENO, TCSAFLUSH, &saved_tio);
! 389: } else if (client_exitreason != CLIENT_EXIT_NONE)
! 390: fprintf(stderr, "%s\n", client_exit_message());
! 391: setblocking(STDIN_FILENO, 1);
! 392: return (client_exitval);
! 393: }
! 394:
! 395: /* Send identify messages to server. */
! 396: static void
! 397: client_send_identify(const char *ttynam, const char *cwd)
! 398: {
! 399: const char *s;
! 400: char **ss;
! 401: size_t sslen;
! 402: int fd, flags = client_flags;
! 403: pid_t pid;
! 404:
! 405: proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
! 406:
! 407: if ((s = getenv("TERM")) == NULL)
! 408: s = "";
! 409: proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
! 410:
! 411: proc_send(client_peer, MSG_IDENTIFY_TTYNAME, -1, ttynam,
! 412: strlen(ttynam) + 1);
! 413: proc_send(client_peer, MSG_IDENTIFY_CWD, -1, cwd, strlen(cwd) + 1);
! 414:
! 415: if ((fd = dup(STDIN_FILENO)) == -1)
! 416: fatal("dup failed");
! 417: proc_send(client_peer, MSG_IDENTIFY_STDIN, fd, NULL, 0);
! 418:
! 419: pid = getpid();
! 420: proc_send(client_peer, MSG_IDENTIFY_CLIENTPID, -1, &pid, sizeof pid);
! 421:
! 422: for (ss = environ; *ss != NULL; ss++) {
! 423: sslen = strlen(*ss) + 1;
! 424: if (sslen > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
! 425: continue;
! 426: proc_send(client_peer, MSG_IDENTIFY_ENVIRON, -1, *ss, sslen);
! 427: }
! 428:
! 429: proc_send(client_peer, MSG_IDENTIFY_DONE, -1, NULL, 0);
! 430: }
! 431:
! 432: /* Callback for client stdin read events. */
! 433: static void
! 434: client_stdin_callback(__unused int fd, __unused short events,
! 435: __unused void *arg)
! 436: {
! 437: struct msg_stdin_data data;
! 438:
! 439: data.size = read(STDIN_FILENO, data.data, sizeof data.data);
! 440: if (data.size < 0 && (errno == EINTR || errno == EAGAIN))
! 441: return;
! 442:
! 443: proc_send(client_peer, MSG_STDIN, -1, &data, sizeof data);
! 444: if (data.size <= 0)
! 445: event_del(&client_stdin);
! 446: }
! 447:
! 448: /* Force write to file descriptor. */
! 449: static void
! 450: client_write(int fd, const char *data, size_t size)
! 451: {
! 452: ssize_t used;
! 453:
! 454: while (size != 0) {
! 455: used = write(fd, data, size);
! 456: if (used == -1) {
! 457: if (errno == EINTR || errno == EAGAIN)
! 458: continue;
! 459: break;
! 460: }
! 461: data += used;
! 462: size -= used;
! 463: }
! 464: }
! 465:
! 466: /* Run command in shell; used for -c. */
! 467: static __dead void
! 468: client_exec(const char *shell, const char *shellcmd)
! 469: {
! 470: const char *name, *ptr;
! 471: char *argv0;
! 472:
! 473: log_debug("shell %s, command %s", shell, shellcmd);
! 474:
! 475: ptr = strrchr(shell, '/');
! 476: if (ptr != NULL && *(ptr + 1) != '\0')
! 477: name = ptr + 1;
! 478: else
! 479: name = shell;
! 480: if (client_flags & CLIENT_LOGIN)
! 481: xasprintf(&argv0, "-%s", name);
! 482: else
! 483: xasprintf(&argv0, "%s", name);
! 484: setenv("SHELL", shell, 1);
! 485:
! 486: setblocking(STDIN_FILENO, 1);
! 487: setblocking(STDOUT_FILENO, 1);
! 488: setblocking(STDERR_FILENO, 1);
! 489: closefrom(STDERR_FILENO + 1);
! 490:
! 491: execl(shell, argv0, "-c", shellcmd, (char *) NULL);
! 492: fatal("execl failed");
! 493: }
! 494:
! 495: /* Callback to handle signals in the client. */
! 496: static void
! 497: client_signal(int sig)
! 498: {
! 499: struct sigaction sigact;
! 500: int status;
! 501:
! 502: if (sig == SIGCHLD)
! 503: waitpid(WAIT_ANY, &status, WNOHANG);
! 504: else if (!client_attached) {
! 505: if (sig == SIGTERM)
! 506: proc_exit(client_proc);
! 507: } else {
! 508: switch (sig) {
! 509: case SIGHUP:
! 510: client_exitreason = CLIENT_EXIT_LOST_TTY;
! 511: client_exitval = 1;
! 512: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 513: break;
! 514: case SIGTERM:
! 515: client_exitreason = CLIENT_EXIT_TERMINATED;
! 516: client_exitval = 1;
! 517: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 518: break;
! 519: case SIGWINCH:
! 520: proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
! 521: break;
! 522: case SIGCONT:
! 523: memset(&sigact, 0, sizeof sigact);
! 524: sigemptyset(&sigact.sa_mask);
! 525: sigact.sa_flags = SA_RESTART;
! 526: sigact.sa_handler = SIG_IGN;
! 527: if (sigaction(SIGTSTP, &sigact, NULL) != 0)
! 528: fatal("sigaction failed");
! 529: proc_send(client_peer, MSG_WAKEUP, -1, NULL, 0);
! 530: break;
! 531: }
! 532: }
! 533: }
! 534:
! 535: /* Callback for client read events. */
! 536: static void
! 537: client_dispatch(struct imsg *imsg, void *arg)
! 538: {
! 539: if (imsg == NULL) {
! 540: client_exitreason = CLIENT_EXIT_LOST_SERVER;
! 541: client_exitval = 1;
! 542: proc_exit(client_proc);
! 543: return;
! 544: }
! 545:
! 546: if (client_attached)
! 547: client_dispatch_attached(imsg);
! 548: else
! 549: client_dispatch_wait(imsg, arg);
! 550: }
! 551:
! 552: /* Dispatch imsgs when in wait state (before MSG_READY). */
! 553: static void
! 554: client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
! 555: {
! 556: char *data;
! 557: ssize_t datalen;
! 558: struct msg_stdout_data stdoutdata;
! 559: struct msg_stderr_data stderrdata;
! 560: int retval;
! 561: static int pledge_applied;
! 562:
! 563: /*
! 564: * "sendfd" is no longer required once all of the identify messages
! 565: * have been sent. We know the server won't send us anything until that
! 566: * point (because we don't ask it to), so we can drop "sendfd" once we
! 567: * get the first message from the server.
! 568: */
! 569: if (!pledge_applied) {
! 570: if (pledge("stdio unix proc exec tty", NULL) != 0)
! 571: fatal("pledge failed");
! 572: pledge_applied = 1;
! 573: };
! 574:
! 575: data = imsg->data;
! 576: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
! 577:
! 578: switch (imsg->hdr.type) {
! 579: case MSG_EXIT:
! 580: case MSG_SHUTDOWN:
! 581: if (datalen != sizeof retval && datalen != 0)
! 582: fatalx("bad MSG_EXIT size");
! 583: if (datalen == sizeof retval) {
! 584: memcpy(&retval, data, sizeof retval);
! 585: client_exitval = retval;
! 586: }
! 587: proc_exit(client_proc);
! 588: break;
! 589: case MSG_READY:
! 590: if (datalen != 0)
! 591: fatalx("bad MSG_READY size");
! 592:
! 593: event_del(&client_stdin);
! 594: client_attached = 1;
! 595: proc_send(client_peer, MSG_RESIZE, -1, NULL, 0);
! 596: break;
! 597: case MSG_STDIN:
! 598: if (datalen != 0)
! 599: fatalx("bad MSG_STDIN size");
! 600:
! 601: event_add(&client_stdin, NULL);
! 602: break;
! 603: case MSG_STDOUT:
! 604: if (datalen != sizeof stdoutdata)
! 605: fatalx("bad MSG_STDOUT size");
! 606: memcpy(&stdoutdata, data, sizeof stdoutdata);
! 607:
! 608: client_write(STDOUT_FILENO, stdoutdata.data,
! 609: stdoutdata.size);
! 610: break;
! 611: case MSG_STDERR:
! 612: if (datalen != sizeof stderrdata)
! 613: fatalx("bad MSG_STDERR size");
! 614: memcpy(&stderrdata, data, sizeof stderrdata);
! 615:
! 616: client_write(STDERR_FILENO, stderrdata.data,
! 617: stderrdata.size);
! 618: break;
! 619: case MSG_VERSION:
! 620: if (datalen != 0)
! 621: fatalx("bad MSG_VERSION size");
! 622:
! 623: fprintf(stderr, "protocol version mismatch "
! 624: "(client %d, server %u)\n", PROTOCOL_VERSION,
! 625: imsg->hdr.peerid & 0xff);
! 626: client_exitval = 1;
! 627: proc_exit(client_proc);
! 628: break;
! 629: case MSG_SHELL:
! 630: if (datalen == 0 || data[datalen - 1] != '\0')
! 631: fatalx("bad MSG_SHELL string");
! 632:
! 633: clear_signals(0);
! 634: client_exec(data, shellcmd);
! 635: /* NOTREACHED */
! 636: case MSG_DETACH:
! 637: case MSG_DETACHKILL:
! 638: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 639: break;
! 640: case MSG_EXITED:
! 641: proc_exit(client_proc);
! 642: break;
! 643: }
! 644: }
! 645:
! 646: /* Dispatch imsgs in attached state (after MSG_READY). */
! 647: static void
! 648: client_dispatch_attached(struct imsg *imsg)
! 649: {
! 650: struct sigaction sigact;
! 651: char *data;
! 652: ssize_t datalen;
! 653:
! 654: data = imsg->data;
! 655: datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
! 656:
! 657: switch (imsg->hdr.type) {
! 658: case MSG_DETACH:
! 659: case MSG_DETACHKILL:
! 660: if (datalen == 0 || data[datalen - 1] != '\0')
! 661: fatalx("bad MSG_DETACH string");
! 662:
! 663: client_exitsession = xstrdup(data);
! 664: client_exittype = imsg->hdr.type;
! 665: if (imsg->hdr.type == MSG_DETACHKILL)
! 666: client_exitreason = CLIENT_EXIT_DETACHED_HUP;
! 667: else
! 668: client_exitreason = CLIENT_EXIT_DETACHED;
! 669: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 670: break;
! 671: case MSG_EXEC:
! 672: if (datalen == 0 || data[datalen - 1] != '\0' ||
! 673: strlen(data) + 1 == (size_t)datalen)
! 674: fatalx("bad MSG_EXEC string");
! 675: client_execcmd = xstrdup(data);
! 676: client_execshell = xstrdup(data + strlen(data) + 1);
! 677:
! 678: client_exittype = imsg->hdr.type;
! 679: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 680: break;
! 681: case MSG_EXIT:
! 682: if (datalen != 0 && datalen != sizeof (int))
! 683: fatalx("bad MSG_EXIT size");
! 684:
! 685: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 686: client_exitreason = CLIENT_EXIT_EXITED;
! 687: break;
! 688: case MSG_EXITED:
! 689: if (datalen != 0)
! 690: fatalx("bad MSG_EXITED size");
! 691:
! 692: proc_exit(client_proc);
! 693: break;
! 694: case MSG_SHUTDOWN:
! 695: if (datalen != 0)
! 696: fatalx("bad MSG_SHUTDOWN size");
! 697:
! 698: proc_send(client_peer, MSG_EXITING, -1, NULL, 0);
! 699: client_exitreason = CLIENT_EXIT_SERVER_EXITED;
! 700: client_exitval = 1;
! 701: break;
! 702: case MSG_SUSPEND:
! 703: if (datalen != 0)
! 704: fatalx("bad MSG_SUSPEND size");
! 705:
! 706: memset(&sigact, 0, sizeof sigact);
! 707: sigemptyset(&sigact.sa_mask);
! 708: sigact.sa_flags = SA_RESTART;
! 709: sigact.sa_handler = SIG_DFL;
! 710: if (sigaction(SIGTSTP, &sigact, NULL) != 0)
! 711: fatal("sigaction failed");
! 712: kill(getpid(), SIGTSTP);
! 713: break;
! 714: case MSG_LOCK:
! 715: if (datalen == 0 || data[datalen - 1] != '\0')
! 716: fatalx("bad MSG_LOCK string");
! 717:
! 718: system(data);
! 719: proc_send(client_peer, MSG_UNLOCK, -1, NULL, 0);
! 720: break;
! 721: }
! 722: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>