我是怎么把组里的BBS搞崩的

老板发邮件了。
说组大人多,你们这些个搞PQE(博士资格测评)的,投CHI(人机交互顶会)的,统统不更新bbs上的project page,记下一些有用的信息,以后进来的新生,搞相同的项目,什么参考资料都没有,又得重新摸石头过河。于是大伙赶紧更新project page去。
新生如我碰巧是没有project page要更新的。我就琢磨着写一个命令行工具用来自动追踪项目文件的变化然后生成project page再push到bbs上去,于是就操起抓包工具什么的写起来。写着写着就到了深夜,准备睡觉,睡前打算再测试一下自动发帖。
意外接着就发生了,在测试用程序自动发帖到idealist板块时,idealist板块忽然打不开了。但是帖子能正常发出和回复,我以为是网络什么之类的问题,加之已是凌晨三点有余,困意袭来,就没再细究先去睡了。
第二日上课中途收到老板邮件,说idealist板块打不开了,看到我的帖子有测试相关的log,问我知不知道是怎么回事。 我赶忙上bbs看一眼,发现idealist板块打不开了,直接报错:

broken_bbs_1.png

顿时腿都吓软了,心想这不会是玩儿脱了把整个板块的数据都整没了吧= = …我们组的BBS建组不久就存在了,有十年历史,其中idealisti板块里面保留着建组开始到现在所有组员记录下的各种idea,有实现了的也有没实现的,包括老板自己的idealist。运作了十年的bbs没出事,我进组才一个月就弄崩了,要是数据真没了,可以去跳海了。尝试了一下直接输入url打开帖子发现没问题,估计数据还在,只是入口没了,放心不少,回邮件给老板立军令状今天之内就修好。
老板一如既往的nice,很淡定,回邮件表示不要紧张,只是小事一件,要什么帮忙的跟他说就好。

想修好谈何容易,主要有三个难题,

  • 首先整个bbs以及源码在老板的个人用户目录下,但是老板没法给权限我进入,权限需要系里的管理员才能给。
  • 其次服务器运作在系里的工作站上,错误日志我是看不到,想看也得找系里管理员才能获得,系里管理员显然没时间配合我debug。
  • 最后这个论坛后端是perl写的,我压根就没接触过…

我首先想到的就是把bug给重现吧。这个论坛是个开源的作品,源码在网上很容易找到,perl写的,把组里对应版本的源码找下来。然后查到系里服务器是Apache,开始在自己电脑上架设Apache + Perl环境。Windows下搭环境奇烦无比,折腾了一番决定直接装集成包。找到Xampp,安装启动一键完成,配置好论坛,一个在本地的bbs就上线了。

接着开始跑自己前晚写的代码,目标地址换成本地自己的bbs。奇怪的是无论怎么跑,都没法将Bug重现。Bug都无法cc重现,怎么debug….考虑到我的操作只是post和修改一个帖子,导致帖子所在板块进不去,猜测bug肯定在于进入板块时读取帖子出问题。于是充分发挥程序员职业素养,一天速成perl,开始阅读相关部分的代码。

其实我早就估计直接把我发的帖子给删掉就能运作正常,但是显然这是不能满足我的,不搞明白具体原因怕是吃饭都不香。具体过程按下不表,主要就从两个方向尝试找出bug的真相,一方面尝试重现bug,一方面从报bug的异常处倒推原因,中间结合各种冥想洞察。这Bug相当离奇,想要触发要满足多个前提条件,并且报bug的代码也有bug,报的异常是错的,迷惑性特别强。最后终于,在一天结束之前,把个中缘由捣鼓清楚,那感觉仿佛奥米伽兽在茫茫多的复制体中一剑捅穿迪亚波罗兽真身一般舒畅,或许柯南说出真相只有一个的时候也是这感觉。具体的总结留在最后,有兴趣的可以看看。

这个故事让我对开源与不开源产品有了新的认识,理论上来说假设是不开源的产品,只要是正版的,出了这种bug应该是一个电话就有人来修才对;只有开源的作品才会要自己去读源码debug。但是不开源的产品理论上是有维修服务的,至于是否能短时间修好又是另一回事了。对于我们这些计算机相关从业者来说,似乎又是开源的更好,因为自己能马上动手去修。不过这种奇葩的bug在不开源的产品里也不应该出现吧。
这次事故虽然说是no zuo no die,但是充分考验了自己的职业素养,学到了新的知识,也是很过瘾的。

 

 

===============这是分割线===============

这个BUG的根本原因是YABBs 2.2.2版本的代码有问题,先说解决方案:

1.简单快速的方法就是把我那个帖子删了~

2.彻底根治的方法要改几行论坛服务端的代码。
病因病理简单解说如下:
1.首先,在系统里有一个变量annboard,它的默认值是“announcement”, 代表的是系统的一个默认的板块(名字叫Global Announcement)。但是我们的论坛估计把这个板块给去掉了,所以这个annboard变量的值就为空了

2.然后还有一个变量叫boardsDir,这个变量用来保存论坛Boards文件的地址,它的值是”./Boards/“;

3. 然后,当向服务器发帖子时,发出的请求指令包含有一个属性”mstate”,这个属性是记录帖子的状态的。当这个字段含有字母”a”时,会触发一个函数MessageTotal。(我昨天就是把这个属性赋值为”save”,结果含有了”a”);

4.精彩的部分来了,MessageTotal函数尝试打开boardsDir下的annboard.txt文件(即boardsDir+”/“+annboard+”.txt”),然后因为annboard的值为空,所以打不开。打不开就报错,巧的是报错的代码是错的,它会报告说打不开annboard下的annboard.txt文件(即annboard + “/“+annboard+”.txt”),因为annboard的值为空,所以就会报告如下错误:

broken_bbs_2.png

正确的报错内容应该是:

broken_bbs_3.png

总结来说,就是各种阴差阳错,巧妙的交汇在一起,触发了bug,还报了一个错误的错误信息,就像是得了胃病的人跟医生说耳朵疼。幸好这个错不会影响已有的数据。

热评文章