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 /libbb | |
| 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
Diffstat (limited to 'libbb')
| -rw-r--r-- | libbb/lineedit.c | 4 | ||||
| -rw-r--r-- | libbb/read.c | 56 |
2 files changed, 57 insertions, 3 deletions
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); |
