我在 Linux 上使用ioctl
系统调用来获取 C 程序中的当前 tty。
我不知道为什么ioctl
如果手动运行程序(作为 root),程序会失败,而如果init.d
使用脚本(作为 root),程序会按预期工作。请注意,我尝试在 GUI 会话中使用 SSH 连接或终端应用程序。我设法使程序正确运行的唯一方法是切换到虚拟终端(CTRL+Fx,其中 x 是数字)并从那里运行命令。我在 RHEL8 和 RHEL6 上测试了该程序。在 RHEL8 上,ioctl
返回ENOTTY
(“设备的 ioctl 不适当”),而在 RHEL6 上,错误为EINVAL
(“无效参数”)。
我编写了这个测试程序来调试该问题,但它的行为似乎与主程序不同:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <linux/vt.h>
#include <linux/kd.h>
int main()
{
long tty = -1;
long fd = 0;
int result = 0;
struct vt_stat ttyinfo;
fd = open("/dev/tty", O_RDWR | O_NOCTTY);
if(fd <= 0)
{
printf("Trying tty0\n");
fd = open("/dev/tty0", O_RDWR | O_NOCTTY);
if(fd <= 0)
{
printf("[open] error = %s\n" , strerror(errno) );
return -1;
}
}
result = ioctl(fd, VT_GETSTATE, &ttyinfo);
if (result == 0)
{
tty = ttyinfo.v_active;
printf("tty: %d\n" , tty);
}
else
{
printf("[ioctl] error = %s\n" , strerror(errno) );
}
return 0;
}
注意:这只是一段代码,真正的程序是守护进程并执行更多操作。
RHEL8:
如果从 UI 或 SSH 运行命令(实际应用程序),则尝试 /dev/tty 和 /dev/tty0,报告的错误为: Inproperty ioctl for device。
RHEL6: 如果命令(实际应用程序)从 UI 运行,则仅使用 SSH /dev/tty,报告的错误为: Invalid argument
你知道为什么会发生这种情况吗?我尝试将守护程序添加到测试程序中,但是当测试程序继续工作时,主程序却没有。肯定还有其他事情是主程序正在做而测试程序没有做的。
答案1
我怀疑发生这种情况是因为您正在从不是虚拟控制台的终端运行程序。/dev/tty
是一种特殊设备,提供对控制终端的访问,这不一定是虚拟控制台;但ioctl
您使用的仅适用于虚拟控制台。
如果您确保fd
点位于某个位置,您的程序将可靠地运行虚拟控制台,以 root 身份运行时最简单的方法是打开/dev/tty0
,是活动的虚拟控制台:
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/vt.h>
int main(int argc, char **argv) {
long tty = -1;
long fd = 0;
int result = 0;
struct vt_stat ttyinfo;
fd = open("/dev/tty0", O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("Error opening /dev/tty0");
return 1;
}
result = ioctl(fd, VT_GETSTATE, &ttyinfo);
if (result == 0) {
tty = ttyinfo.v_active;
printf("Active tty: %ld\n", tty);
} else {
perror("ioctl failed");
}
}