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