R·ex / Zeng


音游狗、安全狗、攻城狮、业余设计师、段子手、苦学日语的少年。 MUGer, hacker, developer, amateur UI designer, punster, Japanese learner.

安装HUSTOJ的心得

注意:本文发布于 2051 天前,文章中的一些内容可能已经过时。

经过不断地折腾,终于把HUSTOJ装到了高中的服务器上。整理了一下,有一种“教练我跟他们遇到的情况不一样啊”的、特别卧槽的感觉,于是就想把我遇到的一些问题写出来,如果以后有人遇到类似的问题,可以借鉴一下。

先声明一下背景,我的高中以前有一套免费的POJ系统,搭建在winserver2003上,里面积累了200来道题。有一天貌似被人攻击了,所以我有点不放心POJ的安全性。各种搜索之后发现了zhblue大神写的HUSTOJ,php写的,好像我可以随意改的样子(这个系统都开源了为什么还没有名气呢)。原题库虽说没有特别难的题,但是不想就这么丢掉,于是事先先把POJ的数据库和题目的数据都导出来了。(数据库一共才1M左右,也没多大嘛~)

接下来的一段很逗逼,如果没有时间看的话可以直接跳到后文的红字部分继续。

正常情况下,学校应该不会这么轻易再给一台服务器,所以我打算把HUSTOJ装到原服务器上。重装系统什么的太麻烦,于是就想搞个虚拟机然后主机随便做个代理完事。结果发现现在的VMWare已经不能装在32位的winserver2003上了,那么只好重装系统了。搞了个Ubuntu的U盘,结果发现服务器不认,最后找到了一张LiveCD……折腾半天发现了一堆问题,但是这并不是重点。最后觉得实在搞不定了,于是就直接从HUSTOJ官方下了一个LiveCD,刻成盘装好了,然后数据导入的时候又是各种问题,而且网络似乎也出了点问题,快把我整崩溃了。与此同时,老师在现有的一台服务器上下好了HUSTOJ的源文件……然后我手贱敲了一行./install.sh回车……

今天早上老师问我为什么学校的教学网站上不去了……

惊出一身冷汗,打开网址之后,chrome自动弹出了下载框,下载下来是一段PHP代码。我想了想(之前在原服务器上搞的时候看过那个install.sh,还有印象),应该是安装脚本里有一句apt-get install apache2,跟服务器原有的apache版本不一样,于是就把原来的给替掉了,但是脚本里没有关于libapache2-mod-php的部分,所以新的apache没法调用php。还好可以走外网ssh,我果断连了过去,装上了php的mod之后重启apache,一切正常了。

赶时间的小伙伴们,从这里开始就是正文了~

要想安装HUSTOJ其实很简单,就是把github上面那段代码clone下来,然后发现里面有个install-interactive.sh的文件,用root权限去执行它即可。途中要求你输入MySQL的用户名和密码。安装的时候它会先下载一些必须的程序,然后从github上svn checkout下来最新版本的代码,将代码放到合适的地方,创建一个judge用户,编译好作者自己写的judged和judge_client以及其它一些东西并放到合适位置,在/home/judge目录下为judge创建一个环境,最后重启apache。

装完之后直接访问网址,已经可以用了,注册一个账号然后就可以直接提交程序了。第一个管理员账号的添加方法官方写了。 以前的数据该怎么办呢?数据文件直接按照官方说明来做就好,至于数据库,官方提供了一个poj2hustoj.sql的文件,先把原来POJ的数据导入进数据库,然后运行一下那个文件即可。导入完成,刷新页面……相信有经验的人看到题目列表之后的第一反应都是:卧槽这不是GBK在UTF8下的乱码么!

确实,我们服务器的数据库collection和charset都是latin1,不知道是POJ这么要求的还是当时Andy同学忘了搞了,如果是后者的话我还是不做评论了…… 其实官方还给了一个方法:用linux自带的iconv命令将数据转换为UTF8编码。但是我试了试,效果十分不明显,不对,就是没效果,不知道为什么。如果有大神知道,欢迎留言,命令如下:

iconv -c -f gbk -t utf8 gbk.sql > utf8.sql
sed 's/latin1/utf8/g' utf8.sql > newjol.sql

后来发现php中提供了转换编码的代码,于是就自己写了一段:

<?php

$OLD = 'judgeonline';
$NEW = 'jol';

mysql_connect('localhost', 'root', '');

$tableres = mysql_query("SHOW TABLES IN " . $OLD);
while ($tablet = mysql_fetch_array($tableres)) {
    $table = $tablet['Tables_in_' . $OLD];
    // echo '<table><caption>' . $table . '</caption><thead><tr>';
    $fieldres = mysql_query("SHOW FIELDS IN " . $OLD . ".`" . $table . "`");
    $field = array();
    while ($fieldt = mysql_fetch_array($fieldres)) {
        $field []= $fieldt['Field'];
        // echo '<td>' . $fieldt['Field'] . '</td>';
    }
    // echo '</tr></thead><tbody>';
    mysql_query("SET NAMES latin1");
    $selectres = mysql_query("SELECT * FROM " . $OLD . ".`" . $table . "`");
    while ($select = mysql_fetch_array($selectres)) {
        // echo '<tr>';
        $sql = "INSERT INTO " . $NEW . ".`" . $table . "` VALUES (";
        $rows = array();
        foreach ($field as $e) {
            // echo '<td>' . (($e == 'password' || $e == 'source') ? $select[$e] : iconv('gbk', 'utf-8', $select[$e])) . '</td>';
            $rows []= "'" . (($e == 'password' || $e == 'source') ? $select[$e] : iconv('gbk', 'utf-8', $select[$e])) . "'";
        }
        $sql .= implode(", ", $rows) . ")";
        mysql_query("SET NAMES utf8");
        mysql_query($sql);
        // echo '</tr>';
    }
    // echo '</tbody></table>';
}
?>

不是操纵文件,而是操纵数据库,因为直接转换文件在我这里会报错,貌似跟blob有关。这段代码的功能是将judgeonline的所有非blob内容都转换成UTF8编码,存到jol数据库中。运行完了之后导出jol数据库即可。

导入之后发现没法进行评测了,一直显示pending,google的结果包括官方给出的结果都没有解决问题。于是我从judged和judge_client的源代码入手,结合apache的error.log,最后发现是数据库结构有问题。咦,升级的脚本并没有给我升级好啊。没办法,自己来吧。重新生成了HUSTOJ的数据库,然后自己慢慢比对,最后发现其实只有题目会出问题,例如HUSTOJ里面没有input_data、output_data、ratio等字段,同时也多了几个字段例如judgetime。judgetime无所谓于是就让它先空着,我把POJ多的字段都删掉,然后导入就正常了。可以开始各种语言的测试了。

测试C和C++都没什么问题,测试Pascal的时候一直是错误答案,然后发现没装fpc……为什么install里面没有写fpc的事情呢?于是apt-get install fpc之后就可以了。

最后一点问题,我在测试TLE的时候,写的是死循环,结果judge_client还真给我死循环了;A+B我记得内存限制是64M,结果我写了个程序开了好大一块内存并刷了一遍,照样通过。看到源码是用setrlimit函数进行的限制,我还在想为什么linux的系统函数都会失效。直到过了好长时间,我发现原来一直运行的程序提示TLE了……好像没有失效哎?一番google之后,发现setrlimit有一个软限制和一个硬限制,网上的介绍特别好玩:

RLIMIT_CPU:最大允许的CPU使用时间,秒为单位。当进程达到软限制,内核将给其发送SIGXCPU信号,这一信号的默认行为是终止进程的执行。然而,可以捕捉信号,处理句柄可将控制返回给主程序。如果进程继续耗费CPU时间,核心会以每秒一次的频率给其发送SIGXCPU信号,直到达到硬限制,那时将给进程发送 SIGKILL信号终止其执行。

啊啊啊这个参数真的好萌好傲娇><……

说回正事,我在想如何降低这个硬限制,后来发现源代码里已经做的很好了,那为什么超时还是不立即结束呢?排除了种种可能,最终我想到了一个最逗逼的可能性,于是我点开了题目编辑……

果然,A+B的时间限制是1000s,内存限制是65536M……瞬间有一种想杀了zhblue的冲动……

本来我想再写一段程序改掉来着,但是想了想这些事情实在是太逗逼了,于是就滚过来写心得了……晚上有时间再搞吧。

P.S.感谢白菜提供的梯子,否则有些问题没有google我真的不知道该怎么办。

版权声明:除文章开头有特殊声明的情况外,所有文章均可在遵从 CC BY 4.0 协议的情况下转载。
上一篇: 【mark】移动前端不得不了解的html5 head 头标签
下一篇: 【教程】不用JS实现鼠标移过的下拉菜单效果

这是我们共同度过的

第 1763 天