rec*_*rom 6 command-line c stdin
在给定的终端窗口中,某些命令开始始终失败:
$ sudo apt-get install ipython
...
After this operation, 3,826 kB of additional disk space will be used.
Do you want to continue? [Y/n] Abort.
$
$ kinit -f <username>
Password for <username>@<domain>:
kinit: Pre-authentication failed: Cannot read password while getting initial credentials
$
$ passwd
Changing password for <username>.
(current) UNIX password:
passwd: Authentication token manipulation error
passwd: password unchanged
$
$ crontab -e
Too many errors from stdincrontab: "/usr/bin/sensible-editor" exited with status 1
$
$ sudo docker run -it ubuntu bash
(hangs forever)
Run Code Online (Sandbox Code Playgroud)
在寻找原因时,strace显示程序尝试从 STDIN 读取但收到错误:
read(0, 0x7fffe1205cc7, 1) = -1 EAGAIN (Resource temporarily unavailable)
Run Code Online (Sandbox Code Playgroud)
从read(2)手册页:
ERRORS
EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.
Run Code Online (Sandbox Code Playgroud)
果然,该终端窗口的 STDIN 被标记为非阻塞(由flags 中的 4 表示):
$ cat /proc/self/fdinfo/0
pos: 0
flags: 0104002
mnt_id: 25
Run Code Online (Sandbox Code Playgroud)
我假设我使用的某个程序将 STDIN 设置为非阻塞模式,然后在退出时没有将其设置回来(或者它在它可以之前被杀死。)
我无法弄清楚如何从命令行解决这个问题,所以我编写了以下程序来解决这个问题(它还允许您将 STDIN 更改为非阻塞模式以查看中断情况。)
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int makeStdinNonblocking(int flags) {
if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK) < 0) {
printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int makeStdinBlocking(int flags) {
if (fcntl(STDIN_FILENO, F_SETFL, flags & ~(O_NONBLOCK)) < 0) {
printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) {
int flags;
if (argc != 2) {
goto usage;
}
if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
return EXIT_FAILURE;
}
if (0 == strncmp(argv[1], "nonblock", 9)) {
return makeStdinNonblocking(flags);
}
else if ( 0 == strncmp(argv[1], "block", 6)) {
return makeStdinBlocking(flags);
}
usage:
printf("Usage: %s <nonblock|block>\n", argv[0]);
return EXIT_FAILURE;
}
Run Code Online (Sandbox Code Playgroud)
无论如何,我想知道:
作为参考,我使用的是 Ubuntu 17.10 和 GNU bash,版本 4.4.12(1)-release (x86_64-pc-linux-gnu)
更新: 看起来这个问题已经在 Fedora 中得到解决,并且有一个 bash 补丁:
https://bugzilla.redhat.com/show_bug.cgi?id=1068697
不过,该修复似乎并未在上游应用,至少在 4.4.18(1)-release 版本(从 2018 年 1 月开始)。此外,bash 维护者提到 bash 不应该对此负责:
https://lists.gnu.org/archive/html/bug-bash/2017-01/msg00043.html
听起来应用程序应该负责恢复 STDIN 的原始标志(如果它更改了它们),所以我使用以下程序来检查 STDIN:
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main() {
int flags;
if ((flags = fcntl(STDIN_FILENO, F_GETFL, 0)) < 0) {
printf("Error calling fcntl in %s: %s\n", __FUNCTION__, strerror(errno));
}
if (0 != (flags & (O_NONBLOCK))) {
printf("Warning, STDIN in nonblock mode\n");
}
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
我编译了程序 ( gcc -o checkstdin checkstdin.c
),然后将以下内容放入我的 .bashrc 以使其在每个命令后运行:
PROMPT_COMMAND+="/path/to/checkstdin"
Run Code Online (Sandbox Code Playgroud)
如果检测到 STDIN 现在处于非阻塞模式,它将向 STDOUT 打印警告。
归档时间: |
|
查看次数: |
4572 次 |
最近记录: |