aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2006-11-22 15:54:52 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2006-11-22 15:54:52 +0000
commit75f8d080a28f2a9076a5f60afeba41ac876a78a4 (patch)
tree21794599ef01c7603faff7efb1fcaf56868684c9
parentd6bbf99a8b174d8535c195c71d3560a0d83d95fc (diff)
downloadbusybox-w32-75f8d080a28f2a9076a5f60afeba41ac876a78a4.tar.gz
busybox-w32-75f8d080a28f2a9076a5f60afeba41ac876a78a4.tar.bz2
busybox-w32-75f8d080a28f2a9076a5f60afeba41ac876a78a4.zip
telnetd: we were having telnetd with is ONLY inetd or ONLY standalone.
What if I need to have both?? This patch introduces CONFIG_FEATURE_TELNETD_STANDALONE: y - both, n - only inetd.
-rw-r--r--include/libbb.h1
-rw-r--r--include/usage.h18
-rw-r--r--libbb/xfuncs.c12
-rw-r--r--networking/Config.in7
-rw-r--r--networking/telnetd.c671
-rw-r--r--runit/runit_lib.c20
-rw-r--r--scripts/defconfig1
7 files changed, 344 insertions, 386 deletions
diff --git a/include/libbb.h b/include/libbb.h
index bec3ce11c..1c82cbbba 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -204,6 +204,7 @@ extern off_t bb_copyfd_size(int fd1, int fd2, off_t size);
204extern off_t bb_copyfd_eof(int fd1, int fd2); 204extern off_t bb_copyfd_eof(int fd1, int fd2);
205extern char bb_process_escape_sequence(const char **ptr); 205extern char bb_process_escape_sequence(const char **ptr);
206extern char *bb_get_last_path_component(char *path); 206extern char *bb_get_last_path_component(char *path);
207extern int ndelay_on(int fd);
207 208
208 209
209extern DIR *xopendir(const char *path); 210extern DIR *xopendir(const char *path);
diff --git a/include/usage.h b/include/usage.h
index 2a6e335ba..69e958d68 100644
--- a/include/usage.h
+++ b/include/usage.h
@@ -3034,22 +3034,24 @@ USE_FEATURE_START_STOP_DAEMON_FANCY( \
3034 "computer over a network using the TELNET protocol." 3034 "computer over a network using the TELNET protocol."
3035#endif 3035#endif
3036 3036
3037#ifdef CONFIG_FEATURE_TELNETD_INETD 3037#ifdef CONFIG_FEATURE_TELNETD_STANDALONE
3038#define telnetd_trivial_usage \ 3038#define telnetd_trivial_usage \
3039 "(inetd mode) [OPTION]" 3039 "[OPTION]"
3040#define telnetd_full_usage \ 3040#define telnetd_full_usage \
3041 "Telnetd uses incoming TELNET connections via inetd.\n" \ 3041 "Telnetd listens for incoming TELNET connections on PORT.\n" \
3042 "Options:\n" \ 3042 "Options:\n" \
3043 "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \ 3043 "\t-p PORT\t\tlisten for connections on PORT (default 23)\n" \
3044 "\t-f issue_file\tDisplay issue_file instead of /etc/issue" 3044 "\t-l LOGIN\texec LOGIN on connect\n" \
3045 "\t-f issue_file\tDisplay issue_file instead of /etc/issue\n" \
3046 "\t-F\t\tForeground mode\n" \
3047 "\t-i\t\tInetd mode"
3045#else 3048#else
3046#define telnetd_trivial_usage \ 3049#define telnetd_trivial_usage \
3047 "[OPTION]" 3050 "[OPTION]"
3048#define telnetd_full_usage \ 3051#define telnetd_full_usage \
3049 "Telnetd listens for incoming TELNET connections on PORT.\n" \ 3052 "Telnetd uses incoming TELNET connections via inetd.\n" \
3050 "Options:\n" \ 3053 "Options:\n" \
3051 "\t-p PORT\tlisten for connections on PORT (default 23)\n" \ 3054 "\t-l LOGIN\texec LOGIN on connect\n" \
3052 "\t-l LOGIN\texec LOGIN on connect (default /bin/sh)\n" \
3053 "\t-f issue_file\tDisplay issue_file instead of /etc/issue" 3055 "\t-f issue_file\tDisplay issue_file instead of /etc/issue"
3054#endif 3056#endif
3055 3057
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index 44a551639..e6f4e3a48 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -110,6 +110,18 @@ int xopen3(const char *pathname, int flags, int mode)
110 return ret; 110 return ret;
111} 111}
112 112
113/*
114int ndelay_off(int fd)
115{
116 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
117}
118*/
119// Turn on nonblocking I/O on a fd
120int ndelay_on(int fd)
121{
122 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
123}
124
113// Die with an error message if we can't write the entire buffer. 125// Die with an error message if we can't write the entire buffer.
114void xwrite(int fd, void *buf, size_t count) 126void xwrite(int fd, void *buf, size_t count)
115{ 127{
diff --git a/networking/Config.in b/networking/Config.in
index ba41ab119..e9694af67 100644
--- a/networking/Config.in
+++ b/networking/Config.in
@@ -586,13 +586,12 @@ config TELNETD
586 with all that done, telnetd _should_ work.... 586 with all that done, telnetd _should_ work....
587 587
588 588
589config FEATURE_TELNETD_INETD 589config FEATURE_TELNETD_STANDALONE
590 bool "Support call from inetd only" 590 bool "Support standalone telnetd (not inetd only)"
591 default n 591 default n
592 depends on TELNETD 592 depends on TELNETD
593 help 593 help
594 Selecting this will make telnetd only callable from inetd, 594 Selecting this will make telnetd able to run standalone.
595 removing the standalone support.
596 595
597config TFTP 596config TFTP
598 bool "tftp" 597 bool "tftp"
diff --git a/networking/telnetd.c b/networking/telnetd.c
index da7911fcc..549488507 100644
--- a/networking/telnetd.c
+++ b/networking/telnetd.c
@@ -22,11 +22,11 @@
22 */ 22 */
23 23
24/*#define DEBUG 1 */ 24/*#define DEBUG 1 */
25#undef DEBUG 25#define DEBUG 0
26 26
27#include "busybox.h" 27#include "busybox.h"
28 28
29#ifdef DEBUG 29#if DEBUG
30#define TELCMDS 30#define TELCMDS
31#define TELOPTS 31#define TELOPTS
32#endif 32#endif
@@ -36,7 +36,7 @@
36 36
37#define BUFSIZE 4000 37#define BUFSIZE 4000
38 38
39#ifdef CONFIG_FEATURE_IPV6 39#if ENABLE_FEATURE_IPV6
40#define SOCKET_TYPE AF_INET6 40#define SOCKET_TYPE AF_INET6
41typedef struct sockaddr_in6 sockaddr_type; 41typedef struct sockaddr_in6 sockaddr_type;
42#else 42#else
@@ -44,27 +44,23 @@ typedef struct sockaddr_in6 sockaddr_type;
44typedef struct sockaddr_in sockaddr_type; 44typedef struct sockaddr_in sockaddr_type;
45#endif 45#endif
46 46
47 47#if ENABLE_LOGIN
48#ifdef CONFIG_LOGIN
49static const char *loginpath = "/bin/login"; 48static const char *loginpath = "/bin/login";
50#else 49#else
51static const char *loginpath; 50static const char *loginpath = DEFAULT_SHELL;
52#endif 51#endif
52
53static const char *issuefile = "/etc/issue.net"; 53static const char *issuefile = "/etc/issue.net";
54 54
55/* shell name and arguments */ 55/* shell name and arguments */
56 56
57static const char *argv_init[] = {NULL, NULL}; 57static const char *argv_init[2];
58 58
59/* structure that describes a session */ 59/* structure that describes a session */
60 60
61struct tsession { 61struct tsession {
62#ifdef CONFIG_FEATURE_TELNETD_INETD
63 int sockfd_read, sockfd_write, ptyfd;
64#else /* CONFIG_FEATURE_TELNETD_INETD */
65 struct tsession *next; 62 struct tsession *next;
66 int sockfd, ptyfd; 63 int sockfd_read, sockfd_write, ptyfd;
67#endif /* CONFIG_FEATURE_TELNETD_INETD */
68 int shell_pid; 64 int shell_pid;
69 /* two circular buffers */ 65 /* two circular buffers */
70 char *buf1, *buf2; 66 char *buf1, *buf2;
@@ -73,7 +69,6 @@ struct tsession {
73}; 69};
74 70
75/* 71/*
76
77 This is how the buffers are used. The arrows indicate the movement 72 This is how the buffers are used. The arrows indicate the movement
78 of data. 73 of data.
79 74
@@ -86,7 +81,6 @@ struct tsession {
86 +-------+ size2++ +------+ size2-- +----------+ 81 +-------+ size2++ +------+ size2-- +----------+
87 82
88 Each session has got two buffers. 83 Each session has got two buffers.
89
90*/ 84*/
91 85
92static int maxfd; 86static int maxfd;
@@ -95,7 +89,6 @@ static struct tsession *sessions;
95 89
96 90
97/* 91/*
98
99 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored 92 Remove all IAC's from the buffer pointed to by bf (received IACs are ignored
100 and must be removed so as to not be interpreted by the terminal). Make an 93 and must be removed so as to not be interpreted by the terminal). Make an
101 uninterrupted string of characters fit for the terminal. Do this by packing 94 uninterrupted string of characters fit for the terminal. Do this by packing
@@ -113,10 +106,10 @@ static struct tsession *sessions;
113 what is the escape character? We aren't handling that situation here. 106 what is the escape character? We aren't handling that situation here.
114 107
115 CR-LF ->'s CR mapping is also done here, for convenience 108 CR-LF ->'s CR mapping is also done here, for convenience
116 109 */
117 */
118static char * 110static char *
119remove_iacs(struct tsession *ts, int *pnum_totty) { 111remove_iacs(struct tsession *ts, int *pnum_totty)
112{
120 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1; 113 unsigned char *ptr0 = (unsigned char *)ts->buf1 + ts->wridx1;
121 unsigned char *ptr = ptr0; 114 unsigned char *ptr = ptr0;
122 unsigned char *totty = ptr; 115 unsigned char *totty = ptr;
@@ -134,8 +127,7 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
134 */ 127 */
135 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end) 128 if (c == '\r' && (*ptr == '\n' || *ptr == 0) && ptr < end)
136 ptr++; 129 ptr++;
137 } 130 } else {
138 else {
139 /* 131 /*
140 * TELOPT_NAWS support! 132 * TELOPT_NAWS support!
141 */ 133 */
@@ -155,14 +147,13 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
155 break; /* incomplete, can't process */ 147 break; /* incomplete, can't process */
156 ws.ws_col = (ptr[3] << 8) | ptr[4]; 148 ws.ws_col = (ptr[3] << 8) | ptr[4];
157 ws.ws_row = (ptr[5] << 8) | ptr[6]; 149 ws.ws_row = (ptr[5] << 8) | ptr[6];
158 (void) ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws); 150 ioctl(ts->ptyfd, TIOCSWINSZ, (char *)&ws);
159 ptr += 9; 151 ptr += 9;
160 } 152 } else {
161 else {
162 /* skip 3-byte IAC non-SB cmd */ 153 /* skip 3-byte IAC non-SB cmd */
163#ifdef DEBUG 154#if DEBUG
164 fprintf(stderr, "Ignoring IAC %s,%s\n", 155 fprintf(stderr, "Ignoring IAC %s,%s\n",
165 TELCMD(*(ptr+1)), TELOPT(*(ptr+2))); 156 TELCMD(ptr[1]), TELOPT(ptr[2]));
166#endif 157#endif
167 ptr += 3; 158 ptr += 3;
168 } 159 }
@@ -184,10 +175,10 @@ remove_iacs(struct tsession *ts, int *pnum_totty) {
184 175
185 176
186static int 177static int
187getpty(char *line) 178getpty(char *line, int size)
188{ 179{
189 int p; 180 int p;
190#ifdef CONFIG_FEATURE_DEVPTS 181#if ENABLE_FEATURE_DEVPTS
191 p = open("/dev/ptmx", O_RDWR); 182 p = open("/dev/ptmx", O_RDWR);
192 if (p > 0) { 183 if (p > 0) {
193 const char *name; 184 const char *name;
@@ -198,7 +189,7 @@ getpty(char *line)
198 bb_perror_msg("ptsname error (is /dev/pts mounted?)"); 189 bb_perror_msg("ptsname error (is /dev/pts mounted?)");
199 return -1; 190 return -1;
200 } 191 }
201 strcpy(line, name); 192 safe_strncpy(line, name, size);
202 return p; 193 return p;
203 } 194 }
204#else 195#else
@@ -216,9 +207,8 @@ getpty(char *line)
216 } 207 }
217 for (j = 0; j < 16; j++) { 208 for (j = 0; j < 16; j++) {
218 line[9] = j < 10 ? j + '0' : j - 10 + 'a'; 209 line[9] = j < 10 ? j + '0' : j - 10 + 'a';
219#ifdef DEBUG 210 if (DEBUG)
220 fprintf(stderr, "Trying to open device: %s\n", line); 211 fprintf(stderr, "Trying to open device: %s\n", line);
221#endif
222 p = open(line, O_RDWR | O_NOCTTY); 212 p = open(line, O_RDWR | O_NOCTTY);
223 if (p >= 0) { 213 if (p >= 0) {
224 line[5] = 't'; 214 line[5] = 't';
@@ -226,7 +216,7 @@ getpty(char *line)
226 } 216 }
227 } 217 }
228 } 218 }
229#endif /* CONFIG_FEATURE_DEVPTS */ 219#endif /* FEATURE_DEVPTS */
230 return -1; 220 return -1;
231} 221}
232 222
@@ -234,7 +224,7 @@ getpty(char *line)
234static void 224static void
235send_iac(struct tsession *ts, unsigned char command, int option) 225send_iac(struct tsession *ts, unsigned char command, int option)
236{ 226{
237 /* We rely on that there is space in the buffer for now. */ 227 /* We rely on that there is space in the buffer for now. */
238 char *b = ts->buf2 + ts->rdidx2; 228 char *b = ts->buf2 + ts->rdidx2;
239 *b++ = IAC; 229 *b++ = IAC;
240 *b++ = command; 230 *b++ = command;
@@ -245,401 +235,376 @@ send_iac(struct tsession *ts, unsigned char command, int option)
245 235
246 236
247static struct tsession * 237static struct tsession *
248#ifdef CONFIG_FEATURE_TELNETD_INETD 238make_new_session(
249make_new_session(void) 239 USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
250#else /* CONFIG_FEATURE_TELNETD_INETD */ 240 SKIP_FEATURE_TELNETD_STANDALONE(void)
251make_new_session(int sockfd) 241) {
252#endif /* CONFIG_FEATURE_TELNETD_INETD */
253{
254 struct termios termbuf; 242 struct termios termbuf;
255 int pty, pid; 243 int fd, pid;
256 char tty_name[32]; 244 char tty_name[32];
257 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2); 245 struct tsession *ts = xzalloc(sizeof(struct tsession) + BUFSIZE * 2);
258 246
259 ts->buf1 = (char *)(&ts[1]); 247 ts->buf1 = (char *)(&ts[1]);
260 ts->buf2 = ts->buf1 + BUFSIZE; 248 ts->buf2 = ts->buf1 + BUFSIZE;
261 249
262#ifdef CONFIG_FEATURE_TELNETD_INETD 250 /* Got a new connection, set up a tty. */
263 ts->sockfd_write = 1; 251 fd = getpty(tty_name, 32);
264#else /* CONFIG_FEATURE_TELNETD_INETD */ 252 if (fd < 0) {
265 ts->sockfd = sockfd;
266#endif /* CONFIG_FEATURE_TELNETD_INETD */
267
268 /* Got a new connection, set up a tty and spawn a shell. */
269
270 pty = getpty(tty_name);
271
272 if (pty < 0) {
273 bb_error_msg("all terminals in use"); 253 bb_error_msg("all terminals in use");
274 return 0; 254 return NULL;
275 } 255 }
276 256 if (fd > maxfd) maxfd = fd;
277 if (pty > maxfd) 257 ndelay_on(ts->ptyfd = fd);
278 maxfd = pty; 258#if ENABLE_FEATURE_TELNETD_STANDALONE
279 259 if (sock_w > maxfd) maxfd = sock_w;
280 ts->ptyfd = pty; 260 if (sock_r > maxfd) maxfd = sock_r;
281 261 ndelay_on(ts->sockfd_write = sock_w);
262 ndelay_on(ts->sockfd_read = sock_r);
263#else
264 ts->sockfd_write = 1;
265 /* xzalloc: ts->sockfd_read = 0; */
266 ndelay_on(0);
267 ndelay_on(1);
268#endif
282 /* Make the telnet client understand we will echo characters so it 269 /* Make the telnet client understand we will echo characters so it
283 * should not do it locally. We don't tell the client to run linemode, 270 * should not do it locally. We don't tell the client to run linemode,
284 * because we want to handle line editing and tab completion and other 271 * because we want to handle line editing and tab completion and other
285 * stuff that requires char-by-char support. 272 * stuff that requires char-by-char support. */
286 */
287
288 send_iac(ts, DO, TELOPT_ECHO); 273 send_iac(ts, DO, TELOPT_ECHO);
289 send_iac(ts, DO, TELOPT_NAWS); 274 send_iac(ts, DO, TELOPT_NAWS);
290 send_iac(ts, DO, TELOPT_LFLOW); 275 send_iac(ts, DO, TELOPT_LFLOW);
291 send_iac(ts, WILL, TELOPT_ECHO); 276 send_iac(ts, WILL, TELOPT_ECHO);
292 send_iac(ts, WILL, TELOPT_SGA); 277 send_iac(ts, WILL, TELOPT_SGA);
293 278
294 if ((pid = fork()) < 0) { 279 pid = fork();
280 if (pid < 0) {
281 free(ts);
282 close(fd);
295 bb_perror_msg("fork"); 283 bb_perror_msg("fork");
284 return NULL;
296 } 285 }
297 if (pid == 0) { 286 if (pid > 0) {
298 /* In child, open the child's side of the tty. */ 287 /* parent */
299 int i; 288 ts->shell_pid = pid;
300 289 return ts;
301 for(i = 0; i <= maxfd; i++)
302 close(i);
303 /* make new process group */
304 setsid();
305
306 xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
307 dup(0);
308 dup(0);
309
310 tcsetpgrp(0, getpid());
311
312 /* The pseudo-terminal allocated to the client is configured to operate in
313 * cooked mode, and with XTABS CRMOD enabled (see tty(4)).
314 */
315
316 tcgetattr(0, &termbuf);
317 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
318 termbuf.c_oflag |= ONLCR|XTABS;
319 termbuf.c_iflag |= ICRNL;
320 termbuf.c_iflag &= ~IXOFF;
321 /*termbuf.c_lflag &= ~ICANON;*/
322 tcsetattr(0, TCSANOW, &termbuf);
323
324 print_login_issue(issuefile, NULL);
325
326 /* exec shell, with correct argv and env */
327 execv(loginpath, (char *const *)argv_init);
328
329 /* NOT REACHED */
330 bb_perror_msg_and_die("execv");
331 } 290 }
332 291
333 ts->shell_pid = pid; 292 /* child */
334 293
335 return ts; 294 /* open the child's side of the tty. */
295 fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
296 dup2(fd, 0);
297 dup2(fd, 1);
298 dup2(fd, 2);
299 while (fd > 2) close(fd--);
300 /* make new process group */
301 setsid();
302 tcsetpgrp(0, getpid());
303
304 /* The pseudo-terminal allocated to the client is configured to operate in
305 * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
306 tcgetattr(0, &termbuf);
307 termbuf.c_lflag |= ECHO; /* if we use readline we dont want this */
308 termbuf.c_oflag |= ONLCR|XTABS;
309 termbuf.c_iflag |= ICRNL;
310 termbuf.c_iflag &= ~IXOFF;
311 /*termbuf.c_lflag &= ~ICANON;*/
312 tcsetattr(0, TCSANOW, &termbuf);
313
314 print_login_issue(issuefile, NULL);
315
316 /* exec shell, with correct argv and env */
317 execv(loginpath, (char *const *)argv_init);
318 bb_perror_msg_and_die("execv");
336} 319}
337 320
338#ifndef CONFIG_FEATURE_TELNETD_INETD 321#if ENABLE_FEATURE_TELNETD_STANDALONE
322
339static void 323static void
340free_session(struct tsession *ts) 324free_session(struct tsession *ts)
341{ 325{
342 struct tsession *t = sessions; 326 struct tsession *t = sessions;
343 327
344 /* Unlink this telnet session from the session list. */ 328 /* unlink this telnet session from the session list */
345 if (t == ts) 329 if (t == ts)
346 sessions = ts->next; 330 sessions = ts->next;
347 else { 331 else {
348 while(t->next != ts) 332 while (t->next != ts)
349 t = t->next; 333 t = t->next;
350 t->next = ts->next; 334 t->next = ts->next;
351 } 335 }
352 336
353 kill(ts->shell_pid, SIGKILL); 337 kill(ts->shell_pid, SIGKILL);
354
355 wait4(ts->shell_pid, NULL, 0, NULL); 338 wait4(ts->shell_pid, NULL, 0, NULL);
356
357 close(ts->ptyfd); 339 close(ts->ptyfd);
358 close(ts->sockfd); 340 close(ts->sockfd_read);
359 341 /* error if ts->sockfd_read == ts->sockfd_write. So what? ;) */
360 if (ts->ptyfd == maxfd || ts->sockfd == maxfd) 342 close(ts->sockfd_write);
361 maxfd--;
362 if (ts->ptyfd == maxfd || ts->sockfd == maxfd)
363 maxfd--;
364
365 free(ts); 343 free(ts);
344
345 /* scan all sessions and find new maxfd */
346 ts = sessions;
347 maxfd = 0;
348 while (ts) {
349 if (maxfd < ts->ptyfd)
350 maxfd = ts->ptyfd;
351 if (maxfd < ts->sockfd_read)
352 maxfd = ts->sockfd_read;
353 if (maxfd < ts->sockfd_write)
354 maxfd = ts->sockfd_write;
355 ts = ts->next;
356 }
366} 357}
367#endif /* CONFIG_FEATURE_TELNETD_INETD */
368 358
369int 359static int
370telnetd_main(int argc, char **argv) 360create_socket(int port, const char *opt_bindaddr)
371{ 361{
372 unsigned opt; 362 static const int on = 1;
373 fd_set rdfdset, wrfdset; 363 int fd;
374 int selret;
375#ifndef CONFIG_FEATURE_TELNETD_INETD
376 sockaddr_type sa; 364 sockaddr_type sa;
377 int master_fd; 365#if !ENABLE_FEATURE_IPV6
378 int on = 1;
379 unsigned portnbr = 23;
380 struct in_addr bind_addr = { .s_addr = 0x0 }; 366 struct in_addr bind_addr = { .s_addr = 0x0 };
381 char *opt_portnbr, *opt_bindaddr;
382#endif /* CONFIG_FEATURE_TELNETD_INETD */
383 int maxlen, w, r;
384 367
385#ifndef CONFIG_LOGIN 368 /* TODO: generic string -> sockaddr converter */
386 loginpath = DEFAULT_SHELL; 369 if (opt_bindaddr && inet_aton(opt_bindaddr, &bind_addr) == 0)
370 bb_show_usage();
387#endif 371#endif
388 372 fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
389 /* We use inetd-style operation unconditionally 373 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
390 * (no --foreground option), user most likely will
391 * look into syslog for all errors, even early ones.
392 * Direct all output to syslog at once.
393 */
394 openlog(applet_name, 0, LOG_USER);
395 logmode = LOGMODE_SYSLOG;
396
397 opt = getopt32(argc, argv, "f:l:" SKIP_FEATURE_TELNETD_INETD("p:b:"),
398 &issuefile, &loginpath
399 SKIP_FEATURE_TELNETD_INETD(, &opt_portnbr, &opt_bindaddr));
400 //if (opt & 1) // -f
401 //if (opt & 2) // -l
402#ifndef CONFIG_FEATURE_TELNETD_INETD
403 if (opt & 4) portnbr = xatou16(opt_portnbr); // -p
404 if (opt & 8) // -b
405 if (inet_aton(opt_bindaddr, &bind_addr) == 0) bb_show_usage();
406#endif /* CONFIG_FEATURE_TELNETD_INETD */
407
408 if (access(loginpath, X_OK) < 0) {
409 bb_error_msg_and_die("'%s' unavailable", loginpath);
410 }
411
412 argv_init[0] = loginpath;
413
414#ifdef CONFIG_FEATURE_TELNETD_INETD
415 maxfd = 1;
416 sessions = make_new_session();
417#else /* CONFIG_EATURE_TELNETD_INETD */
418 sessions = 0;
419
420 /* Grab a TCP socket. */
421
422 master_fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0);
423 (void)setsockopt(master_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
424
425 /* Set it to listen to specified port. */
426
427 memset((void *)&sa, 0, sizeof(sa)); 374 memset((void *)&sa, 0, sizeof(sa));
428#ifdef CONFIG_FEATURE_IPV6 375#if ENABLE_FEATURE_IPV6
429 sa.sin6_family = AF_INET6; 376 sa.sin6_family = AF_INET6;
430 sa.sin6_port = htons(portnbr); 377 sa.sin6_port = htons(port);
431 /* sa.sin6_addr = bind_addr6; */ 378 /* sa.sin6_addr = bind_addr6; */
432#else 379#else
433 sa.sin_family = AF_INET; 380 sa.sin_family = AF_INET;
434 sa.sin_port = htons(portnbr); 381 sa.sin_port = htons(port);
435 sa.sin_addr = bind_addr; 382 sa.sin_addr = bind_addr;
436#endif 383#endif
384 xbind(fd, (struct sockaddr *) &sa, sizeof(sa));
385 xlisten(fd, 1);
386 return fd;
387}
437 388
438 xbind(master_fd, (struct sockaddr *) &sa, sizeof(sa)); 389#else /* !FEATURE_TELNETD_STANDALONE */
439 xlisten(master_fd, 1);
440 xdaemon(0, 0);
441 390
442 maxfd = master_fd; 391/* Never actually called */
443#endif /* CONFIG_FEATURE_TELNETD_INETD */ 392void free_session(struct tsession *ts);
393int create_socket(int port, const char *opt_bindaddr);
444 394
445 while(1) { 395#endif
446 struct tsession *ts;
447 396
448 FD_ZERO(&rdfdset);
449 FD_ZERO(&wrfdset);
450 397
451 /* select on the master socket, all telnet sockets and their 398int
452 * ptys if there is room in their respective session buffers. 399telnetd_main(int argc, char **argv)
453 */ 400{
401 fd_set rdfdset, wrfdset;
402 unsigned opt;
403 int selret, maxlen, w, r;
404 struct tsession *ts;
405#if ENABLE_FEATURE_TELNETD_STANDALONE
406#define IS_INETD (opt & OPT_INETD)
407 int master_fd = -1; /* be happy, gcc */
408 unsigned portnbr = 23;
409 char *opt_bindaddr = NULL;
410 char *opt_portnbr;
411#else
412 enum {
413 IS_INETD = 1,
414 master_fd = -1,
415 portnbr = 23,
416 };
417#endif
418 enum {
419 OPT_PORT = 4 * ENABLE_FEATURE_TELNETD_STANDALONE,
420 OPT_FOREGROUND = 0x10 * ENABLE_FEATURE_TELNETD_STANDALONE,
421 OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
422 };
454 423
455#ifndef CONFIG_FEATURE_TELNETD_INETD 424 opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
425 &issuefile, &loginpath
426 USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
427 /* Redirect log to syslog early, if needed */
428 if (IS_INETD || !(opt & OPT_FOREGROUND)) {
429 openlog(applet_name, 0, LOG_USER);
430 logmode = LOGMODE_SYSLOG;
431 }
432 //if (opt & 1) // -f
433 //if (opt & 2) // -l
434 USE_FEATURE_TELNETD_STANDALONE(
435 if (opt & OPT_PORT) // -p
436 portnbr = xatou16(opt_portnbr);
437 //if (opt & 8) // -b
438 //if (opt & 0x10) // -F
439 //if (opt & 0x20) // -i
440 );
441
442 /* Used to check access(loginpath, X_OK) here. Pointless.
443 * exec will do this for us for free later. */
444 argv_init[0] = loginpath;
445
446#if ENABLE_FEATURE_TELNETD_STANDALONE
447 if (IS_INETD) {
448 sessions = make_new_session(0, 1);
449 } else {
450 master_fd = create_socket(portnbr, opt_bindaddr);
451 if (!(opt & OPT_FOREGROUND))
452 xdaemon(0, 0);
453 }
454#else
455 sessions = make_new_session();
456#endif
457
458 /* We don't want to die if just one session is broken */
459 signal(SIGPIPE, SIG_IGN);
460
461 again:
462 FD_ZERO(&rdfdset);
463 FD_ZERO(&wrfdset);
464 if (!IS_INETD) {
456 FD_SET(master_fd, &rdfdset); 465 FD_SET(master_fd, &rdfdset);
457#endif /* CONFIG_FEATURE_TELNETD_INETD */ 466 /* This is needed because free_session() does not
458 467 * take into account master_fd when it finds new
459 ts = sessions; 468 * maxfd among remaining fd's: */
460#ifndef CONFIG_FEATURE_TELNETD_INETD 469 if (master_fd > maxfd)
461 while (ts) { 470 maxfd = master_fd;
462#endif /* CONFIG_FEATURE_TELNETD_INETD */ 471 }
463 /* buf1 is used from socket to pty
464 * buf2 is used from pty to socket
465 */
466 if (ts->size1 > 0) {
467 FD_SET(ts->ptyfd, &wrfdset); /* can write to pty */
468 }
469 if (ts->size1 < BUFSIZE) {
470#ifdef CONFIG_FEATURE_TELNETD_INETD
471 FD_SET(ts->sockfd_read, &rdfdset); /* can read from socket */
472#else /* CONFIG_FEATURE_TELNETD_INETD */
473 FD_SET(ts->sockfd, &rdfdset); /* can read from socket */
474#endif /* CONFIG_FEATURE_TELNETD_INETD */
475 }
476 if (ts->size2 > 0) {
477#ifdef CONFIG_FEATURE_TELNETD_INETD
478 FD_SET(ts->sockfd_write, &wrfdset); /* can write to socket */
479#else /* CONFIG_FEATURE_TELNETD_INETD */
480 FD_SET(ts->sockfd, &wrfdset); /* can write to socket */
481#endif /* CONFIG_FEATURE_TELNETD_INETD */
482 }
483 if (ts->size2 < BUFSIZE) {
484 FD_SET(ts->ptyfd, &rdfdset); /* can read from pty */
485 }
486#ifndef CONFIG_FEATURE_TELNETD_INETD
487 ts = ts->next;
488 }
489#endif /* CONFIG_FEATURE_TELNETD_INETD */
490 472
491 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0); 473 /* select on the master socket, all telnet sockets and their
474 * ptys if there is room in their session buffers. */
475 ts = sessions;
476 while (ts) {
477 /* buf1 is used from socket to pty
478 * buf2 is used from pty to socket */
479 if (ts->size1 > 0) /* can write to pty */
480 FD_SET(ts->ptyfd, &wrfdset);
481 if (ts->size1 < BUFSIZE) /* can read from socket */
482 FD_SET(ts->sockfd_read, &rdfdset);
483 if (ts->size2 > 0) /* can write to socket */
484 FD_SET(ts->sockfd_write, &wrfdset);
485 if (ts->size2 < BUFSIZE) /* can read from pty */
486 FD_SET(ts->ptyfd, &rdfdset);
487 ts = ts->next;
488 }
492 489
493 if (!selret) 490 selret = select(maxfd + 1, &rdfdset, &wrfdset, 0, 0);
494 break; 491 if (!selret)
492 return 0;
495 493
496#ifndef CONFIG_FEATURE_TELNETD_INETD 494#if ENABLE_FEATURE_TELNETD_STANDALONE
497 /* First check for and accept new sessions. */ 495 /* First check for and accept new sessions. */
498 if (FD_ISSET(master_fd, &rdfdset)) { 496 if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
499 int fd; 497 sockaddr_type sa;
500 socklen_t salen; 498 int fd;
499 socklen_t salen;
500 struct tsession *new_ts;
501
502 salen = sizeof(sa);
503 fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
504 if (fd < 0)
505 goto again;
506 /* Create a new session and link it into our active list */
507 new_ts = make_new_session(fd, fd);
508 if (new_ts) {
509 new_ts->next = sessions;
510 sessions = new_ts;
511 } else {
512 close(fd);
513 }
514 }
515#endif
501 516
502 salen = sizeof(sa); 517 /* Then check for data tunneling. */
503 fd = accept(master_fd, (struct sockaddr *)&sa, &salen); 518 ts = sessions;
504 if (fd < 0) { 519 while (ts) { /* For all sessions... */
520 struct tsession *next = ts->next; /* in case we free ts. */
521
522 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) {
523 int num_totty;
524 char *ptr;
525 /* Write to pty from buffer 1. */
526 ptr = remove_iacs(ts, &num_totty);
527 w = safe_write(ts->ptyfd, ptr, num_totty);
528 /* needed? if (w < 0 && errno == EAGAIN) continue; */
529 if (w < 0) {
530 if (IS_INETD)
531 return 0;
532 free_session(ts);
533 ts = next;
505 continue; 534 continue;
506 } else {
507 /* Create a new session and link it into
508 our active list. */
509 struct tsession *new_ts = make_new_session(fd);
510 if (new_ts) {
511 new_ts->next = sessions;
512 sessions = new_ts;
513 if (fd > maxfd)
514 maxfd = fd;
515 } else {
516 close(fd);
517 }
518 } 535 }
536 ts->wridx1 += w;
537 ts->size1 -= w;
538 if (ts->wridx1 == BUFSIZE)
539 ts->wridx1 = 0;
519 } 540 }
520 541
521 /* Then check for data tunneling. */ 542 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
522 543 /* Write to socket from buffer 2. */
523 ts = sessions; 544 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
524 while (ts) { /* For all sessions... */ 545 w = safe_write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
525#endif /* CONFIG_FEATURE_TELNETD_INETD */ 546 /* needed? if (w < 0 && errno == EAGAIN) continue; */
526#ifndef CONFIG_FEATURE_TELNETD_INETD 547 if (w < 0) {
527 struct tsession *next = ts->next; /* in case we free ts. */ 548 if (IS_INETD)
528#endif /* CONFIG_FEATURE_TELNETD_INETD */ 549 return 0;
529 550 free_session(ts);
530 if (ts->size1 && FD_ISSET(ts->ptyfd, &wrfdset)) { 551 ts = next;
531 int num_totty; 552 continue;
532 char *ptr;
533 /* Write to pty from buffer 1. */
534
535 ptr = remove_iacs(ts, &num_totty);
536
537 w = write(ts->ptyfd, ptr, num_totty);
538 if (w < 0) {
539#ifdef CONFIG_FEATURE_TELNETD_INETD
540 exit(0);
541#else /* CONFIG_FEATURE_TELNETD_INETD */
542 free_session(ts);
543 ts = next;
544 continue;
545#endif /* CONFIG_FEATURE_TELNETD_INETD */
546 }
547 ts->wridx1 += w;
548 ts->size1 -= w;
549 if (ts->wridx1 == BUFSIZE)
550 ts->wridx1 = 0;
551 }
552
553#ifdef CONFIG_FEATURE_TELNETD_INETD
554 if (ts->size2 && FD_ISSET(ts->sockfd_write, &wrfdset)) {
555#else /* CONFIG_FEATURE_TELNETD_INETD */
556 if (ts->size2 && FD_ISSET(ts->sockfd, &wrfdset)) {
557#endif /* CONFIG_FEATURE_TELNETD_INETD */
558 /* Write to socket from buffer 2. */
559 maxlen = MIN(BUFSIZE - ts->wridx2, ts->size2);
560#ifdef CONFIG_FEATURE_TELNETD_INETD
561 w = write(ts->sockfd_write, ts->buf2 + ts->wridx2, maxlen);
562 if (w < 0)
563 exit(0);
564#else /* CONFIG_FEATURE_TELNETD_INETD */
565 w = write(ts->sockfd, ts->buf2 + ts->wridx2, maxlen);
566 if (w < 0) {
567 free_session(ts);
568 ts = next;
569 continue;
570 }
571#endif /* CONFIG_FEATURE_TELNETD_INETD */
572 ts->wridx2 += w;
573 ts->size2 -= w;
574 if (ts->wridx2 == BUFSIZE)
575 ts->wridx2 = 0;
576 } 553 }
554 ts->wridx2 += w;
555 ts->size2 -= w;
556 if (ts->wridx2 == BUFSIZE)
557 ts->wridx2 = 0;
558 }
577 559
578#ifdef CONFIG_FEATURE_TELNETD_INETD 560 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) {
579 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd_read, &rdfdset)) { 561 /* Read from socket to buffer 1. */
580#else /* CONFIG_FEATURE_TELNETD_INETD */ 562 maxlen = MIN(BUFSIZE - ts->rdidx1, BUFSIZE - ts->size1);
581 if (ts->size1 < BUFSIZE && FD_ISSET(ts->sockfd, &rdfdset)) { 563 r = safe_read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen);
582#endif /* CONFIG_FEATURE_TELNETD_INETD */ 564 if (r < 0 && errno == EAGAIN) continue;
583 /* Read from socket to buffer 1. */ 565 if (r <= 0) {
584 maxlen = MIN(BUFSIZE - ts->rdidx1, 566 if (IS_INETD)
585 BUFSIZE - ts->size1); 567 return 0;
586#ifdef CONFIG_FEATURE_TELNETD_INETD 568 free_session(ts);
587 r = read(ts->sockfd_read, ts->buf1 + ts->rdidx1, maxlen); 569 ts = next;
588 if (!r || (r < 0 && errno != EINTR)) 570 continue;
589 exit(0);
590#else /* CONFIG_FEATURE_TELNETD_INETD */
591 r = read(ts->sockfd, ts->buf1 + ts->rdidx1, maxlen);
592 if (!r || (r < 0 && errno != EINTR)) {
593 free_session(ts);
594 ts = next;
595 continue;
596 }
597#endif /* CONFIG_FEATURE_TELNETD_INETD */
598 if (!*(ts->buf1 + ts->rdidx1 + r - 1)) {
599 r--;
600 if (!r)
601 continue;
602 }
603 ts->rdidx1 += r;
604 ts->size1 += r;
605 if (ts->rdidx1 == BUFSIZE)
606 ts->rdidx1 = 0;
607 } 571 }
608 572 if (!ts->buf1[ts->rdidx1 + r - 1])
609 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) { 573 if (!--r)
610 /* Read from pty to buffer 2. */
611 maxlen = MIN(BUFSIZE - ts->rdidx2,
612 BUFSIZE - ts->size2);
613 r = read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
614 if (!r || (r < 0 && errno != EINTR)) {
615#ifdef CONFIG_FEATURE_TELNETD_INETD
616 exit(0);
617#else /* CONFIG_FEATURE_TELNETD_INETD */
618 free_session(ts);
619 ts = next;
620 continue; 574 continue;
621#endif /* CONFIG_FEATURE_TELNETD_INETD */ 575 ts->rdidx1 += r;
622 } 576 ts->size1 += r;
623 ts->rdidx2 += r; 577 if (ts->rdidx1 == BUFSIZE)
624 ts->size2 += r;
625 if (ts->rdidx2 == BUFSIZE)
626 ts->rdidx2 = 0;
627 }
628
629 if (ts->size1 == 0) {
630 ts->rdidx1 = 0; 578 ts->rdidx1 = 0;
631 ts->wridx1 = 0; 579 }
580
581 if (ts->size2 < BUFSIZE && FD_ISSET(ts->ptyfd, &rdfdset)) {
582 /* Read from pty to buffer 2. */
583 maxlen = MIN(BUFSIZE - ts->rdidx2, BUFSIZE - ts->size2);
584 r = safe_read(ts->ptyfd, ts->buf2 + ts->rdidx2, maxlen);
585 if (r < 0 && errno == EAGAIN) continue;
586 if (r <= 0) {
587 if (IS_INETD)
588 return 0;
589 free_session(ts);
590 ts = next;
591 continue;
632 } 592 }
633 if (ts->size2 == 0) { 593 ts->rdidx2 += r;
594 ts->size2 += r;
595 if (ts->rdidx2 == BUFSIZE)
634 ts->rdidx2 = 0; 596 ts->rdidx2 = 0;
635 ts->wridx2 = 0;
636 }
637#ifndef CONFIG_FEATURE_TELNETD_INETD
638 ts = next;
639 } 597 }
640#endif /* CONFIG_FEATURE_TELNETD_INETD */
641
642 } /* while(1) */
643 598
644 return 0; 599 if (ts->size1 == 0) {
600 ts->rdidx1 = 0;
601 ts->wridx1 = 0;
602 }
603 if (ts->size2 == 0) {
604 ts->rdidx2 = 0;
605 ts->wridx2 = 0;
606 }
607 ts = next;
608 }
609 goto again;
645} 610}
diff --git a/runit/runit_lib.c b/runit/runit_lib.c
index c95d641f1..5ebbc5840 100644
--- a/runit/runit_lib.c
+++ b/runit/runit_lib.c
@@ -34,10 +34,6 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34#include "libbb.h" 34#include "libbb.h"
35#include "runit_lib.h" 35#include "runit_lib.h"
36 36
37#ifndef O_NONBLOCK
38#define O_NONBLOCK O_NDELAY
39#endif
40
41/*** buffer.c ***/ 37/*** buffer.c ***/
42 38
43void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len) 39void buffer_init(buffer *s,int (*op)(int fd,char *buf,unsigned len),int fd,char *buf,unsigned len)
@@ -625,22 +621,6 @@ int lock_exnb(int fd)
625} 621}
626 622
627 623
628/*** ndelay_off.c ***/
629
630int ndelay_off(int fd)
631{
632 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) & ~O_NONBLOCK);
633}
634
635
636/*** ndelay_on.c ***/
637
638int ndelay_on(int fd)
639{
640 return fcntl(fd,F_SETFL,fcntl(fd,F_GETFL,0) | O_NONBLOCK);
641}
642
643
644/*** open_append.c ***/ 624/*** open_append.c ***/
645 625
646int open_append(const char *fn) 626int open_append(const char *fn)
diff --git a/scripts/defconfig b/scripts/defconfig
index 11361dc42..102c21a5f 100644
--- a/scripts/defconfig
+++ b/scripts/defconfig
@@ -560,7 +560,6 @@ CONFIG_TELNET=y
560CONFIG_FEATURE_TELNET_TTYPE=y 560CONFIG_FEATURE_TELNET_TTYPE=y
561CONFIG_FEATURE_TELNET_AUTOLOGIN=y 561CONFIG_FEATURE_TELNET_AUTOLOGIN=y
562CONFIG_TELNETD=y 562CONFIG_TELNETD=y
563CONFIG_FEATURE_TELNETD_INETD=y
564CONFIG_TFTP=y 563CONFIG_TFTP=y
565CONFIG_FEATURE_TFTP_GET=y 564CONFIG_FEATURE_TFTP_GET=y
566CONFIG_FEATURE_TFTP_PUT=y 565CONFIG_FEATURE_TFTP_PUT=y