terminal
我有一个非常奇怪的问题,发生在、su
和w3m
的交叉点处/dev/null
:
当我su -
从user1
到 时user2
,以下命令不起作用:
$ w3m zz.html 2>/dev/null
Error occurred while reset 800b: errno=25
但相同的命令无需2>/dev/null
重定向即可正常工作:
$ w3m zz.html
最后,当我直接登录user2
(而不是从 切换user1
)时,一切正常(无论有2>/dev/null
还是没有)
当我w3m
使用strace
:运行时strace -o zz.strace w3m zz.html 2>/dev/null
,我看到两种情况(工作和非工作)此时开始出现分歧:
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=77, ws_col=199, ws_xpixel=0, ws_ypixel=0}) = 0
ioctl(2, TIOCGWINSZ, 0x7318fb8e7c40) = -1 ENOTTY (Inappropriate ioctl for device)
fstat(2, {st_mode=S_IFCHR|0666, st_rdev=makedev(0x1, 0x3), ...}) = 0
ioctl(2, TCGETS, 0x7318fb8e7400) = -1 ENOTTY (Inappropriate ioctl for device)
brk(0x16a51a8a1000) = 0x16a51a8a1000
brk(0x16a51a8b1000) = 0x16a51a8b1000
brk(0x16a51a8c2000) = 0x16a51a8c2000
brk(0x16a51a8d2000) = 0x16a51a8d2000
brk(0x16a51a8e8000) = 0x16a51a8e8000
ioctl(2, TCGETS, 0x7318fb8e7bd0) = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(2, TCGETS, 0x7318fb8e7bc0) = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(2, SNDCTL_TMR_START or TCSETS, {B0 -opost -isig -icanon -echo ...}) = -1 ENOTTY (Inappropriate ioctl for device)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x7), ...}) = 0
write(1, "Error occurred while reset 800b:"..., 42) = 42
write(2, "\33[?1049h\33[22;0;0t\33[H\33[2J\33[39;49m"..., 58) = 58
ioctl(2, TCGETS, 0x7318fb8e7ba0) = -1 ENOTTY (Inappropriate ioctl for device)
ioctl(2, SNDCTL_TMR_START or TCSETS, {B0 -opost -isig -icanon -echo ...}) = -1 ENOTTY (Inappropriate ioctl for device)
exit_group(1) = ?
+++ exited with 1 +++
据我所知,这TIOCGWINSZ
ioctl
似乎与终端尺寸有关。但我无法找到到底是什么导致了这个问题。
答案1
由于评论而更新
-
显然, Debian 已经修复了这个无意义的问题。问题的核心仍然是一样的——混合环境。
问题是/proc/*/fd/*
!
引用来自的优秀答案用户1686,别忘了点赞哦sudo 后 /dev/stderr 的权限被拒绝。
问题的核心是:
这是 /proc 的 Linux 实现的一个众所周知的怪癖;多年来从未得到修复的问题。对于 Linux,打开 /proc/ 下的链接/fd/不会像 dup() 那样直接复制文件描述符(尽管理论上 /proc 链接可以实现这一点) - 相反,它会重新打开文件,并将执行新的权限检查。
此问题的原始帖子来自 Theodore Tso(2008 年!) -回复:RFC:/dev/stdin,符号链接和权限
解决方案
是要换新pty
的用户B:
userA:~$ su -l -P userB
来自男人页面su
:
-, -l, --login Start the shell as a login shell with an environment similar to a real login: • clears all the environment variables except TERM and variables specified by --whitelist-environment • initializes the environment variables HOME, SHELL, USER, LOGNAME, and PATH • changes to the target user’s home directory -P, --pty Create a pseudo-terminal for the session. The independent terminal provides better security as the user does not share a terminal with the original session. This can be used to avoid TIOCSTI ioctl terminal injection and other security attacks against terminal file descriptors. The entire session can also be moved to the background (e.g., su --pty - username -c application &). If the pseudo-terminal is enabled, then su works as a proxy between the sessions (sync stdin and stdout). This feature is mostly designed for interactive sessions. If the standard input is not a terminal, but for example a pipe (e.g., echo "date" | su --pty), then the ECHO flag
我有机会在 Debian Bookworm 上测试它,它可以工作:
ghu@magnetron:/tmp$ w3m testing.html 2>/dev/null
Error occurred while reset 800b: errno=25
ughu@magnetron:/tmp$ exit
logout
tukan@magnetron:~/Downloads/html$ su -l -P ughu
Password:
ughu@magnetron:~$ cd /tmp
ughu@magnetron:/tmp$ w3m testing.html 2>/dev/null
ughu@magnetron:/tmp$ exit
根据OP讨论进行编辑
OP 和我的版本之间的区别(2.33.1-0.1
vs 2.38.1-1.1+b1
- 这里是完整的变更日志)。
OP 发现,stderr
如果重定向足够短,即使使用简单的su -
.例如,这有效 - 在 shellnon-existing-command 2>/dev/null
或non-existing-command 2>/tmp/output
.
另一方面,如果输出太长(错误消息指示的最有可能在 ~2^15 (32768) 字节处重置十六进制:800b 是 12 月:32768+0+0+11)就像在应用程序中一样,w3m
重置完成并再次检查用户权限,但失败。
一些我不知道的补丁修复了 util-linux 版本2.33.1-0.1
和2.38.1-1.1+b1
.
结论
您需要使用较新的版本(我不知道它适用于哪个版本),但在我的情况下,开关2.38.1-1.1+b1
工作-P
正常。然后,即使对于大输出,重定向也将起作用,w3m
如我上面所示。