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); |