VirtualBox 还能这样玩

在工作中经常遇到这样的情况:忽然发现开源界有了个新玩意儿,但是下载到自己电脑上一看,不支持我的操作系统,或者不支持这个版本的操作系统。只能老老实实下载某个版本的 Linux 安装镜像,然后开始安装配置虚拟机,等把环境都折腾得差不多了,已经忘了自己装系统来干什么了。

我曾经想过直接从网上寻找构建好的虚拟机镜像,最终发现并不容易。但我很遗憾没有早些遇见 Vagrant,因为它更进一步地满足了上述的需求。

Vagrant 能做什么呢?一句话来说,就是它用简单的命令封装了虚拟机创建、分发和配置的过程。如果把 VirtualBox 比作 dpkg、rpm,Vagrant 就是 apt-get、yum。用 Vagrant 可以非常简单地下载网上封装好的虚拟机镜像(叫做 box),然后启动起来,并且登录进去。配置虚拟机的端口转发等功能也变得非常容易,并且可以脚本化。

当然,Vagrant 也可以将配置好的虚拟机打包成 box 分发给别人。这就带来很大一个好处,那就是用它可以非常简单地实现团队开发环境的统一。创建一个 Linux 虚拟机,安装好必要的开发工具、运行环境,配置好代码仓库,打包成 box,然后再分发一个 Vagrantfile 支持启动虚拟机后执行一个脚本更新代码。新人只需要下载 Vagrantfile,然后 vagrant up。Bingo! 就可以 ssh 上去开发了。

当开发环境出现各种问题时,非常简单地用 vagrant 重新配置下即可。这大大避免了追查『在我的环境里执行没错啊!』这种问题的麻烦发生。

对于团队来说,开发环境的 box 还是自己打包更为合适,能够尽可能避免从网上下载的 box 带来的安全问题。不过对于普通用户来说,最酷的就是有各种来自官方或非官方现成的 box 可玩,不用痛苦地自己去一遍遍重装操作系统。比如以下几个:

虽然事实上 Vagrant 已经支持了 VMware 和 KVM,不过资源上就没有 VirtualBox 那么丰富了,大家有兴趣也可以尝试一下。

64位Ubuntu上使用Network Connect

Network Connect 是 Juniper 公司出品的配合其安全硬件 VPN 解决方案的软件包,很多公司使用这个 VPN 解决方案,一般需要使用 RSA Token 动态密码登录。Network Connect 支持 Windows 和 Linux 操作系统,但很遗憾的是,它只支持 32 位 Linux。下面以 Ubuntu 为例,介绍如何

在 64 位 Linux 上安装并使用 Juniper Network Connect

1. 安装浏览器 Java 插件(64位);

$ sudo apt-get install icedtea-plugin

虽然看起来只安装了一个软件包,但实际上可能会下载依赖的 OpenJDK 的一系列软件包。这是为了在 Firefox 下能够正确启动 Network Connect 的安装过程。

2. 修改 root 密码;

$ sudo passwd

由于 Ubuntu 默认不使用 root 用户,下面自动安装 Network Connect 软件时候又必须提供 root 密码,所以这里必须先初始化 root 的密码。

3. 打开 Firefox,访问 VPN 网站。

像在 Windows 下那样,先登录进入 VPN 页面,再点击 start,启动 Network Connect 的自动安装过程。过程中会弹出一个很丑的终端,安装时需要输入 root 密码,但是最终必定是无法弹出 Network Connect 小图标,也连接不上。

4. 下载脚本 junipernc[1],并且安装到执行目录 /usr/bin 中;

$ wget http://mad-scientist.net/junipernc -O junipernc 
$ chmod 755 junipernc
$ sudo mv junipernc /usr/bin

5. 安装 Network Connect 二进制程序依赖的 32 位动态链接库;

NC 具体的可执行程序是 ~/.juniper_networks/network_connect/ncsv ,是 32 位的可执行程序。如果不安装它依赖的 32 位动态链接库[2],该程序是执行不了的。

$ sudo apt-get install libc6-i386 lib32z1 lib32nss-mdns

6. 执行 junipernc 脚本,会跳出各种对话框,对应填入各种参数;

$ junipernc --nojava

URL 就填入 VPN 网站的域名,USER 就是自己的用户名,REALM 比较麻烦,需要自己查看 VPN 网站登录页面的源代码,看对应 REALM 域实际表单提交的 value 是什么,填进去即可。

--nojava 的意思是,只执行 VPN 连接,不启动 Network Connect 小锁图标的 Java 程序。因为该 Java 程序要求 32 位 Java 环境。

连接失败会有提示;连接成功后,junipernc 会一直停在那里,终止连接可以使用 Ctrl-C 命令行,或者 sudo killall -9 ncsv。

6-1. 修改 junipernc 配置;

junipernc 有两个配置文件,一个是 ~/.vpn.default.cfg,保存着用户手工输入的配置;一个是 ~/.vpn.default.crt,这个是从网站上下载下来的证书。

这样,一般的 VPN 连接功能就实现了。如果希望启动 Network Connect 小锁图标并监控 VPN 的流量信息,就需要

在 64 位 Ubuntu 上安装 32 位 Java 环境[3]

如果不是特别需要,不建议折腾下面这套东西。

a. 到 Oracle 网站上下载 32位 Java 的 tar 包;

到这个地址:http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html#javasejdk下载例如 jre-7u3-linux-i586.tar.gz 的 Linux JRE 包。重要的特征是那个 i586。

b. 解压并安装到 jvm 目录,调整默认 java 的链接到 32位 JRE。

$ tar xzvf jre-7u3-linux-i586.tar.gz
$ sudo mv jre1.7.0_03 /usr/lib/jvm
$ sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jre1.7.0_03/bin/
java 73
$ sudo update-alternatives --config java

最后一个命令会给出一个 Java 的列表,如下所示,选择 jre1.7.0_03 对应的编号即可。

There are 2 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                      Priority   Status
------------------------------------------------------------
  0            /usr/lib/jvm/java-6-openjdk/jre/bin/java   1061      auto mode
  1            /usr/lib/jvm/java-6-openjdk/jre/bin/java   1061      manual mode
* 2            /usr/lib/jvm/jre1.7.0_03/bin/java          73        manual mode

Press enter to keep the current choice[*], or type selection number: 2

c. 验证 java 可以执行且版本正确(其实依赖上面的第 5 步);

$ java -version
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b04)
Java HotSpot(TM) Server VM (build 22.1-b02, mixed mode)

d. 安装 32位 JRE 可能依赖的 32位动态链接库。

$ sudo apt-get install ia32-libs

这个包会依赖非常多的 32位链接库,安装过程会比较漫长。

参考文章:

[1] Using Juniper Network Connect on Ubuntu
[2] Debian6(64位)搞掂Juniper VPN
[3] Installing 32 bit java now that ia32-sun-java6-bin is not available

Shell Tips: Unix 时间到字面

我的工作需要天天跟报表数据打交道,在交换的文件中,一般时间的字段内容都是 Unix 时间。为了检查数据的正确性,不可避免地需要转换 Unix 时间到人类可读的字面时间。

下面想分享的是一个在 Shell 下转换 Unix 时间到字面的小方法。与前面几篇一样,这个小 shell 函数仍然可以放在 ~/.bashrc 中方便快捷使用。

# 转换 Unix 时间到本地时间字符串
function ctime()
{   
    date -d "UTC 1970-01-01 $1 secs"
}

使用方法很简单:

$ ctime 1234567890
Sat Feb 14 07:31:30 CST 2009

对 date 命令熟悉的同学会说,date 不是已经有直接转 Unix 时间的参数了吗?

$ date -d @1234567890
Sat Feb 14 07:31:30     2009

但是不好意思的是,小弟有时候用的 date 程序好老,不支持 @ 符号。

$ date --version
date (coreutils) 5.2.1
Written by David MacKenzie.

Copyright (C) 2004 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

PS: 写完这篇博文,我又想到了一个有趣的事情,既然很多 Linux 64 位版本的 time_t 已经是 long long 格式了,那么 date 命令有没有 year 2038 问题呢?

下面是 date (coreutils) 5.2.1 在 64 位服务器上的尝试结果:

$ date +%s -d "Tue Jan 19 11:14:07 CST 2038"
2147483647
$ date +%s -d "Tue Jan 19 11:14:08 CST 2038"
2147483648
$ date +%s -d "Tue Jan 19 11:14:09 CST 2999"
32473710849
$ ctime 2147483647
Tue Jan 19 11:14:07 CST 2038
$ ctime 2147483648
Sat Dec 14 04:51:44 LMT 1901
$ ctime 32473710849
Mon Mar 28 07:33:53 LMT 1910

看来字面时间和 Unix 时间之间互转存在着问题啊!但是用 Ubuntu 11.04 的 date (GNU coreutils) 8.5 尝试就不存在这个问题了。

这货还是Kindle吗?

自从大家了解到 Kindle 的系统是 Linux 后,无数仁人志士前赴后继地尝试以各种方式 hack 这个系统,对于我等用户来说真是一大幸事!

好久没有跟进电纸书论坛,居然不知道神器 FBReader Kindle 版 —— fbKindle 的发布。mobileread 网站的原帖中,已经给出了安装和配置修改方法。不过我是遵从这篇帖子中的步骤,无它,摘要读起来更快。

没用过的朋友可能不太了解 FBReader,它是一个开源的跨平台电子书阅读器,支持很多种格式,包括我们常用的 GBK 编码 txt 文档以及 epub 格式。由于 epub 格式电子资源很多,以前我还纠结于为了这些资源要不要装多看系统。现在有了原生的 fbKindle,从 epub 支持这点来看,多看是没有什么必要装了。

此外,还有国人开发的Kindle 原生系统拼音输入法,可以支持在书中或者浏览器中输入中文。虽然我没有安装成功,但我对作者的努力是相当的佩服!

Kindle 原生系统拼音输入法
图片截取于输入法作者相册

在最近的使用中,我也发现了一个 Kindle3 离线读网上文章的小技巧。用 pdfcreator 将网页打印成 A6 幅面的 pdf 文件,并且在页面设置中设置四周页边距为 0(Chrome不支持,Firefox/IE可以),上下不打印网址、页数等信息。这样的 pdf 文件在 Kindle3 上会有比较好的展示效果(字体很小时顶多需要横屏),并且也有比较好的打印效果。由于 pdfcreator 还可以设置自动保存位置和自动文件命名规则,用起来还是挺方便的。

当然,如果你是可注册 K3-wifi 版用户,SendToKindle 浏览器插件可能更有用。

在越来越多有趣的事情发生在 Kindle 上之后,真期待这个生态圈将会变成什么样子。

Shell Tips: 用GNU Screen实现发送交互到所有会话

服务器冗余和分拆是互联网服务中经常用来缓解访问压力的手段,那么检查或者管理多台同构服务器也是互联网行业工程师们绕不开的操作。经常面临的问题是:如何高效地在多台服务器上执行相同的命令,进行批量系统操作或问题检查。

Windows 下的 ssh 客户端 XShellSecureCRT 都提供了类似的功能,当每个标签页都连接到一个服务器时,可以在命令窗口中发送交互到所有的标签页以实现同时操作多台服务器的目的。这招我还是从 OP 那里学来的,的确大大提高了生产力。

但这种方法也存在一些问题:

  1. 只适用于特定的 ssh 客户端。例如对 Linux 来说就有些不适用,不过据说 Konsole 也提供了类似功能,未验证。
  2. 每个标签页中,还是得一台一台地登陆上服务器,很难自动化。据说有的客户端支持编写脚本实现,但还要学习对应脚本语言,且灵活性有限。
  3. 无法一直保持持续的连接。特别是对有开发机的工程师,本来开发机是一直在线的,但由于客户端的限制,只能在本地电脑连接多服务器。当本地网络断开后,自然多服务器的连接也断开了。

为了解决这些问题,小弟想到了神器 GNU Screen。Screen 也是终端,难道无法做这件事吗?您还别说,在我费心劳力一上午之后,总算摸索出了用 Screen 解决上述问题的方法。下面两个可以放到 ~/.bashrc 中的函数,就是我心血的“结晶” :)

function screenssh ()
{
    local username=YOUR_USERNAME
    local password=YOUR_PASSWORD
    local server=''
    local timeout=3
    for server in $@; do
        screen -S $STY -X screen ssh $username@$server
    done
    sleep $timeout
    local cmd="screen -S $STY -X at ssh# stuff $'$password\n'"
    eval $cmd
}

function lets ()
{
    local cmd="screen -S $STY -X at ssh# stuff $'$1\n'"
    eval $cmd
}

Screen 的用法和技巧,在我之前的文章中也有提及,此处不再赘述。这里主要介绍一下上面两个函数的作用和用法:

screenssh 是在 screen 中自动登陆多台服务器的命令。这个 bash 函数接受服务器列表作为输入,执行后会在当前 screen 中为每个服务器打开一个 window,并使用提供的用户名和密码登陆这些服务器。这样当前 screen 中就会多出 N 个 window,分别对应登陆到 N 个服务器。在使用前,你要修改用户名、密码变量值为你需要的内容,而且该命令必须在 screen 中执行,在 screen 外执行是无效的。

执行完 screenssh 后,就可以祭出 lets 命令来在多个 window 中同时执行操作命令了。lets 接受一个字符串作为输入,执行后该字符串会作为命令发送到 N 个服务器对应的 N 个 windows 中执行。

看完以后令人困惑的地方可能是,我到底应该在哪里执行 screenssh 和 lets 这两个命令呢?下面用一个例子来更直白地阐述一下这两个命令的使用方法。

假设你需要在 3 台服务器:s1.solrex.org, s2.solrex.org, s3.solrex.org 上执行 grep FATAL ~/error_log 查看错误日志。那么你应当:

1. $ screen -S admin
# 首先创建一个 screen,这时候你有了 0 号 window;
2. $ screenssh s1.solrex.org s2.solrex.org s3.solrex.org
# 在 0 号 window 中执行 screenssh 命令,自动打开 3 个 window,连接到三个不同的服务器;
3. $ lets "grep FATAL ~/error_log"
# 在 0 号 window 中执行 lets,将命令自动分发到 3 台服务器上执行;
4. ctrl-a N 切换到不同的 window 查看命令的执行情况;
5. ctrl-a 0 切换到 0 号 window 执行下一条批量命令;

下面我们再回顾一下上文中提到的 3 个问题是否解决了:1. GNU Screen Linux 一般均自带,不存在专用客户端问题;2. screenssh 解决了自动化登陆多台服务器问题,且服务器列表作为参数,非常灵活且易定制;3. 开发机上运行的 screen 保证了客户端离线连接不断。

Shell Tips: cppath、scppath、mybackup

分享几个觉得有用的小 shell 函数。

1. scppath

在进行一些跨机器的操作时,每次 scp 总要手动去拼那个路径,首先从 PS1 拷贝粘贴用户名和主机名,然后再 pwd 拷贝粘贴当前目录,然后再 ls 拷贝粘贴要 scp 的文件名。好烦啊,所以就写了下面这个小函数来生成 scp 的文件路径,放到 ~/.bashrc 里。

function scppath()
{
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    local _file
    for _file in $@; do
        echo "\"$USER@$HOSTNAME:$PWD/$_file\""
    done
    IFS=$_IFS
}

2. cppath

同样可以有 cppath。

function cppath()
{
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    local _file
    for _file in $@; do
        echo "\"$PWD/$_file\""
    done
    IFS=$_IFS
}

3. mybackup

这个函数是偷懒备份用的。当写代码写到一半,不想或者不能 check in,但又想备份一下时,就用这个命令对文件或者目录进行自动的备份。

function mybackup()
{
    local _bak_dir=~/history
    local _path=''
    mkdir -p $_bak_dir
    local _IFS=$IFS
    IFS=$(echo -en "\n\b")
    for _path in $@; do
        if [ -f $_path ]; then
            cp $_path $_bak_dir/"$_path".`date +%Y-%m-%d.%H-%M-%S`
        elif [ -d $_path ]; then
            _path=`basename $_path`
            tar -cvf $_bak_dir/$_path.`date +%Y-%m-%d.%H-%M-%S`.tar $_path
        fi 
        echo "Backuped $_path to $_bak_dir."
    done
    IFS=$_IFS
}

Linux screen窗口中文乱码问题

环境:Linux Dist: CentOS 4.3,locale: en_US.UTF-8, .vimrc: set fencs=gbk

目标:终端使用 less/more/grep 等命令正确显示 GBK 编码文件内容,vim 正确显示 GBK 编码文件汉字

症状:

1. 系统自带 gnome-terminal 在设置终端编码为 GBK 后,能达到目标。

2. 使用 xshell 在 windows 平台上设置终端编码为 default 时,ssh 登录到 CentOS,能达到目标。

3. 在 screen 命令窗口内,无论终端还是 vim, 中文均显示为乱码,无法达到目标。

解决办法:在 ~/.screenrc 中,添加下面两句:

defencoding GBK
encoding UTF-8 GBK

我的猜测是 xshell、gnome-terminal 等终端能够将自身编码传给系统,因此系统能够对输出自动进行转码。而 screen 属于终端中的终端,它自身的编码不是 GBK,导致传给系统以后没有对输出进行转码。设置 screen 的编码和转换规则后,就 OK 了。

Shell Tips: GNU Screen 的一些小技巧

由于工作环境的问题,最近越来越感觉到 screen 命令的可贵,下面总结一点使用 screen 命令的小技巧。

最常用的参数组合:

screen -ls // 列出已有的 screen
screen -D -R // 进入指定的 screen 名,如果没有,则以该名称创建 screen

由于很常用,我把这两个命令取了个 alias:

alias sl='screen -ls'
alias sr='screen -D -R'

除了命令之外,还有快捷键 Ctrl+ac 创建 screen;Ctrl+aa 在两个 screen 之间相互切换;Ctrl+ad 从 screen 中 detach;Ctrl+a数字,跳转到数字指代的 screen。

在 screen 最下方显示状态栏,状态栏包括已经打开的 screen 标签列表,当前的 screen 和时间。其中在 screen 标签处显示该 screen 所处的目录名。显示 screen 所处的目录名这一点实现起来要困难一些,首先得修改 .bashrc,加入 screen term 对应的信息

case $TERM in
    screen*)
        # This is the escape sequence ESC k \w ESC
        # Use current dir as the title
        SCREENTITLE='\[\ek\W\e\\\]'
        PS1="${SCREENTITLE}${PS1}"
        ;;
    *)
        ;;
esac

然后 . 或者 source 一下,再修改 screen 的配置文件,添加状态栏,在 .screenrc 中添加:

caption always '%{=b cw}%-w%{=rb db}%>%n %t%{-}%+w%{-b}%< %{= kG}%-=%D %c%{-}'
shelltitle '$ |bash'

最终效果为:

GNU Screen 多标签状态栏

删除 MBR 引发的诡异问题

我要跟女友交换一下笔记本电脑,她不常用 Linux,而我的 Ubuntu 分区占了好几十 G 空间,因此我想还是删了再给她吧。

我的电脑有两个系统:Windows Server 2008 和 Ubuntu 10.04。按照惯常的思维,删除 Ubuntu 只需要先格式化 MBR,然后删除 Ubuntu 分区即可。因为手头没有 DOS 启动盘,我想到一键恢复的硬盘版是带 DOS 工具的,就在 Windows 下装了个一键恢复硬盘版,然后进 DOS 命令行 “fdisk /mbr”。

可谁知道做完这些之后,MBR 是清掉了,但系统无法启动了,提示消息是这样的:

Windows 未能将启动原因可能是最近更改了硬件或软件
文件:\Windows\system32\winload.exe
状态:0xc000000e
信息:无法加载所选项,因为应用程序丢失或损坏。
...

然后我就傻眼了,从来没有遇到过这种情况呀!搜索了一番之后,才明白了这是什么意思。

Windows Vista 之后的系统,不再使用 boot.ini 保存启动菜单,而是使用一种叫做 BCD(Boot Configuration Data)机制来管理启动菜单,其默认的配置文件是活动分区(一般是 C:\)的 \Boot\BCD。简单的来说,可以将 \Boot\BCD 文件看成是 GRUB 的 menu.lst(grub.conf)文件,里面储存着系统装载程序的路径和参数等。

在我这里,出现上面问题的原因是 BCD 每项记录中的 device 选项被“一键恢复硬盘版”改成了 unknown,这样启动程序不知道到哪里去找系统的装载程序,自然也就无法启动了。使用 bcdedit /store C:\Boot\BCD 可以查看系统的 BCD 每项记录。(较为诡异的是,在没有删除 MBR 之前,我是如何进入到启动项里的?)

我用的解决方法是把所有默认启动项中的 unknown 改成了 boot。还得依靠工具,使用 WinPE U 盘(DOS 启动盘未尝试)启动,进入 C:\Windows\System32\,执行 bcdedit 命令:

bcdedit /store C:\Boot\BCD /set {default} osdevice boot
bcdedit /store C:\Boot\BCD /set {default} device boot
bcdedit /store C:\Boot\BCD /set {default} detecthal 1

然后就可以启动进入 Windows 了。

SVN 技巧:GUI 版本比较和可执行属性

我曾经在《使用 kdiff3 进行 svn 版本比较》中介绍了为什么以及如何使用 kdiff3 或者 meld 等 GUI 比较工具进行 SVN 版本比较。但这样做有个小问题,就是如果设置了 GUI 工具作为比较工具,那么就没办法输出 diff 文件,而且每次都要关掉窗口才会出现下一个文件,就无法比较多个文件了。所以我觉得下面这种做法会更好一些:

$ more svndiff
#!/bin/bash
sed -i -e 's/^# diff-cmd.*$/diff-cmd=meld/' ~/.subversion/config
svn diff
sed -i -e 's/^diff-cmd.*$/# diff-cmd = meld/' ~/.subversion/config

其实就是用一个脚本 svndiff 来做 GUI 比较的工作。svndiff 执行时首先将 svn 配置文件中的比较工具改为 meld,然后进行比较,比较完后再将修改注释掉,这样就不会影响正常 svn diff 的功能。这样一来,svndiff 是 GUI diff,svn diff 就是命令行 diff。

设置文件可执行属性对 Windows 用户来说可能没什么用,可是对 Linux 用户来说用处就大了。没人希望每次一 update,就要重新对需要执行的脚本 chmod 一下。svn 修改文件可执行属性的命令太长了,我老记不住,所以放在这里做个笔记吧:

svn propset svn:executable ON filename

在 Ubuntu 9.04 上安装 Kscope

Kscope 是我很喜欢的 Linux 平台上的代码查看工具,因为我不会用 Emacs,vim + ctags 又用得不熟,看看小程序还可以,看大项目就傻眼了。以前也尝试过 Source-Navigator(这个项目N年没更新,06年时候我装都装不上,08年底居然又复活了,有空了再去试试)、Eclipse、Kdevelop、CodeBlocks,总之都没有 Kscope 用着最舒服。Kscope 让我欣赏的特点主要有:

1. 它号称是代码编辑环境(source-editing environment),而不是IDE。我不用在建立 Kscope 项目时烦心地去选择项目类型、编译器、编译选项等等。编译我有 Makefile,我就是找个工具看看代码,用得着那么麻烦吗。 建立 Kscope 项目时只需要干两件事:选择项目名、项目保存地址和添加源文件。

2. 它不会在源文件目录下建立一堆乱七八糟的文件,影响市容。我记得 Eclipse、CodeBlocks 等都会把项目信息保存在源文件目录下,而 Kscope 的项目保存位置可以自己选,比如我一般都保存在 workspace/kscope 目录下面,这样对要查看的源文件目录没有任何影响。因此 Kscope 的项目和源文件基本没关系,我可以添加任何位置的源文件到某个项目中去。

3. 它不会去读非指定类型的文件。这是针对 Eclipse 来说的,每次在 Eclipse 项目中搜索时,一堆 .svn 目录中文件的结果让我感觉非常闹心,两年没用不知道现在的 Eclipse 是不是更智能点儿了,但是 Eclipse 改不了的毛病就是慢和吃内存。

4. 它支持代码查看的基本功能。其实我最常用的也就那么几个功能:语法高亮、同时打开多文件、整个项目中搜索字符串、查找函数定义位置和引用、项目文件列表+搜索。在这些条上据说 Windows 下的 SourceInsight 做得更好,但我没用过没有发言权。

简而言之,Kscope 与其它工具比就是快、简单、省心。但是时代在变革呀,转眼到了 KDE4 的时代,而 Kscope 仍然停留在 KDE3.5 上。现在的 Ubuntu 9.04 的依赖关系里,居然已经撤掉了 Kscope,在 9.04 上 sudo apt-get install kscope,会得到这样的消息:E: Couldn't find package kscope,真是让人丧气。

其实 Kscope 之所以不能安装,主要原因是它依赖于 Kate 的两个库:libkateinterfaces.so.0 和 libkateinterfaces.so.0,只需要从 KDE3.5 的 Kate 中提取出来这两个库安装到系统中后,Kscope 就可以正常运行了。Ubuntu 9.04 的依赖关系中虽然找不到 Kscope,但是 Ubuntu 的软件仓库中还有 Kscope 的包,我们可以手动下载安装。下面这个脚本的功能就是自动安装 kscope 到 Ubuntu 9.04,稍微修改一下也可以用于在其它 KDE4 桌面系统中安装 Kscope,或者解决 Kscope 无法运行的问题。您也可以从这里下载到该脚本:

#!/bin/bash
# This script helps you install Kscope on Ubuntu 9.04.
# You can also use it to fix "Kscope doesn't run in KDE4" bug.

echo "Determining machine hardware name... "
MACHINE=`uname -m`
case "$MACHINE" in
  i386 | i586 | i686)
    ARCH="i386"
    ;;
  x86_64)
    ARCH="amd64"
    ;;
  *)
    ARCH="i386"
    ;;
esac

# If Kscope is not installed, install it.
which kscope &> /dev/null
if [ $? -ne 0 ]; then
  echo "Installing kscope..."
  sudo apt-get install kscope || \
  wget http://archive.ubuntu.com/ubuntu/pool/universe/k/kscope/kscope_1.6.0-1_${ARCH}.deb && \
  sudo dpkg -i kscope_*.deb || \
  sudo apt-get -fy install || \
  echo "Oops, some error happens..."
fi

kscope -v &> /dev/null
if [ $? -eq 0 ]; then
  echo "Kscope works fine."
  exit
fi

echo "Downloading KDE3 libraries needed by kscope..."
wget http://ftp.debian.org/debian/pool/main/k/kdebase/kate_3.5.9.dfsg.1-6_${ARCH}.deb
dpkg -x kate_3*.deb kate

echo "Installing KDE3 libraries..."
sudo cp kate/usr/lib/libkateinterfaces.so.0.0.0 /usr/local/lib/
sudo cp kate/usr/lib/libkateutils.so.0.0.0 /usr/local/lib
sudo ln -s /usr/local/lib/libkateinterfaces.so.0.0.0 /usr/local/lib/libkateinterfaces.so.0
sudo ln -s /usr/local/lib/libkateutils.so.0.0.0 /usr/local/lib/libkateutils.so.0
sudo ldconfig

echo "Finished."

Linux 下 Firefox 变身 Google Chrome

几乎可以达到以假乱真的效果,屏幕截图请看:

Linux 下 Firefox 变身 Chrome

要求:
1. KDE 4 ---> Gnome 的窗口无法隐藏标题栏,影响变身效果(这也是目前我觉得 KDE 更好玩的原因之一)
2. Firefox 3.5 以上 ---> 3.5 以下无法使用 Chromifox Extreme 主题,只能使用 Basic Chromifox Extreme 主题
3. Chromifox Companion 扩展 ---> 配合 Chromifox Extreme 主题修改工具栏图标
4. Hide Menubar 扩展 ---> 隐藏菜单栏,可以使用 alt 呼出

步骤:
1. 安装 Firefox 3.5。下载 tar.bz 包之后解压到 /opt 目录下,比如目录名为 /opt/firefox,修改 /usr/bin/firefox 软链接到 /opt/firefox/firefox。
2. 安装 Flash 插件。在 /opt/firefox/plugins 目录下建立软链接 libflashplayer.so 到 /usr/lib/flashplugin-installer/libflashplayer.so。
3. 打开 Firefox,查看版本是否 3.5,安装 Chromifox Extreme 主题
4. 安装 Chromifox Companion 扩展
5. 安装 Hide Menubar 扩展
6. 设置隐藏标题栏。右击标题栏,Advanced->Special Application Settings->Preferences->No border,下拉选择 Apply Initially,勾上后面的复选框。
7. 重启 Firefox。

Update:在 Ubuntu 下安装 Google Chrome (开发者版)和 Google Earth

$ echo "deb http://dl.google.com/linux/deb/ stable main" > google-chrome.list
$ sudo mv google-chrome.list /etc/apt/sources.list.d/
$ wget https://dl-ssl.google.com/linux/linux_signing_key.pub -O - | sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install google-chrome googleearth

遗憾的是,Chrome4Linux 目前还不支持中文。

Solrex Linux Cheatsheet

Cheatsheet:原意是考试的时候带的小抄,所以说是 cheat(作弊) sheet。在计算机科学领域里,主要是指记录一些难记命令或者操作的快查表,有时候和 quick reference card 意思相同,中文或者可以翻译成:命令速查表?

由于很少看到中文的比较完善的 Cheatsheet,我想,做一个这样的 Cheatsheet 应该挺不错。所以我就我自己使用 Linux 的经验,打算整理出来一个中文的 Linux Cheatsheet。目前放出零点零一版,您可以到这里下载,目前的具体状况如下图所示:

Solrex Linux Cheatsheet

这个 Cheatsheet 包含两个文件,一个 pdf 文档,一个 odt 文档。如果您想帮助我完善它,您可以将您修改过的 odt 文档发送给我。如果您的建议被采纳,您的名字(或代号)和链接将被添加到该页面的“感谢”一栏中,谢谢。

最后重复,Solrex Linux Cheatsheet 的主页在:https://github.com/solrex/solrex/tree/master/linux_cheatsheet/

PS: 其实我昨天就想发这篇博客了,但是两天来我所在的北京网通 ADSL 网络访问我的博客一直有问题,不排除被封的危险。如果您想继续关注本博客,推荐您使用 Google Reader 或者抓虾等在线 RSS 订阅器订阅本博客的 RSS,这样被封后至少您能看到本博客的文章更新。

使用 kdiff3 进行 svn 版本比较

svn diff 命令的效果总的来说还是不错的。因为它是基于行的比较,在比较格式规范的程序代码文件时候也足够了,尤其是每行不超过 termial 宽度的时候。但是在比较那些单行长度比较长的文件时,比如 HTML, TEX 等,仅仅修改一个字母也会引起两行不同,用 svn diff 查看被修改的地方时就比较痛苦了。

Linux 下有一些比较好的比较程序,比如 kdiff3, gdiff, vimdiff 等,kdiff3 可以用不同颜色显示两个文档中不同的行、字符,算是比较理想的比较程序。我们可以拿它来替换掉 svn diff 默认的比较程序。

很显然这种需求别人也会有,所以早就有高手解决了这样的问题,您可以从这里下载一个 bash 脚本,将其重命名为 svndiffwrapper,加上可执行权限放到一个可执行路径(比如 /usr/bin/)下。然后修改您的 ~/.subversion/config 文件,找到 diff-cmd,修改为如下所示:

### Set diff-cmd to the absolute path of your 'diff' program.
###   This will override the compile-time default, which is to use
###   Subversion's internal diffimplementation.
diff-cmd = svndiffwrapper
### Set diff3-cmd to the absolute path of your 'diff3' program.
###   This will override the compile-time default, which is to use
###   Subversion's internal diff3 implementation.
diff3-cmd = svndiffwrapper

这样,当您再执行 svn diff 的时候,svn 会默认调用 kdiff3 进行文件比较。由于 kdiff3 是 GUI 程序,所以文件比较将会是一个一个进行的,关掉第一个文件比较的 kdiff3 窗口,才会显示第二个文件的比较窗口。

网友 sleetdrop 评论指出,如果使用基于 Python 的 Meld 作为 GUI 比较工具,那么不需要任何 wrapper,只要修改上述配置为:

diff-cmd = meld

即可。不可否认,这比 kdiff3 简便了不少,而且 meld 还支持在比较时编辑,这也是一个很大的优点。

vasprintf 会将空间分配到栈上吗?

由于提交过几次 Linux Fetion 的 bug 和 patch,Linux Fetion 的开发者邀请我加入了 Linux Fetion GUI 的维护者团队中。

昨天晚上和今天下午,我和邓东东(DDD)一直在调试一个 Linux Fetion 在 64 位电脑上的段错误 BUG。这是一个非常奇怪的 BUG,其表现为在 64 位电脑上(Ubuntu 9.04)运行 Linux Fetion 在登录成功后会经常出现 Segmentation Fault。DDD 确定该 BUG 存在于 Libfetion 库中,并且和读取联系人信息的函数有关。Libfetion 论坛上一直有人抱怨类似问题,但是在 DDD 的 64 位虚拟机上却无法重现此 BUG(他的 libc 是 2.7 版本的)。

由于 DDD 仍然不愿意公开 libfetion 库的源代码,我只好等每次他修改库文件之后发给我再调试。经过了好几个小时的努力,今天下午我发现,该 BUG 的主要成因非常有可能是:子函数中本应被动态分配到堆(heap)上的空间被分配(或误写)到了栈(stack)上,子函数返回调用者之后指向子函数栈内容的指针非法。

由于该动态分配的空间是使用 vasprintf 自动分配的,从 DDD 给我的部分代码来看指针传递出问题的可能性不大。那么我想,是不是 vasprintf 函数并不能保证动态分配的空间在 heap 上呢?希望对此有了解的朋友指点一下,谢谢!

$ uname -a
Linux Slytherin 2.6.28-12-generic #43-Ubuntu SMP Fri May 1 19:31:32 UTC 2009 x86_64 GNU/Linux
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.3.3-5ubuntu4' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.3 (Ubuntu 4.3.3-5ubuntu4)
$ ll /lib/libc.so.6
lrwxrwxrwx 1 root root 11 2009-04-13 10:26 /lib/libc.so.6 -> libc-2.9.so

PS:我在写这篇博文过程中搜索了一下 Wikipedia,发现这样一段话:

int asprintf(char **ret, const char *format, ...)

asprintf automatically allocates enough memory to hold the final string. It sets *ret to a pointer to the resulting string, or to an undefined value if an error occurred (GLibc is notable in being the only implementation that doesn't always set *ret to NULL on error).

那么是不是分配失败导致了错误的发生呢?但是如果分配失败,为什么子函数返回前的指针的确指向一段在栈上的字符串呢?

跨平台音乐播放软件 Songbird

Songbird 是一款开源音乐播放软件,目前可以支持在 Windows, Linux 和 Mac 操作系统上运行。从我的角度来看,它有几大特性:(1)支持 iPod——对我来说没诱惑,我又没有 iPod;(2)类 iTunes 界面,这一点我还是很欣赏的,管理音乐比较方便;(3)内嵌浏览器,用某篇文章的话说就是:“如果iTunes和Firefox来一场甜蜜的网络性爱,他们产下的卵就是Songbird。”虽然形同鸡肋,不过也蛮有趣的;(4)支持类似于 Firefox 的插件功能,这将会是 Songbird 的杀手锏;(5)支持大部分中文歌曲 ID3 标签——这一点使我不得不喜欢它,下面我还会说到;(6)可用插件支持歌词显示。

总的来说,Songbird 吸引我的主要原因是后两条,因为我很难在 Linux 下找到实现这两条的播放软件,但是这后两条特性又貌似不那么完全。本地化、本地化,Linux 的本地化一直是一个问题,再加上像我这样的“事儿妈”喜欢在 en_US.UTF-8 的 LOCALE 下追求中文使用的无障碍,使得问题更糟糕。Linux 下的经典播放软件 xmms, RhythmBox, Amarok,无一例外都被中文 ID3 标签显示问题打败(或者说我不知道如何配置?)。

Songbird 让我看到了一丝希望。我目前歌曲库中所有歌曲都是从 Google Music 下载的,乍一装上 Songbird 运行后,我发现它居然能识别出大部分歌曲的 ID3 信息(Windows 和 Linux 平台下效果是一样的),不过仍然有乱码存在。我想可能是 ID3 标签自身问题,于是到 Windows 下用千千静听编辑了一下这些显示为乱码的歌曲信息。经过多次尝试,发现用千千静听将 ID3 信息先写入为 ID3v1,然后再重新以 UTF-16 格式写入为 ID3v1 & ID3v2 貌似可以解决 Songbird 的乱码问题,至少我的歌曲库已经没有乱码了。

这个乱码解决方案的重要之处在于:Windows Explorer 支持该方案,也意味着大部分 MP3 音乐播放器(随身听)支持该方案。不像通常为使 Linux 下的播放软件(比如 Amarok)支持 ID3 标签,要强行将 mp3 文件的 ID3 标签转换为 UTF-8 编码,这样会造成大部分 mp3 随身听和 Windows Explorer 无法显示歌曲信息。(插一句:在我使用过的 mp3 里,只有魅族 Miniplayer(M6) 支持 UTF-8 格式的 ID3 标签和 UTF-8 格式的 txt 文档,这也是我对魅族这间公司有很大好感的原因之一。)

Songbird 的歌词插件也很奇怪,貌似它只支持内嵌于 ID3 标签中的歌词,而且没有时间轴,还好 Google Music 的歌曲大部分内嵌有歌词。不知道存不存在可以从外部文件读取歌词并且以时间轴显示的插件?

关于 ID3 标签,我还有一些疑问,下面列出来,希望对此有了解的朋友能够指点一二。

1. 网上讨论 Linux ID3 标签编码问题时经常会说 ID3 标签的编码是 GBK, BIG5 所以造成了乱码问题。但 Wikipedia 上说,ID3v1 只支持 ISO-8859-1,ID3v2.1 增加了 UTF-16 支持,ID3v2.4 增加了 UTF-8 支持,貌似 ID3 标签根本没有 GBK 编码这一说。那么乱码问题到底和 GBK 有关系吗?

Li Fanxi 网友的重要评论:ID3v1只支持ISO-8859-1,不过很多传统的软件,比如WinAMP或Tag&Rename,都会用System Code Page去存放非ISO-8859-1中的字符。对于CP936,自然就是GB2312了。一些严格符合标准的软件如果没有考虑到这个问题,就会出现乱码。

2. 这篇疑似 Amarok 开发人员的博客中说:“(我们限制自动检测编码功能仅检测 ID3v1 )基于这样的假设:所有的 ID3v2 使用的是 UTF-8 编码,所有的 ID3v1 使用的是非 UTF-8 编码。”但是要知道大部分中文 MP3 歌曲使用的是 ID3v2.3,根本不支持 UTF-8。那是否能得出这样的结论:Amarok 乱码问题是因为 Amarok 不支持 ID3v2 标准,而不是 Windows Explorer 和其他 mp3 播放器厂家不支持标准?那么我想,有了 Songbird,是放弃 Amarok 的时候了。

Li Fanxi 网友的重要评论:ID3v2.3不支持UTF-8,如果用UTF-8存ID3v2.3 Tag可以看成是软件的Bug,我不太确定有多少软件会这么做。

3. 为什么某些同样是 ID3v2.3 编码的 MP3,Windows Explorer 和千千静听能读取正确,Songbird 中却有可能显示为乱码呢?

Li Fanxi 网友的重要评论:这个问题还是要具体问题再具体分析,可以用软件分析一下这个文件中倒底存了多少种Tag,一个文件中可以同时存在几种版本的Tag,比如ID3v1、ID3v2、APE,不同的软件可能对不同的Tag的识别优先级不一样,导致显示结果不一样。

虽然我天天在Linux下工作,不过管理Tag我还是wine一个Windows下的Mp3tag来用,因为以前在Windows下用惯了,而且我也在维护这个软件的简体中文语言文件。这个软件Usability不算好,不过功能很强,适合对MP3 Tag管理有比较高要求的朋友用用。http://www.mp3tag.de/en

对于解决乱码MP3的问题,我用Mp3tag的方案是先对用System Code Page存放的Tag做一次Convert Codepage的Action。然后把MP3 Tag清除动作设成清除所有,写入动作设成只写入ID3v2.3 UTF-16,然后对所有的文件做一次Ctrl+X再Ctrl+V。世界就清静了。

PS: 一定要试试 Songbird 的 MashTape 插件,很好很强大!

用 Vim 对矩阵转置

前两天某个同学在科苑星空 BBS 上问到了一个有趣的问题:如何在 Vim 中对矩阵进行转置?

我当时想,转置不就是行列互换嘛,awk 可以取一列,那么拿出来每列然后打印成一行不就好了?类似于:

echo `awk '{printf "%s ",$1}' file`
echo `awk '{printf "%s ",$2}' file`
echo `awk '{printf "%s ",$3}' file`
...

本来觉得用 bash 写一个循环语句就可以了,但是怎么也尝试不出来如何替换那个 $1, $2, $3...就想没办法了只能搞 eval 了。但是我觉得这种事情 Unix 前辈们应该干过,所以就搜了一斧子,果然搜到了一个 AWK 程序,《Sed & Awk》 Ch13.9 Perform a Matrix Transposition:

#! /bin/sh
# Transpose a matrix: assumes all lines have same number of fields

exec awk '
NR == 1 {
    n = NF
    for (i = 1; i <= NF; i++)
        row[i] = $i
    next
}
{
    if (NF > n)
        n = NF
    for (i = 1; i <= NF; i++)
        row[i] = row[i] " " $i
}
END {
    for (i = 1; i <= n; i++)
        print row[i]
}' ${1+"$@"}

哈,除了觉得这样内存占用可能比较大之外,这可是一个相当不错的程序。

在 Vim 中调用就很简单了,将上面脚本保存成 transpose,加上可执行属性放在某个可执行路径下(比如 ~/bin),然后在 Vim 编辑矩阵文件时 :%!transpose 就可以了。

PS: 另外,在查证矩阵的“转置”应该写成“转秩”还是“转置”时,我在 Wikipedia 发现一个很有意思的东西:

矩阵用词
在中国大陆,横向称为“行”,纵向称为“列”。 在台湾,横向称为“列”,纵向称为“行”。

天那,要是用汉语和台湾同学讨论矩阵的话,该有多痛苦呀!

Vim 中文输入法插件 Vimim

有位朋友在 TopLanguage 讨论组里向我推荐了一款非常 cool 的 vim 输入法插件:vimim,您可以从 Vim 插件页下载 Vimim,演示程序和码表可以在 Vimim Googlepage下载。Vimim 作为一个 Vim 插件项目起始于 09 年一月份,可能知晓它的用户还比较少,所以我觉得有必要向大家推荐一下这款优秀的 Vim 插件。

作为中文用户,在 Vim 里输入中文往往是一件非常痛苦的事情,最别扭的地方在于使用中文输入法时进行 Vim 编辑模式之间切换比较麻烦,还容易误操作,这也是我一般不使用 Vim 编辑中文文档的原因。

简单地来说,这个插件使得你在 Vim 里直接输入中文(使用 Ctrl+\ 开启选词单而不是通常输入法使用的空格键)。当你不按 Ctrl+\ 时,你就是在输入英文字母,这样就避免了中英文输入法切换的问题。这个插件的使用方法和演示在 Demo 页展示得很清楚,这里我就不再赘述了。

不过在使用上我仍然面临着一个小问题,Vimim 目前提供的双拼码表是基于微软双拼的,而我习惯于智能 ABC 双拼,所以我在 Vimim 的讨论组里抱怨了一下:《希望双拼音节映射独立于码表》。

------------------------------ 我是分割线 ------------------------------

我的 BSP yo2.cn 服务器宕机从 09 年 5 月 12 日起延续了将近三天,期间可能导致部分用户遇到无法访问问题,请见谅。

谈谈 CoolSIP

说明:本文有一些内容是基于本人的合理猜测。如果您知道更多细节,请不吝指出,但最好请提供事实依据,而不是仅仅指责我主观臆测,不尊重事实。我不知道事实,才会猜测,我如果知道事实,就不用猜测了。

CoolSIP 是清华大学信息网络工程研究中心建设的 IPV6“下一代互联网应用示范平台”提供的一个在 IPV6 网络上运行的 VoIP 软件,该软件提供了主机间通话和主机拨打传统 PSTN 电话的功能。目前该服务仅提供给 IPV6 教育和科研网用户使用,注册该服务需要 edu.cn 或者 ac.cn 后缀的大学或科研机构电子信箱。测试期间是免费的,但需要用在线时长换取话费。

我这里呢,就是说几句闲话,谈谈我对这个软件和服务的看法。

1. CoolSIP 的知识产权问题

我对 CoolSIP 软件的结构并不清楚,但基本可以肯定的一点是 CoolSIP 使用了开源的 osipeXosip。不用逆向工程该软件,查看 CoolSIP 帐户信息的保存位置就可以知道,CoolSIP 的帐户信息保存在 C:Documents and SettingsUsernameeXosip 目录下。

由于 osip 遵从的是 LGPL 协议,eXosip 遵从的却是 GPL 协议,那么从我有限的对知识产权的了解(关于 LGPL 和 GPL 可以参考这里),CoolSIP 也应该遵从 GPL 协议(对这点有问题您可以在评论中指出)。而目前来看,清华大学信息网络工程研究中心并没有发布 CoolSIP 的源代码,而且 CoolSIP 貌似将 osip 和 eXosip 库静态链接进了程序里(大概有隐藏的意图),那么可以认为他们并不认可 GPL 许可证——不过他们还没有错误地在安装文件中包含一份私有版权声明。

另外,http://ngmylife.wirelesslan.edu.cn/multimedia.html 页面中说:“当eTone出现无法登录的问题时,可以使用CoolSIP试试。”可以猜测 eTone 和 CoolSIP 有非常紧密的关系,而 eTone 又是一款流行 VoIP 软件“快门” 的前身,那么我对“快门”软件的版权也持谨慎地怀疑态度——不过我更倾向于快门不大可能犯这种错误。

2. CoolSIP 本身的程序设计问题

CoolSIP 是一个极不成熟的软件,使用过程中居然会出现程序错误,点“忽略”才能继续运行的情况;由于其版本历史仅仅显示到 2007 年 9 月 3 日 ,我想其对 SIP 协议的实现是存在问题的,例如在拨打一个通话中的电话号码时,CoolSIP 不会提示对方正在通话中。总之,该软件除了是 sip6.edu.cn 提供的 VoIP 服务的官方客户端以外,几乎一无是处。

3. CoolSIP 的 VoIP 服务

虽然 CoolSIP 客户端没有什么优点,但是 sip6.edu.cn 的 VoIP 服务器端对 SIP 协议的实现应该还算很标准的。由于 CoolSIP 使用了 eXosip 库,那么我就尝试使用另一款使用 eXosip 库的遵从 GPL 协议的开源软件 Linphone 尝试连接了一下 CoolSIP 服务器,居然能正常连接。

并且 Linphone 显示出比 CoolSIP 更友好的用户体验,例如:Linphone 支持 Windows 和 Linux 系统,更完整地支持 SIP 协议,支持自定义电话联系人列表,多帐户同时在线,通话统计记录显示,多种音频编码,更好的视频支持。像上面提到的 CoolSIP 不支持的“您拨打的电话正在通话中”提示,也可以在 Linphone 中听到。

官方提供的专门客户端 CoolSIP 竟然不如一个开源的通用客户端 Linphone,这大概是“下一代互联网应用示范平台”服务的一个笑柄吧。

PS: 使用 Linphone 登录 CoolSIP 的服务器,需要 libexosip2 和 libosip2 版本大于 3.3,我的经验表明 3.1 会出现较多问题。推荐手动依次编译 libosip2->libeXosip2->linphone。Windows 版的可执行安装程序包含了这两个库,所以不存在库版本低的问题。

关于 eYouIPB 和 CASNET

我不知道为什么在干正事的时候想写这个东西,大概也算是一种强迫症吧,想起来某件事情就总觉得有义务马上去做。那么就赶快写完吧。

这篇文章是写给有心人看的,如果您不知道 eYouIPB 和 CASNET 是什么东西,那么您可以无视以下内容。

eYouIPB 是中科院研究生院目前使用的网关登录客户端,但只能在 Windows 下使用。我用 Python 写的一个小软件 CASNET,就是一个 Linux 下的登录替代品,不过目前它也可以支持 Windows。昨天有同学发信问我有关科苑网关登录的问题,我顺便在这里做个笔记吧。

1. 有关流量统计。目前 CASNET 不支持像 eYouIPB 那样的实时的流量统计功能,因为我觉得没有必要。科苑上网是按照流量收费的,而这个流量是指网关服务器统计的流量,而不是客户端统计的流量,所以客户端的实时精确流量统计没有太大意义。

eYouIPB 带有实时的统计流量功能,但这却造成了一些问题。eYouIPB 使用 WinPcap 库做流量统计,而且这个库的版本比较低。假如系统里安装了新的 WinPcap 库,例如 Wireshark(Ethereal) 网络监听程序就会安装 WinPcap 库,那么 eYouIPB 就无法工作了。WinPcap 是一个非常强大的抓包库,而 eYouIPB 仅仅使用了其中的流量统计功能,实在很让人费解它为什么要使用 WinPcap 库并且将它的 dll 包含在自己的软件中。我想应该有更简单的办法做流量统计——如果非做不可的话。

2. 有关登录方式。科苑上网有两种登录方式:一种是 eYouIPB,使用私有的协议;一种是网页登录,使用 https 表单交互。由于 eYouIPB 协议私有,CASNET 只能模拟网页登录的方式,所以您会发现 CASNET 登录速度会比 eYouIPB 慢一点儿,因为它有一个完整的 https 安全协商和交互过程,而 eYouIPB 只需要几次简单 tcp 数据包交换就可以了。不过因为同在一个局域网内,这个速度还是在可以忍受的范围之内。

3. 有关安全性。去年我曾经研究过 eYouIPB 的协议,发现还是相对简单的。我记录的笔记已经丢失,但大概是这个样子的(应用层):“首先 eYouIPB 向服务器发起一个连接,服务器应答可以连接,然后 eYouIPB 将用户名和域发送给服务器,服务器看上去会返回一个密钥,然后 eYouIPB 使用该密钥用某种加密算法加密用户口令(或者还有其它信息)发送给服务器,服务器验证口令,回应是否可以登入。”

从该协议的实现来看,安全性有限。一个是用户可以通过不停地登入登出收集明密文对,另外反汇编 eYouIPB 看起来也不是那么困难,尤其是可以使用 Winsock 32 的 API 来定位负责加密的代码段。一旦加密算法被了解,窃听 eYouIPB 的数据包就能获得用户的口令,那么帐户就可以被窃取。所以相比而言,https 的登录方式更值得信任。

PS: 今天顺便搜索了一下,有个朋友曾经对 1.03 版本的 eYouIPB 协议进行过分析,我感觉和 2.0 版本差距不大。而且这个人完全用 C 实现了 eYouIPB 1.03 的加解密函数如果他仅仅凭逆向工程做出这个结果的话,我是相当地佩服的(因为我花了一整天的工夫也没有做出来一点儿东西)。如果当初我能看到这个结果的话,说不定就有 clue 去模拟 eYouIPB 的协议而不是使用 https 方式了,现在是懒得再去钻研这个东西了。只是不知道 2.0 是否还在使用同样的加解密函数,如果仍在使用的话,那么对有心人来说这个口令保护措施基本上算是不存在了。

4. 有关软件运行速度。这个比较主要是 Windows 平台下,CASNET 是用 Python 脚本写成的,而看起来 eYouIPB 使用 VC++ 写的,在软件的大小和运行速度上 eYouIPB 要显然优于 CASNET。由于 CASNET 的 Windows GUI 版需要额外的 GTK 运行时库和 Python 库支持,大概会加载到内存中 16M 左右的数据,不过这些东西运行时真正用到的不多,所以有些部分除了初始化时,其它时候很可能是驻留在交换区中,所以还是可以接受的。

在 Linux 平台下,因为 GTK 和 Python 库是被非常广泛使用的,所以 CASNET 并不需要额外占用很多内存。

之所以会发布 Windows 版,是因为相比 eYouIPB 而言,Wireshark 对我更重要,所以我无法使用 eYouIPB,而又讨厌网页登录时每次都要选择下拉菜单。再加上顺便看看 PyGtk 在 Windows 下的表现如何。但比较讽刺的是,Windows GUI 版的 CASNET 下载量要超过其它版本加一起的下载量,看来它的存在还是对某些用户有一些帮助。

5. 有关软件功能。eYouIPB 的功能已经被锁定了很多年,但是 CASNET 一直在根据用户的需求增加或者删减某些功能,例如余额不足提醒,断线自动重连,一键切换登录模式等。还有一个未发布的功能是关机自动下线。这是一个我一直想实现的功能,因为当用户关机忘记离线时,恰好分配到原 IP 的用户就可以使用该帐号,造成流量被窃取。这个功能在 Linux 版本上已经基本实现,但是 Windows 版本仍然没有找到可用的方法实现。