首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 Ttyutils 截获 UNIX/Linux 终端 (2)

使用 Ttyutils 截获 UNIX/Linux 终端 (2)

使用 ttyadmin 管理 ttyexec 实例  在系统中通常会有多个 ttyexec 实例在运行,前面的 ttyadmin 截图已经大致说明了 ttyadmin 的用途,但它除了可以列出当前系统中所有的 ttyexec 实例外,还有一些其它的用途,例如它可以很容易的启动 ttylook,查看 ttyexec 实例的事件表等等。
使用 ttyexec 的事件触发  如果只是希望监控用户在终端上的会话的话,那么只需要使用前面描述的内容即可,但是如果还想得到更多,那么就可能需要这里描述的内容- 事件触发。
ttyexec 的一个有用的机制是事件触发,它是根据被监控程序的标准输出引起终端屏幕的变化来工作的,每当被监控程序改变终端屏幕的内容时,ttyexec 会根据更新后的屏幕内容与事件表中的事件条件进行匹配,如果成功,那么就会执行一个事件定义的程序。
事件表
在 ttyexec 内容维护了一个事件表,每个节点是一个事件,每个事件由 4 个部分组成:
  • 事件的名称;
  • 事件的组名称;
  • 事件的匹配器;
  • 事件的动作;
事件名称 是事件唯一的标识, 事件组名称 是对多个事件的划分,这两个名称主要用于事件的管理。
事件匹配器 由一个或多个 事件条件 组成,每个 事件条件 又根据类型的不同而不同,我们介绍完虚拟终端后再回头描述事件条件。
事件动作 是一个程序的名称和它的参数 ( 如果有的话 ),它是一个标准的可执行程序,在事件的匹配器得到匹配时执行。
虚拟终端
ttyexec 内部运行着一个虚拟的终端,它可以正确的解析发送到实际终端的那些控制序列,并在内存中重构一个和真实终端屏幕一样大小的屏幕,它的内容和用户在实际终端上看到的是一样的,但是对于用户而言,它是不可见的。
每当被监控的程序改变终端屏幕的内容时,ttyexec 的虚拟终端也会同步更新自己的屏幕,然后它开始检查事件表,如果发现了匹配的事件,就执行为事件定义的事件动作。
事件匹配
在 ttyexec 的事件表的每个事件中, 事件匹配器 由一到多个 事件条件 组成, 事件条件 是真正要去和虚拟终端屏幕比对的单元, 事件匹配器 只有在所有事件条件都匹配的情况下才是匹配的。
每个 事件条件 的类型有两种,第一种是根据光标的位置,第二种是根据屏幕上的数据。
光标位置是指光标到达某个指定的坐标时,条件就得到了满足,这种类型的条件需要两个参数,坐标的行和列。
屏幕的内容是指屏幕某个区域的内容达到预期的值,这种类型的条件需要四个参数,行,起始列,长度,以及一个正则表达式。
因为一个 事件匹配器 可以包含多个条件,并且只有匹配器中的所有条件都得到满足时,匹配器才算匹配,所以可以定义出很精确的匹配,当然也可以定义很模糊的匹配。
事件表维护
因为事件需要根据不同的应用场合而进行独立的设置,所以必须有能力去管理 ttyexec 的事件表,这需要了解一些基本的计算机编程。
有两种途径来管理 ttyexec 的事件表,第一种是通过 ttyexec 的配置文件,叫做 ttyexec.lua,这个文件使用标准的 Lua 程序语言。这种方式称为静态事件管理。
第二种方式是使用 ttyutils 提供的编程接口,外部程序可以动态的维护 ttyexec 事件表,这种方式称为动态事件管理。动态事件管理很强很实用,因为这样我们就可以在事件触发之后的程序中再去动态的管理 ttyexec 的事件表,这可以为事件提供了上下文关系。
在一个复杂的环境中,通常利用静态管理来维护 ttyexec 的初始事件,而在适当的时候利用动态事件管理来动态的维护 ttyexec 的事件表。
RPC 服务
远程过程调用 (remote process call)RPC 服务是 ttyexec 为进程间通讯提供的接口,注意它和 SUN RPC 没有任何的关系,并且不能跨网络使用。
当事件触发时,事件动作程序被启用,事件动作程序是一个标准的应用程序,它可以做任何它喜欢的事情,但是有时候,它需要和 ttyexec 交互,例如它可能需要获取当前终端屏幕的内容, ttyexec 为这样的情况提供了准备,称为 RPC 服务。
Ttyexec 提供了下面的服务 :
  • snap: 获取当前终端屏幕的快照;
  • feed: 发送数据作为实终端的标准输入;
  • suspend/resume_stdio: 悬挂 / 恢复实终端标准输入;
  • suspend/resume_stdout: 悬挂 / 恢复实终端标准输出;
  • insert/remove_event: 添加 / 删除事件表中的事件;
下面用一个例子描述如何编写一个 C 程序来使用上面提供的 feed 服务,包括从源代码编写到编译,连接完整的步骤。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <ttyutils.h>

int
main (int argc, char *argv[])
{
TRpcTarget target;

if (!t_init ())
return 1;

target.rt_timeout = 3;
target.rt_type = RPC_TARGET_PTS;
target.rt_pts = "/dev/pts/3";

if (!t_rpc_call_set_target (&target))
{
t_println ("set rpc target failed.");
return 1;
}

if (!t_rpc_call_feed ("hello", 5))
{
g_warning (_("rpc call failed."));
return 1;
}

t_uninit ();
return 0;
}




将上面的代码保存到文件 example.c,然后到用下面的命令编译它 :
1
$ cc -o example example.c `pkg-config --cflags --libs ttyutils-1`




第 1 行包含了头文件 <ttyutils.h>,这是每个外挂程序应该做的。
第 6 行声明了一个 TRpcTarget 结构的变量,这个结构描述如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef enum
{
RPC_TARGET_TTY,
RPC_TARGET_PTS,
RPC_TARGET_PID,
RPC_TARGET_PATH
} TRpcTargetType;
  
typedef struct _TRpcTarget TRpcTarget;
struct _TRpcTarget
{
TRpcTargetType rt_type;
guint32 rt_timeout;
union
{
gchar *t_tty;
gchar *t_pts;
pid_t t_pid;
gchar *t_path;
} rt_target;
};
#define rt_tty rt_target.t_tty
#define rt_pts rt_target.t_pts
#define rt_pid rt_target.t_pid
#define rt_path rt_target.t_path




第 8 行调用了 t_init() 函数,要使用 ttyexec 接口的程序应该首先调用这个函数 ( 在调用其它接口函数之前 )。
第 11 行到 13 行设置 RPC 的目标,这里我们选择的是使用伪终端名称 "/dev/pts/3"。
上面 TRcTargetType 的枚举定义了我们可以使用的连接方式,它们分别是 :
RPC_TARGET_TTY         ttyexec 实例使用的终端名称
RPC_TARGET_PTS         ttyexec 实例运行命令的伪终端名称
RPC_TARGET_PID         ttyexec 的进程 ID
RPC_TARGET_PATH         ttyexec 为 RPC 服务建立的 UNIX 套接字名称
结构 TRpcTarget 中的联合体 rt_target 中的每个成员分别对应于每种不同的连接方式。为了方便的访问到 rt_target 中的成员,还分别定义了一些简化的宏 (rt_tty 等等 )。
第 15 到 19 行调用 t_rpc_call_set_target() 函数,它用我们上面设置好的 target 结构体作为参数。
t_println() 是一个调试用的函数,当编译程序时定义了 DEBUG 或者 T_DEBUG 时,它的功能和 printf() 类似,当没有定义上面的两个宏时,它不生成任何代码。
第 21 到 25 行调用函数 t_rpc_call_feed (),这个函数访问 ttyexec 提供的 feed 服务,它的作用是发送一些数据到 ttyexec 作为标准输入。
g_warning() 是 glib 提供的一个函数,它的作用是输出一条警告消息。
第 27 行调用 t_uninit() 函数,它的作用是释放资源,应该在程序退出之前调用。
返回列表