xtrace 原理分析
xtrace 可以用来查看X client
与X server
之间的通信内容,并以易读的方式显示。
基本原理是建立一个 fake X server
并在启动客户程序前修改 DISPLAY
环境变量,这样客户程序启动时连接的是这个 fake X server
, xtrace
在检测到有客户程序连接时会建立与 real X server
的连接,随后的工作就是转发客户程序与 real X server
之间的通信,并把通信的内容已易读的方式打印出来。
使用方法:
[yyuan@bcnode001 ~]$ xtrace --help
xtrace: Dump all X protocol data being tunneled from a fake X display to a real one.
usage: xtrace [options] [[--] command args ...]
--display, -d <display to connect to>
--fakedisplay, -D <display to fake>
--copyauthentication, -c Copy credentials
--nocopyauthentication, -n Do not copy credentials
--authfile, -f <file instead of ~/.Xauthority to get credentials from>
--newauthfile, -F <file instead of ~/.Xauthority to put credentials in>
--waitforclient, -W wait for connection even if command terminates
--stopwhendone, -s Return when last client disconnects
--keeprunning, -k Keep running
--denyextensions, -e Fake unavailability of all extensions
--readwritedebug, -w Print amounts of data read/sent
--maxlistlength, -m <maximum number of entries in each list shown>
--outfile, -o <filename> Output to file instead of stdout
--buffered, -b Do not output every line but only when buffer is full
−d name | −−display name
转发与X server
的连接给指定的name
而不是通过环境变量DISPLAY
指定−D name | −−fakedisplay name
建立一个fake X server
于指定的name
上,而不是环境变量FAKEDISPLAY
或者:9
−c | −−copyauthentication (默认)
复制一份real X server
用到的authentication token
给fake X server
,后面会详细解释−n | −−nocopyauthentication
不复制authentication token
−f filename | −−authfile filename
指定从哪个文件复制authentication token
−F filename | −−newauthfile filename
指定复制authentication token
到哪个文件-W | --waitforclient
客户程序没有建立任何连接就退出时不要退出xtrace
-s | --stopwhendone (默认)
当所有连接的客户程序都断开时退出xtrace
-k | --keeprunning
当所有连接的客户程序都断开时继续等待新的连接-e | --denyextensions
xtrace
目前只支持一部分的X
扩展, 因此遇到无法识别的request
和reply
时分别打印unknown
和unexpected
-w | --readwritedebug
打印每条转发信息的字节数-m count | --maxlistlength count
只打印list
的前面count
个元素 (这个还不是很清楚)-o | --outfile filename
指定输出文件-b | --buffered
指定输出时使用full buffer
, 默认使用line buffer
--timestamps
打印时间戳--relative-timestamps
打印的时间戳是相对于连接建立时的时间差--monotonic-timestamps
使用clock_gettime()
获取时间而不是gettimeofday
--print-offsets
打印每个字段的offset
--print-counts
打印list
中的元素个数
一个实际使用的例子:
[yyuan@bcnode001 ~]$ xtrace --timestamps -w ~/TFLEX_ROOT/app/r2013.09.01/gui/appTflex
No display name to create specified, trying :9
1397101987.345 000:<: am lsb-first want 11:0 authorising with 'MIT-MAGIC-COOKIE-1' of length 16
1397101987.346 000:>: Success, version is 11:0 vendor='The X.Org Foundation' release=60900000 resource-id=0x02800000 resource-mask=0x001fffff mo tion-buffer-size=256 max-request-len=65535 image-byte-order=LSBFirst(0x00) bitmap-bit-order=LeastSignificant(0x00) scanline-unit=32 scanline-pad =32 min-keycode=0x08 max-keycode=0xa0 pixmap-formats={depth=1 bits/pixel=1 scanline-pad=32},{depth=4 bits/pixel=8 scanline-pad=32},{depth=8 bits /pixel=8 scanline-pad=32},{depth=15 bits/pixel=16 scanline-pad=32},{depth=16 bits/pixel=16 scanline-pad=32},{depth=24 bits/pixel=32 scanline-pad =32},{depth=32 bits/pixel=32 scanline-pad=32}; roots={root=0x00000057 default-colormap=0x0000003d white-pixel=0x00ffffff black-pixel=0x00000000 input-mask=KeyPress,KeyRelease,EnterWindow,LeaveWindow,ButtonMotion,StructureNotify,SubstructureNotify,SubstructureRedirect,FocusChange,Property Change,ColormapChange width[pixel]=1674 height[pixel]=978 width[mm]=567 height[mm]=331 min-installed-maps=1 max-installed-maps=1 root=0x00000039 backing-stores=Always(0x02) save-unders=true(0x01) root-depth=24 allowed depths={depth=24 visuals={id=0x00000039 class=TrueColor(0x04) bits/rgb -value=8 colormap-entries=256 red-mask=0x00ff0000 green-mask=0x0000ff00 blue-mask=0x000000ff},{id=0x0000003a class=TrueColor(0x04) bits/rgb-valu e=8 colormap-entries=256 red-mask=0x00ff0000 green-mask=0x0000ff00 blue-mask=0x000000ff},{id=0x0000003b class=TrueColor(0x04) bits/rgb-value=8 c olormap-entries=256 red-mask=0x00ff0000 green-mask=0x0000ff00 blue-mask=0x000000ff},{id=0x0000003c class=TrueColor(0x04) bits/rgb-value=8 colorm ap-entries=256 red-mask=0x00ff0000 green-mask=0x0000ff00 blue-mask=0x000000ff};},{depth=1 visuals=;},{depth=4 visuals=;},{depth=8 visuals=;},{de pth=15 visuals=;},{depth=16 visuals=;},{depth=32 visuals=;};};
1397101987.346 000:<:0001: 20: Request(98): QueryExtension name='BIG-REQUESTS'
1397101987.347 000:>:0001:32: Reply to QueryExtension: present=true(0x01) major-opcode=133 first-event=0 first-error=0
1397101987.347 000:<:0002: 4: BIG-REQUESTS-Request(133,0): Enable
1397101987.347 000:>:0002:32: Reply to Enable: maximum-request-length=4194303
1397101987.347 000:<:0003: 20: Request(55): CreateGC cid=0x02800000 drawable=0x00000057 values={background=0x00ffffff}
1397101987.347 000:<:0004: 24: Request(20): GetProperty delete=false(0x00) window=0x00000057 property=0x17("RESOURCE_MANAGER") type=0x1f("STRING ") long-offset=0x00000000 long-length=0x05f5e100
...
1397102061.842 000:>:471a: Event PropertyNotify(28) window=0x0280082e atom=0xfa("_NET_WM_NAME") time=0x49c5a7d7 state=Deleted(0x01)
1397102061.842 000:>:471a:32: Reply to GetInputFocus: revert-to=Parent(0x02) focus=0x0260001f
000:>:wrote 32 bytes
000:<:got EOF
000:<:sent EOF
接下来看下具体的流程。
工作流程
int main(int argc, char *argv[])
{
struct parser *parser;
... // 1. 解析传入参数.
translate(parser, "all.proto"); // 2. 解析从搜索路径中找到的 all.proto 协议定义文件.
...
// 3. 解析display name, 根据协议不同随后会创建不同的socket.
// in_displayname 是 fake X server,out_displayname才是 real X server.
msg = parseDisplay(in_displayname, &in_protocol, &in_hostname, &in_display, &in_screen, &in_family);
msg = parseDisplay(out_displayname, &out_protocol, &out_hostname, &out_display, &out_screen, &out_family);
// 4. 复制一份 real X server 用到的authentication token给 fake X server.
copy_authentication(in_displayname, out_displayname, in_authfile, out_authfile);
// 5. 启动 fake X server, 等待 X client连接
listenForClients(in_displayname, in_family, in_display);
// 6. 启动客户程序,启动时修改 DISPLAY 环境变量成 fake X server
startClient(argv + optind)
// 7. 主循环,侦听到有 X client 连接时建立与 real X server 的连接,
// 随后转发客户程序与 real X server 的通讯,并通过 parser 解析通讯内容后输出.
mainqueue(listener);
}
关键步骤是4
,5
和6
。
复制 authentication token
关于X Window authorization, 主要有三种:Host-based access
, Cookie-based access
和 User-based access
。xtrace
目前只支持 Cookie-based access
。
### 什么是Cookie-based access? ###
简单来说就是 X client
尝试与 X server
建立连接时会传一个 magic cookie
过去进行验证。这些 cookie
是在 X server
启动时创建的并默认存储在 $HOME/.Xauthority
文件中(除非指定了 XAUTHORITY
环境变量)。我们可以使用 xauth list
命令查看这个文件的内容。
[yyuan@bcnode001 ~]$ xauth list
bcnode001.briontech.com/unix:13 MIT-MAGIC-COOKIE-1 da7eaeaad1d4738384247ead8b7a5a31
bcnode001.briontech.com/unix:14 MIT-MAGIC-COOKIE-1 285e32abbcdb869766376e7cfe26bb91
bcnode001.briontech.com/unix:9 MIT-MAGIC-COOKIE-1 285e32abbcdb869766376e7cfe26bb91
...
- 第一列是
X server
的display name
- 第二列是
cookie
的类型 - 第三列是
cookie
的值
有两种类型的 cookie
: MIT-MAGIC-COOKIE-1
和 XDM-AUTHORIZATION-1
。目前 xtrace
只支持 MIT-MAGIC-COOKIE-1
。
DISPLAY NAME
官方文档有详细介绍。
格式是 hostname:displaynumber.screennumber
。
* hostname
指定了 X server
运行在哪里,为空的话表示运行在本地
* displaynumber
用于区分同一 hostname
上的不同 X server
,从 0 开始,不能为空
* screennumber
用于区分不同的屏幕,从 0 开始,为空的话表示 0
默认的 display name
是存储在 DISPLAY
环境变量中的。
X client
与 X server
之间有几种连接方式: local
,TCP/IP
,DECnet
:
* local
这种方式下 hostname
为空,unix
或者 localhost
,比如: :0.0
, unix:0.0
,localhost:0.0
。
在 local
方式下会使用 UNIX domain socket
,该socket
会使用 /tmp/.X11-unix/Xdisplaynumber
文件。这也是效率最高的一种通信方式。
* TCP/IP
这种方式下 hostname
可以为domain name
, node name
,IP address
等,比如: x.org:0
, expo:0
, 192.168.1.100:0.1
。
在这种方式下会使用 TCP/IP
连接,并使用端口 6000 + displaynumber
。
* DECnet
这种方式下 hostname
必须是 node name
, 并紧跟两个冒号,比如: foo::0.1
。
$HOME/.Xauthority
文件中的第一列的格式和 DISPLAY
环境变量的格式有一点不一样,但大体相同。
所以 bcnode001.briontech.com/unix:14
表示的是使用 local
方式通信,displaynumber
是 14,screennumber
没有指定默认是 0,会使用 /tmp/.X11-unix/X14
文件。
运行xtrace
时出现以下错误怎么办?
[yyuan@bcnode001 ~]$ xtrace --timestamps -w ~/TFLEX_ROOT/app/r2013.09.01/gui/appTflex
No display name to create specified, trying :9
Error binding socket for ':9': 2=No such file or directory
这是因为没有指定 fake displayname
的情况下,xtrace
会使用 :9
,因为 UNIX domain socket
需要使用 /tmp/.X11-unix/X9
文件,而 /tmp/.X11-unix
文件夹没有创建好所以导致失败。
可以通过 strace
验证下:
[yyuan@bcnode001 ~]$ strace xtrace --timestamps -w ~/TFLEX_ROOT/app/r2013.09.01/gui/appTflex
...
socket(PF_FILE, SOCK_STREAM, 0) = 3
unlink("/tmp/.X11-unix/X9") = -1 ENOENT (No such file or directory)
bind(3, {sa_family=AF_FILE, path="/tmp/.X11-unix/X9"}, 110) = -1 ENOENT (No such file or directory)
...
解决办法就是
* 手动创建 /tmp/.X11-unix
文件夹
* 或者修改源代码,添加创建该文件夹的代码
怎样复制 token
使用 xauth list display-name
获取 real X server
的token
, 然后 xauth add display-name cookie-name cookie-value
添加一条新记录:
[yyuan@bcnode001 ~]$ xauth list :100
[yyuan@bcnode001 ~]$ xauth list :14
bcnode001.briontech.com/unix:14 MIT-MAGIC-COOKIE-1 285e32abbcdb869766376e7cfe26bb91
[yyuan@bcnode001 ~]$ xauth add :100 MIT-MAGIC-COOKIE-1 285e32abbcdb869766376e7cfe26bb91
[yyuan@bcnode001 ~]$ xauth list :100
bcnode001.briontech.com/unix:100 MIT-MAGIC-COOKIE-1 285e32abbcdb869766376e7cfe26bb91
含瀚家的老袁,转载请注明出处