2007-09-06 21:01 现在这个程序已经变成大写了
MAKEDEV,可以创建设备文件。
MAKEDEB pty,就可以创建pty了
可是我strace bindtty的时候,还是报pty错误
8020 open("/dev/ptyp0", O_RDWR) = -1 ENXIO (No such device or address)
8020 open("/dev/ptyp1", O_RDWR) = -1 ENXIO (No such device or address)
8020 open("/dev/ptyp2", O_RDWR) = -1 ENXIO (No such device or address)
8020 open("/dev/ptyp3", O_RDWR) = -1 ENXIO (No such device or address)
8020 open("/dev/ptyp4", O_RDWR) = -1 ENXIO (No such device or address)
但是我有这个设备啊,郁闷
[root@xsec screen]# file /dev/ptyp0
/dev/ptyp0: character special (2/0)
|
2007-09-06 20:47
一个 tty 设备得名于电传打字机的很老的简称, 并且起初只和连接到一台 UNIX 机器的物理或者虚拟终端有关联. 长时间以来, 这个名子还逐渐表示任何串口类型的设备, 因为终端连接也能够在这样的一个连接上建立. 一些物理 tty 设备的例子是串口, USB-串口 转换器, 以及某些类型的需要特殊处理来正确工作的调制解调器(例如传统的 Win-Modem 类型设备). tty 虚拟设备支持虚拟控制台以用来登录到一台计算机, 或者从键盘, 或者从网络连接, 或者通过一个 xterm 会话.
Linux tty 驱动的核心正好位于标准字符驱动级别之下, 并且提供了一些特性集中在为使用终端类型设备提供一个接口. 这个核心负责控制跨越一个 tty 设备的数据流和数据格式. 这允许 tty 驱动以一种一致的方式集中于处理到硬件和出自硬件的数据, 而不必担心如何控制对用户空间的接口. 为控制数据流, 有几个不同的线路规程可以虚拟地"插入"任何一个 tty 设备. 这由不同的 tty 线路规程驱动来完成.
如同图tty 核心概览所示, tty 核心从一个用户获取将要发送给一个 tty 设备的数据. 它接着传递它到一个 tty 线路规程驱动, 接着传递它到一个 tty 驱动. 这个 tty 驱动转换数据为可以发送给硬件的格式. 从 tty 硬件收到的数据向上回流通过 tty 驱动, 进入 tty 线路规程驱动, 再进入 tty 核心, 在这里它被一个用户获取. 有时 tty 驱动直接和 tty 核心通讯, 并且 tty 核心直接发送数据到 tty 驱动, 但是常常 tty 线路规程有机会修改在 2 者之间发送的数据.
tty 驱动从未看见 tty 线路规程. 这个驱动不能直接和线路规程通讯, 它甚至也不知道它存在. 驱动的工作是以硬件能够理解的方式格式化发送给它的数据, 并且从硬件接收数据. tty 线路规程的工作是以特殊的方式格式化从一个用户或者硬件收到的数据. 这种格式化常常采用一个协议转换的形式, 例如 PPP 和 Bluetooth.
有 3 种不同类型 tty 驱动: 控制台, 串口, 和 pty. 控制台和 pty 驱动硬件已经被编写以及可能是唯一需要的 tty 驱动的类型. 这使得任何使用 tty 核心来与用户和系统交互的新驱动作为串口驱动.
为知道什么类型的 tty 驱动当前被加载到内核以及什么 tty 设备当前存在, 查看 /proc/tty/drivers 文件. 这个文件包括一个当前存在的不同 tty 驱动的列表, 显示驱动的名子, 缺省的节点名子, 驱动的主编号, 这个驱动使用的次编号范围, 以及 tty 驱动的类型. 下面是一个这个文件的例子:
/dev/tty /dev/tty 5 0 system:/dev/tty
/dev/console /dev/console 5 1 system:console
/dev/ptmx /dev/ptmx 5 2 system
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
usbserial /dev/ttyUSB 188 0-254 serial
serial /dev/ttyS 4 64-67 serial
pty_slave /dev/pts 136 0-255 pty:slave
pty_master /dev/ptm 128 0-255 pty:master
pty_slave /dev/ttyp 3 0-255 pty:slave
pty_master /dev/pty 2 0-255 pty:master
unknown /dev/tty 4 1-63 console
/proc/tty/driver/ 目录给一些 tty 驱动包含了单独的文件, 如果它们实现这个功能. 缺省的串口驱动创建一个文件在这个目录中来展示许多串口特定的硬件信息. 如何在这个目录建立一个文件的信息后面描述.
所有的当前注册的以及在内核中出现的 tty 设备有它们自己的子目录在 /sys/class/tty 下面. 在那个子目录下, 有一个 "dev" 文件包含有分配给那个 tty 设备的主次编号. 如果这个驱动告知内核物理设备和关联到这个 tty 设备的驱动的所在, 它创建符号连接到它们. 这个树的一个例子是:
/sys/class/tty/
|-- console
| `-- dev
|-- ptmx
| `-- dev
|-- tty
| `-- dev
|-- tty0
| `-- dev
...
|-- ttyS1
| `-- dev
|-- ttyS2
| `-- dev
|-- ttyS3
| `-- dev
...
|-- ttyUSB0
| |-- dev
| |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB0
| `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
|-- ttyUSB1
| |-- dev
| |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB1
| `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
|-- ttyUSB2
| |-- dev
| |-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB2
| `-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
`-- ttyUSB3
|-- dev
|-- device -> ../../../devices/pci0000:00/0000:00:09.0/usb3/3-1/3-1:1.0/ttyUSB3
`-- driver -> ../../../bus/usb-serial/drivers/keyspan_4
为解释 tty 核心如何工作, 我们创建一个小 tty 驱动, 可以被加载, 以及写入读出, 并且卸载. 任何一个 tty 驱动的主要数据结构是 struct tty_driver. 它用来注册和注销一个 tty 驱动到 tty 内核, 在内核头文件 <linux/tty_driver.h> 中描述.
为创建一个 struct tty_driver, 函数 alloc_tty_driver 必须用这个驱动作为参数而支持的 tty 设备号来调用. 这可使用下面的简短代码来完成:
/* allocate the tty driver */
tiny_tty_driver = alloc_tty_driver(TINY_TTY_MINORS);
if (!tiny_tty_driver)
return -ENOMEM;
在 alloc_tty_driver 函数被成功调用后, struct tty_driver 应当用基于 tty 驱动的需要的正确信息被初始化. 这个结构包含很多不同成员, 但不是为了有一个可工作的 tty 驱动而全部都必须被初始化. 这里有一个例子展示如何初始化这个结构并且建立足够的成员来创建一个工作的 tty 驱动. 它使用 tty_set_operations 函数来帮助拷贝驱动中定义的函数操作集合:
static struct tty_operations serial_ops = {
.open = tiny_open,
.close = tiny_close,
.write = tiny_write,
.write_room = tiny_write_room,
.set_termios = tiny_set_termios,
};
...
/* initialize the tty driver */
tiny_tty_driver->owner = THIS_MODULE;
tiny_tty_driver->driver_name = "tiny_tty";
tiny_tty_driver->name = "ttty";
tiny_tty_driver->devfs_name = "tts/ttty%d";
tiny_tty_driver->major = TINY_TTY_MAJOR,
tiny_tty_driver->type = TTY_DRIVER_TYPE_SERIAL,
tiny_tty_driver->subtype = SERIAL_TYPE_NORMAL,
tiny_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
tiny_tty_driver->init_termios = tty_std_termios;
tiny_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
tty_set_operations(tiny_tty_driver, &serial_ops);
上面列出的变量和函数, 以及这个结构如何使用, 在本章的剩下部分讲解.
为注册这个驱动到 tty 核心, struct tty_driver 必须传递到 tty_register_driver 函数:
/* register the tty driver */
retval = tty_register_driver(tiny_tty_driver);
if (retval)
{
printk(KERN_ERR "failed to register tiny tty driver");
put_tty_driver(tiny_tty_driver);
return retval;
}
当调用 tty_register_driver, 内核创建了所有的不同 sysfs tty 文件为这个 tty 驱动可能有的整个范围的次设备. 如果你使用 devfs ( 本书不涉及 ) 并且除非指定 TTY_DRIVER_NO_DEVFS 标志, devfs 文件也被创建. 这个标志可被指定如果你只想为这个实际在系统中存在的设备调用 tty_register_device, 因此用户一直有一个内核中有的最新的设备视图, 这就是 devfs 用户期望的.
在注册它自己后, 这个驱动通过 tty_register_device 注册它控制的设备. 这个函数有 3 个参数:
-
一个指针指向这个设备所属的 struct tty_driver.
-
设备的次编号
-
一个指针指向这个 tty 设备所绑定的 struct device. 如果这个 tty 设备没绑定到任何一个 struct device, 这个参数可被设为 NULL.
我们的驱动一次注册所有的 tty 设备, 因为它们是虚拟的并且没有绑定到任何一个物理设备:
for (i = 0; i < TINY_TTY_MINORS; ++i)
tty_register_device(tiny_tty_driver, i, NULL);
为从 tty 核心注销这个驱动, 所有的通过调用 tty_register_device 而注册的 tty 设备需要使用对 tty_unregister_device 的调用来清理. 接着 struct tty_driver 必须使用一个 tty_unregister_driver 调用来注销.
for (i = 0; i < TINY_TTY_MINORS; ++i)
tty_unregister_device(tiny_tty_driver, i);
tty_unregister_driver(tiny_tty_driver);
18.1.1. 结构 struct termios
在 struct tty_driver 中的 init_termios 变量是一个 struct termios. 这个变量被用来提供一个健全的线路设置集合, 如果这个端口在被用户初始化前使用. 驱动初始化这个变量使用一个标准的数值集, 它拷贝自 tty_std_termios 变量. tty_std_termos 在 tty 核心被定义为:
struct termios tty_std_termios = {
.c_iflag = ICRNL | IXON,
.c_oflag = OPOST | ONLCR,
.c_cflag = B38400 | CS8 | CREAD | HUPCL,
.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN,
.c_cc = INIT_C_CC
};
这个 struct termios 结构用来持有所有的当前线路设置, 给这个 tty 设备的一个特定端口. 这些线路设置控制当前波特率, 数据大小, 数据流控设置, 以及许多其他值. 这个结构的不同成员是:
- tcflag_t c_iflag;
-
输入模式标志
- tcflag_t c_oflag;
-
输出模式标志
- tcflag_t c_cflag;
-
控制模式标志
- tcflag_t c_lflag;
-
本地模式标志
- cc_t c_line;
-
线路规程类型
- cc_t c_cc[NCCS];
-
一个控制字符数组
所有的模式标志被定义为一个大的位段. 模式的不同值, 以及它们用在哪里, 可以见在任何 Linux 发布中都有的 termios 手册页. 内核提供了一套有用的宏定义来获得不同的位. 这些宏定义在头文件 include/linux/tty.h 中定义.
所有的在 tiny_tty_driver 变量中定义的成员有必要有一个工作的 tty 驱动. owner 成员是为了防止 tty 驱动在 tty 端口打开时被卸载. 在以前的内核版本, 它由 tty 驱动自己负责处理模块引用计数逻辑. 但是内核程序员认为可能有困难来解决所有的不同的可能的竞争条件, 因此 tty 核心为 tty 驱动处理所有的这样的控制..
driver_name 和 name 成员看起来非常相似, 然而用于不同用途. driver_name 变量应当设为某个简单的, 描述性的并且和内核中所有 tty 驱动中是唯一的值. 这是因为它在 /proc/tty/drivers 文件中出现来描述这个驱动给用户, 以及在当前已加载的 tty 驱动的 sysfs tty 类目录. name 成员用来定义一个名子给单独的分配给这个 tty 驱动的 tty 节点在 /dev 树中. 这个字符串用来创建一个 tty 设备通过在这个字串的后面追加在使用的 tty 设备号. 它还用来创建一个设备名子在 sysfs /sys/class/tty 目录中. 如果 devfs 在内核中被使能, 这个名子应当包含任何这个 tty 驱动想被放入的子目录. 作为一个例子, 内核中的串口驱动设置这个 name 成员为 tts/ 如果 devfs 被使能, ttyS 如果它没有被使能. 这个字串也显示在 /proc/tty/drivers 文件中.
如同我们提及的, /proc/tty/drivers 文件展示所有的当前注册的 tty 驱动. 在内核中注册的 tiny_tty 驱动并且没有 devfs, 这个文件看来如下:
$ cat /proc/tty/drivers
tiny_tty /dev/ttty 240 0-3 serial
usbserial /dev/ttyUSB 188 0-254 serial
serial /dev/ttyS 4 64-107 serial
pty_slave /dev/pts 136 0-255 pty:slave
pty_master /dev/ptm 128 0-255 pty:master
pty_slave /dev/ttyp 3 0-255 pty:slave
pty_master /dev/pty 2 0-255 pty:master
unknown /dev/vc/ 4 1-63 console
/dev/vc/0 /dev/vc/0 4 0 system:vtmaster
/dev/ptmx /dev/ptmx 5 2 system
/dev/console /dev/console 5 1 system:console
/dev/tty /dev/tty 5 0 system:/dev/tty
还有, 当 tny_tty driver 被注册到 tty 核心, sysfs 目录 /sys/class/tty 看来有些象下面:
$ tree /sys/class/tty/ttty*
/sys/class/tty/ttty0
`-- dev
/sys/class/tty/ttty1
`-- dev
/sys/class/tty/ttty2
`-- dev
/sys/class/tty/ttty3
`-- dev
$ cat /sys/class/tty/ttty0/dev
240:0
major 变量描述这个驱动的主编号是什么. type 和 subtype 变量声明这个驱动是什么 tty 驱动. 对于我们的例子, 我们是一个"正常"类型的串口驱动. 一个 tty 驱动的唯一的其他子类型可能是一个 "callout" 类型. callout 设备传统上用来控制一个设备的线路设置. 数据应当通过一个设备节点被发送和接收, 并且任何路线设置改变应当被发送给一个不同的设备节点, 它是这个 callout 设备. 这要求使用 2 个次编号为每个 tty 设备. 感激地, 所有的驱动既处理数据也处理线路设置在同一个设备节点, 并且这个 callout 类型很少用在新驱动中.
tty 驱动和 tty 核心都使用 flags 变量来指示驱动的当前状态和它是什么类型 tty 驱动. 几个在测试或者操作 flags 时你必须使用的位掩码宏被定义了. flags 变量中的 3 个位可被驱动设置:
- TTY_DRIVER_RESET_TERMIOS
-
这个标志说明 tty 核心复位了 termios 设置, 无论何时最后一个进程已关闭这个设备. 对于控制台和 pty 驱动这是有用的. 例如, 假定用户留置一个终端在一个奇怪的状态. 在设置了这个标志时, 这个终端被复位为一个正常值当用户注销或者控制个会话的进程被"杀掉".
- TTY_DRIVER_REAL_RAW
-
这个标志说明 tty 驱动保证发送奇偶或者坏字符通知给线路规程. 这允许线路规程以一种更快的方式来处理接收到的字符, 因为它不必查看从 tty 驱动收到的每个字符. 因为速度的得益, 这个值常常为所有 tty 驱动设置.
- TTY_DRIVER_NO_DEVFS
-
这个标志说明当调用 tty_register_driver 时, tty 核心不创建任何 devfs 入口给这个 tty 设备. 这对任何动态创建和销毁次设备的驱动都是有益的. 设置这个的驱动的例子是这个 USB-到-串口 驱动, USB 猫驱动, USB 蓝牙 tty 驱动, 以及好多标准串口设备.
当 tty 驱动后来想注册一个特殊的 tty 设备到 tty 核心, 它必须调用 tty_register_device, 有一个指针到这个 tty 驱动, 并且设备的次编号已被创建. 如果这个没有完成, tty 核心仍然传递所有的调用到这个 tty 驱动, 但是一些内部的 tty 相关的功能可能不存在. 这个包括新 tty 设备的 /sbin/hotplug 通知和 tty 设备的 sysfs 表示. 当注册的 tty 设备从机器中被移出, tty 驱动必须调用 tty_unregister_device.
The one remaining bit in this variable is controlled by the tty core and is called TTY_DRIVER_INSTALLED. This flag is set by the tty core after the driver has been regis-tered and should never be set by a tty driver.
这个变量中剩下的一位被 tty 核心控制, 被称为 TTY_DRIVER_INSTALLED. 这个标志被tty 核心在驱动已注册后设置并且应当从不被 tty 驱动设置.
|
2007-09-06 20:43 apue2应该也有描述这个。
TTY终端设备文件接口的基本结构
TTY终端设备文件接口的基本结构
=============================
1) 在操作系统中, 应用程序一般不能直接操作硬件, 只能通过系统调用按照内核所提供的抽象形式来间接访问硬件. 在Linux系统中, 所有的硬件对象抽象为文件对象, 通过对文件的系统调用来访问. 用户对文件的系统调用操作基于该文件对象的操作函数表(file_operations), 每一打开的文件都继承了某类文件操作表. 对于一般文件来说(包括目录文件和符号链接文件), 文件对象继承了它所在文件系统的某种文件操作表. 对于设备文件来说, 文件对象继承了它的主设备号所在驱动程序的文件操作表. 设备的文件操作表是设备驱动的起点, 但往往并不在这个层次上直接完成设备操作. 如果在某个层次上对不同对象的某类操作具有某种共性, 那么就可以象提取"公因式"那样将这类操作抽象出来, 然后建立更低一级的操作层次来区分不同的对象, 这样就形成了不同层次的驱动接口.
2) 打开终端的文件操作表为tty_fops, 终端的打开结构用tty_struct结构描述, 文件结构的private_data指针指向打开的终端结构, 如果当前进程的终端指针(current->tty)指向某个打开的终端结构, 则表示该进程与此终端相关联. tty_driver结构描述终端的输出设备, 终端线路规程(tty_ldisc)用来在文件接口与输入设备和输出设备之间进行调控. TTY规程(N_TTY)用于连接终端输入驱动设备和终端显示驱动设备, PPP规程(N_PPP)用来连接终端驱动设备和网络驱动设备.
3) 当用户写入终端文件时, 通过TTY规程的write_chan操作间接输出到终端输出设备. 终端的输入设备将接收数据写入终端打开结构的轮换缓冲区(tty_flip_buffer)之中, 轮换缓冲区使用两个各512字节的缓冲区交替进行读写, 使数据的接收和处理互不重叠同时进行. 轮换缓冲区包含字符数据和字符标志两组缓冲区, 每一字符都具有对应的属性字符. 接收的数据在任务队列中传递给线路规程的receive_buf进行操作. 对于TTY规程, receive_buf操作对接收字符按照终端协议进行处理, 输出数据最终转移到打开终端的read_buf缓冲区中供用户接收. 对于PPP规程, receive_buf操作将识别接收到的PPP帧, 转变为IP帧作为ppp设备的接收帧处理.
4) 伪终端设备是一种特殊的终端驱动设备, 它并不驱动某个物理设备, 而是用来将终端的输出定向到应用程序中进行处理. 伪终端设备由主-从两个成对的设备构成, 当打开主设备(pty)时, 对应的从设备(ttyp)随之打开, 形成连接状态. 输入到主设备数据成为从设备的输出, 输入到从设备的数据成为主设备的输出, 形成双向管道. 伪终端设备常用于远程登录服务器来建立网络和终端的关联. 当通过telnet远程登录到另一台主机时, telnet进程与远程主机的telnet服务器相连接. telnet服务器使用某个pty主设备并通过对应的ttyp从设备与你在远程主机上所运行的程序相互通信.
5) Linux内核是一个以文件为对象的系统, 用户进程的虚拟空间是可执行文件的映射框架, CPU在可执行文件的映射空间中运行. 用户的文件接口实际上是字符流的接口, UNIX将人机接口归结为与终端文件的接口. 我想与文件相对的另一种对象就是窗口, 它们在不同的方向上产生了截然不同的人机界面风格.
6) 熟悉Windows API编程的都知道, Windows应用程序初始化时需要用RegisterClass()注册自已所定义的窗口类, 然后就可以用该窗口类创建它的窗口实例. 从驱动程序观点来看, 每一个窗口类可看成一个以窗口为对象的驱动程序, 所谓窗口元件就是一种窗口驱动设备, Windows应用程序则是在整个操作系统范围内((而不是内核范围内)以窗口为对象的各种层次的驱动程序集合. 如果将窗口类的消息函数(WndProc)看成第一个窗口驱动层次, 那么MFC所提供的应用程序接口可看成更末端层次的一种窗口驱动接口(用户只要在MFC基础上定义一些枝节的操作就可以了). 按照这种推理, 窗口系统由窗口管理核心和各种窗口驱动程序构成, 窗口应用程序则以窗口系统某种抽象而又统一的接口与窗口对象交互.
; drivers/char/tty_io.c:
回头看看rhel4的tty_io.c |
2007-09-06 20:28 coolq提醒我可以strace一下screen程序在rhel4下的调用,看看他是怎么做的,但是貌似没什么发现。
下面是所有操作了dev的记录。
1409 readlink("/proc/self/fd/0", "/dev/pts/0", 4095) = 10
1409 readlink("/proc/self/fd/0", "/dev/pts/0", 4095) = 10
1409 stat64("/dev/pts/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
1409 open("/dev/pts/0", O_RDWR|O_NONBLOCK) = 3
1410 open("/dev/pts/0", O_RDWR <unfinished ...>
1410 open("/dev/null", O_RDONLY <unfinished ...>
1410 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666 <unfinished ...>
1410 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 2
1410 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbde48) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbde48) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbde08) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbd608) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(1, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbd608) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbd608) = -1 ENOTTY (Inappropriate ioctl for device)
1410 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbffbd5c8) = -1 ENOTTY (Inappropriate ioctl for device)
1410 open("/dev/ptmx", O_RDWR) = 6
1410 statfs("/dev/pts", {f_type="DEVPTS_SUPER_MAGIC", f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={0, 0}, f_namelen=255, f_frsize=4096}) = 0
1410 stat64("/dev/pts/1", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
1410 stat64("/dev/pts/1", {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
1410 statfs("/dev/pts/1", {f_type="DEVPTS_SUPER_MAGIC", f_bsize=4096, f_blocks=0, f_bfree=0, f_bavail=0, f_files=0, f_ffree=0, f_fsid={0, 0}, f_namelen=255, f_frsize=4096}) = 0
1410 chown32("/dev/pts/1", 0, 5) = 0
1410 chmod("/dev/pts/1", 0620) = 0
1411 open("/dev/pts/1", O_RDWR) = 0
1411 open("/dev/tty", O_RDWR|O_NONBLOCK|O_LARGEFILE) = 3
1411 read(3, "/dev/sda1 / ext3 rw 0 0\nnone /pr"..., 4096) = 255
1414 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tb"..., 4095) = 256
1416 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tb"..., 4095) = 256
1418 read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tb"..., 4095) = 256
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1421 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1421 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1427 fstat64(0, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
1411 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 3
1411 fstat64(2, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
1411 ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, 0xbfea6b10) = -1 ENOTTY (Inappropriate ioctl for device)
1411 fstat64(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
1411 stat64("/root/.bash_history", {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
1411 fstat64(3, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
1411 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
1410 chmod("/dev/pts/1", 0666) = 0
1410 chown32("/dev/pts/1", 0, 0) = 0 |
2007-09-06 20:16 最近wzt写了个wnps rootkit,我在给他测试和提供完善建议。
遇到了pty的问题,老问题了
rhel4/fc3之后都没了/dev/pty*设备,导致很多后门都无法正常使用 ,比如bindtty.c,如果不用tty后门用起来巨难受的。就这个问题我折腾了一下。
我们看看debian的pty设备
root@debian~# ls /dev/pty* -l | wc -l
256
/dev/ptya0 /dev/ptyb8 /dev/ptyd0 /dev/ptye8 /dev/ptyq0 /dev/ptyr8 /dev/ptyt0 /dev/ptyu8 /dev/ptyw0 /dev/ptyx8 /dev/ptyz0
/dev/ptya1 /dev/ptyb9 /dev/ptyd1 /dev/ptye9 /dev/ptyq1 /dev/ptyr9 /dev/ptyt1 /dev/ptyu9 /dev/ptyw1 /dev/ptyx9 /dev/ptyz1
/dev/ptya2 /dev/ptyba /dev/ptyd2 /dev/ptyea /dev/ptyq2 /dev/ptyra /dev/ptyt2 /dev/ptyua /dev/ptyw2 /dev/ptyxa /dev/ptyz2
/dev/ptya3 /dev/ptybb /dev/ptyd3 /dev/ptyeb /dev/ptyq3 /dev/ptyrb /dev/ptyt3 /dev/ptyub /dev/ptyw3 /dev/ptyxb /dev/ptyz3
在rhel4下面显然是没有的
[root@rhel4 screen]# ls /dev/pty*
ls: /dev/pty*: No such file or directory
我们再在debian里看看
root@debian~# grep pty /proc/tty/drivers
pty_slave /dev/pts 136 0-1048575 pty:slave
pty_master /dev/ptm 128 0-1048575 pty:master
pty_slave /dev/ttyp 3 0-255 pty:slave
pty_master /dev/pty 2 0-255 pty:master
再去rhel4下看看
[root@rhel4 screen]# grep pty /proc/tty/drivers
pty_slave /dev/pts 136 0-1048575 pty:slave
pty_master /dev/ptm 128 0-1048575 pty:master
我们可以发现,debian下的/dev/pty的含义是pty_master,而rhel4下的pty_master是/dev/ptm,功能都是一样的应该,所以我个人觉得通过编程应该是可以实现的。
看看bindtty.c里的片段吧
/* creates tty/pty name by index */
void get_tty(int num, char *base, char *buf)
{
char series[] = "pqrstuvwxyzabcde";
char subs[] = "0123456789abcdef";
int pos = strlen(base);
strcpy(buf, base);
buf[pos] = series[(num >> 4) & 0xF];
buf[pos+1] = subs[num & 0xF];
buf[pos+2] = 0;
}
/* search for free pty and open it */
int open_tty(int *tty, int *pty)
{
char buf[512];
int i, fd;
fd = open("/dev/ptmx", O_RDWR);
close(fd);
for (i=0; i < 256; i++) {
get_tty(i, "/dev/pty", buf);
*pty = open(buf, O_RDWR);
if (*pty < 0) continue;
get_tty(i, "/dev/tty", buf);
*tty = open(buf, O_RDWR);
if (*tty < 0) {
close(*pty);
continue;
}
return 1;
}
return 0;
}
如果没,就报错了
/* open slave & master side of tty */
if (!open_tty(&tty, &pty)) {
char msg[] = "Can't fork pty, bye!\n";
write(scli, msg, strlen(msg));
close(scli);
exit(0);
}
|
| | |