我正在读取另一个生成输出(慢速和无限)的过程的输出。因为我想实时读取这些数据,所以我使用“stdbuf -oL”(行缓冲,数据是文本)。我无法控制生成过程,因此我无法修改源以强制刷新。
到目前为止,stdbuf工作正常,但是该过程使用SOCK_RAW并且需要以root身份运行,具有setuid(0)或cap_net_raw
功能。当使用setuid或功能作为非root运行时,stdbuf似乎被忽略了。让我演示一下这个问题:
这是一个简单的作家:
#include <stdio.h>
#include <unistd.h>
int main(){
int i;
for ( i = 0;; i++){
fprintf(stdout, "%d\n", i);
sleep(1);
}
}
还有一个简单的读者:
#include <stdio.h>
int main(){
char* line = NULL;
size_t n = 0;
while (getline(&line, &n, stdin) != -1 ) {
fputs(line, stdout);
}
}
正如预期的那样,通过执行./writer | ./reader
,在填充缓冲区之前不会显示任何内容。预先添加stdbuf -oL
可以实现行缓冲,并且我将这些行放入读者:
% stdbuf -oL ./writer | ./reader
0
1
2
...
但如果我添加cap_net_raw+ep
它停止工作:
% sudo setcap cap_net_raw+ep ./writer
% stdbuf -oL ./writer | ./reader
(no output)
使用setuid时会出现相同的行为:
% sudo chown root:root ./writer
% sudo chmod +s ./writer
% stdbuf -oL ./writer | ./reader
(no output)
我有兴趣了解为什么会发生这种情况以及如何在不以root身份运行的情况下继续使用stdbuf。我承认我并不完全理解setuid在幕后所做的事情。
从查看stdbuf source code看起来它可以通过设置LD_PRELOAD来工作。使用LD_PRELOAD和setuid可执行文件或sudo当然存在安全问题。
我找到的一个建议是disable the noatsecure selinux attribute你的可执行文件。
另一个更简单的选择是避免使用stdbuf并直接从源代码中调用fflush(stdout)
。
LD_PRELOAD
的解决方案您可以使用unbuffer
实用程序,它是expect
(expect-devel
)包的一部分。 unbuffer
是一个非常短的期望剧本。它不需要LD_PRELOAD
因为它使用了另一种技巧。 expect
创建了一个伪终端(如xterm
或ssh
),因此使用unbuffer
执行的进程被欺骗,认为它正在写入交互设备,因此默认情况下它使用stdout
上的行缓冲。
用法:
unbuffer ./writer | ./reader
如果stdbuf
与该计划合作,那么unbuffer
也很有可能。因为LD_PRELOAD
有一些限制,unbuffer
比stdbuf
有优势。与stdbuf
相反,它将适用于这些类型的可执行文件:
libc