diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-20 22:23:24 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-02-20 22:23:24 +0000 |
commit | e376d454bb70ed41bbc3eb0358d37fa30c94358d (patch) | |
tree | eb53c600dcde841a7617a19f819ae3e9cfe7fd84 | |
parent | ae86a338b89c1339588226cb2298e1785aaa7b90 (diff) | |
download | busybox-w32-e376d454bb70ed41bbc3eb0358d37fa30c94358d.tar.gz busybox-w32-e376d454bb70ed41bbc3eb0358d37fa30c94358d.tar.bz2 busybox-w32-e376d454bb70ed41bbc3eb0358d37fa30c94358d.zip |
libbb: introduce and use nonblock_safe_read(). Yay!
Our shells are immune from this nasty O_NONBLOCK now!
function old new delta
nonblock_safe_read - 78 +78
file_get 276 295 +19
generateMTFValues 428 435 +7
read_line_input 1776 1772 -4
preadbuffer 543 450 -93
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/2 up/down: 104/-97) Total: 7 bytes
text data bss dec hex filename
615190 715 23924 639829 9c355 busybox_old
615168 715 23924 639807 9c33f busybox_unstripped
-rw-r--r-- | include/libbb.h | 2 | ||||
-rw-r--r-- | libbb/lineedit.c | 4 | ||||
-rw-r--r-- | libbb/read.c | 56 | ||||
-rw-r--r-- | shell/ash.c | 11 | ||||
-rw-r--r-- | shell/hush.c | 8 | ||||
-rw-r--r-- | shell/msh.c | 9 |
6 files changed, 75 insertions, 15 deletions
diff --git a/include/libbb.h b/include/libbb.h index a91eac466..eb8b2f620 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -475,6 +475,7 @@ extern void *xzalloc(size_t size); | |||
475 | extern void *xrealloc(void *old, size_t size); | 475 | extern void *xrealloc(void *old, size_t size); |
476 | 476 | ||
477 | extern ssize_t safe_read(int fd, void *buf, size_t count); | 477 | extern ssize_t safe_read(int fd, void *buf, size_t count); |
478 | extern ssize_t nonblock_safe_read(int fd, void *buf, size_t count); | ||
478 | extern ssize_t full_read(int fd, void *buf, size_t count); | 479 | extern ssize_t full_read(int fd, void *buf, size_t count); |
479 | extern void xread(int fd, void *buf, size_t count); | 480 | extern void xread(int fd, void *buf, size_t count); |
480 | extern unsigned char xread_char(int fd); | 481 | extern unsigned char xread_char(int fd); |
@@ -482,6 +483,7 @@ extern unsigned char xread_char(int fd); | |||
482 | extern char *reads(int fd, char *buf, size_t count); | 483 | extern char *reads(int fd, char *buf, size_t count); |
483 | // Read one line a-la fgets. Reads byte-by-byte. | 484 | // Read one line a-la fgets. Reads byte-by-byte. |
484 | // Useful when it is important to not read ahead. | 485 | // Useful when it is important to not read ahead. |
486 | // Bytes are appended to pfx (which must be malloced, or NULL). | ||
485 | extern char *xmalloc_reads(int fd, char *pfx); | 487 | extern char *xmalloc_reads(int fd, char *pfx); |
486 | extern ssize_t read_close(int fd, void *buf, size_t count); | 488 | extern ssize_t read_close(int fd, void *buf, size_t count); |
487 | extern ssize_t open_read_close(const char *filename, void *buf, size_t count); | 489 | extern ssize_t open_read_close(const char *filename, void *buf, size_t count); |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index 529344f6c..9aab63702 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -1408,9 +1408,9 @@ int read_line_input(const char *prompt, char *command, int maxsize, line_input_t | |||
1408 | parse_and_put_prompt(prompt); | 1408 | parse_and_put_prompt(prompt); |
1409 | 1409 | ||
1410 | while (1) { | 1410 | while (1) { |
1411 | fflush(stdout); | 1411 | fflush(NULL); |
1412 | 1412 | ||
1413 | if (safe_read(STDIN_FILENO, &c, 1) < 1) { | 1413 | if (nonblock_safe_read(STDIN_FILENO, &c, 1) < 1) { |
1414 | /* if we can't read input then exit */ | 1414 | /* if we can't read input then exit */ |
1415 | goto prepare_to_die; | 1415 | goto prepare_to_die; |
1416 | } | 1416 | } |
diff --git a/libbb/read.c b/libbb/read.c index 502d407c4..2cd86b81b 100644 --- a/libbb/read.c +++ b/libbb/read.c | |||
@@ -20,6 +20,58 @@ ssize_t safe_read(int fd, void *buf, size_t count) | |||
20 | return n; | 20 | return n; |
21 | } | 21 | } |
22 | 22 | ||
23 | /* Suppose that you are a shell. You start child processes. | ||
24 | * They work and eventually exit. You want to get user input. | ||
25 | * You read stdin. But what happens if last child switched | ||
26 | * its stdin into O_NONBLOCK mode? | ||
27 | * | ||
28 | * *** SURPRISE! It will affect the parent too! *** | ||
29 | * *** BIG SURPRISE! It stays even after child exits! *** | ||
30 | * | ||
31 | * This is a design bug in UNIX API. | ||
32 | * fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) | O_NONBLOCK); | ||
33 | * will set nonblocking mode not only on _your_ stdin, but | ||
34 | * also on stdin of your parent, etc. | ||
35 | * | ||
36 | * In general, | ||
37 | * fd2 = dup(fd1); | ||
38 | * fcntl(fd2, F_SETFL, fcntl(fd2, F_GETFL, 0) | O_NONBLOCK); | ||
39 | * sets both fd1 and fd2 to O_NONBLOCK. This includes cases | ||
40 | * where duping is done implicitly by fork() etc. | ||
41 | * | ||
42 | * We need | ||
43 | * fcntl(fd2, F_SETFD, fcntl(fd2, F_GETFD, 0) | O_NONBLOCK); | ||
44 | * (note SETFD, not SETFL!) but such thing doesn't exist. | ||
45 | * | ||
46 | * Alternatively, we need nonblocking_read(fd, ...) which doesn't | ||
47 | * require O_NONBLOCK dance at all. Actually, it exists: | ||
48 | * n = recv(fd, buf, len, MSG_DONTWAIT); | ||
49 | * "MSG_DONTWAIT: | ||
50 | * Enables non-blocking operation; if the operation | ||
51 | * would block, EAGAIN is returned." | ||
52 | * but recv() works only for sockets! | ||
53 | * | ||
54 | * So far I don't see any good solution, I can only propose | ||
55 | * that affected readers should be careful and use this routine, | ||
56 | * which detects EAGAIN and uses poll() to wait on the fd. | ||
57 | * Thanksfully, poll() doesn't give rat's ass about O_NONBLOCK flag. | ||
58 | */ | ||
59 | ssize_t nonblock_safe_read(int fd, void *buf, size_t count) | ||
60 | { | ||
61 | struct pollfd pfd[1]; | ||
62 | ssize_t n; | ||
63 | |||
64 | while (1) { | ||
65 | n = safe_read(fd, buf, count); | ||
66 | if (n >= 0 || errno != EAGAIN) | ||
67 | return n; | ||
68 | /* fd is in O_NONBLOCK mode. Wait using poll and repeat */ | ||
69 | pfd[0].fd = fd; | ||
70 | pfd[0].events = POLLIN; | ||
71 | safe_poll(pfd, 1, -1); | ||
72 | } | ||
73 | } | ||
74 | |||
23 | /* | 75 | /* |
24 | * Read all of the supplied buffer from a file. | 76 | * Read all of the supplied buffer from a file. |
25 | * This does multiple reads as necessary. | 77 | * This does multiple reads as necessary. |
@@ -93,6 +145,7 @@ char *reads(int fd, char *buffer, size_t size) | |||
93 | 145 | ||
94 | // Read one line a-la fgets. Reads byte-by-byte. | 146 | // Read one line a-la fgets. Reads byte-by-byte. |
95 | // Useful when it is important to not read ahead. | 147 | // Useful when it is important to not read ahead. |
148 | // Bytes are appended to pfx (which must be malloced, or NULL). | ||
96 | char *xmalloc_reads(int fd, char *buf) | 149 | char *xmalloc_reads(int fd, char *buf) |
97 | { | 150 | { |
98 | char *p; | 151 | char *p; |
@@ -106,7 +159,8 @@ char *xmalloc_reads(int fd, char *buf) | |||
106 | p = buf + sz; | 159 | p = buf + sz; |
107 | sz += 128; | 160 | sz += 128; |
108 | } | 161 | } |
109 | if (safe_read(fd, p, 1) != 1) { /* EOF/error */ | 162 | /* nonblock_safe_read() because we are used by e.g. shells */ |
163 | if (nonblock_safe_read(fd, p, 1) != 1) { /* EOF/error */ | ||
110 | if (p == buf) { | 164 | if (p == buf) { |
111 | /* we read nothing [and buf was NULL initially] */ | 165 | /* we read nothing [and buf was NULL initially] */ |
112 | free(buf); | 166 | free(buf); |
diff --git a/shell/ash.c b/shell/ash.c index 65f94f682..debe8ecdd 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5428,7 +5428,7 @@ expbackq(union node *cmd, int quoted, int quotes) | |||
5428 | read: | 5428 | read: |
5429 | if (in.fd < 0) | 5429 | if (in.fd < 0) |
5430 | break; | 5430 | break; |
5431 | i = safe_read(in.fd, buf, sizeof(buf)); | 5431 | i = nonblock_safe_read(in.fd, buf, sizeof(buf)); |
5432 | TRACE(("expbackq: read returns %d\n", i)); | 5432 | TRACE(("expbackq: read returns %d\n", i)); |
5433 | if (i <= 0) | 5433 | if (i <= 0) |
5434 | break; | 5434 | break; |
@@ -8678,7 +8678,7 @@ preadfd(void) | |||
8678 | retry: | 8678 | retry: |
8679 | #if ENABLE_FEATURE_EDITING | 8679 | #if ENABLE_FEATURE_EDITING |
8680 | if (!iflag || parsefile->fd) | 8680 | if (!iflag || parsefile->fd) |
8681 | nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); | 8681 | nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1); |
8682 | else { | 8682 | else { |
8683 | #if ENABLE_FEATURE_TAB_COMPLETION | 8683 | #if ENABLE_FEATURE_TAB_COMPLETION |
8684 | line_input_state->path_lookup = pathval(); | 8684 | line_input_state->path_lookup = pathval(); |
@@ -8700,9 +8700,11 @@ preadfd(void) | |||
8700 | } | 8700 | } |
8701 | } | 8701 | } |
8702 | #else | 8702 | #else |
8703 | nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); | 8703 | nr = nonblock_safe_read(parsefile->fd, buf, BUFSIZ - 1); |
8704 | #endif | 8704 | #endif |
8705 | 8705 | ||
8706 | #if 0 | ||
8707 | /* nonblock_safe_read() handles this problem */ | ||
8706 | if (nr < 0) { | 8708 | if (nr < 0) { |
8707 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 8709 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
8708 | int flags = fcntl(0, F_GETFL); | 8710 | int flags = fcntl(0, F_GETFL); |
@@ -8715,6 +8717,7 @@ preadfd(void) | |||
8715 | } | 8717 | } |
8716 | } | 8718 | } |
8717 | } | 8719 | } |
8720 | #endif | ||
8718 | return nr; | 8721 | return nr; |
8719 | } | 8722 | } |
8720 | 8723 | ||
@@ -11801,7 +11804,7 @@ readcmd(int argc, char **argv) | |||
11801 | backslash = 0; | 11804 | backslash = 0; |
11802 | STARTSTACKSTR(p); | 11805 | STARTSTACKSTR(p); |
11803 | do { | 11806 | do { |
11804 | if (read(0, &c, 1) != 1) { | 11807 | if (nonblock_safe_read(0, &c, 1) != 1) { |
11805 | status = 1; | 11808 | status = 1; |
11806 | break; | 11809 | break; |
11807 | } | 11810 | } |
diff --git a/shell/hush.c b/shell/hush.c index 4d4843173..820fd888d 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1273,10 +1273,10 @@ static void get_user_input(struct in_str *i) | |||
1273 | prompt_str = setup_prompt_string(i->promptmode); | 1273 | prompt_str = setup_prompt_string(i->promptmode); |
1274 | #if ENABLE_FEATURE_EDITING | 1274 | #if ENABLE_FEATURE_EDITING |
1275 | /* Enable command line editing only while a command line | 1275 | /* Enable command line editing only while a command line |
1276 | * is actually being read; otherwise, we'll end up bequeathing | 1276 | * is actually being read */ |
1277 | * atexit() handlers and other unwanted stuff to our | 1277 | do { |
1278 | * child processes (rob@sysgo.de) */ | 1278 | r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); |
1279 | r = read_line_input(prompt_str, user_input_buf, BUFSIZ-1, line_input_state); | 1279 | } while (r == 0); /* repeat if Ctrl-C */ |
1280 | i->eof_flag = (r < 0); | 1280 | i->eof_flag = (r < 0); |
1281 | if (i->eof_flag) { /* EOF/error detected */ | 1281 | if (i->eof_flag) { /* EOF/error detected */ |
1282 | user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ | 1282 | user_input_buf[0] = EOF; /* yes, it will be truncated, it's ok */ |
diff --git a/shell/msh.c b/shell/msh.c index fd287f16e..917b08a1e 100644 --- a/shell/msh.c +++ b/shell/msh.c | |||
@@ -42,6 +42,7 @@ | |||
42 | # define xmalloc(size) malloc(size) | 42 | # define xmalloc(size) malloc(size) |
43 | # define msh_main(argc,argv) main(argc,argv) | 43 | # define msh_main(argc,argv) main(argc,argv) |
44 | # define safe_read(fd,buf,count) read(fd,buf,count) | 44 | # define safe_read(fd,buf,count) read(fd,buf,count) |
45 | # define nonblock_safe_read(fd,buf,count) read(fd,buf,count) | ||
45 | # define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1]) | 46 | # define NOT_LONE_DASH(s) ((s)[0] != '-' || (s)[1]) |
46 | # define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1]) | 47 | # define LONE_CHAR(s,c) ((s)[0] == (c) && !(s)[1]) |
47 | # define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) | 48 | # define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) |
@@ -3376,7 +3377,7 @@ static int doread(struct op *t) | |||
3376 | } | 3377 | } |
3377 | for (wp = t->words + 1; *wp; wp++) { | 3378 | for (wp = t->words + 1; *wp; wp++) { |
3378 | for (cp = global_env.linep; !nl && cp < elinep - 1; cp++) { | 3379 | for (cp = global_env.linep; !nl && cp < elinep - 1; cp++) { |
3379 | nb = read(0, cp, sizeof(*cp)); | 3380 | nb = nonblock_safe_read(0, cp, sizeof(*cp)); |
3380 | if (nb != sizeof(*cp)) | 3381 | if (nb != sizeof(*cp)) |
3381 | break; | 3382 | break; |
3382 | nl = (*cp == '\n'); | 3383 | nl = (*cp == '\n'); |
@@ -4799,7 +4800,7 @@ static int filechar(struct ioarg *ap) | |||
4799 | if (i) | 4800 | if (i) |
4800 | lseek(ap->afile, ap->afpos, SEEK_SET); | 4801 | lseek(ap->afile, ap->afpos, SEEK_SET); |
4801 | 4802 | ||
4802 | i = safe_read(ap->afile, bp->buf, sizeof(bp->buf)); | 4803 | i = nonblock_safe_read(ap->afile, bp->buf, sizeof(bp->buf)); |
4803 | if (i <= 0) { | 4804 | if (i <= 0) { |
4804 | closef(ap->afile); | 4805 | closef(ap->afile); |
4805 | return 0; | 4806 | return 0; |
@@ -4830,7 +4831,7 @@ static int filechar(struct ioarg *ap) | |||
4830 | return c; | 4831 | return c; |
4831 | } | 4832 | } |
4832 | #endif | 4833 | #endif |
4833 | i = safe_read(ap->afile, &c, sizeof(c)); | 4834 | i = nonblock_safe_read(ap->afile, &c, sizeof(c)); |
4834 | return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0); | 4835 | return i == sizeof(c) ? (c & 0x7f) : (closef(ap->afile), 0); |
4835 | } | 4836 | } |
4836 | 4837 | ||
@@ -4841,7 +4842,7 @@ static int herechar(struct ioarg *ap) | |||
4841 | { | 4842 | { |
4842 | char c; | 4843 | char c; |
4843 | 4844 | ||
4844 | if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) { | 4845 | if (nonblock_safe_read(ap->afile, &c, sizeof(c)) != sizeof(c)) { |
4845 | close(ap->afile); | 4846 | close(ap->afile); |
4846 | c = '\0'; | 4847 | c = '\0'; |
4847 | } | 4848 | } |