Saturday, January 20, 2007

ssh-agent 简介

ssh-agent 是一个保管私钥的程序。然后的故事呢是这样的,ssh-agent 在一个 X-session 或者一个 login session 的一开始启动,所有其他窗口以及程序都会作为 ssh-agent 的客户端启动。
以上译自 ssh-agent 的在线手册(man)。

按我的理解来说,ssh-agent 就是一个管理私钥的代理,受管理的私钥通过 ssh-add 来添加,所有 ssh-agent 的客户端都可以共享使用这些私钥。

用处一,不用重复输入密码。

用 ssh-add 添加私钥时,如果私钥有密码的话,照例会被要求输入一次密码,在这之后 ssh-agent 可直接使用该私钥,无需再次密码认证。

用处二,不用到处部署私钥。

假设我有私钥分别可以登录同一内网的主机 A 和主机 B,出于一些原因,不能直接登录 B。我可以通过在 A 上部署私钥或者设置 PortForwarding 来登录 B,也可以转发认证代理连接在 A 上面使用 ssh-agent 管理的私钥登录 B。

以 cygwin 环境为例,描述一下操作流程

islue@localhost $ ssh-agent bash --login -i

通过 ssh-agent 启动 bash,或者说把 bash 挂到 ssh-agent 下面

islue@localhost $ ssh-add

添加私钥,如果跳过第一步直接添加私钥,也就是说当前的 shell 并没有挂在 ssh-agent 下面,则会收到这样的错误信息

Could not open a connection to your authentication agent.

接下来登录主机 A

islue@localhost $ ssh -A HOST_A

-A 的作用就是启用转发认证代理连接

islue@HOST_A $ ssh HOST_B
islue@HOST_B $

从主机 A 上顺利登录主机 B,完毕。

更多内容请参阅 ssh 和 ssh-agent 的在线手册。

用好 MySQL 字符集

我们都知道,MySQL 支持 30 多种字符集,其中包括了我们常用的 gb2312、gbk 和 uft8 等字符集。

在很多简单应用场合下,字符集设置是可有可无的。把后台存储的视为裸数据(raw data),存入的数据可以被原封不动地取出,对于前端的处理及应用,通常是不会有问题的。

当然,不设置正确的字符集,在实际应用中也有诸多不便之处。假设情况是这样的:

前端应用:GB2312
后端存储:latin1

那么大概会得到这样的结果:

字符终端(GB2312环境):正确
phpMyAdmin:乱码
MySQL Query Browser:乱码

如果你是个完全无视 GUI 管理工具的开发者,那么无视字符集设置就是了。

设置合适的字符集,我觉得主要是可以获得 GUI 管理工具强大支持。除此以外,其实用处并不是很多,以我的使用经验为例,只是用来做文本排序。

那么以 Perl 为例,说说我的经验,怎样使用合适的字符集,情况如下

前端应用:GB2312
后端存储:utf8

设置字符集可以分为四个环节:
客户端 <-> 前端 <-> 连接 <-> 后端

客户端,这里指浏览器。当前输出内容编码为 GB2312,所以应该在 HTML 头部加入如下语句:

<meta http-equiv="Content-Type" content="text/html; charset=GB2312">

通常,即使没有上述语句,多数浏览器也能“猜”出正确编码,这是题外话,不深入了。

前端,这里指数据处理程序。因为后端存储是 utf8,所以在这里需要把数据从 GB2312 转为 UTF8,就绪。

use Encode;
Encode::from_to( $data, 'euc-cn', 'utf8' );

连接,这里指由程序创建的数据库连接。通常我们这样连接数据库:

use DBI;
my $dbh = DBI->connect('dbi:mysql:database=test;host=localhost', $user, $password);

为了加入字符集信息,需要在连接后执行下面的命令:

$dbh->do('SET NAMES utf8');

声明了输入输出使用的都是 utf8 字符集。要保证建立连接后,首先执行字符集声明。

这在某些自动重连的情况下可能会失效,我推荐用读取配置文件的方式声明连接字符集。

首先,在配置文件 /etc/my.cnf 中加入字符集设置

[client]
default-character-set=utf8

然后,连接语句相应地改为

my $dbh = DBI->connect('dbi:mysql:database=test;host=localhost;mysql_read_default_file=/etc/my.cnf', $user, $password);

或者
my $dbh = DBI->connect('dbi:mysql:database=test;host=localhost;mysql_read_default_file=/etc/my.cnf;mysql_read_default_group=client', $user, $password);

这样保证了创建连接前就能读取到字符集设置。

后端,这里指 MySQL 数据库。在建表或数据库时,声明字符集

CREATE TABLE (
...
) CHARSET utf8;

以上就是各环节字符集的设置,一些描述乃属经验记忆,难免会有错漏,还望海涵。更多请参阅 MySQL 参考手册相关章节

Monday, December 25, 2006

TortoiseSVN 共享文件夹下一样显示图标

虽然是 TortoiseSVN 既有功能,猜想很多人都不知道的。

正常情况下,如果你在一个网络共享文件夹下面 checkout 一个 subversion 控制的项目。这个项目的文件夹并不会像在本地文件夹中一样,以各式各样的图标来表示文件状态。你需要做的是稍许改一下配置:
TortoiseSVN -> Settings -> Look and Feel -> Icon Overlays -> Network Drivers

选中 Network Drivers,就能看到文件状态图标了。

货比三家

时常在网上书店买书,本着货比三家的原则,往返于各书店的价格页面,甚是累人。

也听说过用来比较价格的搜索网站,乘兴而去,败兴而归!

http://www.tejiawang.com/
收录站点少(2),价格信息单一

http://www.uobuy.com/
收录站点多(4),但是没有 china-pub,价格信息单一、滞后

http://www.danawa.com.cn/
不知所云

http://www.souchang.com/
收录站点少(1)


罢罢罢!

自己动手,丰衣足食。
http://sym.noticpan.org/

暂时只收录了当当和卓越,做了简单的消重再确认。

感想,卓越的页面要比当当规整一些,比较容易解析。

Sunday, December 24, 2006

也谈 Google 和百度

“百度更懂中文”,大家都这么说,著名用例:马勒隔壁的。

就当挑刺吧,正好刚才碰到了,举个例子
测试站点:
www.google.cn
www.baidu.com
搜索用例:
site:www.joyo.com Perl语言入门

从结果看,Google 给我的就是我要的;百度?不知道在干什么。

在中国,恐怕 Google 最痛的莫过于“不能用”。不谈 GFW,百度能做到,Google 为什么做不到?一样是企业,Google 确实逊人一筹。不过我没有这个烦恼,至少现在没有,所以我只用 Google。

Sunday, December 10, 2006

小心时区陷阱(二)

测试用例
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece qw//;
use Date::Simple qw//;
use POSIX qw//;

sub show_info {
my $msg = shift;
my $hour = (localtime)[2];
printf "tz=$ENV{TZ}, tzname=%s:%s, hour=%2d # $msg\n", POSIX::tzname, $hour;
}

$ENV{TZ} = 'JST-9';
print "Date::Simple version is $Date::Simple::VERSION\n";
show_info('start');
my $date = Date::Simple->new;
show_info('after new');
$date->strftime("%Y-%m-%d\n");
show_info('after strftime');
POSIX::tzset;
show_info('after tzset');


输出结果
tz=JST-9, tzname=JST:JST, hour= 8 # start
tz=JST-9, tzname=JST:JST, hour= 8 # after new
tz=JST-9, tzname=UTC:UTC, hour=23 # after strftime
tz=JST-9, tzname=JST:JST, hour= 8 # after tzset


另外,关于 $ENV{TZ},提醒一下。
假设机器的时区设置为 UTC+9,不考虑非法的时区设置,那么根据值的不同,分别会是这样的结果
1.  not exists $ENV{TZ}        UTC+9
2.1 if ( $ENV{TZ} ) {...} UTC+0
2.2 else {...} UTC+9


Patch for Date::Simple 3.02
$ diff -r Date-Simple-3.02.orig Date-Simple-3.02
diff -r Date-Simple-3.02.orig/lib/Date/Simple.pm Date-Simple-3.02/lib/Date/Simple.pm
158a159
>
160,161c161,174
< local $ENV{TZ} = 'UTC+0';
< return POSIX::strftime ($format, _gmtime ($self));
---
> my $exists_env_tz = exists $ENV{TZ};
> my $tz = $ENV{TZ} if $exists_env_tz;
>
> $ENV{TZ} = 'UTC+0';
> my $result = POSIX::strftime ($format, _gmtime ($self));
>
> if ( $exists_env_tz ) {
> $ENV{TZ} = $tz;
> } else {
> delete $ENV{TZ};
> }
> POSIX::tzset();
>
> return $result;

参考链接
rt#23875

Tuesday, December 05, 2006

小心时区陷阱(一)

不知道大家有没有碰到过这样的问题,原本好好的当前时区时间突然变为 0 时区时间。

这个问题困扰了我很久,原因有二:
1、程序不是个单文件脚本
2、单元测试无法重现错误

后来在同事的帮助下,总算找到了问题所在

localtime(3) calls tzset(3), but localtime_r(3) not.
threaded perl localtime() calls localtime_r(3) instead of localtime(3)

来看一下 Date::Simple 的一段源代码

sub format {
my ($self,$format)=@_;

$format= $fmts{refaddr($self)||''} || $fmts{ref($self)} || $Standard_Format
if @_==1;

return "$self" unless defined ($format);
require POSIX;
local $ENV{TZ} = 'UTC+0';
return POSIX::strftime ($format, _gmtime ($self));
}

POSIX::strftime() 调用 tzset() 时看到的环境变量 $ENV{TZ} 是 UTC+0。在这之后就有问题了,如果使用 localtime(),因为 localtime_r(3) 并不会调用 tzset(),所以即使环境变量 $ENV{TZ} 已经恢复原值,localtime() 查看内部变量的时候,还是之前的 UTC+0。

至于为什么 localtime_r(3) 不像 localtime(3) 一样调用 tzset(3) 根据环境变量重设时区,我理解下来的主要原因是因为,它是线程不安全的。可能会出现下面这种情况

thread1
thread2
tzset()
tzset()
localtime()
localtime()

另外,关于环境变量 $ENV{TZ}存在(exists),但其值为空的时候,也会得到 UTC+0 的结果。

相关链接:
perlbug #26136

Monday, November 13, 2006

Friday, November 03, 2006

AppLocale 和乱码

在安装 AppLocale 之后,微软安装程序包(MSI)的执行就会出现乱码。
网上广为流传的一个方法是把下面这个文件的内容清空,并将属性改为只读。

C:\WINDOWS\AppPatch\AppLoc.tmp


另有说法是直接删除亦可,并不会影响 AppLocale 的正常使用。

Monday, October 30, 2006

不那么成功的 Mozilla Firefox 2.0

在经历了短暂的亲密接触之后,工作和娱乐我都降级成了 1.5.0.7,很遗憾。

我很少降级软件,除非迫不得已。

2.0 的 UI 改进并不大,但是挺漂亮的,我很喜欢。至于把关闭按钮添加到每个标签页上,我倒觉得是个见仁见智的事情。除此以外,对我来说,就没有什么变化了。

工作环境下,2.0 本身并没有什么问题,蛮好的。但是,LiveHTTPHeaders 还没兼容 2.0,而对于我的工作来说,有时候它还真是那么得重要。在没找到替代插件之前,我能做的只能是降级 2.0。

娱乐环境下,出于不知名的原因,2.0 频频死机。好好的就失去响应了,还是缓不过神的那种。我现在用的网络本来就不好,这样一来,简直就是雪上加霜。没辙儿,怎么地我也犯不着跟自己过不去,降!

就这样,恋恋不舍地,我离开了 2.0。