blob: fc959baef8e78c04508fb69dcca43df230a20576 [file] [log] [blame]
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001/*
Damien Miller95def091999-11-25 00:26:21 +11002 * Author: Tatu Ylonen <ylo@cs.hut.fi>
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved
5 * Created: Sun Sep 10 00:30:37 1995 ylo
6 * Server main loop for handling the interactive session.
7 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10008
9#include "includes.h"
10#include "xmalloc.h"
11#include "ssh.h"
12#include "packet.h"
13#include "buffer.h"
14#include "servconf.h"
15#include "pty.h"
16
17static Buffer stdin_buffer; /* Buffer for stdin data. */
18static Buffer stdout_buffer; /* Buffer for stdout data. */
19static Buffer stderr_buffer; /* Buffer for stderr data. */
20static int fdin; /* Descriptor for stdin (for writing) */
21static int fdout; /* Descriptor for stdout (for reading);
22 May be same number as fdin. */
23static int fderr; /* Descriptor for stderr. May be -1. */
24static long stdin_bytes = 0; /* Number of bytes written to stdin. */
25static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */
26static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */
27static long fdout_bytes = 0; /* Number of stdout bytes read from program. */
28static int stdin_eof = 0; /* EOF message received from client. */
29static int fdout_eof = 0; /* EOF encountered reading from fdout. */
30static int fderr_eof = 0; /* EOF encountered readung from fderr. */
31static int connection_in; /* Connection to client (input). */
32static int connection_out; /* Connection to client (output). */
33static unsigned int buffer_high;/* "Soft" max buffer size. */
34static int max_fd; /* Max file descriptor number for select(). */
35
36/* This SIGCHLD kludge is used to detect when the child exits. The server
37 will exit after that, as soon as forwarded connections have terminated. */
38
Damien Miller95def091999-11-25 00:26:21 +110039static int child_pid; /* Pid of the child. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100040static volatile int child_terminated; /* The child has terminated. */
41static volatile int child_wait_status; /* Status from wait(). */
42
Damien Miller95def091999-11-25 00:26:21 +110043void
44sigchld_handler(int sig)
Damien Millerd4a8b7e1999-10-27 13:42:43 +100045{
Damien Miller95def091999-11-25 00:26:21 +110046 int save_errno = errno;
47 int wait_pid;
48 debug("Received SIGCHLD.");
49 wait_pid = wait((int *) &child_wait_status);
50 if (wait_pid != -1) {
51 if (wait_pid != child_pid)
52 error("Strange, got SIGCHLD and wait returned pid %d but child is %d",
53 wait_pid, child_pid);
54 if (WIFEXITED(child_wait_status) ||
55 WIFSIGNALED(child_wait_status))
56 child_terminated = 1;
57 }
58 signal(SIGCHLD, sigchld_handler);
59 errno = save_errno;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100060}
61
Damien Miller95def091999-11-25 00:26:21 +110062/*
63 * Process any buffered packets that have been received from the client.
64 */
65void
66process_buffered_input_packets()
Damien Millerd4a8b7e1999-10-27 13:42:43 +100067{
Damien Miller95def091999-11-25 00:26:21 +110068 int type;
69 char *data;
70 unsigned int data_len;
71 int row, col, xpixel, ypixel;
72 int payload_len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100073
Damien Miller95def091999-11-25 00:26:21 +110074 /* Process buffered packets from the client. */
75 while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
76 switch (type) {
77 case SSH_CMSG_STDIN_DATA:
78 /* Stdin data from the client. Append it to the buffer. */
79 /* Ignore any data if the client has closed stdin. */
80 if (fdin == -1)
81 break;
82 data = packet_get_string(&data_len);
83 packet_integrity_check(payload_len, (4 + data_len), type);
84 buffer_append(&stdin_buffer, data, data_len);
85 memset(data, 0, data_len);
86 xfree(data);
87 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100088
Damien Miller95def091999-11-25 00:26:21 +110089 case SSH_CMSG_EOF:
90 /* Eof from the client. The stdin descriptor to
91 the program will be closed when all buffered
92 data has drained. */
93 debug("EOF received for stdin.");
94 packet_integrity_check(payload_len, 0, type);
95 stdin_eof = 1;
96 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100097
Damien Miller95def091999-11-25 00:26:21 +110098 case SSH_CMSG_WINDOW_SIZE:
99 debug("Window change received.");
100 packet_integrity_check(payload_len, 4 * 4, type);
101 row = packet_get_int();
102 col = packet_get_int();
103 xpixel = packet_get_int();
104 ypixel = packet_get_int();
105 if (fdin != -1)
106 pty_change_window_size(fdin, row, col, xpixel, ypixel);
107 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000108
Damien Miller95def091999-11-25 00:26:21 +1100109 case SSH_MSG_PORT_OPEN:
110 debug("Received port open request.");
111 channel_input_port_open(payload_len);
112 break;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000113
Damien Miller95def091999-11-25 00:26:21 +1100114 case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
115 debug("Received channel open confirmation.");
116 packet_integrity_check(payload_len, 4 + 4, type);
117 channel_input_open_confirmation();
118 break;
119
120 case SSH_MSG_CHANNEL_OPEN_FAILURE:
121 debug("Received channel open failure.");
122 packet_integrity_check(payload_len, 4, type);
123 channel_input_open_failure();
124 break;
125
126 case SSH_MSG_CHANNEL_DATA:
127 channel_input_data(payload_len);
128 break;
129
130 case SSH_MSG_CHANNEL_CLOSE:
131 debug("Received channel close.");
132 packet_integrity_check(payload_len, 4, type);
133 channel_input_close();
134 break;
135
136 case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
137 debug("Received channel close confirmation.");
138 packet_integrity_check(payload_len, 4, type);
139 channel_input_close_confirmation();
140 break;
141
142 default:
143 /* In this phase, any unexpected messages cause a
144 protocol error. This is to ease debugging;
145 also, since no confirmations are sent messages,
146 unprocessed unknown messages could cause
147 strange problems. Any compatible protocol
148 extensions must be negotiated before entering
149 the interactive session. */
150 packet_disconnect("Protocol error during session: type %d",
151 type);
152 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000153 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000154}
155
Damien Miller95def091999-11-25 00:26:21 +1100156/*
157 * Make packets from buffered stderr data, and buffer it for sending
158 * to the client.
159 */
160void
161make_packets_from_stderr_data()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000162{
Damien Miller95def091999-11-25 00:26:21 +1100163 int len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000164
Damien Miller95def091999-11-25 00:26:21 +1100165 /* Send buffered stderr data to the client. */
166 while (buffer_len(&stderr_buffer) > 0 &&
167 packet_not_very_much_data_to_write()) {
168 len = buffer_len(&stderr_buffer);
169 if (packet_is_interactive()) {
170 if (len > 512)
171 len = 512;
172 } else {
173 /* Keep the packets at reasonable size. */
174 if (len > packet_get_maxsize())
175 len = packet_get_maxsize();
176 }
177 packet_start(SSH_SMSG_STDERR_DATA);
178 packet_put_string(buffer_ptr(&stderr_buffer), len);
179 packet_send();
180 buffer_consume(&stderr_buffer, len);
181 stderr_bytes += len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000182 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000183}
184
Damien Miller95def091999-11-25 00:26:21 +1100185/*
186 * Make packets from buffered stdout data, and buffer it for sending to the
187 * client.
188 */
189void
190make_packets_from_stdout_data()
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000191{
Damien Miller95def091999-11-25 00:26:21 +1100192 int len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000193
Damien Miller95def091999-11-25 00:26:21 +1100194 /* Send buffered stdout data to the client. */
195 while (buffer_len(&stdout_buffer) > 0 &&
196 packet_not_very_much_data_to_write()) {
197 len = buffer_len(&stdout_buffer);
198 if (packet_is_interactive()) {
199 if (len > 512)
200 len = 512;
201 } else {
202 /* Keep the packets at reasonable size. */
203 if (len > packet_get_maxsize())
204 len = packet_get_maxsize();
205 }
206 packet_start(SSH_SMSG_STDOUT_DATA);
207 packet_put_string(buffer_ptr(&stdout_buffer), len);
208 packet_send();
209 buffer_consume(&stdout_buffer, len);
210 stdout_bytes += len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000211 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000212}
213
Damien Miller95def091999-11-25 00:26:21 +1100214/*
215 * Sleep in select() until we can do something. This will initialize the
216 * select masks. Upon return, the masks will indicate which descriptors
217 * have data or can accept data. Optionally, a maximum time can be specified
218 * for the duration of the wait (0 = infinite).
219 */
220void
221wait_until_can_do_something(fd_set * readset, fd_set * writeset,
222 unsigned int max_time_milliseconds)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000223{
Damien Miller95def091999-11-25 00:26:21 +1100224 struct timeval tv, *tvp;
225 int ret;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000226
Damien Miller95def091999-11-25 00:26:21 +1100227 /* When select fails we restart from here. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000228retry_select:
229
Damien Miller95def091999-11-25 00:26:21 +1100230 /* Initialize select() masks. */
231 FD_ZERO(readset);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000232
Damien Miller95def091999-11-25 00:26:21 +1100233 /* Read packets from the client unless we have too much buffered
234 stdin or channel data. */
235 if (buffer_len(&stdin_buffer) < 4096 &&
236 channel_not_very_much_buffered_data())
237 FD_SET(connection_in, readset);
238
239 /* If there is not too much data already buffered going to the
240 client, try to get some more data from the program. */
241 if (packet_not_very_much_data_to_write()) {
242 if (!fdout_eof)
243 FD_SET(fdout, readset);
244 if (!fderr_eof)
245 FD_SET(fderr, readset);
246 }
247 FD_ZERO(writeset);
248
249 /* Set masks for channel descriptors. */
250 channel_prepare_select(readset, writeset);
251
252 /* If we have buffered packet data going to the client, mark that
253 descriptor. */
254 if (packet_have_data_to_write())
255 FD_SET(connection_out, writeset);
256
257 /* If we have buffered data, try to write some of that data to the
258 program. */
259 if (fdin != -1 && buffer_len(&stdin_buffer) > 0)
260 FD_SET(fdin, writeset);
261
262 /* Update the maximum descriptor number if appropriate. */
263 if (channel_max_fd() > max_fd)
264 max_fd = channel_max_fd();
265
266 /* If child has terminated and there is enough buffer space to
267 read from it, then read as much as is available and exit. */
268 if (child_terminated && packet_not_very_much_data_to_write())
269 if (max_time_milliseconds == 0)
270 max_time_milliseconds = 100;
271
272 if (max_time_milliseconds == 0)
273 tvp = NULL;
274 else {
275 tv.tv_sec = max_time_milliseconds / 1000;
276 tv.tv_usec = 1000 * (max_time_milliseconds % 1000);
277 tvp = &tv;
278 }
279
280 /* Wait for something to happen, or the timeout to expire. */
281 ret = select(max_fd + 1, readset, writeset, NULL, tvp);
282
283 if (ret < 0) {
284 if (errno != EINTR)
285 error("select: %.100s", strerror(errno));
286 else
287 goto retry_select;
288 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000289}
290
Damien Miller95def091999-11-25 00:26:21 +1100291/*
292 * Processes input from the client and the program. Input data is stored
293 * in buffers and processed later.
294 */
295void
296process_input(fd_set * readset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000297{
Damien Miller95def091999-11-25 00:26:21 +1100298 int len;
299 char buf[16384];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000300
Damien Miller95def091999-11-25 00:26:21 +1100301 /* Read and buffer any input data from the client. */
302 if (FD_ISSET(connection_in, readset)) {
303 len = read(connection_in, buf, sizeof(buf));
304 if (len == 0) {
305 verbose("Connection closed by remote host.");
306 fatal_cleanup();
307 }
308 /* There is a kernel bug on Solaris that causes select to
309 sometimes wake up even though there is no data
310 available. */
311 if (len < 0 && errno == EAGAIN)
312 len = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000313
Damien Miller95def091999-11-25 00:26:21 +1100314 if (len < 0) {
315 verbose("Read error from remote host: %.100s", strerror(errno));
316 fatal_cleanup();
317 }
318 /* Buffer any received data. */
319 packet_process_incoming(buf, len);
320 }
321 /* Read and buffer any available stdout data from the program. */
322 if (!fdout_eof && FD_ISSET(fdout, readset)) {
323 len = read(fdout, buf, sizeof(buf));
324 if (len <= 0)
325 fdout_eof = 1;
326 else {
327 buffer_append(&stdout_buffer, buf, len);
328 fdout_bytes += len;
329 }
330 }
331 /* Read and buffer any available stderr data from the program. */
332 if (!fderr_eof && FD_ISSET(fderr, readset)) {
333 len = read(fderr, buf, sizeof(buf));
334 if (len <= 0)
335 fderr_eof = 1;
336 else
337 buffer_append(&stderr_buffer, buf, len);
338 }
339}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000340
Damien Miller95def091999-11-25 00:26:21 +1100341/*
342 * Sends data from internal buffers to client program stdin.
343 */
344void
345process_output(fd_set * writeset)
346{
347 int len;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000348
Damien Miller95def091999-11-25 00:26:21 +1100349 /* Write buffered data to program stdin. */
350 if (fdin != -1 && FD_ISSET(fdin, writeset)) {
351 len = write(fdin, buffer_ptr(&stdin_buffer),
352 buffer_len(&stdin_buffer));
353 if (len <= 0) {
354#ifdef USE_PIPES
355 close(fdin);
356#else
357 if (fdout == -1)
358 close(fdin);
359 else
360 shutdown(fdin, SHUT_WR); /* We will no longer send. */
361#endif
362 fdin = -1;
363 } else {
364 /* Successful write. Consume the data from the buffer. */
365 buffer_consume(&stdin_buffer, len);
366 /* Update the count of bytes written to the program. */
367 stdin_bytes += len;
368 }
369 }
370 /* Send any buffered packet data to the client. */
371 if (FD_ISSET(connection_out, writeset))
372 packet_write_poll();
373}
374
375/*
376 * Wait until all buffered output has been sent to the client.
377 * This is used when the program terminates.
378 */
379void
380drain_output()
381{
382 /* Send any buffered stdout data to the client. */
383 if (buffer_len(&stdout_buffer) > 0) {
384 packet_start(SSH_SMSG_STDOUT_DATA);
385 packet_put_string(buffer_ptr(&stdout_buffer),
386 buffer_len(&stdout_buffer));
387 packet_send();
388 /* Update the count of sent bytes. */
389 stdout_bytes += buffer_len(&stdout_buffer);
390 }
391 /* Send any buffered stderr data to the client. */
392 if (buffer_len(&stderr_buffer) > 0) {
393 packet_start(SSH_SMSG_STDERR_DATA);
394 packet_put_string(buffer_ptr(&stderr_buffer),
395 buffer_len(&stderr_buffer));
396 packet_send();
397 /* Update the count of sent bytes. */
398 stderr_bytes += buffer_len(&stderr_buffer);
399 }
400 /* Wait until all buffered data has been written to the client. */
401 packet_write_wait();
402}
403
404/*
405 * Performs the interactive session. This handles data transmission between
406 * the client and the program. Note that the notion of stdin, stdout, and
407 * stderr in this function is sort of reversed: this function writes to
408 * stdin (of the child program), and reads from stdout and stderr (of the
409 * child program).
410 */
411void
412server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg)
413{
414 int wait_status, wait_pid; /* Status and pid returned by wait(). */
415 int waiting_termination = 0; /* Have displayed waiting close message. */
416 unsigned int max_time_milliseconds;
417 unsigned int previous_stdout_buffer_bytes;
418 unsigned int stdout_buffer_bytes;
419 int type;
420
421 debug("Entering interactive session.");
422
423 /* Initialize the SIGCHLD kludge. */
424 child_pid = pid;
425 child_terminated = 0;
426 signal(SIGCHLD, sigchld_handler);
427
428 /* Initialize our global variables. */
429 fdin = fdin_arg;
430 fdout = fdout_arg;
431 fderr = fderr_arg;
432 connection_in = packet_get_connection_in();
433 connection_out = packet_get_connection_out();
434
435 previous_stdout_buffer_bytes = 0;
436
437 /* Set approximate I/O buffer size. */
438 if (packet_is_interactive())
439 buffer_high = 4096;
440 else
441 buffer_high = 64 * 1024;
442
443 /* Initialize max_fd to the maximum of the known file descriptors. */
444 max_fd = fdin;
445 if (fdout > max_fd)
446 max_fd = fdout;
447 if (fderr != -1 && fderr > max_fd)
448 max_fd = fderr;
449 if (connection_in > max_fd)
450 max_fd = connection_in;
451 if (connection_out > max_fd)
452 max_fd = connection_out;
453
454 /* Initialize Initialize buffers. */
455 buffer_init(&stdin_buffer);
456 buffer_init(&stdout_buffer);
457 buffer_init(&stderr_buffer);
458
459 /* If we have no separate fderr (which is the case when we have a
460 pty - there we cannot make difference between data sent to
461 stdout and stderr), indicate that we have seen an EOF from
462 stderr. This way we don\'t need to check the descriptor
463 everywhere. */
464 if (fderr == -1)
465 fderr_eof = 1;
466
467 /* Main loop of the server for the interactive session mode. */
468 for (;;) {
469 fd_set readset, writeset;
470
471 /* Process buffered packets from the client. */
472 process_buffered_input_packets();
473
474 /* If we have received eof, and there is no more pending
475 input data, cause a real eof by closing fdin. */
476 if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) {
477#ifdef USE_PIPES
478 close(fdin);
479#else
480 if (fdout == -1)
481 close(fdin);
482 else
483 shutdown(fdin, SHUT_WR); /* We will no longer send. */
484#endif
485 fdin = -1;
486 }
487 /* Make packets from buffered stderr data to send to the
488 client. */
489 make_packets_from_stderr_data();
490
491 /* Make packets from buffered stdout data to send to the
492 client. If there is very little to send, this arranges
493 to not send them now, but to wait a short while to see
494 if we are getting more data. This is necessary, as some
495 systems wake up readers from a pty after each separate
496 character. */
497 max_time_milliseconds = 0;
498 stdout_buffer_bytes = buffer_len(&stdout_buffer);
499 if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
500 stdout_buffer_bytes != previous_stdout_buffer_bytes) {
501 /* try again after a while */
502 max_time_milliseconds = 10;
503 } else {
504 /* Send it now. */
505 make_packets_from_stdout_data();
506 }
507 previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
508
509 /* Send channel data to the client. */
510 if (packet_not_very_much_data_to_write())
511 channel_output_poll();
512
513 /* Bail out of the loop if the program has closed its
514 output descriptors, and we have no more data to send to
515 the client, and there is no pending buffered data. */
516 if (fdout_eof && fderr_eof && !packet_have_data_to_write() &&
517 buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) {
518 if (!channel_still_open())
519 goto quit;
520 if (!waiting_termination) {
521 const char *s = "Waiting for forwarded connections to terminate...\r\n";
522 char *cp;
523 waiting_termination = 1;
524 buffer_append(&stderr_buffer, s, strlen(s));
525
526 /* Display list of open channels. */
527 cp = channel_open_message();
528 buffer_append(&stderr_buffer, cp, strlen(cp));
529 xfree(cp);
530 }
531 }
532 /* Sleep in select() until we can do something. */
533 wait_until_can_do_something(&readset, &writeset,
534 max_time_milliseconds);
535
536 /* Process any channel events. */
537 channel_after_select(&readset, &writeset);
538
539 /* Process input from the client and from program stdout/stderr. */
540 process_input(&readset);
541
542 /* Process output to the client and to program stdin. */
543 process_output(&writeset);
544 }
545
546quit:
547 /* Cleanup and termination code. */
548
549 /* Wait until all output has been sent to the client. */
550 drain_output();
551
552 debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
553 stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
554
555 /* Free and clear the buffers. */
556 buffer_free(&stdin_buffer);
557 buffer_free(&stdout_buffer);
558 buffer_free(&stderr_buffer);
559
560 /* Close the file descriptors. */
561 if (fdout != -1)
562 close(fdout);
563 fdout = -1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000564 fdout_eof = 1;
Damien Miller95def091999-11-25 00:26:21 +1100565 if (fderr != -1)
566 close(fderr);
567 fderr = -1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000568 fderr_eof = 1;
Damien Miller95def091999-11-25 00:26:21 +1100569 if (fdin != -1)
570 close(fdin);
571 fdin = -1;
572
573 /* Stop listening for channels; this removes unix domain sockets. */
574 channel_stop_listening();
575
576 /* Wait for the child to exit. Get its exit status. */
577 wait_pid = wait(&wait_status);
578 if (wait_pid < 0) {
579 /*
580 * It is possible that the wait was handled by SIGCHLD
581 * handler. This may result in either: this call
582 * returning with EINTR, or: this call returning ECHILD.
583 */
584 if (child_terminated)
585 wait_status = child_wait_status;
586 else
587 packet_disconnect("wait: %.100s", strerror(errno));
588 } else {
589 /* Check if it matches the process we forked. */
590 if (wait_pid != pid)
591 error("Strange, wait returned pid %d, expected %d",
592 wait_pid, pid);
593 }
594
595 /* We no longer want our SIGCHLD handler to be called. */
596 signal(SIGCHLD, SIG_DFL);
597
598 /* Check if it exited normally. */
599 if (WIFEXITED(wait_status)) {
600 /* Yes, normal exit. Get exit status and send it to the client. */
601 debug("Command exited with status %d.", WEXITSTATUS(wait_status));
602 packet_start(SSH_SMSG_EXITSTATUS);
603 packet_put_int(WEXITSTATUS(wait_status));
604 packet_send();
605 packet_write_wait();
606
607 /* Wait for exit confirmation. Note that there might be
608 other packets coming before it; however, the program
609 has already died so we just ignore them. The client is
610 supposed to respond with the confirmation when it
611 receives the exit status. */
612 do {
613 int plen;
614 type = packet_read(&plen);
615 }
616 while (type != SSH_CMSG_EXIT_CONFIRMATION);
617
618 debug("Received exit confirmation.");
619 return;
620 }
621 /* Check if the program terminated due to a signal. */
622 if (WIFSIGNALED(wait_status))
623 packet_disconnect("Command terminated on signal %d.",
624 WTERMSIG(wait_status));
625
626 /* Some weird exit cause. Just exit. */
627 packet_disconnect("wait returned status %04x.", wait_status);
628 /* NOTREACHED */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000629}