chroot导致日志时间出错的BUG

下面都是Inmoreliu的总结,我只是抄袭了,如果哪天我集结出版了,我一定付他稿酬。首先要说明一下chroot函数是一个UNIX函数,用于改变整个程序的虚拟的根目录,这个函数的最主要的用途是保证运行系统的安全,降低被攻击的可能。

问题现象:

由于anna MM反映无法更新pet_def.db3文件,于是到dlsvrd上跟了一把日志。发现输出的日志时间不太对,也没有太在意,以为可能是谁把dlsvrd停了,ps一把,发现dlsvrd进程还在,而且是几天以前了,感觉有点奇怪,于是重启了dlsvrd,发现时间又正常了,于是让anna再测一次,猛然发现正确的时间突然又不对了,而且正好和现在的时间相差8小时,日志时间从0948倒回到0148Date发现,系统时钟仍然是0948,有意思。初步认为是程序中的什么地方将时区进行了修改。

定位过程:根据日志打出的上下文去查代码,发现在此期间只有format_gmt_time一个函数进行了实际的操作,函数代码如下:

size_t DlsvrTask::format_gmt_time(time_t timestamp, char *buff, size_t buflen)

{

struct tm tm;

gmtime_r(&timestamp, &tm);

return strftime(buff, buflen, “%a, %d %b %Y %X GMT”, &tm);

}

Gmtime_r:将timestamp解析成GMT然后将结果保存到tm中,线程安全(dlsvrd采用了多线程)。

Strftime:将tm已指定的格式和长度输入到buff中。

理论上来说,这两个函数都没有可能去修改时区,由于该函数位于子线程中,所以sail认为可能和多线程有关系,于是将这段代码单独移到主循环中(线程初始化之后),发现仍然会出现这个问题。同时分别将这两个函数进行屏蔽,发现当屏蔽strftime时不会出现此现象,断定是由于strftime造成的时区变化。

但是sail单独写了一段测试代码发现,无论怎么调strftimegmtime_r,都不会产生时区变化,于是又怀疑是多线程的原因,将gmtime_rstrftime移到线程初始化之前,发现问题依旧,无语。但无意间将这两个函数移到InitInstance的最开始,发现没有出现时区变化的问题,神了。那么从InitInstance的最开始到线程初始化之间的代码和strftime共同作用的结果,且存在顺序问题。

无奈,只好老老实实、一个一个比较测试程序和InitInstance的最开始到线程初始化之间代码的差异。

发现将strftimegmtime_r移到下面一段代码之前和之后,输出结果明显不同,同时,执行和不执行strftime,也会影响时区(也难怪sailwindows在调了半天dlsvrd也没有重现这个问题)

#ifndef WIN32

//chroot

ret = chroot(InstOfDlsvrSvrConfig::instance()->root_path_.c_str());

if (ret != 0)

{

ACE_DEBUG((LM_ERROR,”chroot failed: %u | %s/n”, ACE_OS::last_error(), strerror(ACE_OS::last_error())));

return ret;

}

#endif

于是自己写了一段测试代码(见附件),发现是由chrootstrftime一起调用引起的问题。

原因:

strftime在《The Single UNIX ® Specification, Version 2 Copyright © 1997 The Open Group》有如下一段解释:

Local timezone information is used as though strftime() called tzset().”

strftime会通过tzset来获取时区的信息。

man tzset中有这样一段解释:

If the TZ variable does not appear in the environment, the tzname variable is initialized with the best approximation  of local  wall  clock  time, as specified by the tzfile(5)-format file localtime found in the system timezone directory (see below).  (One also often sees /etc/localtime used here, a symlink to the right file in the system timezone directory.)”

简单来说,就是tzset是通过TZ环境变量或是/etc/localtime来设置timezone,而我们目前没有设置TZ变量,所以实际是通过/etc/localtime来设置的。由于我们没有在InstOfDlsvrSvrConfig::instance()->root_path_下设置/etc目录,所以在chroot后在调用strftimetzset会找不到localtime,因此将timezone设置成0.这样,日志的时间就从原来的CST变成了GMT

修改方案:

方案一:使用TZ的环境变量。

方案二:由于dlsvrd已经对字符”..”进行了屏蔽,那么其实chroot也就不那么必要了,初步考虑是将chroot去掉。

思考:

Q:为什么strftime会调用tzset

A:因为strftime有一个参数%Z,用于格式化时区,而其无法通过tm参数来获得时区的信息,所以必须通过tzset来获得timezone,这个是可以理解。但是我们在程序中其实并没有用到%Z这个参数,     那么理论上来说,不应该去调tzset,这个有点想不通。

Q:linux下的3个时间文件的作用

A

/etc/sysconfig/clock - this is a short text file that defines the timezone, whether or not the hardware clock is using UTC, and an ARC option that is only relevant to DEC systems.

/etc/localtime - this is a symbolic link to the appropriate time zone file in /usr/share/zoneinfo

/usr/share/zoneinfo - this directory contains the time zone files that were compiled by zic. These are binary files and cannot be viewed with a text viewer. The files contain information such      as rules about DST. They allow the kernel to convert UTC UNIX time into appropriate local dates and times.

如果只关注timezone的话,简单而言,3个文件作用如下:

/etc/sysconfig/clock是定义timezone的文本文件

/etc/localtime  zoneinfo下一个文件的硬连接,这个文件由/etc/sysconfig/clock中的ZONE选项指定

/usr/share/zoneinfo  这个目录下存储了各个时区与UTC转换的相关规则和信息。

那么,为什么需要三个这样的文件呢?两个应该是完全够用的。如果没有clock,那么程序每次到localtime去查询转换规则即可,如果没有localtime,那么每次到clock去查询ZONE选项,再通过ZONE去定位/usr/share/zoneinfo目录下的相关文件,查询转换规则,这样也是可以的。由此可以感觉到/etc/sysconfig/clock/etc/localtime至少有一个是多余的,不知道为什么会设计如此。Google了很久,未果,待续……

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值