Annotation of src/usr.bin/ssh/mux.c, Revision 1.1
1.1 ! djm 1: /* $OpenBSD$ */
! 2: /*
! 3: * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
! 4: *
! 5: * Permission to use, copy, modify, and distribute this software for any
! 6: * purpose with or without fee is hereby granted, provided that the above
! 7: * copyright notice and this permission notice appear in all copies.
! 8: *
! 9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 16: */
! 17:
! 18: /* ssh session multiplexing support */
! 19:
! 20: #include <sys/types.h>
! 21: #include <sys/param.h>
! 22: #include <sys/queue.h>
! 23: #include <sys/stat.h>
! 24: #include <sys/socket.h>
! 25: #include <sys/un.h>
! 26:
! 27: #include <errno.h>
! 28: #include <fcntl.h>
! 29: #include <signal.h>
! 30: #include <stdarg.h>
! 31: #include <stddef.h>
! 32: #include <stdlib.h>
! 33: #include <stdio.h>
! 34: #include <string.h>
! 35: #include <unistd.h>
! 36: #include <util.h>
! 37: #include <paths.h>
! 38:
! 39: #include "xmalloc.h"
! 40: #include "log.h"
! 41: #include "ssh.h"
! 42: #include "pathnames.h"
! 43: #include "misc.h"
! 44: #include "match.h"
! 45: #include "buffer.h"
! 46: #include "channels.h"
! 47: #include "msg.h"
! 48: #include "packet.h"
! 49: #include "monitor_fdpass.h"
! 50: #include "sshpty.h"
! 51: #include "key.h"
! 52: #include "readconf.h"
! 53: #include "clientloop.h"
! 54:
! 55: /* from ssh.c */
! 56: extern int tty_flag;
! 57: extern Options options;
! 58: extern int stdin_null_flag;
! 59: extern char *host;
! 60: int subsystem_flag;
! 61: extern Buffer command;
! 62:
! 63: /* fd to control socket */
! 64: int muxserver_sock = -1;
! 65:
! 66: /* Multiplexing control command */
! 67: u_int muxclient_command = 0;
! 68:
! 69: /* Set when signalled. */
! 70: static volatile sig_atomic_t muxclient_terminate = 0;
! 71:
! 72: /* PID of multiplex server */
! 73: static u_int muxserver_pid = 0;
! 74:
! 75:
! 76: /* ** Multiplexing master support */
! 77:
! 78: /* Prepare a mux master to listen on a Unix domain socket. */
! 79: void
! 80: muxserver_listen(void)
! 81: {
! 82: struct sockaddr_un addr;
! 83: mode_t old_umask;
! 84:
! 85: if (options.control_path == NULL ||
! 86: options.control_master == SSHCTL_MASTER_NO)
! 87: return;
! 88:
! 89: debug("setting up multiplex master socket");
! 90:
! 91: memset(&addr, '\0', sizeof(addr));
! 92: addr.sun_family = AF_UNIX;
! 93: addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
! 94: strlen(options.control_path) + 1;
! 95:
! 96: if (strlcpy(addr.sun_path, options.control_path,
! 97: sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
! 98: fatal("ControlPath too long");
! 99:
! 100: if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
! 101: fatal("%s socket(): %s", __func__, strerror(errno));
! 102:
! 103: old_umask = umask(0177);
! 104: if (bind(muxserver_sock, (struct sockaddr *)&addr, addr.sun_len) == -1) {
! 105: muxserver_sock = -1;
! 106: if (errno == EINVAL || errno == EADDRINUSE)
! 107: fatal("ControlSocket %s already exists",
! 108: options.control_path);
! 109: else
! 110: fatal("%s bind(): %s", __func__, strerror(errno));
! 111: }
! 112: umask(old_umask);
! 113:
! 114: if (listen(muxserver_sock, 64) == -1)
! 115: fatal("%s listen(): %s", __func__, strerror(errno));
! 116:
! 117: set_nonblock(muxserver_sock);
! 118: }
! 119:
! 120: /* Callback on open confirmation in mux master for a mux client session. */
! 121: static void
! 122: client_extra_session2_setup(int id, void *arg)
! 123: {
! 124: struct mux_session_confirm_ctx *cctx = arg;
! 125: const char *display;
! 126: Channel *c;
! 127: int i;
! 128:
! 129: if (cctx == NULL)
! 130: fatal("%s: cctx == NULL", __func__);
! 131: if ((c = channel_lookup(id)) == NULL)
! 132: fatal("%s: no channel for id %d", __func__, id);
! 133:
! 134: display = getenv("DISPLAY");
! 135: if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
! 136: char *proto, *data;
! 137: /* Get reasonable local authentication information. */
! 138: client_x11_get_proto(display, options.xauth_location,
! 139: options.forward_x11_trusted, &proto, &data);
! 140: /* Request forwarding with authentication spoofing. */
! 141: debug("Requesting X11 forwarding with authentication spoofing.");
! 142: x11_request_forwarding_with_spoofing(id, display, proto, data);
! 143: /* XXX wait for reply */
! 144: }
! 145:
! 146: if (cctx->want_agent_fwd && options.forward_agent) {
! 147: debug("Requesting authentication agent forwarding.");
! 148: channel_request_start(id, "auth-agent-req@openssh.com", 0);
! 149: packet_send();
! 150: }
! 151:
! 152: client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
! 153: cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env);
! 154:
! 155: c->open_confirm_ctx = NULL;
! 156: buffer_free(&cctx->cmd);
! 157: xfree(cctx->term);
! 158: if (cctx->env != NULL) {
! 159: for (i = 0; cctx->env[i] != NULL; i++)
! 160: xfree(cctx->env[i]);
! 161: xfree(cctx->env);
! 162: }
! 163: xfree(cctx);
! 164: }
! 165:
! 166: /*
! 167: * Accept a connection on the mux master socket and process the
! 168: * client's request. Returns flag indicating whether mux master should
! 169: * begin graceful close.
! 170: */
! 171: int
! 172: muxserver_accept_control(void)
! 173: {
! 174: Buffer m;
! 175: Channel *c;
! 176: int client_fd, new_fd[3], ver, allowed, window, packetmax;
! 177: socklen_t addrlen;
! 178: struct sockaddr_storage addr;
! 179: struct mux_session_confirm_ctx *cctx;
! 180: char *cmd;
! 181: u_int i, j, len, env_len, mux_command, flags;
! 182: uid_t euid;
! 183: gid_t egid;
! 184: int start_close = 0;
! 185:
! 186: /*
! 187: * Accept connection on control socket
! 188: */
! 189: memset(&addr, 0, sizeof(addr));
! 190: addrlen = sizeof(addr);
! 191: if ((client_fd = accept(muxserver_sock,
! 192: (struct sockaddr*)&addr, &addrlen)) == -1) {
! 193: error("%s accept: %s", __func__, strerror(errno));
! 194: return 0;
! 195: }
! 196:
! 197: if (getpeereid(client_fd, &euid, &egid) < 0) {
! 198: error("%s getpeereid failed: %s", __func__, strerror(errno));
! 199: close(client_fd);
! 200: return 0;
! 201: }
! 202: if ((euid != 0) && (getuid() != euid)) {
! 203: error("control mode uid mismatch: peer euid %u != uid %u",
! 204: (u_int) euid, (u_int) getuid());
! 205: close(client_fd);
! 206: return 0;
! 207: }
! 208:
! 209: /* XXX handle asynchronously */
! 210: unset_nonblock(client_fd);
! 211:
! 212: /* Read command */
! 213: buffer_init(&m);
! 214: if (ssh_msg_recv(client_fd, &m) == -1) {
! 215: error("%s: client msg_recv failed", __func__);
! 216: close(client_fd);
! 217: buffer_free(&m);
! 218: return 0;
! 219: }
! 220: if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
! 221: error("%s: wrong client version %d", __func__, ver);
! 222: buffer_free(&m);
! 223: close(client_fd);
! 224: return 0;
! 225: }
! 226:
! 227: allowed = 1;
! 228: mux_command = buffer_get_int(&m);
! 229: flags = buffer_get_int(&m);
! 230:
! 231: buffer_clear(&m);
! 232:
! 233: switch (mux_command) {
! 234: case SSHMUX_COMMAND_OPEN:
! 235: if (options.control_master == SSHCTL_MASTER_ASK ||
! 236: options.control_master == SSHCTL_MASTER_AUTO_ASK)
! 237: allowed = ask_permission("Allow shared connection "
! 238: "to %s? ", host);
! 239: /* continue below */
! 240: break;
! 241: case SSHMUX_COMMAND_TERMINATE:
! 242: if (options.control_master == SSHCTL_MASTER_ASK ||
! 243: options.control_master == SSHCTL_MASTER_AUTO_ASK)
! 244: allowed = ask_permission("Terminate shared connection "
! 245: "to %s? ", host);
! 246: if (allowed)
! 247: start_close = 1;
! 248: /* FALLTHROUGH */
! 249: case SSHMUX_COMMAND_ALIVE_CHECK:
! 250: /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
! 251: buffer_clear(&m);
! 252: buffer_put_int(&m, allowed);
! 253: buffer_put_int(&m, getpid());
! 254: if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
! 255: error("%s: client msg_send failed", __func__);
! 256: close(client_fd);
! 257: buffer_free(&m);
! 258: return start_close;
! 259: }
! 260: buffer_free(&m);
! 261: close(client_fd);
! 262: return start_close;
! 263: default:
! 264: error("Unsupported command %d", mux_command);
! 265: buffer_free(&m);
! 266: close(client_fd);
! 267: return 0;
! 268: }
! 269:
! 270: /* Reply for SSHMUX_COMMAND_OPEN */
! 271: buffer_clear(&m);
! 272: buffer_put_int(&m, allowed);
! 273: buffer_put_int(&m, getpid());
! 274: if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
! 275: error("%s: client msg_send failed", __func__);
! 276: close(client_fd);
! 277: buffer_free(&m);
! 278: return 0;
! 279: }
! 280:
! 281: if (!allowed) {
! 282: error("Refused control connection");
! 283: close(client_fd);
! 284: buffer_free(&m);
! 285: return 0;
! 286: }
! 287:
! 288: buffer_clear(&m);
! 289: if (ssh_msg_recv(client_fd, &m) == -1) {
! 290: error("%s: client msg_recv failed", __func__);
! 291: close(client_fd);
! 292: buffer_free(&m);
! 293: return 0;
! 294: }
! 295: if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
! 296: error("%s: wrong client version %d", __func__, ver);
! 297: buffer_free(&m);
! 298: close(client_fd);
! 299: return 0;
! 300: }
! 301:
! 302: cctx = xcalloc(1, sizeof(*cctx));
! 303: cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
! 304: cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
! 305: cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
! 306: cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
! 307: cctx->term = buffer_get_string(&m, &len);
! 308:
! 309: cmd = buffer_get_string(&m, &len);
! 310: buffer_init(&cctx->cmd);
! 311: buffer_append(&cctx->cmd, cmd, strlen(cmd));
! 312:
! 313: env_len = buffer_get_int(&m);
! 314: env_len = MIN(env_len, 4096);
! 315: debug3("%s: receiving %d env vars", __func__, env_len);
! 316: if (env_len != 0) {
! 317: cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env));
! 318: for (i = 0; i < env_len; i++)
! 319: cctx->env[i] = buffer_get_string(&m, &len);
! 320: cctx->env[i] = NULL;
! 321: }
! 322:
! 323: debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
! 324: cctx->want_tty, cctx->want_subsys, cmd);
! 325: xfree(cmd);
! 326:
! 327: /* Gather fds from client */
! 328: for(i = 0; i < 3; i++) {
! 329: if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) {
! 330: error("%s: failed to receive fd %d from slave",
! 331: __func__, i);
! 332: for (j = 0; j < i; j++)
! 333: close(new_fd[j]);
! 334: for (j = 0; j < env_len; j++)
! 335: xfree(cctx->env[j]);
! 336: if (env_len > 0)
! 337: xfree(cctx->env);
! 338: xfree(cctx->term);
! 339: buffer_free(&cctx->cmd);
! 340: close(client_fd);
! 341: xfree(cctx);
! 342: return 0;
! 343: }
! 344: }
! 345:
! 346: debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
! 347: new_fd[0], new_fd[1], new_fd[2]);
! 348:
! 349: /* Try to pick up ttymodes from client before it goes raw */
! 350: if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
! 351: error("%s: tcgetattr: %s", __func__, strerror(errno));
! 352:
! 353: /* This roundtrip is just for synchronisation of ttymodes */
! 354: buffer_clear(&m);
! 355: if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
! 356: error("%s: client msg_send failed", __func__);
! 357: close(client_fd);
! 358: close(new_fd[0]);
! 359: close(new_fd[1]);
! 360: close(new_fd[2]);
! 361: buffer_free(&m);
! 362: xfree(cctx->term);
! 363: if (env_len != 0) {
! 364: for (i = 0; i < env_len; i++)
! 365: xfree(cctx->env[i]);
! 366: xfree(cctx->env);
! 367: }
! 368: return 0;
! 369: }
! 370: buffer_free(&m);
! 371:
! 372: /* enable nonblocking unless tty */
! 373: if (!isatty(new_fd[0]))
! 374: set_nonblock(new_fd[0]);
! 375: if (!isatty(new_fd[1]))
! 376: set_nonblock(new_fd[1]);
! 377: if (!isatty(new_fd[2]))
! 378: set_nonblock(new_fd[2]);
! 379:
! 380: set_nonblock(client_fd);
! 381:
! 382: window = CHAN_SES_WINDOW_DEFAULT;
! 383: packetmax = CHAN_SES_PACKET_DEFAULT;
! 384: if (cctx->want_tty) {
! 385: window >>= 1;
! 386: packetmax >>= 1;
! 387: }
! 388:
! 389: c = channel_new("session", SSH_CHANNEL_OPENING,
! 390: new_fd[0], new_fd[1], new_fd[2], window, packetmax,
! 391: CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
! 392:
! 393: /* XXX */
! 394: c->ctl_fd = client_fd;
! 395:
! 396: debug3("%s: channel_new: %d", __func__, c->self);
! 397:
! 398: channel_send_open(c->self);
! 399: channel_register_open_confirm(c->self,
! 400: client_extra_session2_setup, cctx);
! 401: return 0;
! 402: }
! 403:
! 404: /* ** Multiplexing client support */
! 405:
! 406: /* Exit signal handler */
! 407: static void
! 408: control_client_sighandler(int signo)
! 409: {
! 410: muxclient_terminate = signo;
! 411: }
! 412:
! 413: /*
! 414: * Relay signal handler - used to pass some signals from mux client to
! 415: * mux master.
! 416: */
! 417: static void
! 418: control_client_sigrelay(int signo)
! 419: {
! 420: int save_errno = errno;
! 421:
! 422: if (muxserver_pid > 1)
! 423: kill(muxserver_pid, signo);
! 424:
! 425: errno = save_errno;
! 426: }
! 427:
! 428: /* Check mux client environment variables before passing them to mux master. */
! 429: static int
! 430: env_permitted(char *env)
! 431: {
! 432: int i, ret;
! 433: char name[1024], *cp;
! 434:
! 435: if ((cp = strchr(env, '=')) == NULL || cp == env)
! 436: return (0);
! 437: ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env);
! 438: if (ret <= 0 || (size_t)ret >= sizeof(name))
! 439: fatal("env_permitted: name '%.100s...' too long", env);
! 440:
! 441: for (i = 0; i < options.num_send_env; i++)
! 442: if (match_pattern(name, options.send_env[i]))
! 443: return (1);
! 444:
! 445: return (0);
! 446: }
! 447:
! 448: /* Multiplex client main loop. */
! 449: void
! 450: muxclient(const char *path)
! 451: {
! 452: struct sockaddr_un addr;
! 453: int i, r, fd, sock, exitval[2], num_env;
! 454: Buffer m;
! 455: char *term;
! 456: extern char **environ;
! 457: u_int flags;
! 458:
! 459: if (muxclient_command == 0)
! 460: muxclient_command = SSHMUX_COMMAND_OPEN;
! 461:
! 462: switch (options.control_master) {
! 463: case SSHCTL_MASTER_AUTO:
! 464: case SSHCTL_MASTER_AUTO_ASK:
! 465: debug("auto-mux: Trying existing master");
! 466: /* FALLTHROUGH */
! 467: case SSHCTL_MASTER_NO:
! 468: break;
! 469: default:
! 470: return;
! 471: }
! 472:
! 473: memset(&addr, '\0', sizeof(addr));
! 474: addr.sun_family = AF_UNIX;
! 475: addr.sun_len = offsetof(struct sockaddr_un, sun_path) +
! 476: strlen(path) + 1;
! 477:
! 478: if (strlcpy(addr.sun_path, path,
! 479: sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
! 480: fatal("ControlPath too long");
! 481:
! 482: if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
! 483: fatal("%s socket(): %s", __func__, strerror(errno));
! 484:
! 485: if (connect(sock, (struct sockaddr *)&addr, addr.sun_len) == -1) {
! 486: if (muxclient_command != SSHMUX_COMMAND_OPEN) {
! 487: fatal("Control socket connect(%.100s): %s", path,
! 488: strerror(errno));
! 489: }
! 490: if (errno == ENOENT)
! 491: debug("Control socket \"%.100s\" does not exist", path);
! 492: else {
! 493: error("Control socket connect(%.100s): %s", path,
! 494: strerror(errno));
! 495: }
! 496: close(sock);
! 497: return;
! 498: }
! 499:
! 500: if (stdin_null_flag) {
! 501: if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
! 502: fatal("open(/dev/null): %s", strerror(errno));
! 503: if (dup2(fd, STDIN_FILENO) == -1)
! 504: fatal("dup2: %s", strerror(errno));
! 505: if (fd > STDERR_FILENO)
! 506: close(fd);
! 507: }
! 508:
! 509: term = getenv("TERM");
! 510:
! 511: flags = 0;
! 512: if (tty_flag)
! 513: flags |= SSHMUX_FLAG_TTY;
! 514: if (subsystem_flag)
! 515: flags |= SSHMUX_FLAG_SUBSYS;
! 516: if (options.forward_x11)
! 517: flags |= SSHMUX_FLAG_X11_FWD;
! 518: if (options.forward_agent)
! 519: flags |= SSHMUX_FLAG_AGENT_FWD;
! 520:
! 521: signal(SIGPIPE, SIG_IGN);
! 522:
! 523: buffer_init(&m);
! 524:
! 525: /* Send our command to server */
! 526: buffer_put_int(&m, muxclient_command);
! 527: buffer_put_int(&m, flags);
! 528: if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)
! 529: fatal("%s: msg_send", __func__);
! 530: buffer_clear(&m);
! 531:
! 532: /* Get authorisation status and PID of controlee */
! 533: if (ssh_msg_recv(sock, &m) == -1)
! 534: fatal("%s: msg_recv", __func__);
! 535: if (buffer_get_char(&m) != SSHMUX_VER)
! 536: fatal("%s: wrong version", __func__);
! 537: if (buffer_get_int(&m) != 1)
! 538: fatal("Connection to master denied");
! 539: muxserver_pid = buffer_get_int(&m);
! 540:
! 541: buffer_clear(&m);
! 542:
! 543: switch (muxclient_command) {
! 544: case SSHMUX_COMMAND_ALIVE_CHECK:
! 545: fprintf(stderr, "Master running (pid=%d)\r\n",
! 546: muxserver_pid);
! 547: exit(0);
! 548: case SSHMUX_COMMAND_TERMINATE:
! 549: fprintf(stderr, "Exit request sent.\r\n");
! 550: exit(0);
! 551: case SSHMUX_COMMAND_OPEN:
! 552: /* continue below */
! 553: break;
! 554: default:
! 555: fatal("silly muxclient_command %d", muxclient_command);
! 556: }
! 557:
! 558: /* SSHMUX_COMMAND_OPEN */
! 559: buffer_put_cstring(&m, term ? term : "");
! 560: buffer_append(&command, "\0", 1);
! 561: buffer_put_cstring(&m, buffer_ptr(&command));
! 562:
! 563: if (options.num_send_env == 0 || environ == NULL) {
! 564: buffer_put_int(&m, 0);
! 565: } else {
! 566: /* Pass environment */
! 567: num_env = 0;
! 568: for (i = 0; environ[i] != NULL; i++)
! 569: if (env_permitted(environ[i]))
! 570: num_env++; /* Count */
! 571:
! 572: buffer_put_int(&m, num_env);
! 573:
! 574: for (i = 0; environ[i] != NULL && num_env >= 0; i++)
! 575: if (env_permitted(environ[i])) {
! 576: num_env--;
! 577: buffer_put_cstring(&m, environ[i]);
! 578: }
! 579: }
! 580:
! 581: if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)
! 582: fatal("%s: msg_send", __func__);
! 583:
! 584: if (mm_send_fd(sock, STDIN_FILENO) == -1 ||
! 585: mm_send_fd(sock, STDOUT_FILENO) == -1 ||
! 586: mm_send_fd(sock, STDERR_FILENO) == -1)
! 587: fatal("%s: send fds failed", __func__);
! 588:
! 589: /* Wait for reply, so master has a chance to gather ttymodes */
! 590: buffer_clear(&m);
! 591: if (ssh_msg_recv(sock, &m) == -1)
! 592: fatal("%s: msg_recv", __func__);
! 593: if (buffer_get_char(&m) != SSHMUX_VER)
! 594: fatal("%s: wrong version", __func__);
! 595: buffer_free(&m);
! 596:
! 597: signal(SIGHUP, control_client_sighandler);
! 598: signal(SIGINT, control_client_sighandler);
! 599: signal(SIGTERM, control_client_sighandler);
! 600: signal(SIGWINCH, control_client_sigrelay);
! 601:
! 602: if (tty_flag)
! 603: enter_raw_mode();
! 604:
! 605: /*
! 606: * Stick around until the controlee closes the client_fd.
! 607: * Before it does, it is expected to write this process' exit
! 608: * value (one int). This process must read the value and wait for
! 609: * the closure of the client_fd; if this one closes early, the
! 610: * multiplex master will terminate early too (possibly losing data).
! 611: */
! 612: exitval[0] = 0;
! 613: for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) {
! 614: r = read(sock, (char *)exitval + i, sizeof(exitval) - i);
! 615: if (r == 0) {
! 616: debug2("Received EOF from master");
! 617: break;
! 618: }
! 619: if (r == -1) {
! 620: if (errno == EINTR)
! 621: continue;
! 622: fatal("%s: read %s", __func__, strerror(errno));
! 623: }
! 624: i += r;
! 625: }
! 626:
! 627: close(sock);
! 628: leave_raw_mode();
! 629: if (i > (int)sizeof(int))
! 630: fatal("%s: master returned too much data (%d > %lu)",
! 631: __func__, i, sizeof(int));
! 632: if (muxclient_terminate) {
! 633: debug2("Exiting on signal %d", muxclient_terminate);
! 634: exitval[0] = 255;
! 635: } else if (i < (int)sizeof(int)) {
! 636: debug2("Control master terminated unexpectedly");
! 637: exitval[0] = 255;
! 638: } else
! 639: debug2("Received exit status from master %d", exitval[0]);
! 640:
! 641: if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET)
! 642: fprintf(stderr, "Shared connection to %s closed.\r\n", host);
! 643:
! 644: exit(exitval[0]);
! 645: }