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