当前位置

eYou Team Archive(技术)

FreeBSD 下 lseek(fd, -sizeof(XXX), SEEK_CUR) 的问题

写程序经常有这样的应用: 从文件中读取一段数据,然后修改其中一部分,再 seek 回去重新覆盖. 一遍来说用 lseek/fseek 一个负值.

昨天发现在 FreeBSD 下 lseek -sizeof(XXX) 这样做就会导致产生一个巨大的空洞文件,而直接写 lseek 一个负数就没有关系. 百思不得其解. 上网搜索发现台湾兄弟在8年前就有研究过这个问题. 其实和编译器处理 -sizeof(XXX) 有关系.

如果不实际动手运算,谁能正确回答下列程序的输出?

  1. int main()
  2. {
  3.     printf("%d\n", (-sizeof(int))/4);
  4. }

JEP-0078 Non-SASL 认证

老的 jabber 认证协议使用了 'jabber:iq:auth' 名字空间中的定义,但是 XMPP 协议去掉了 'jabber:iq:auth' 名字空间,而通过 SASL 进行认证。

不幸的是,SASL 库并没有普遍实现,比如 J2ME、Symbian 等,而且现在并不是所有的服务器都完整的实现了 XMPP SASL,因此虽然老的认证方法最终会消失,现在仍然有必要提供一种向前兼容的机制,这样如果客户机或服务器有一方不支持 SASL 仍然能正常通信。

实现中,要求客户端必须能支持一种预定义的认证机制,这些机制现在包括:
1. plaintext
2. digest (RFC3174 SHA1)

正确的 XMPP 客户端首先会发送一个初始头,其中包括 XMPP 版本信息:

如果服务器并没有实现 XMPP,所以将返回一个无版本号,或者版本号小于 1.0 的响应头。这种情况下 XMPP 客户端无需等待服务器返回它的 ,可以直接送出一个 IQ get 请求,老版本服务器将返回可以使用的认证机制,以及所需的认证字段,过程如下:

Example 1. Client Opens Stream to Server

(这里是否包括 version="1.0" 对下面的会话没有影响)

Example 2. Server Opens Streams to Client

Example 3. Client Requests Authentication Fields from Server

bill

Example 4. Server Returns Authentication Fields to Client

如果第 3 步中发过来的用户名不存在,服务器这时不能返回失败(为了避免用户扫描);服务器需要预先得到用户名信息决定这个用户是否支持 digest 认证(如果存储了明文口令)。在第 4 步和接下去的认证请求都必须包括 username 和 resource 字段。

Example 5. Client Provides Required Information (Plaintext)

bill
Calli0pe
globe

Plaintext passwords are straightforward (obviously, characters that map to predefined XML entities MUST be escaped according to the rules defined in section 4.6 of the XML specification, and any non-US-ASCII characters MUST be encoded according to the encoding of XML streams as specified in XMPP Core, i.e., UTF-8 as defined in RFC 3269 [7]).

其中 认证使用如下算法
1. 取得服务器返回的 stream id 和用户口令 concat 在一起。注意:In Digest authentication, password characters that map to predefined XML entities SHOULD NOT be escaped as they are for plaintext passwords, but non-US-ASCII characters MUST be encoded as UTF-8 since the SHA-1 hashing algorithm operates on byte arrays.

2. SHA1(concat(sid, password))

3. Ensure that the hash output is in hexidecimal format, not binary or base64.

4. hex 结果全部小写表示

Example 6. Client Provides Required Information (Digest)

bill
48fc78be9ec8f86d8ce1c39c320c97c21d62334d
globe

这个 hash 结果里 stream ID 用 '3EE948B0',明文口令是 'Calli0pe'

服务器或者成功返回,或者失败。失败原因包括:认证失败、resource 冲突(已经存在一个连接会话)、没有提供 resource 或者 username

Example 7. Server Informs Client of Successful Authentication

Example 8. Server Informs Client of Failed Authentication (Incorrect Credentials)

Example 9. Server Informs Client of Failed Authentication (Resource Conflict)

Example 10. Server Informs Client of Failed Authentication (Required Information Not Provided)

对于支持 XMPP 的服务器,可以在响应的 stream feature 里面包括对于 Non-SASL 的支持。

Example 11. Advertising non-SASL authentication as a stream feature

...

DIGEST-MD5
PLAIN

...

注意:一个服务器 SHOULD NOT 广告 Non-SASL 支持给另外的服务器

如果实现的服务器不支持 Non-SASL 认证,碰上了一个不支持 SASL 认证的老版本客户端,那么这里依照 XMPP 协议必须返回一个 ;出于安全考量,如果服务器和客户端都支持 SASL,那么必须使用 SASL 认证;如果客户端在 SASL 认证失败的时候试图发起一个 Non-SASL 认证,服务器必须拒绝认证,返回 .

客户端的实现 MUST NOT 将 plaintext 作为缺省认证方式,而且 SOULD 警告用户该方式是不安全的,除非是在 SSL/TLS 这样的加密层之上,plaintext 认证 SHOULD NOT 被使用。

一旦 XMPP SASL 认证被广泛部署,本 JEP 将成为 deprecated 状态,目前本协议将在 2005/04/20 过期,届时会决定延期 6 个月或者是 deprecated。

附录:XML Schemas

一、jabber:iq:auth

The protocol documented by this schema is defined in
JEP-0078: http://www.jabber.org/jeps/jep-0078.html

二、Stream Feature

The protocol documented by this schema is defined in

JEP-0078: http://www.jabber.org/jeps/jep-0078.html

Disco Dance Floor

在 slashdot 上看到了这个新奇玩意,不得不佩服 MIT 学生的创意和能力。

用 USB 控制舞池下的 LED,听起来好像也就那么回事,不过创造者们玩酷到了极致:“and then an xmms plugin to control patterns and do some other neat tricks. Grant wrote a Matlab script to convert an array into a special format readable by the control software. Schuyler then went crazy in Matlab and made a bunch of cool patternsand then an xmms plugin to control patterns and do some other neat tricks. Grant wrote a Matlab script to convert an array into a special format readable by the control software. Schuyler then went crazy in Matlab and made a bunch of cool patterns”

不知道他们申请专利没有,说不定很快就有集成公司作这项业务的...附赠盗版 Matlib 一套。

mysqldump 4.1 技巧

关于 mysql 4.1 的中文问题,这里有一篇文章已经阐述的比较系统了,只需要再多一些想象力,就可以完全理解 mysql 4.1 的字符集问题该怎么处理了。

如果以前没有注意 mysql 4.1 的这个问题,直接 php 连接上就开始数据库存取,乍看中文处理也没有问题,但是实际上,存储的数据是 latin1 的,直接表现就是 mysqldump 获取的中文信息都是乱码。(悄悄说一句,目前邮件网关的 mysql 4.1 应该就有这个问题)

实际上,用合适的参数,mysqldump 还是可以把中文都导出来的。方法就是:
mysqldump --default-character-set=latin1 .....
另外我估计,用老版本的 mysqldump 连接 mysql 4.1 也应该不需要附加参数就能直接导出。

还有 mysqldump 新版本获取的 insert 语句都是多行数据放在一条 sql 内,看起来可能会有些头晕,其实增加一个参数就可以获得老版本的效果:
mysqldump --extended-insert=FALSE .....

在其它情况下如果导出中文还有问题,可以尝试运行:mysqldump --compatible=mysql323 or mysqldump --compatible=mysql40 看看,要么就是 --default-character-set、--compatible、--set-charset 的组合,总能解决问题。

FreeBSD regex 函数族处理多字节语言

今天一个问题让我们郁闷有半天,不够后来终于解决,幸好幸好

事情是这样的,我们在内部的开发机器和正式运营机器的操作系统并不一样,本来不以为意,心想难道 PHP 写的程序也会有移植性问题么??没想到还真就出了问题。

为了给用户一个简单的表现,我们使用了 apache 的 URL 重写模块,在内部运行的好好的,到了外部就发现英文的路径名可以可以正确重写,但中文路径名(编码为 UTF-8)就无法正确重写了。。。于是开始 google,没有任何抱怨过这个问题,只好自己试图解决。

首先选择了另外一台一模一样的系统,一模一样的配置编译命令,一模一样的重写规则。奇怪,可以使用啊,仔细对比两台机器有什么不同,发现原来新系统没有加入 libphp4.so 的支持,于是在问题机器上去掉了它,果然能正确重写中文路径了,可是总不能没有 PHP 吧。于是在原有搜索关键字基础上加上 PHP 继续 google。。。还是一无所获。

后来实在没辙,已经通知天津的同事杀往机房去重装操作系统了,鄙人灵机一动,在 apachectl 脚本里面加了一行 export LANG=zh_CN.UTF-8 果然就全部正常了

继续刨根究底,为什么是这样子的呢?看 apache 代码,它的 URL 重写规则是通过操作系统的 regex 函数族来匹配的,man regex,一行行仔细的看,发现一个宏 REG_ILLSEQ,解释是 illegal byte sequence (bad multibyte character)。哈,熟悉 iconv 的人看到这里应该就明白在 regex 函数内部对字符串会做编码上的校验。那为什么其它系统没有这样的问题呢????google 一下 REG_ILLSEQ,结果不多,顺藤摸瓜上去,原来这个特性是从 FreeBSD 5.3 才开始引入的。

到了这一步已经基本清楚了,如果你在用 FreeBSD 5.3 及其以上版本的,如果你的 apache 重写规则对中文编码不生效,那么就用我们的方法看看吧。不过还没有深入到实质部分,为什么 apache 这个特性又和 php 模块相关呢??谁有空就仔细研究吧

学习了一下 DomainKeys

http://antispam.yahoo.com/domainkeys
http://www.ietf.org/internet-drafts/draft-delany-domainkeys-base-02.txt

DomainKeys 简单说是一个邮件发送的认证框架——管理员生成一对或多对公密钥,然后把公钥通过 DNS 发布出去,所有外发信件则使用密钥签名并把签名写在信头部分;信件接受方则从邮件头部解出签名,并通过 DNS 取得该域的公钥,然后就可以校验信件是否被合法的系统所发送出来的。

internet-draft 回答了如下几个问题:

1. 对于一封 email,怎么来判断它是属于哪个 domain 签名并发送的呢?
2. 怎么取得一个发送域的公钥?
3. 在邮件的传递过程中,信件内容可能会被修改,那么哪些内容是被签名的并怎么保证在传输过程中没有被改动呢?
4. 签名信息怎么在信件中表示?
5. 如果签名不合法,那接受方如何处理?
6. 最后,如果一封信件被认证了,how is that result conveyed to participating UAs?

技术要点:

1. 判断发送者
 合法的信件头部必须包括 "From:" 行
 合法的信件头部也许会包括 "Sender:" 行
 如果信件即包括 "From:" 又包括 "Sender:",那么 "Sender:" 是发送者
 如果信件包括 "From:" 但不包括 "Sender:",那么 "From:" 里面的第一个地址是发送者
 如果信件不包括 "From:",那么这封信肯定不可能是 DomainKey 签名过的(包括有 "Sender:" 而无 "From:" 的情况)

2. 获得该域名发布的公钥
 草案提议使用 "_domainkey." 前缀存储公钥。比如发送域是 "example.net",那么公钥存储在 "_domainkey.example.net" NameSpace 内.
 为了支持多个并行使用的公钥,DNS namespace 可能被分隔出 "selectors",比如:"default._domainkey.example.net" 或 "2005.rhv._domainkey.example.net"
 PKI 体系中目前并没有一个在 DNS 上存储公钥的标准方法,目前采取把公钥的 PEM 格式存储在 DNS 的 TXT 记录中。格式是以分号分隔的 tag=value 对,例如:brisbane._domainkey IN TXT "g=; k=rsa; p=MHww ... IDAQAB"
 校验者必须能支持 512, 768, 1024, 1536 和 2048 位长的公钥,签名者只要支持以上一种长度公钥即可
 当前合法的 tag 包括:
  g = 如果 value 长度非零,那么必须严格符合发送者地址的 local part 部分
  k = 公钥类型,签名者和校验者都必须至少支持 rsa
  n =
  p = 公钥信息. Base64 编码
  t =
 由于部分 DNS 服务处理大的 TXT 记录有问题,所以 TXT 记录应该尽可能小。对于 512 字节 DNS UDP 响应包而言,公钥最大也只能 2048 位。
 通常公钥长度不要超过 1024 位

3. 签名的存储
 以 "DomainKey-Signature:" 存储在邮件头部
 包括以分号分隔的 tag=vaule 对,例如:"DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=beta; d=gmail.com; h=...; b=ot...I="
 当前合法的 tag 包括:
  a = 签名算法,缺省是 "rsa-sha1",签名者和校验者都必须支持
  b = 签名数据,有可能因为格式而插入 Whitespace
  c = 规范化,准备签名的数据是怎么被表示的,校验者必须支持 "simple" 和 "nofws",签名者只需要支持一种
  d = 域名,用于和 selector tag 联合得到公钥
  h = 邮件头部用于签名的 field name
  q = 用于取得公钥的方法,目前只有 "dns" 是合法的
  s = selector。这里的例子就是 beta._domainkey.gmail.com 做公钥发布

4. simple 规范化方法
 email 的每一行数据都按顺序组织起来签名
 如果有 "h" tag 的话,那么只有处于 value 中的头部才被用于签名
 body 的每一行都被用于签名
 删除每一行的逻辑结束符
 然后每一行再增加 CRLF
 所有的尾部空行被忽略,用于分隔 header/body 的空行需要被包括

5. nofws 规范化方法,基本符合 simple,就是两个地方不一样:除去所有的 folding white space 和头部续行的处理
 email 的每一行数据都按顺序组织起来签名
 头部续行被处理成一行
 如果有 "h" tag 的话,那么只有处于 value 中的头部才被用于签名
 body 的每一行都被用于签名
 删除 email 中每一行的 folding white space,RFC2822 对之有定义,十进制值是 9 (HTAB), 10 (NL), 13 (CR) and 32 (SP)
 然后每一行再增加 CRLF
 所有的尾部空行被忽略,用于分隔 header/body 的空行需要被包括

选择 SSL 证书提供商

就我所见,国内绝大部分的网站是没有提供 https 加密传输的,实在和中国互联网发展水平不相称。这里把我这次购买 SSL 证书的经验和体会在这里公布一下,希望对需要的人能有所帮助。

首先要明确一个观念,比起托管费/流量费/硬件折旧费用,SSL 证书相当便宜,eYou 买的 Thawte 的证书已经算是最顶级的品牌了,也只需要 99$/年,而我见过的网上最便宜的证书用 28$/年就能搞定。所以提供 SSL 加密链接绝对是互联网企业应该立即动手去做的事情。

通常来说,一个网站只需要一台服务器(就是说一个域名)拥有一个证书就足够了。针对这样的需求,可供挑选的产品包括:
1. verisign 的 Secure Site,一年报价 349$
2. thawte 的 SSL123,一年报价 149$
3. geotrust 的 QuickSSL,一年报价 169$
4. RapidSSL,一年报价 49$
5. InstantSSL,一年报价 49$
6. TurboSSL,这个最便宜,网上没有超过 30$ 的,如果你在运营一个 opensource 项目,甚至可以送一年证书

这么多产品,我们怎么来挑选呢?请继续看我介绍:

如果希望用户能够无障碍的使用 SSL 加密链接,要求你的证书是被浏览器所安装的根证书所签发,这样才不会蹦出错误提示对话框。这样就有了一个小小的门槛:你是否需要支持 IE5.0 用户?

IE 5.0是 Win98SE 的缺省浏览器,虽然已经有7年的历史,但使用该款浏览器的人在中国仍然占据一部分相当的比例,坏消息随之而来,IE 5.0 预埋的根证书只有非常少的商业机构,它们是 VeriSign/Thawte/CyberTrust。CyberTrust 现在通过 comodo 签发证书,而 InstantSSL 是 comodo 的一款产品.

简单总结,如果你希望支持 IE 5.0,那么有三个选择:VeriSign/Thawte/InstantSSL。那么为什么 eYou 没有考虑最便宜的 InstantSSL 呢?

因为 InstantSSL 是通过所谓"证书链"的方式来签发证书的,就是说它用浏览器预埋的 CyberTrust 签发了证书 A,再用这个证书 A 签发证书 B 卖给你。这样你在配置服务器的时候就必须也配置这个中间证书 A。传输数据的时候为了证明自己的合法性,也必须把证书 A 的一些信息交给浏览器。浏览器的运算步骤比平常会多一步(SSL 3.0中定义),首先验证证书 A,再验证证书 B。这种证书链方式有两个问题,其中之一可以参考 RapidSSL 网站上的攻击,另一个问题是要冒着 Mac 平台上无法使用的风险

本着少冒险也少花钱的想法,我们选择了 Thawte

自从 IE 5.01 开始(内置在 Win2000 中),IE 内置的根证书就很多了,对于新兴网站来说,的确有很大的理由抛弃该浏览器之前的 Win98SE 用户。我强烈建议这类网站选择 RapidSSL,TurboSSL 的问题是它也是证书链方式,但真没有必要为了省 200 块钱而给未来惹麻烦。RapidSSL 还有一个好处就是它能申请一个试用证书,而且这个试用证书是真正可用的,经过合法CA签发的,而不是 verisign/thawte 那样假模假样的用一个 TEST CA 糊弄申请者。而且 RapidSSL 正在打折提供 Wildcard SSL,只需要 199$元/年,好便宜啊。

如果要省钱,比如个人用户,那么可以试试 30$ 以下的 TurboSSL

另外要说的就是 VeriSign 买了 Thawte,GeoTrust 买了 RapidSSL。其实所有证书的可信度是一样的,只不过不同品牌不同价格而已。GeoTrust 宣称它是全球第二大 SSL 证书签发机构,仅次于 VeriSign

RapidSSL 有中文语音服务,帮助你完成申请过程。而 Thawte 的服务真是不错,这是一个南非公司,里面居然有中国人,我购买以后有些小小的麻烦,先是通过 24x5 的 chatroom 和技术支持人员交流,然后就打了电话到我们办公室沟通,实在不错。

最后是购买 Thawte SSL123 的技巧,从这个链接的购买价格只有 99$,不过需要你填写更多的隐私信息,看你怎么选择了。

strptime 无法处理时区

邮件 RFC822 的头部字段 Date 里面的格式状如:Wed, 10 Aug 2005 15:23:29 +0800
这个字符串可以通过 strftime 来产生,或者通过 strptime 解析. 今天刚刚发现 "+0800" 这样的时区信息是无法用 strptime 解析的,FreeBSD/Linux 都是如此,测试程序如下:
  1. #include
  2.  
  3. int main()
  4. {
  5. struct tm result;
  6. time_t now;
  7. char buf[256];
  8. char *p;
  9. now = time(NULL);
  10. localtime_r(&now, &result);
  11. strftime(buf, 256, "%a, %d %b %Y %T %z", &result);
  12. printf("%s\n", buf);
  13. p = strptime(buf, "%a, %d %b %Y %T", &result);
  14. printf("%s\n", p);
  15. p = strptime(buf, "%a, %d %b %Y %T %z", &result);
  16. if (p) {
  17. printf("%s\n", p);
  18. } else {
  19. printf("null\n", p);
  20. }
  21. }

最要命的是,只要在 linux 下试图去解析 %z 会导致返回 NULL,而不是手册里面说的,"从左到右匹配合法的格式,碰到无法匹配的就返回该处地址;如果一开始就匹配失败则返回 NULL"。以前程序在 FreeBSD 上跑的很好,放到 linux 上就出现了问题,查了半天错误。鄙视一下 glibc

研究了一下邮件线索的实现

GMail 的邮件线索排列是 WebMail 的一个革命性的变化。那邮件线索是怎么实现的呢?参阅蓝宝书从第20页开始的文章,通过 Message-ID, In-Reply-To, References 这三个字段即可实现。

按照蓝宝书的说法,Message-ID 应该由 MUA 或者途经的第一个 MTA 产生。我的同事 wanbin 发现,gmail 是在 MUA 端实现的 Message-ID,就是说,看'已发送信件'的信头部分,就能看到 Message-ID 的出现;当然它这么做就是为了有助于邮件线索显示。其他 WebMail 厂商,比如 yahoo 则是在 MTA 端产生的。

研究中还发现了 eYou-MTA 的一个问题,就是 Message-ID 的产生做的不够细致。应该是在插入 Message-ID 之前检查信头,如果不存在 Message-ID 才新增,如果已经存在 Message-ID 就忽略。检查了一下 qmail 的代码,qmail 是仅在 qmail-inject 的时候才插入 Message-ID(newfield_msgidmake函数),甚至它还处理了 Resent-Message-ID 头部。

下面计划是一方面在 MUA 端实现 Message-ID 的产生, 另外修改 MTA 中处理 Message-ID 的逻辑。性能是够好了,但细微之处需要学习的地方还很多。

在 IBM T2x 上启动红外支持

我用的是 debian testing. 2.6 内核应该都一样.

小黑是 T23, 以前在 T20 上配置也成功过.

用 apt 安装了 irda-utils.

内核的配置为在 /etc/modules.conf 里面增加

options nsc-ircc dongle_id=0x09
alias irda0 nsc-ircc

加载模块后应该能看到 dmesg

nsc-ircc, Found chip at base=0x02e
nsc-ircc, driver loaded (Dag Brattli)
IrDA: Registered device irda0
nsc-ircc, Using dongle: IBM31T1100 or Temic TFDS6000/TFDS6500

按照 irattach manual 里面的说法:执行 irattach irda0 -s 就可以启动 irda 了.
但我安装的 irda-utils 包在 init.d 中自动执行的命令为 irattach irda0 none -s

现在可以打开你的红外设备测试了,执行 irdadump 能看到设备通信的信息

FreeBSD 5.4 安装碰到的一些问题

计划把一台开发机从 FreeBSD 4.7 升级到 5.4,周五下班后,升级开始,噩梦也随之而来。

插入安装盘,启动,Upgrade 菜单,保留所有目录,然后开始升级
。。。系统提示:磁盘满,备份根目录失败

没有关系,只保留 /home 目录,其它所有目录重新 newfs
。。。麻烦来了:"error code 36"

???? 重试,还是 error code 36,此时已经无法直接从硬盘启动了
上 google 搜索,不得要领

重试多次之后,发现每次进入分区菜单,都会报告一个磁盘物理信息不匹配的警告。
抱着一丝侥幸心理,照着警告信息里面的提示去 BIOS 里面修改磁盘信息
(郑重提示,碰到这种情况千万不要这么干!!!)

重启以后,傻眼了
以前的情况是这块 80G 的硬盘分一个 slice,里面是 2G 的根,
256M 的 SWAP,2G 的 var,4G 的 usr,剩下的全部是 home。
现在变成 2 个 15G 的 slice,剩下的是 free space,
在第一个 slice 里面,根/SWAP/var/usr 都还在,但是最重要的 home 没有了!

当时天色已晚,只好暂时忽略这块硬盘,赶紧换块硬盘安装,别误了周一的工作。
可换了硬盘后还是"error code 36"!*(%$^%$@^%#^$#@

重试几次,居然有一次能 newfs 通过,不过安装速度极为缓慢,切换到 log 输出的终端
(友情提示,安装中碰到不明白的问题一定首先这么观察一下!!!)
发现很多这样的信息:WARNING - WRITE_DMA UDMA ICRC error (retrying request)....

打电话求救,jasper 说需关掉 ACPI。修改 BIOS,不成;进入系统的时候选择内核禁止,
也不成。于是又重新在 BIOS 里面打开 ACPI。

google 之,说在 /boot/loader.conf 中配置 hw.ata.ata_dma="0" 就可以。
于是进入提示模式,执行 set hw.ata.ata_dma=0,然后 boot,再安装,一切顺利!
(后来想,是不是直接在 BIOS 里面 disable DMA 也可以呢??)

安装成功后,配置网络,兴奋中忘了测试是否可 ping 通,装上机箱上架。
启动后接上网线,不通。只好再次下架拆机。SHIT,这个周五的晚上过得糟透了。

console 报告 "watchdog timeout",网上说这种情况应该 "switched 'PnP BIOS' to NO"
可配置后依然不起作用。

绝望中在 BIOS 里面关掉了 ACPI,居然网络通了。呵呵,顿时有种大赦的感觉...

+++++++++++++++++++++++++++++++++++++++++++

后记:

周一来到公司,关于 home 的灾情报告邮件如雪片一般飞来,大伙强烈要求尽快恢复 home。由于FreeBSD 独特的 slice/label 机制,使得普通的恢复软件无法检测到丢失 /home 的 label 信息。无奈只能手工重写 slace 的分区信息。

其实运气还是挺好的,因为是知道原来的 slice 占据了整个硬盘,而且丢失的仅仅是最后一整块连续空间。

1. 首先进入 sysinstall,模拟正常安装,把 2 个错误的 slice 全部删掉,再选择"使用所有空间",记下这种情况下的 slice 所使用的起始和结束扇区信息。

2. 千万不能写入,而是 Undo 所有修改,退出。

3. 用 TestDisk 这个工具,清除所有分区信息,按照上面记录下来的扇区信息手工创建新的分区(就是 slice)

4. 参考另一块硬盘的 label 数据分布信息,用 bsdlabel(disklabel)这个程序最终恢复出了 home

当然,上述整个过程中还离不开 bc 的计算

发现 mysql 可以自动处理 master/slave 连接

在 master/slave 环境里面,应该是写操作去 master,读操作去master/slave任意一台机器。但是从来没有见过有对 master/slave 环境封装的 PHP 操作库,一直对这个问题耿耿于怀,难道从来没有人想过解决这个问题么?结果发现原来还是因为我们不够了解 mysql,原来 mysql 已经内置了对 master/slave 连接的处理...

发现该知识点的步骤如下:
1. 搜索 php master slave. 最后发现 php5 的 mysqli 函数族里面包括对 master/slave 的处理
2. 怀着崇敬之情看了看 php5 的 ext/mysqli 的代码,发现有一堆奇怪的函数从来没有见过,比如 mysql_master_query, mysql_disable_reads_from_master 之类. 而且它们在 mysqli 目录没有任何定义,看来来自 libmysqlclient
3. 因为知道 mysqli 需要 mysql41,所以去检查 4.1 的代码,结果在 libmysql/libmysql.c 里面找到了这些函数的定义,以及 mysql_set_master、mysql_add_slave 这样的函数!同时在 include/mysql.h 看到 MYSQL 结构体里面包括 master、next_slave、last_used_slave 这样的指针
4. 进一步检查,发现 mysql 在连接的时候能通过 "SHOW SLAVE STATUS" 命令自动配置 master/slave 环境。不过我试了一下,发现我的 MASTER 没有报告任何信息,检查手册,说是要启动 master 的时候加入--report-host=slave_name参数
5. 检查了 4.0 和 3.23 的代码,发现这个特性是 4.0 开始引入的。但是 4.0 缺省是打开这段功能的,在 4.1 则用了

#ifndef TO_BE_DELETED
if (mysql->options.rpl_probe && mysql_rpl_probe(mysql))
goto error;
#endif

来决定该功能是否起作用。缺省好像 TO_BE_DELETED 的确是没有定义的.. 但不知道这个特性什么时候会被修改....

总结:自动 MySQL 4.0 以后,无需程序作任何修改,应该就可以同时访问 master/slave 来均衡负载。不过没有降低连接数目,很可能是每个 mysql link 会导致多个 TCP 连接——每个 server 一个,这样在高并发的情况下会导致连接数不足的问题,尽管负载并不高,可能是因为这个缺陷,mysql 在 4.1 有些拿不定主意。

其它信息:关于 MySQL 的 connection pool,有一个开源的解决方案:SQL Relay,可能是可用度最好的开源方案。但它目前还不支持 master/slave 的分担,好消息是作者说已经提高了实现该特性的优先级。

什么是 APN、CMWAP、CMNET

在用手机设置 GPRS 上网的过程中,需要配置一下"接入点",即所谓 APN (access point name).

照我的理解,所谓配置 APN 就是选择一个"接入服务器",GPRS 拨号到接入服务器后,它检查一下你的 SIM 卡是否有权限拨入,然后从它管理的 IP Pool 里面分配一个地址给你这个连接,然后就可以上网了。基本上和 ppp 拨号是一致的,包括计费我猜也是各个"接入服务器"把计费信息传递给 radius 去计费。

目前我们可以公开使用的 APN 只有两个,CMWAP、CMNET。很明显,它们所使用的 radius 的计费策略是不一样的,CMWAP 使用的是包月计费(有的地区可能还不是包月),CMNET 用的是流量计费,0.03/k。传说有的同事就是因为不明白这两个 APN 的区别遭到了惨痛损失。

显然 CMWAP 和 CMNET 的服务质量是有本质区别的。CMWAP 拨号分配的 IP 只能访问一个 IP 地址的一个端口(当然理论上拨入的 CMWAP 手机之间可以互相通信),CMWAP 必须通过这个 proxy 才能访问 internet;而 CMNET 接入后就可以访问 Internet 上的任何资源了。
附带说一句:今天刚刚听到一个消息,就是中国移动会为 PushMail 业务单独开一个 CMMAIL 的 APN,我想接入这个 APN 后大概只能访问在中国移动那里登记的邮件服务器的 SMTP/POP3 等很少的端口,但会有单独的计费政策。

由于 CMWAP 实在太便宜了,所以中国移动就得防着"不法分子"通过这个 proxy 钻空子。比如这个 Proxy 只能支持 HTTP 协议,再比如这个 Proxy 会检查 HTTP Header,发现 Agent 是 MSIE or Mozilla 就禁止连接。

我上上周做了一个测试,在笔记本上插一个支持 GPRS 的 PCCard,拨入 CMWAP,然后配置 firefox 的 proxy 为 CMWAP 的代理服务器,安装一个可以修改 Agent 信息的插件。访问 google 没有问题,访问 mail.eyou.com 首页也没有问题,访问 link.eyou.com...麻烦来了,它不知道为什么,擅自把 UTF-8 页面当作 ISO-8859-1 又转换一遍 UTF-8,弄得页面无法正常显示,浏览 team.eyou.com 也是有同样的问题。
由于我并不是想真的移动上网,所以没有深入研究下去,如果有谁了解这个东东的细节,希望多多赐教。不过我觉得理论上可以开发出一套服务器和客户端的程序,把所有的 IP 包都封装成一个 HTTP tunnel,加上 image/jpeg 这样类似的 Header 传输,这样移动以为客户在下载彩信,实际上我是在笔记本上用 putty 去连接 SSH..真的会有这样的软件和服务出来么?? 现在的 0.03/k 太贵了,比当年的 CERNET 访问国外还要贵三倍!

好像有的手机上的 QQ java 客户端就是把 QQ 协议封装在 HTTP 上传输,这样动感地带的小年轻们就可以很便宜的随时用 QQ 聊天了。

发现 FreeBSD 缺省一个进程只能 malloc 大约 500M 的内存

不论怎么设置 ulimit 都只能用 512M.

1. 的确很安全,防止恶意进程
2. 可能你真的需要单一进程使用非常大非常大的空间,那么就参考 MySQL 的设置,修改 /boot/loader.conf 然后重启机器,可能重新编译内核也可以.

转贴一篇邮件列表上的文章

对安恒公司的刘世伟佩服的五体投地. 虽然我们公司也用 samba 做 PDC,但用法比人家差远了,比如备份的方案,说穿了很简单,但是就是没有想到去做..

今天在QQ上跟朋友聊了很多,整理一下,发上来。

2006-01-08 16:51:45 !刘世伟
我的域服务器上面有每天一次的完整备份,可以回溯到那一天的状态,
当然我从域服务器上很容易就找回来某个文档在某一天的状态。

2006-01-08 16:53:24 梅州客家人
这个怎么做的?

2006-01-08 16:53:33 !刘世伟
linux做的。

2006-01-08 16:53:48 梅州客家人
在2K下有没有办法?

2006-01-08 16:54:21 !刘世伟
没有可能,2k下需要大量的磁盘。

2006-01-08 16:54:02 !刘世伟
备份目录,按年,月,日自动开3级目录,每个目录底部是整个的一个需要备份的目录树。

2006-01-08 16:54:20 eastos
linux做域服务器?

2006-01-08 16:54:57 !刘世伟
linux可以把多个相同的文件做硬联接。

2006-01-08 16:55:42 !刘世伟
这样,我们的整个需要备份的目录树体积是300G, 但每天变化的文件大约200M.
备份时,我们还忽略掉大于50M的文件,以及avi,mp3,iso,rm,其实,我们用了一个160G的硬盘,可以做60天的每日完整备份。

2006-01-08 16:56:31 !刘世伟
我们公司用linux做域服务器2年多了。

2006-01-08 16:57:03 eastos
你是指用Linux代替DC?

2006-01-08 16:57:19 !刘世伟
BDC PDC都可以代替,

2006-01-08 16:57:38 !刘世伟
我主要看中的是域的帐号的管理和帐号漫游。

2006-01-08 16:58:28 eastos
你用的哪个Linux的发行版

2006-01-08 16:58:39 !刘世伟
都可以。没有区别,我用debian。

2006-01-08 16:59:51 !刘世伟
linux做的域服务器,跟一般的域服务器的灵活的地方:
1.域设置是文本文件,容易移植。
2.针对不同的客户端ip可以inclide不同的设置。
3.针对不同的用户和组,可以有不同的域服务器设置
4.同一个共享名, 对不同的用户,可以指向不同的目录。

2006-01-08 17:00:44 eastos
像是组策略之类的怎么处理呢
2006-01-08 17:01:39 !刘世伟
组策略有,但是我没有用到,也就没有研究它。

2006-01-08 17:02:52 eastos
你是说\\server\share对A用户可以指向/usr/a, 对B用户可以指向/usr/b ?

2006-01-08 17:03:13 !刘世伟
对,你说的对。
比如,同样是mydocs的共享名,可以指向 /home/$g/$U
这样,财务组的人a上来,就是 /home/caiwuzu/a

2006-01-08 17:03:33 eastos
good, 真的是好东西
还有个问题, 虚拟的PDC和Windows的DC互操作性怎样?

2006-01-08 17:04:28 !刘世伟
BDC可以通过PDC进行用户身份认证
然后BDC可以自动在自己的机器上面建立这个linux下的系统帐号

2006-01-08 17:05:14 eastos
就是说可以同步用户帐号

2006-01-08 17:05:33 !刘世伟
在windows下的帐号,在linux下都有一个对应帐号,代表它在linux下的文件的权限。

windows的用户,可以在自己的windows的修改密码界面上面修改linux下用户的密码。

这样。我在linux下,还开放了ftp,跟用户的域帐号是统一的。

2006-01-08 17:06:50 eastos
哪这样搞就不用买MS的Windows Server了 #C

2006-01-08 17:07:03 !刘世伟
当然了。
我没发现有什么需要还要用windows的server

我们的打印机也是在linux的域上面集中的存放。

小公司,做一个域服务器,挺好。

我们的域服务器,可以无盘启动,每台机器的c:都有一个ghost镜像。我们的网管,
只要会ghost以及会把机器加入域,就行了。
这个模式,应该适合小公司。

linux的域服务器+无盘启动+ghost
然后linux的域服务器,可以用于文件存放。我们的机器上面都用raid.

2006-01-08 17:14:32 eastos
你们的客户端一般是什么系统, XP? 还有没有98的

2006-01-08 17:15:00 !刘世伟
xp或者win2000,不用98

公司的网关,是一台透明代理,自动的走http_proxy

在http_proxy上面,我设置过滤了所有的cab文件。
除了微软的升级和swf播放
顺手还过滤了mp3和rm和avi

垃圾邮件还不是很多,我们都跟员工说,不要向无关的人展示自己的邮件地址,
不要到论坛提交自己的电子邮件地址。
如其影响到客户的信件交流,多看几封垃圾邮件还是值得的,

具体是这样部署的:
默认封闭所有的联接。
只开放网关发起的对外的80,
在网关,架设http_proxy,
所有流经网关的80留量,自动灌到http_proxy
这样的功能。win下是没有的。
一方面,http_proxy加速访问。
另一方面,我可以留url访问日志。
再一方面,http_proxy在应用层进行url过滤。比路由器在ip层解码分析过滤要少占用cpu

这是http
对于dns,
我在网关架设了dns服务器,然后将所有流经本机的53的udp访问,灌到本机的dns,

这实现了,
1.dns加速。说真的,接入商的dns服务器太忙。
2.封QQ,因为QQ聪明的利用udp 53 ,靠封QQ的服务器ip的方式真的不行。
3.纠错,即使同事们设置了一个错误的dns地址,也可以用。

说了dns和http还有pop3 和 smtp
我们是把pop3和smtp留量强制灌到公司的服务器。

这样,即使他们设了个1.1.1.1的pop3收信地址,也保证可以收到自己的邮件。
其实这样挺省事。 大家从来不因为dns,pop3,smtp的故障来烦我。

流量转移用的是iptables的REDIRECT

然后作为加密。我从网关到外网服务器架设了一个openvpn,我们的pop3,smtp就都走了加密通道了。

这样一个防护,对于一个公司来说,已经可以够用了。
如果有特殊需要, 可以特殊的来开端口。
实际已经是一种网闸的模式了,就是所有的流量不直接跟外届交流,要通过http_proxy或者dns服务器在应用层进行中转。

倒是有些同事,在公司外面收不了信, 因为它的pop3设置是错误的,在公司内部,网关会自动纠错,在家就不行了。

关于软件开发环境和运行环境的备忘

1. 看到 OpenLDAP 邮件列表上提到 2.3.17 解决了一个可能导致 slapd 99% CPU 什么都不工作的问题,和 Linux Kernel 2.6 + 多线程环境有关。
2. 由此引发我的考虑就是是不是应该针对不同的环境出不同的产品,这样对效率和稳定性都有好处。

后来在网上找到一篇文章,描述二进制兼容性的,强烈建议阅读:
发行版迁移及二进制兼容性注意事项

结合 eYou 的软件发布简单总结一下这篇文档:

a. 我们安装的 Linux 产品中,主流是两套平台
glibc 2.2.x、Linux Thread 模型、libstdc++ 2.9x 平台。说白了就只是 RedHat 7.3,可能有用户安装使用的是 RedHat 7.2 或 RedHat 8.0。以后简称 Linux Platform 1。
glibc 2.3.x、NTPL 模型、libstdc++ 3.x 平台。从 RedHat 9 以后的 RedHat/Fedora/CentOS 产品就都是这个平台的了。该平台按照 Kernel 2.4 和 Kernel 2.6 又可以分为两个部分。以后简称 Linux Platform 2(根据内核不同可以分别称为 LP2.0 和 LP2.1)。
预计未来随着 gcc 4.x 的 c++ ABI 调整,06 年将出现 kernel 2.6 + g++4.x 的 Linux Platform 3。不过估计要到 07 年才会成熟。
Linux 标准化团体为 LSB 已经做了很多的工作,但偶觉得并不符合我们目前的情况,还是自己土法定义平台吧。

b. 虽然我们的产品没有使用 thread 和 c++,但是依赖的第三方库,尤其是 openldap,mysql 的性能、稳定性表现是和它们相关的。按上述文档里面的说法,上述第二个平台虽然涵盖了非常多的操作系统,但是 ABI 是可以保证兼容的。尤其是 RHEL3 和 RHEL 4 之间尽管跨越了一个 kernel 版本升级,但是 RedHat 尽力保证为 RHEL 3 开发的程序无需重新编译就可以在 RHEL 4 上运行。

我们现有的产品并没有涉及到线程和c++的部分,因此计划仅仅针对 base 包做两个(甚至三个)不同的 package。

技术支持可以按照上述 LP1、LP2 的划分方法将其它 Distribution 比如 Debian/SuSE/RedFlag/TurboLinux 加入我们的产品支持目标。研发部门尽力保证产品binary是一个独立的无特殊依赖的安装包。

4.1.x 继续维持对 Linux Platform 1 的支持(如果算上 RedHat 7.2 的话,我们对这个平台已经支持了快 4 年半了)。但在 06 年将完成该分支的历史使命。

下一个主流版本——暂时命名为 4.1.9x,将不考虑 LP1 的官方支持。开发平台是 AS3 或 CentOS3,这样开发出来的产品应该最古老是可以支持到 RedHat 9 的。

最后,从我个人的角度,强烈建议大家给客户配置 LP2.1,即 AS4/CentOS4 这样的操作系统。

最近两天在 Solaris 上的工作

最近两天在 Solaris 上的工作
由 qyb 在 周四, 2006-01-19 12:13 提交 eyou.com

至少有三年没有接触 SUN 的图形终端了,突然发现自己还记得 STOP-A,用键盘来 Power ON,能准确记得 admintool、defaultrouter、nsswitch.conf 这些文件名,实在是不得不佩服自己..

闲话少说,技术支持部一直对我们发布的 Solaris-SPARC 二进制版本不满意,因为缺乏很多动态链接库依赖,比如 ncurses、libg++ 之类...这两天的工作就是看看怎么解决这些问题

*系统配置篇

手头这台是 Solaris 8,机器到我手里的时候发现上面缺最基本的开发环境,比如没有系统头文件,没有 ar 这样的工具,只是安装了一个 gcc,真不知道安装者怎么想的.

最方便的方法可能是重新安装了事,但不能确认 SOHO 的 Solaris 安装光盘是否能正常工作,而且也觉得太慢。于是把软件光盘上的所有 pkgmap 文件定向到一个单独的文件里,发现缺什么文件就去找对应的包。恰好所有正常开发所依赖的包都不能在原机上读出(因此无法用 admintool 管理),只好在我的笔记本上读出来,打包一个 tar(7-zip就是好),ftp 过去,解开,执行 "pkgadd -d . 目录名" 就直接安装上啦.

还发现 bash3.0 作为用户 shell 不支持 Ctrl-C 中断当前操作,上网查资料,发现将 /etc/profile 里面的trap "" 2 3 那行分成两行trap 2和trap 3就可以了

*准备开发环境

其实也就是安装从 sunfreeware 下载的 automake、cvs、make 等工具,但有一个核心问题要注意,就是为了保证软件编译不会自动寻找、链接非系统库,因此绝对不能安装第三方函数库。比如 libncurses 和 libz。因此也导致了不能使用 sunfreeware 预编译好的 vim 和 gdb,只能自己来重新编译了.

还有一个问题就是如何避免编译出来的程序链接 libg++、libstdc++,最简单的办法就是编译 gcc 的时候禁止产生共享库,于是又重新编译了 gcc 3.2.3,编译参数为:configure --disable-shared --disable-nls --with-as=/usr/ccs/bin/as --with-ld=/usr/ccs/bin/ld --enable-languages=c,c++

最后 vim、gdb、gcc 都参照 sunfreeware 上的说明重新制作了 pkg,gcc 现在压缩后只有 15M,比 sunfreeware 的巨无霸好多了.

*结果

重新增加了一个 contrib 目录,产品所需要的 libz、libgdbm、libiconv 就放在这个目录下

专门写一个脚本检查动态库依赖:

for i in `find /var/eyou -exec file {} \; | grep ELF | sed -e "s/://" | awk '{print $1}'`; \
do \
echo $i; \
ldd $i; \
echo; \
done

UltraSPARC

简单测试了一下手头这台 220R 的性能,仅仅是测试编译软件的速度..先写结论,最后是测试数据.

1. 双 UltraSPARC-II 450 的能力比单颗 P-III 550 稍强, 有些让人失望, 显然 4M 的二级 cache 对编译这样的任务而言没有什么帮助

2. 双 UltraSPARC-II 450 性能是单颗 UltraSPARC-IIi 440 的两倍. Solaris/UltraSPARC 的 SMP 果然很牛,完全符合线形增长.

3. PIII Mobile 1.2G(使用了 gcc 4.0.2) 的编译速度是 PIII 550 的近三倍,是 P4 2.4G 的 2/3,完全不符合一贯的认知规律(别忘了笔记本的硬盘速度肯定是最糟糕的),也不太象是 Linux Kernel 2.6 的功劳...唯一的解释是 gcc 4.x 效率比 gcc 3.x 有了大幅度提升.

4. 个人感觉 UltraSPARC 家族最好的使用场所是高性能计算。在 97 年的时候就有这种 32/64 位计算通吃的价格也不高的 CPU 实在是奇迹..想想 AMD64 是什么时候才出现的,Intel 就更不用说了。而且借助于其架构和 Solaris 操作系统,可伸缩性scalibility 相当优良(E10000好像最高能高达 64 颗 CPU),对于那种计划逐步升级其计算能力的机构来说非常合适。
但是说回到购买 SUN 用于做服务器的单位,感觉十分不值得,除非它真的需要 Solaris 上的专用软件,或者计划没事升级 CPU,否则不如购买 Intel 高档服务器装 Linux/FreeBSD。不过从另外一个角度,当时的年代,Linux/FreeBSD 确实也不怎么样,呵呵,文档、社区、基础人才远远不如现在,买 SUN 的服务器也是不得已为之.
现在 UltraSPARC 家族已经发展到 IV 和 IV+,但看它主页仍然有 IIi 650 MHz 的型号在销售中.. 真是佩服啊,谁能想象 Intel 现在还在卖 PII?

测试方法,解开 mysql-4.1.16,configure 好,然后执行 `time make > /dev/null 2>&1`

220R: 2 x UltraSPARC-II 450 MHz, 1G, Sol8, gcc 3.2.3
`make -j 3` 编译.
real 30m54.166s
user 51m24.590s
sys 3m2.220s

Netra t1:UltraSPARC-IIi 440MHz,512M,Sol9, gcc 3.2.3
real 63m16.241s
user 56m18.570s
sys 4m22.140s

Intel P4 2.4GHz, 1G, RH AS3, gcc 3.2.3
real 8m59.867s
user 8m21.760s
sys 0m33.050s

T23:P-III Mobile 1.2G, Debain Testing, kernel 2.6 + gcc 4.0.2
real 12m40.583s
user 11m47.749s
sys 0m48.604s

Intel P3 550MHz, 256M, FreeBSD 5.4, gcc version 3.4.2
real 32m45.180s
user 30m3.798s
sys 2m28.779s

打开可读写的文件流...

今早同事问我一个 Solaris 上的故障,知道现象后立刻想起这个是很久以前我碰到的,当时也是百思不得其解,因为同样的代码在 Linux/FreeBSD 上没有问题,可是 Solaris 上就有错,最后检查 fopen 手册才知道 Solaris 是严格按 ANSI C 标准来做的,Linux/FreeBSD 则有意无意的纵然程序员犯这样的错误..

具体故障不说了,我们来看看这三个操作系统关于 fopen 的 man 吧.

FreeBSD
...
Reads and writes may be intermixed on read/write streams in any order, and do not require an intermediate seek as in previous versions of stdio. This is not portable to other systems, however; ANSI C requires that a file positioning function intervene between output and input, unless an input operation encounters end-of-file.
...

Linux
...
Reads and writes may be intermixed on read/write streams in any order. Note that ANSI C requires that a file positioning function intervene between output and input, unless an input operation encounters end-of-file. (If this condition is not met, then a read is allowed to return the result of writes other than the most recent.) Therefore it is good practice (and indeed sometimes necessary under Linux) to put an fseek or fgetpos operation between write and read operations on such a stream. This operation may be an apparent no-op (as in fseek(..., 0L, SEEK_CUR) called for its synchronizing side effect.
...

Solaris
...
When a file is opened with update mode (+ as the second or third character in the mode argument), both input and output may be performed on the associated stream. However, output must not be directly followed by input without an intervening call to fflush(3C) or to a file positioning function (fseek(3C), fsetpos(3C) or rewind(3C)), and input must not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.
...

一般来说大家以 r+ 这样的模式打开文件,是为了读出一段来,修改一部分内容,然后再写回去. 这样做肯定是要执行 fseek 的;但是如果循环的执行,就会发生在 fwrite 之后,紧接着 fread 的操作。在 Linux/FreeBSD 上操作系统会按照预想的完成,但是在Solaris上,接下去的 fread 返回的就不一定是什么内容了...

分支合并

Mozilla 是我的超级偶像,我对它的景仰甚至过于Linux Kernel Team——这个团队持续的在它的主页上公布 Roadmap、Branching Diagram、nightly build,为保证项目的网络协作开发了 Bugzilla、Tinderbox、Bonsai 这样的工具。简而言之,其网站是入门软件项目管理的极佳典范

今天在看它的 1.9 Trunk 1.8 Branch Plan 的时候,注意到:

Avoid requiring developers to merge and land changes in two places (trunk and a major branch)

* Merging means diverging, which implies conflicts
* Merging by hand is hard, errors are likely
* Developers may just forget to land in one place, or decide not to

从这段文字得到两个信息:即使强大如 Mozilla,也在为在不同分支上合并烦恼不已;第二,看起来 Mozilla 已经有解决方案.

马上在这里面寻找,原来是:

How to land changes once for both places

* We will extend CVS commitinfo and bonsai to automate synchronization of files between trunk and the 1.8 branch
* The synchronization can be specified for source repository subtrees such as browser, mail, xpfe, toolkit, extensions/inspector, etc.
* Exception lists per directory may be supported if necessary
* Developers are expected to test appropriately to avoid breaking trunk or 1.8 branch

虽然说这里 synchronization 是由提交者主动操作,或是 cvs server 自动完成(哪怕是由一个 cron 处理)还没有提及,碰到synchronization conflict 是否自动处理也未知,但看起来仍然是一个有意思的方向。于是我在 google 上搜索 "synchronization brunch trunk",结果发现一个叫 SVK 的东东

从介绍上来看它可以创建远程仓库的本地镜像,然后可以创建本地分支,最后合并回远程服务器。类似分布式仓库的概念,看起来也是通过用 perl 分析日志进行合并的。看起来比 eYou 的远程开发模式要好——通过一个 SSL 通道连接回公司内部的仓库做 check in/out,有功夫了可以参考参考.

SVK 本来是台湾兄弟起步的,现在看来是老外在维护,有个 blog 关于 SVK 的内容很多

关于亿邮的产品代号

起代号是技术人员拥有的乐趣之一,不管产品未来会因为市场噱头注册个什么NB名字,内部的设计者们可以先起个名字自得其乐一番。比如 Windows 95 内部叫 Chicago,Windows Vista 好几年来一直被称为 Longhorn.

不过很遗憾,亿邮内部产品只有 IPB 这一项是有过代号的。由于 IPB 第一个版本由于内核工作机制是由 Bridge 演化而来所以直接叫 Bridge. 后来主持开发第二个和第三个版本的 Ajian 决定按照字母顺序起名字,第二代叫做 Candy,第三代顺着糖果的意思叫做 delicious,第四代我自作主张起名叫 earthquake,来源是想到真侍魂里的地震,不过从那时候起内核应该就没有很大变化,不知道有没有最后叫做 haohmaru 的一天(大家会不会觉得我哈日??)

产品没有正式开发出来之前还是用代号比较好,否则说 5.0、412 这样的东西感觉不够响亮。如果内部会议上大吼一声 haohmaru,估计所有反对这个产品的人都立时有身中奥义旋风裂斩的感觉,^_^

到 SOHO 办公后,发现开发服务器机器名都是水果名,尤其是 papaya 这样的名字让我这种泡过 BBS 的人会心一笑,希望这个传统能够保持下去....严重鼓励.

最后要说的是,感觉用中文起产品代号怪怪的,比如“泰山”、“官渡”这样的地名?还是“天矶”、“太白”这样的天文名称,亦或是“蚩尤”、“共工” 这样的神话名?反正对于习惯英文代号的我需要好好一段时间适应。当然金山的“盘古”这名字真是不错,估计先是内部代号,后来成了产品正式名称,可惜这样的好名字没有一个好结局。

关于软件的数据结构变更

一篇我今天刚刚发出去的 email

我觉得贸然决定更改签名档的数据结构是很严重的错误.

如果现有数据存储无法满足需求,应该
1. 看看怎么绕过障碍
2. 试图增加新的结构
3. 最后才考虑更改数据存储结构.

凡是要"更改数据结构"才能解决问题的话,必须找 azhuang 和我协商这个问题。
如果这个问题实在避免不了,那么这个 feature 不能在现存的 4.x.x 出现。

到未来的 5.x 解决去。

这里再次重申一下软件(库)发布的版本号规律.

按照 major.minor.patch 这样的命名规则

major 升级意味着数据存储结构被更改(库的接口方式有修改,或者是被删除;依赖这个库的程序需要重写、编译)
minor 升级意味着增加新的数据存储方式,或者是程序运行结构有重大调整(库的接口被增加;依赖这个库的程序不需要重写或编译就可以运行)
patch 升级意味着 bugfix,或者是不那么重要的功能增强

当然严格来说,目前我们的 minor 定义还是有一些问题的.. 主要是最近版本变化太快,理想状态下 major 至少两年才升级一次,minor 至少半年才升级一次,patch 至少一个季度升级一次

将 jabberd2 集成进入已有的系统

在 jabberd 的上个版本 1.4.x 中,有一个 xdb_auth_pop3 模块,jabber 用户登录上来通过已有邮件系统的 pop3 协议进行认证,这样 IM/email 就可以共享同样的口令机制了.

但从 jabberd2 开始,分成了 authreg 和 storage 两个部分,似乎无法通过原有的 pop3 方式作口令认证了,要完成以前的集成,必须 patch 部分代码才可以。

本来觉得 jabberd2 很弱智,但最近发现 jabberd2 原来提供了一个认证方面更灵活的方案,就是 PIPE。通过 unix pipe,jabberd2 把认证/注册相关的信息提交给另外一个应用程序,该程序去处理请求后将结果通过 pipe 返回给 jabberd2.

jabberd2 源代码里面提供了一个 perl 写的例子:tools/pipe-auth.pl
相关文档可以参考 c2s-pipe-authenticator

依赖这种方式可以很容易的写一个脚本来作 pop3 认证等

我们现在的做法是 pipe auth/reg + mysql storage. 需要作的更改就是创建用户的时候向 user 表和 active 表插入用户,然后 Exodus 就可以正常登录了

用 twisted 实现一个简单的 echo server

发现 python 的 twisted 实在很方便, 很短的代码就可以实现一个完备的 echo server.
  1. import os, os.path, time, sys, codecs, getopt
  2. from twisted.internet import selectreactor as bestreactor
  3. bestreactor.install()
  4.  
  5.  
  6. from twisted.internet import reactor
  7.  
  8.  
  9. class App:
  10.     pidfile = "/tmp/echo.pid"
  11.  
  12.     def __init__(self):
  13.         twistd.checkPID(self.pidfile)
  14.         open(self.pidfile,'wb').write(str(os.getpid()))
  15.         reactor.addSystemEventTrigger('before', 'shutdown', self.shuttingDown)
  16.  
  17.     def shuttingDown(self):
  18.         twistd.removePID(self.pidfile)
  19.  
  20.  
  21. from twisted.internet import protocol
  22. from twisted.internet.protocol import Protocol, Factory
  23. from twisted.protocols import policies
  24.  
  25.  
  26. class EchoServer(Protocol, policies.TimeoutMixin):
  27.     timeout = 5
  28.  
  29.     def connectionMade(self):
  30.         print "Connected OK"
  31.         self.transport.write('OK\r\n')
  32.         self.setTimeout(self.timeout)
  33.  
  34.     def dataReceived(self, data):
  35.         self.resetTimeout()
  36.         if data[:4].lower() == "quit":
  37.             print "Connected QUIT"
  38.             self.transport.loseConnection()
  39.         else:
  40.             self.transport.write(data)
  41.  
  42.     def connectionLost(self, reason):
  43.         print "Connected ABORT"
  44.  
  45.  
  46. from twisted.scripts import twistd
  47.  
  48.  
  49. def main():
  50.     app = App()
  51.     factory = protocol.ServerFactory()
  52.     factory.protocol = EchoServer
  53.     reactor.listenTCP(9990, factory)
  54.     reactor.run()
  55.  
  56. if __name__ == "__main__":
  57.     main()

首次尝试 IPv6 开发

起因很简单,都什么时代了,MTA 当然需要支持 IPv6

安排新同志研究《UNIX 网络编程》,发现 IPv4IPv6 地址转换的过程还挺麻烦,于是决定从简单的开始做——首先实现一个纯 IPv6 的 Daemon.

给网卡设定 IPv6 地址后,试图指定该地址去 bind,结果返回 "Invalid Argument"

google 之,http://oss.sgi.com/archives/netdev/2002-04/msg00084.html 这篇文章排在第一位,但看的不大懂..

后来尝试把 sin6_scope_id 设置为 1 就成功 bind 了. sin6_scope_id = htonl(1)

后来找到一个邮件列表的讨论介绍这里的概念,大致明白是这么回事情:

操作系统认为这个 IPv6 地址是"link scope"的——还不清楚这个概念,难道和 172.16.x.x/10.x.x.x 这样的么,总之这个 IP 并不是我配置的..——而这样的话不能确保同一台机器上没有其它的相同地址,于是在 Linux 上必须做这个设置(因为 Linux 发展比较快,符合最新的 RFC)

解决 "NETDEV WATCHDOG: eth0: transmit timed out"

安装 x86_64 的 CentOS 4.3,结果网络死活不通,就是 dmesg 可以看到不断的输出

NETDEV WATCHDOG: eth0: transmit timed out
.....
.....

解决方法:在 bootloader 增加参数 acpi=off 即可

经常在 FreeBSD 讨论中看到 ACPI 相关的问题,这回是第一次在 Linux 上碰到.

看来 Linux 对 x86 平台支持是足够好了,但其它平台还有不少问题要解决啊. 即使是近亲 x86_64 也不例外.. debian Sarge 只支持 IA64,而不支持 x86_64 是不是就是这个原因呢??

另外:我下载了最新的 etch debian-installer 仍然同样需要 acpi=off 才能工作.

试了试 glib 的 GString 类型

参考 http://developer.gnome.org/doc/API/2.0/glib/glib-Strings.html

提供的接口比我们以前封装的 sa 丰富多了,但看那些 insert/prepend 函数,如果头文件里的
GString 结构体只有那么简单的化,估计此类操作性能不会好多少。

闲话少说,举一个 "Hello, World!" 的例子

  1. #include <glib.h>
  2. #include <stdio.h>
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6.     GString *gs;
  7.  
  8.     gs = g_string_new(NULL);
  9.     printf("%d, %d\n", gs->len, gs->allocated_len);
  10.     fflush(stdout);
  11.  
  12.     g_string_printf(gs, "%s", "ello");
  13.     g_string_append_printf(gs, "%s", "World");
  14.     g_string_append_c(gs, '!');
  15.     g_string_append(gs, "\n");
  16.     g_string_prepend(gs, "H");
  17.     g_string_insert_len(gs, 5, ", xxx", 2);
  18.  
  19.     write(1, gs->str, gs->len);
  20.     printf("%d, %d\n", gs->len, gs->allocated_len);
  21. }

可以利用 pkg-config 提供编译帮助

$ gcc -o gstring gstring.c `pkg-config --cflags --libs glib-2.0`

glib 的 HashTable 类型

除了字符串类型外,程序中再一个用的比较多的应该就是哈希表了,今天上午看了看 glib 里面的 hashtable,一下子联想起前不久看到的一篇讨论 python 性能的文章.

其实在此文所举的例子里面,用 shell 的 sort + uniq 就可以轻松实现,一行命令就搞定;但是所作的 C 和 Python 的对比很有意思,可以看出辛辛苦苦用大段 C 代码实现的哈希表,速度反而没有 python 快......于是乎我用 glib 的 hashtable 也实现了这个程序

  1. #include <glib.h>
  2. #include <stdio.h>
  3.  
  4. void writefp(gpointer key, gpointer value, FILE *fp)
  5. {
  6.     fputs((gchar *)key, fp);
  7. }
  8.  
  9. int main(int argc, char *argv[])
  10. {
  11.     FILE *fp, *wfp;
  12.     char buf[256];
  13.     GHashTable *htable;
  14.  
  15.     if (argc < 3) {
  16.         printf("usage: %s oldfile newfile\n", argv[0]);
  17.         return -1;
  18.     }
  19.     fp = fopen(argv[1], "r");
  20.     if (NULL == fp) {
  21.         return -1;
  22.     }
  23.     /* 退出的时候需要释放 g_strdup 产生的 key */
  24.     htable = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
  25.     wfp = fopen(argv[2], "w");
  26.     while (fgets(buf, sizeof(buf), fp)) {
  27.         g_hash_table_insert(htable, g_strdup(buf), NULL);
  28.     }
  29.     fclose(fp);
  30.     printf("%d\n", g_hash_table_size(htable));
  31.     g_hash_table_foreach(htable, (GHFunc)writefp, wfp);
  32.     fclose(wfp);
  33.     g_hash_table_destroy(htable);
  34. }

结果非常理想,速度比 python 快,而代码的复杂度是一个数量级上的,这个例子再一次教育我们,熟练掌握优秀的第三方库是多么的重要.

一个 cvs pserver 在 xinetd 上配置的问题.

忘了是从那里抄来的配置,一开始我们 cvs pserver 的配置是这样子的:
service cvspserver
{
        disable = no
        socket_type             = stream
        wait                    = no
        user                    = root
        server                  = /usr/bin/cvs
        server_args             = --allow-root=/home/cvs/CVS pserver
        env                     = HOME=/home/cvs
        passenv                 =
        log_on_success          += DURATION USERID
        log_on_failure          += USERID
        nice                    = 10
}

本来也运行的好好的,但随着部分开发人员开始从 VMware 里 checkout 代码,问题就出来了.. 如果虚拟机的配置是 NAT 方式,那么执行任何 cvs 操作都会等待一段时间.

一开始怀疑是属于典型的反向解析故障,但在 hosts 文件里加了主机定义后问题依旧. 后来发现,在 cvs client 等待的期间,server 上ps查看进程,发现服务仍然停留在 xinetd 阶段,迟迟不去 fork cvs.

于是焦点集中在 xinetd 配置上面,一条条的尝试,最后发现如果去掉 log_on_xxxx 的配置,一切就正常了. 阿门

关于 WINMAIL.DAT

outlook 有个很变态的特性,就是转发邮件的时候,它会把附件变成一个什么 winmail.dat , 非常烦人. 但一直也没有研究怎么去解决这个问题,因为缺省感觉微软一定是用什么秘密格式,这个错误应该在客户端解决,而不是 webmail 去分析。

今天 yuhj 告诉我这个东东是什么 application/ms-tnef , 去搜索了一下,原来有个 sf 项目 TNEF 可以处理 winmail.dat

看起来这个项目运转的还不错,2002-07-03 07:11 创建,2006-08-05 发布 1.4.2 版,看起来是一个可以值得信任的开源项目 :)