diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 22:09:15 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-10-15 22:09:15 +0000 |
commit | 2450c450ab8af271521c18b3c5e8e1b3c9041838 (patch) | |
tree | e784b890030dfc060bbfe4e3b3a8cd697db48dce | |
parent | 10916c5c6b34a9268356ef5447f5de0b20089aa7 (diff) | |
download | busybox-w32-2450c450ab8af271521c18b3c5e8e1b3c9041838.tar.gz busybox-w32-2450c450ab8af271521c18b3c5e8e1b3c9041838.tar.bz2 busybox-w32-2450c450ab8af271521c18b3c5e8e1b3c9041838.zip |
telnetd: at Alexander Kriegisch <Alexander@kriegisch.name> insistence
add an option to close sessions as soon as child exits.
Maybe it should be a CONFIG option. OTOH, maybe it should be always on,
as it mimics, say, getty's behaviour.
function old new delta
handle_sigchld - 49 +49
telnetd_main 1312 1355 +43
.rodata 123429 123466 +37
packed_usage 22770 22806 +36
make_new_session 525 532 +7
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 4/0 up/down: 172/0) Total: 172 bytes
text data bss dec hex filename
676285 2538 12104 690927 a8aef busybox_old
676421 2538 12104 691063 a8b77 busybox_unstripped
-rw-r--r-- | include/usage.h | 2 | ||||
-rw-r--r-- | networking/telnetd.c | 99 |
2 files changed, 66 insertions, 35 deletions
diff --git a/include/usage.h b/include/usage.h index 210e1f246..1e05532d5 100644 --- a/include/usage.h +++ b/include/usage.h | |||
@@ -3517,6 +3517,8 @@ USE_FEATURE_RUN_PARTS_FANCY("\n -l Prints names of all matching files even when | |||
3517 | "\n\nOptions:" \ | 3517 | "\n\nOptions:" \ |
3518 | "\n -l LOGIN Exec LOGIN on connect" \ | 3518 | "\n -l LOGIN Exec LOGIN on connect" \ |
3519 | "\n -f issue_file Display issue_file instead of /etc/issue" \ | 3519 | "\n -f issue_file Display issue_file instead of /etc/issue" \ |
3520 | "\n -K Close connection as soon as login exits" \ | ||
3521 | "\n (normally wait until all programs close slave pty)" \ | ||
3520 | USE_FEATURE_TELNETD_STANDALONE( \ | 3522 | USE_FEATURE_TELNETD_STANDALONE( \ |
3521 | "\n -p PORT Port to listen to" \ | 3523 | "\n -p PORT Port to listen to" \ |
3522 | "\n -b ADDR Address to bind to" \ | 3524 | "\n -b ADDR Address to bind to" \ |
diff --git a/networking/telnetd.c b/networking/telnetd.c index 85c2ebc44..96620d949 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c | |||
@@ -37,7 +37,7 @@ | |||
37 | struct tsession { | 37 | struct tsession { |
38 | struct tsession *next; | 38 | struct tsession *next; |
39 | int sockfd_read, sockfd_write, ptyfd; | 39 | int sockfd_read, sockfd_write, ptyfd; |
40 | /*int shell_pid;*/ | 40 | int shell_pid; |
41 | 41 | ||
42 | /* two circular buffers */ | 42 | /* two circular buffers */ |
43 | /*char *buf1, *buf2;*/ | 43 | /*char *buf1, *buf2;*/ |
@@ -265,7 +265,7 @@ make_new_session( | |||
265 | } | 265 | } |
266 | if (pid > 0) { | 266 | if (pid > 0) { |
267 | /* Parent */ | 267 | /* Parent */ |
268 | /*ts->shell_pid = pid;*/ | 268 | ts->shell_pid = pid; |
269 | return ts; | 269 | return ts; |
270 | } | 270 | } |
271 | 271 | ||
@@ -305,7 +305,8 @@ make_new_session( | |||
305 | login_argv[0] = loginpath; | 305 | login_argv[0] = loginpath; |
306 | login_argv[1] = NULL; | 306 | login_argv[1] = NULL; |
307 | execvp(loginpath, (char **)login_argv); | 307 | execvp(loginpath, (char **)login_argv); |
308 | /* Safer with vfork, and we shouldn't send this to the client anyway */ | 308 | /* Safer with vfork, and we shouldn't send message |
309 | * to remote clients anyway */ | ||
309 | _exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/ | 310 | _exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/ |
310 | } | 311 | } |
311 | 312 | ||
@@ -357,6 +358,24 @@ void free_session(struct tsession *ts); | |||
357 | 358 | ||
358 | #endif | 359 | #endif |
359 | 360 | ||
361 | static void handle_sigchld(int sig) | ||
362 | { | ||
363 | pid_t pid; | ||
364 | struct tsession *ts; | ||
365 | |||
366 | pid = waitpid(-1, &sig, WNOHANG); | ||
367 | if (pid > 0) { | ||
368 | ts = sessions; | ||
369 | while (ts) { | ||
370 | if (ts->shell_pid == pid) { | ||
371 | ts->shell_pid = -1; | ||
372 | return; | ||
373 | } | ||
374 | ts = ts->next; | ||
375 | } | ||
376 | } | ||
377 | } | ||
378 | |||
360 | 379 | ||
361 | int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 380 | int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
362 | int telnetd_main(int argc, char **argv) | 381 | int telnetd_main(int argc, char **argv) |
@@ -379,29 +398,35 @@ int telnetd_main(int argc, char **argv) | |||
379 | }; | 398 | }; |
380 | #endif | 399 | #endif |
381 | enum { | 400 | enum { |
382 | OPT_INETD = (1 << 2) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ | 401 | OPT_WATCHCHILD = (1 << 2), /* -K */ |
383 | OPT_PORT = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */ | 402 | OPT_INETD = (1 << 3) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -i */ |
384 | OPT_FOREGROUND = (1 << 5) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ | 403 | OPT_PORT = (1 << 4) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -p */ |
404 | OPT_FOREGROUND = (1 << 6) * ENABLE_FEATURE_TELNETD_STANDALONE, /* -F */ | ||
385 | }; | 405 | }; |
386 | 406 | ||
387 | /* If !STANDALONE, we accept (and ignore) -i, thus people | 407 | /* Even if !STANDALONE, we accept (and ignore) -i, thus people |
388 | * don't need to guess whether it's ok to pass -i to us */ | 408 | * don't need to guess whether it's ok to pass -i to us */ |
389 | opt = getopt32(argv, "f:l:i" USE_FEATURE_TELNETD_STANDALONE("p:b:F"), | 409 | opt = getopt32(argv, "f:l:Ki" USE_FEATURE_TELNETD_STANDALONE("p:b:F"), |
390 | &issuefile, &loginpath | 410 | &issuefile, &loginpath |
391 | USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); | 411 | USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr)); |
412 | if (!IS_INETD /*&& !re_execed*/) { | ||
413 | /* inform that we start in standalone mode? | ||
414 | * May be useful when people forget to give -i */ | ||
415 | /*bb_error_msg("listening for connections");*/ | ||
416 | if (!(opt & OPT_FOREGROUND)) { | ||
417 | /* DAEMON_CHDIR_ROOT was giving inconsistent | ||
418 | * behavior with/wthout -F, -i */ | ||
419 | bb_daemonize_or_rexec(0 /*DAEMON_CHDIR_ROOT*/, argv); | ||
420 | } | ||
421 | } | ||
392 | /* Redirect log to syslog early, if needed */ | 422 | /* Redirect log to syslog early, if needed */ |
393 | if (IS_INETD || !(opt & OPT_FOREGROUND)) { | 423 | if (IS_INETD || !(opt & OPT_FOREGROUND)) { |
394 | openlog(applet_name, 0, LOG_USER); | 424 | openlog(applet_name, 0, LOG_USER); |
395 | logmode = LOGMODE_SYSLOG; | 425 | logmode = LOGMODE_SYSLOG; |
396 | } | 426 | } |
397 | //if (opt & 1) // -f | ||
398 | //if (opt & 2) // -l | ||
399 | USE_FEATURE_TELNETD_STANDALONE( | 427 | USE_FEATURE_TELNETD_STANDALONE( |
400 | if (opt & OPT_PORT) // -p | 428 | if (opt & OPT_PORT) |
401 | portnbr = xatou16(opt_portnbr); | 429 | portnbr = xatou16(opt_portnbr); |
402 | //if (opt & 8) // -b | ||
403 | //if (opt & 0x10) // -F | ||
404 | //if (opt & 0x20) // -i | ||
405 | ); | 430 | ); |
406 | 431 | ||
407 | /* Used to check access(loginpath, X_OK) here. Pointless. | 432 | /* Used to check access(loginpath, X_OK) here. Pointless. |
@@ -413,12 +438,8 @@ int telnetd_main(int argc, char **argv) | |||
413 | if (!sessions) /* pty opening or vfork problem, exit */ | 438 | if (!sessions) /* pty opening or vfork problem, exit */ |
414 | return 1; /* make_new_session prints error message */ | 439 | return 1; /* make_new_session prints error message */ |
415 | } else { | 440 | } else { |
416 | //vda: inform that we start in standalone mode? | ||
417 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); | 441 | master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr); |
418 | xlisten(master_fd, 1); | 442 | xlisten(master_fd, 1); |
419 | if (!(opt & OPT_FOREGROUND)) | ||
420 | //vda: NOMMU? | ||
421 | bb_daemonize(DAEMON_CHDIR_ROOT); | ||
422 | } | 443 | } |
423 | #else | 444 | #else |
424 | sessions = make_new_session(); | 445 | sessions = make_new_session(); |
@@ -429,6 +450,9 @@ int telnetd_main(int argc, char **argv) | |||
429 | /* We don't want to die if just one session is broken */ | 450 | /* We don't want to die if just one session is broken */ |
430 | signal(SIGPIPE, SIG_IGN); | 451 | signal(SIGPIPE, SIG_IGN); |
431 | 452 | ||
453 | if (opt & OPT_WATCHCHILD) | ||
454 | signal(SIGCHLD, handle_sigchld); | ||
455 | |||
432 | /* | 456 | /* |
433 | This is how the buffers are used. The arrows indicate the movement | 457 | This is how the buffers are used. The arrows indicate the movement |
434 | of data. | 458 | of data. |
@@ -450,14 +474,6 @@ int telnetd_main(int argc, char **argv) | |||
450 | again: | 474 | again: |
451 | FD_ZERO(&rdfdset); | 475 | FD_ZERO(&rdfdset); |
452 | FD_ZERO(&wrfdset); | 476 | FD_ZERO(&wrfdset); |
453 | if (!IS_INETD) { | ||
454 | FD_SET(master_fd, &rdfdset); | ||
455 | /* This is needed because free_session() does not | ||
456 | * take into account master_fd when it finds new | ||
457 | * maxfd among remaining fd's: */ | ||
458 | if (master_fd > maxfd) | ||
459 | maxfd = master_fd; | ||
460 | } | ||
461 | 477 | ||
462 | /* Select on the master socket, all telnet sockets and their | 478 | /* Select on the master socket, all telnet sockets and their |
463 | * ptys if there is room in their session buffers. | 479 | * ptys if there is room in their session buffers. |
@@ -465,15 +481,28 @@ int telnetd_main(int argc, char **argv) | |||
465 | * before each select. Can be a problem with 500+ connections. */ | 481 | * before each select. Can be a problem with 500+ connections. */ |
466 | ts = sessions; | 482 | ts = sessions; |
467 | while (ts) { | 483 | while (ts) { |
468 | if (ts->size1 > 0) /* can write to pty */ | 484 | struct tsession *next = ts->next; /* in case we free ts. */ |
469 | FD_SET(ts->ptyfd, &wrfdset); | 485 | if (ts->shell_pid == -1) { |
470 | if (ts->size1 < BUFSIZE) /* can read from socket */ | 486 | free_session(ts); |
471 | FD_SET(ts->sockfd_read, &rdfdset); | 487 | } else { |
472 | if (ts->size2 > 0) /* can write to socket */ | 488 | if (ts->size1 > 0) /* can write to pty */ |
473 | FD_SET(ts->sockfd_write, &wrfdset); | 489 | FD_SET(ts->ptyfd, &wrfdset); |
474 | if (ts->size2 < BUFSIZE) /* can read from pty */ | 490 | if (ts->size1 < BUFSIZE) /* can read from socket */ |
475 | FD_SET(ts->ptyfd, &rdfdset); | 491 | FD_SET(ts->sockfd_read, &rdfdset); |
476 | ts = ts->next; | 492 | if (ts->size2 > 0) /* can write to socket */ |
493 | FD_SET(ts->sockfd_write, &wrfdset); | ||
494 | if (ts->size2 < BUFSIZE) /* can read from pty */ | ||
495 | FD_SET(ts->ptyfd, &rdfdset); | ||
496 | } | ||
497 | ts = next; | ||
498 | } | ||
499 | if (!IS_INETD) { | ||
500 | FD_SET(master_fd, &rdfdset); | ||
501 | /* This is needed because free_session() does not | ||
502 | * take into account master_fd when it finds new | ||
503 | * maxfd among remaining fd's */ | ||
504 | if (master_fd > maxfd) | ||
505 | maxfd = master_fd; | ||
477 | } | 506 | } |
478 | 507 | ||
479 | count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); | 508 | count = select(maxfd + 1, &rdfdset, &wrfdset, NULL, NULL); |