This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[RFC][PATCH] Add envvar IO_BUFMODE to forcibly set buffering mode
- From: Sunyeop Lee <sunyeop97 at gmail dot com>
- To: libc-alpha at sourceware dot org
- Date: Thu, 23 Mar 2017 02:14:31 +0900
- Subject: [RFC][PATCH] Add envvar IO_BUFMODE to forcibly set buffering mode
- Authentication-results: sourceware.org; auth=none
Hello all,
When a program interacts via pipes with a program that doesn't call
fflush(stdout) well, they may not communicate data through the pipes
at a proper time.
Let me give an example.
test.c:
// compile with gcc -o test test.c
#include <unistd.h>
#include <stdio.h>
int main() {
for(int i = 0; i < 5; i++) {
printf("test");
usleep(500000); // sleep 0.5 sec
putchar('\n');
usleep(500000); // sleep 0.5 sec
}
return 0;
}
When I execute this program via tty, I would get "test\n" every 1
second. If I execute the program via "./test > test.log" and read the
output by "tail -f test.log", otherwise, I will get the entire output
after 5 seconds.
Let me give another example.
test.py:
import subprocess
import sys
p = subprocess.Popen(['./test'], stdout=subprocess.PIPE)
while True:
tmp = p.stdout.read(1)
if not tmp:
break
print(tmp.decode(), end='')
sys.stdout.flush()
The python code tries to interact with './test', it would get the
entire output at a time after 5 seconds, however.
I thought it's too hard and tricky to solve this problem. Some of
the solutions are: to modify the source code, to open a pty to get the
output through it, or to use LD_PRELOAD to hook printf or some
functions.
Therefore, I propose a new environment variable IO_BUFMODE to
forcibly set the buffering mode in these cases. To force "fully
buffering mode", I can easily set IO_BUFMODE=0. To force "line
buffering mode", I set IO_BUFMODE=1. And to force "no buffered mode",
I set IO_BUFMODE=2. The constants are from _IOFBF, _IOLBF, _IONBF used
in setvbuf().
With IO_BUFMODE support, it's not needed to write a program that
opens a pty to interact. Also, it is not required to use LD_PRELOAD to
hook functions to set "no buffered mode" (because no buffered mode is
impossible even with pty.)
Thank you.
diff --git a/ChangeLog b/ChangeLog
index ef5388c..266ada3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2017-03-22 Sunyeop Lee <sunyeop97@gmail.com>
+
+ * libio/filedoallocate.c (_IO_file_doallocate): Add environment variable
+ IO_BUFMODE and relevant constants (_IOFBF, _IOLBF, _IONBF) to forcibly
+ set buffering mode
+
2017-03-20 Mike Frysinger <vapier@gentoo.org>
[BZ #21275]
diff --git a/libio/filedoalloc.c b/libio/filedoalloc.c
index 50919c9..1070f72 100644
--- a/libio/filedoalloc.c
+++ b/libio/filedoalloc.c
@@ -61,6 +61,12 @@
#include <stdlib.h>
#include <unistd.h>
+
+#define _IOFBF 0 /* Fully buffered. */
+#define _IOLBF 1 /* Line buffered. */
+#define _IONBF 2 /* No buffering. */
+
+
/* Return the result of isatty, without changing errno. */
static int
local_isatty (int fd)
@@ -77,7 +83,8 @@ int
_IO_file_doallocate (_IO_FILE *fp)
{
_IO_size_t size;
- char *p;
+ char *p, *temp;
+ int io_bufmode;
struct stat64 st;
size = _IO_BUFSIZ;
@@ -98,6 +105,24 @@ _IO_file_doallocate (_IO_FILE *fp)
size = st.st_blksize;
#endif
}
+ if ((temp = getenv ("IO_BUFMODE")))
+ {
+ io_bufmode = atoi (temp);
+ switch (io_bufmode)
+ {
+ case _IOFBF:
+ fp->_flags &= ~(_IO_LINE_BUF|_IO_UNBUFFERED);
+ break;
+ case _IOLBF:
+ fp->_flags &= ~_IO_UNBUFFERED;
+ fp->_flags |= _IO_LINE_BUF;
+ break;
+ case _IONBF:
+ fp->_flags &= ~_IO_LINE_BUF;
+ fp->_flags |= _IO_UNBUFFERED;
+ break;
+ }
+ }
p = malloc (size);
if (__glibc_unlikely (p == NULL))
return EOF;