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