+ fdin = -1;
+ }
+ /* Make packets from buffered stderr data to send to the client. */
+ make_packets_from_stderr_data();
+
+ /*
+ * Make packets from buffered stdout data to send to the
+ * client. If there is very little to send, this arranges to
+ * not send them now, but to wait a short while to see if we
+ * are getting more data. This is necessary, as some systems
+ * wake up readers from a pty after each separate character.
+ */
+ max_time_milliseconds = 0;
+ stdout_buffer_bytes = buffer_len(&stdout_buffer);
+ if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 &&
+ stdout_buffer_bytes != previous_stdout_buffer_bytes) {
+ /* try again after a while */
+ max_time_milliseconds = 10;
+ } else {
+ /* Send it now. */
+ make_packets_from_stdout_data();
+ }
+ previous_stdout_buffer_bytes = buffer_len(&stdout_buffer);
+
+ /* Send channel data to the client. */
+ if (packet_not_very_much_data_to_write())
+ channel_output_poll();
+
+ /*
+ * Bail out of the loop if the program has closed its output
+ * descriptors, and we have no more data to send to the
+ * client, and there is no pending buffered data.
+ */
+ if (((fdout_eof && fderr_eof) ||
+ (child_terminated && child_has_selected)) &&
+ !packet_have_data_to_write() &&
+ (buffer_len(&stdout_buffer) == 0) &&
+ (buffer_len(&stderr_buffer) == 0)) {
+ if (!channel_still_open())
+ break;
+ if (!waiting_termination) {
+ const char *s = "Waiting for forwarded connections to terminate...\r\n";
+ char *cp;
+ waiting_termination = 1;
+ buffer_append(&stderr_buffer, s, strlen(s));
+
+ /* Display list of open channels. */
+ cp = channel_open_message();
+ buffer_append(&stderr_buffer, cp, strlen(cp));
+ xfree(cp);
+ }
+ }
+ /* Sleep in select() until we can do something. */
+ wait_until_can_do_something(&readset, &writeset,
+ max_time_milliseconds);
+
+ /* Process any channel events. */
+ channel_after_select(&readset, &writeset);
+
+ /* Process input from the client and from program stdout/stderr. */
+ process_input(&readset);
+
+ /* Process output to the client and to program stdin. */
+ process_output(&writeset);
+ }
+
+ /* Cleanup and termination code. */
+
+ /* Wait until all output has been sent to the client. */
+ drain_output();
+
+ debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.",
+ stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes);
+
+ /* Free and clear the buffers. */
+ buffer_free(&stdin_buffer);
+ buffer_free(&stdout_buffer);
+ buffer_free(&stderr_buffer);
+
+ /* Close the file descriptors. */
+ if (fdout != -1)
+ close(fdout);
+ fdout = -1;
+ fdout_eof = 1;
+ if (fderr != -1)
+ close(fderr);
+ fderr = -1;
+ fderr_eof = 1;
+ if (fdin != -1)
+ close(fdin);
+ fdin = -1;
+
+ /* Stop listening for channels; this removes unix domain sockets. */
+ channel_stop_listening();
+
+ /* Wait for the child to exit. Get its exit status. */
+ wait_pid = wait(&wait_status);
+ if (wait_pid < 0) {
+ /*
+ * It is possible that the wait was handled by SIGCHLD
+ * handler. This may result in either: this call
+ * returning with EINTR, or: this call returning ECHILD.
+ */
+ if (child_terminated)
+ wait_status = child_wait_status;
+ else
+ packet_disconnect("wait: %.100s", strerror(errno));
+ } else {
+ /* Check if it matches the process we forked. */
+ if (wait_pid != pid)
+ error("Strange, wait returned pid %d, expected %d",
+ wait_pid, pid);