My Emacs configuration

Features

  • support c++/python/perl/ruby-on-rails/javascript
  • eim (Chinese Pinying IME for emacs)
  • org2blog (post wordpress blog by org-mode)

Install

git clone git@github.com:redguardtoo/emacs.d.git ~/.emacs.d;

Some plugins need be compiled for better performance (OPTIONAL)

  • cedet,ecb,emacs-w3m,org-mode,nxml-mode,mmm-mode,auto-complete
cd ~/.emacs.d/site-lisp;make -C cedet;make -C ecb;make -C org-mode;make -C nxml-mode;make -C auto-complete
cd ~/.emacs.d/site-lisp/emacs-w3m;autoconf;./configure;make;sudo make install-info-en;
cd ~/.emacs.d/site-lisp/mmm-mode;./autogen.sh;./configure;make;sudo make install-info;

sh

  • rinari (install info only)
cd ~/.emacs.d/site-lisp/rinari;rake doc:make_info;sudo cp doc/rinari.info /usr/share/info;sudo install-info /usr/share/info/rinari.info /usr/share/info/dir
  • eim
cd ~/.emacs.d/site-lisp/eim;emacs -batch -f batch-byte-compile *.el;

Linux桌面使用之道

我在个人电脑使用Linux桌面已4年(工作中使用Linux的时间要长的多),深深体会到了Linux桌面的好处.

从某种角度来看,Linux桌面要比Windows桌面好用得多.前提条件是,

  1. 使用者有一定的理性思维
  2. 坚持适合自己的原则
  3. 方法和工具

我使用的Linux发行版是Arch Linux,其特点(缺点?)是软件更新快,最新的软件包未必经过长时间的严谨测试.所以Arch Linux绝不是发行版中最不折腾人的,我能和它和谐,和其他发行版就不用说了.

第一原则,不折腾

对我而言,使用软件的目的是工作和娱乐.软件只是帮助我实现目的的手段而已,和实现目的无关的资源消耗越少越好.

坚持该第一原则,日常操作就可以很有效率.

选择软件的标准

选软件就是投资

我的标准是选软件要成本小收益大.收益如前所述,就是工作和娱乐.所以只需考虑削减成本了.

具体措施,就是只用少量软件做尽可能多的事.

Arch Linux的流行作风是一日一更新.我使用的软件数量少,要更新的也少.

那么我是否需要一日一更新呢? Arch Linux有这个选项并不等于我必须这么干.今天我刚升级完1.3版本,明天1.4又出来了,为什么我不多等一天,直接升级到1.4呢?

我的作法就是大多数软件我短期内不升级.最常用的软件我会根据需要理性升级.例如,firefox的安全升级我总是及时跟进,Linux的kernel我一年才升级一次.

依据投资收益的原则,我偏爱命令行软件,因为它们不依赖于图形界面库,升级的压力小很多.同类的命令行软件,我会优先考虑依赖的第三方库比较小的.例如用C写的软件优于用python写的软件.

我有个根据编写软件的语言来判定软件是否轻小的主观标准,c < perl < python, ruby, ….. < c++, java.

以下是我用的主力软件,其中的编辑器,浏览器,Shell,桌面,都是功能强大,定制度高,可编程.

编辑器

我用Emacs,考虑到它可以做任何事,可以认为它是轻量级的.

Vim也用,万不得已用nano,结束了.

浏览器

没多少选择,firefox,如上所述,firefox是我更新频繁的软件.

Shell

我用bash,在我的.bashrc中有如下一行,

export HISTSIZE=200000

确保我输入的所有命令都不会丢失,因为我说过,我偏爱命令行软件,但是要记住常用命令行会消耗我很多脑细胞,这是不符合投资收益原则的.所以我让shell替我记住.我只要记住按Ctrl+R可搜索历史就好了.

桌面

Gnome,KDE是绝对不会考虑的.我安装了Xfce4以给我太太用.我使用更轻量级的musca,安装包大小才100K.

其他

网络管理我用Netcfg(命令行)而不是wicd,因为wicd的依赖多而且曾经折腾过我,播放视频我之用mplayer(命令行),等等.

软件的使用也需维护

安装软件在包管理系统的帮助下很简单.麻烦的是最新软件有时候不稳定,这通常是因为软件的配置文件更新引起的.

将/etc目录下的默认配置文件备份

Linux的软件默认配置文件在/etc/目录下. 我使用分布式版本控制软件git管理/etc目录,分布式版本控制系统好处很多,最大好处就是数据到处有完整备份,非常安全.具体细节请参考wikipedia.

同类软件mercurial,bzr也可,要考虑的因素是第三方服务器是否支持(对分布式版本控制软件来说,第三方服务器不是必需的).

在Arch Linux上每次更新完系统或者安装最新软件包后,可以考虑手动将/etc目录下的文件用git备份(Debian可自动备份).

sudo git add /etc/
sudo git commit -am "backup"

一旦某个软件工作不正常了,可以检查其/etc下默认配置文件的修改纪录,将配置文件恢复到上一个正常工作的版本.

sudo git log /etc/mysql/my.cnf #check history
sudo git checkout commit_ref(version) /etc/mysql/my.cnf #restoren file

/etc/目录下的临时文件不需要备份,以下是我的.gitignore

*~
*.pacnew
*.pacsave
*.swp
*.pacorig
*.swp
mtab

将用户目录下的用户定制配置文件备份

我也是用git,配置文件备份到github的服务器上.这里必须用第三方服务器同步配置,因为我使用多台电脑.

具体操作可写成长篇大论,这里不展开了.

将软件版本降级

最新版本的软件可能不稳定,这时降级是必要的.

请参考Archwiki,实际上很简单,安装名为downgrade的软件,然后让它干所有的体力活就行了.

yaourt downgrade

云备份

电子图书,多媒体文件用专业的云备份服务更好,我推荐Dropbox.

顺便说一下,国内的服务不要用,安全原因,不言自明.

备份操作系统

不用经常作,又有云备份,又有分布式版本控制系统,怕什么.

备份整个硬盘是用Clonezilla,只是备份操作系统的话用以下命令,

tar zcvpf mylinux.tgz --exclude=/proc/* --exclude=/lost+found --exclude='mylinux.tgz' --exclude=/mnt/* --exclude=/home/username/* --exclude=/sys/* /

我建议用前者,因为省事.

阅读官方文档

照着ArchWiki安装软件,一般不会有问题.

加入社区

紧急客户支持还是用IRC比较好,没用过IRC的可以装firefox插件Chatzilla体验一下.

虚拟机的用处

我虚拟机软件使用VirtualBox,虚拟机为Windows XP,该操作系统我会定期更新安全补丁.软件只安装了IE和firefox.该操作系统我只用来操作网络银行.每次操作完毕,我会重置虚拟机状态到操作前的snapshot.

救急工具箱

即使采用了上述做法,一年中也会有一次倒霉.就是我要用的某个版本的某个软件出问题了.没人能帮忙,不能降级,改软件配置也没用,也没有可用的错误信息.

问题看起来很难,事实上5分钟就能搞定.这往往是因为某软件需要的第三方库的版本出问题了.

比如说firefox不能启动,运行如下命令调试,

strace -o err.txt firefox

读一下err.txt就可以知道是哪个库出问题了.相信我,这是不需要任何编程知识的,猜加google就可以了.

你知道哪个库文件出问题了(保险起见,ldd firefox确认一下).比如/usr/lib/libpng.so.14找不到,简单的解决办法是用另一个版本的库文件代替.

如果/usr/lib/libpng.so.15是存在的,我们可以用该库顶替libpng.so.14,

sudo ln -s /usr/lib/libpng.so.15 /usr/lib/libpng.so.14

通常这样就行了.

万一冒名顶替不行,我们就必须根据丢失的库文件查找需要安装的软件包(pacman没用的),

pkgfile libpng

pkgfile属于软件包pkgtools,所以你必须安装pkgtools,

yaourt -S pkgtools-git

坚持使用gnu screen

虽然tmux很优秀,但是目前很难支持cygwin(Nicholas Marriott在2010年12月说的),而我的工作需要使用windows.

最新版的screen的一个重要特性就是已支持垂直分屏,其快捷键为”C-a |”.

要安装最新版的screen,在我的Arch Linux下很简单,

yaourt -S screen-git

在windows的cygwin下安装要多几步.

首先安装软件包libncurse-devel,然后去官方网站下载最新源代码包(目前版本为4.0.3),解压缩,

tar zxf screen-4.0.3.tar.gz

编译安装,

cd screen-4.0.3;configure;make;make install;

如用urxvt作为Terminal Simulator(比如我),直接启动screen会报错,

$ screen
$TERM too long - sorry.

原因为screen的开发者对于环境变量$TERM的最大长度做了不合理的假定.这里是具体讨论,解决办法很简单. 在.Xdefaults中添加一行,

urxvt*termName: rxvt-256color

在.screenrc中添加一行,

term rxvt-256color

使用screen前我将screen的Ctrl-a改为Ctrl-t

因为Ctrl-a在bash和emacs中都是常用快捷键,在.screenrc中添加一行,

escape"^Tt"  #the default of A interferes with emacs keybindings for terminal, and I never use the emacs transpose command

这个修改我是从Balaji S. Srinivasan.screenrc抄来的. 可以参考emacs wiki获得更多兼容emacs的设置.

晒晒我的常用软件清单(和某安全中心推荐清单无交集)

Windows软件清单

工作中我90%的时间用perforce+cygwin+Visual Studio+vi+firefox.下班后基本不碰windows.

名称 说明 频率 性质
perforce 版本控制 收费
cygwin Linux下常用工具集合 开源
7-Zip 压缩解压缩 开源
AviSynth 2.5 视频编辑 开源
BitSpirit BT下载 免费
utorrent BT下载 免费
Brother Brother打印机附赠软件,其ControlCenter3的ocr功能对我有点用.如扫描账单备份到网上 免费
CDBurnerXP 刻录 免费
CDex170b2 cd转mp3,可从网上获得cd信息后自动生成mp3文件名 开源
ditto 剪贴板管理 免费
CMake 2.6 跨平台build工具,相见恨晚 开源
CodeBlocks 跨平台的c++ IDE 开源
ColorPic 4.1 取色 免费
comicsviewe 看漫画 免费
DAEMON Tools 虚拟光驱 免费
Debugging Tools for Windows 如题 免费
Wink 录屏 免费
Dependency Walker 检查dll依赖 免费
eMule p2p下载 开源
FBReader 电子图书阅读 开源
foobar2000 音乐播放 开源
Foxit Reader PDF阅读器 免费
GhostScript pdf解析引擎 开源
GIMP 图像编辑 开源
gnuplot 数据分析,主要用于数据显示 开源
gtalk 即时通信,语音服务佳 免费
Picasa (google) 照片管理 免费
google拼音 输入法,主要特色在于可以在线同步词库,资源消耗高 免费
AVG Free 杀毒 免费
hoekey 热键 免费
ImageMagick-6.3.5 命令行图像处理 开源
InfraRecorder 刻录 开源
Inno Setup 5 打包,开发用 开源
Internet Explorer 系统自带 免费
IPMsg 局域网通信 开源
IrfanView 看图,可与total commander集成 免费
Mercurial 分布式版本控制 开源
Msn Messenger 微软的即时通信软件 免费
Microsoft Platform SDK for Windows Server 2003 R2 和Visual C++ 2005 Express结合开发win32平台软件 免费
Microsoft Visual Studio Express 2005 C++ IDE 免费
Miranda IM 集成了yahoo,icq,qq,msn,gtalk,irc,msn的即时通信 开源
Firefox 网页浏览 开源
Mozilla Thunderbird 读email,在web mail出问题时有用 开源
mtPaint mspaint的替代品,简单画图,界面费解 开源
MWSnap 截图 免费
Notepad++ 文本编辑,我用vi,给别人用 开源
LibreOffice 办公字处理,导出word到pdf.最,画组织结构图,中文教程少 开源
OpenVPN VPN 开源
Opera 网页浏览 免费
Chrome 网页浏览 免费
Pandora Recovery 恢复误删除的文件 免费
Passcape Mozilla套件密码恢复 免费
password-recovery 密码恢复 免费
PPLive 在线电视 免费
reshacker 从可执行文件中提取资源(图标) 免费
ScanSoft 买打印机附送的扫描软件,可以扫描为pdf文件 免费
sitman 复读机 共享
Skype 即时通信,语音通话 免费
SopCast 在线电视,在国外看央视效果不错:) 开源
StarDict 辞典 开源
Subversion 版本控制 开源
SumatraPDF pdf阅读器 开源
VirtualBox 虚拟机,用于模拟开发环境,或是网上银行专用的操作系统 开源
QQ 如题 免费
MiniThunder(迅雷) 下载 免费
PDF-XChange Viewer PDF阅读器(由于工作关系,我装了很多PDF阅读器) 免费
UltraVNC 远程桌面控制 开源
Subtitle Workshop 字幕调整 免费
USBOOT 1.7 使usb能够启动电脑,制作usb自启动盘时用 免费
F…gate 翻墙 开源
Vim/GVim 文本编辑 开源
Total Commander 说是世界第一的文件管理器不算过分,未注册版 共享
bblean windows shell 开源
Bridge Base Online 游戏 免费
ntemacs 文本编辑 开源
openssl 如题 开源
python2.7 一种脚本语言 开源
wtl 微软的c++ gui程序开发库 开源
wxWidgets 跨平台c++ GUI库 开源
mplayer 命令行视频播放软件 开源
autoruns 检查启动时默认启动那些软件,用于手动杀木马 免费
process explorer 检查电脑中有什么程序在运行,用于手动杀木马 免费
Everything 找文件 免费
treewalk 缓存DNS 免费

firefox插件

名称 说明 频率
adblock plus 过滤广告,支持正则表达式
del.icio.us 1.2 网络书签的插件,较老版本
downloadhelper 视频下载
Autoproxy 代理上网
Firefox sync 同步个人数据
Greasemonkey 安装了Greased Lightbox (看图)和Lookitup(资料查找)脚本
pronounce 输入单词发标准美音
firebug 最好的web开发工具
KeySnail 安装了HoK(快捷键设置)脚本代替vimperator
Autopager 自动翻页
ChatZilla IRC
ColorZilla 取色
Vacuum Palaces Improved firefox使用的sqlite数据库加速

total commander插件

名称 说明 频率
CatalogMaker.wcx 创建文件列表
CHMDir.wcx 生成chm文件
7zip.wcx 压缩解压缩7zip格式的压缩文件
UnInstTC.wfx 管理”添加/删除软件”面板
Registry 管理注册表
ex2fs.wfx 访问linux分区(只读)
sftpplug.wfx 支持sftp
imagine.wlx 看图

vim插件

名称 说明 频率
cscopemaps.vim 为vim中使用cscope(看c/c++代码工具)添加快捷键支持
LargeFile.vim 看大文件时自动优化vim
matchit.vim 跳转至匹配的符号,比如在匹配的括号见跳来跳去
NERDcommenter.vim 码(支持很多语言)添加/取消注释
taglist.vim 显示函数列表

emacs插件

我正全面转向emacs中,所使用的插件清单尚未稳定,这里列的只是固定下来的项目.

名称 说明 频率
gnus gmail,新闻组
org-mode 个人项目管理,笔记
w3m 网页浏览
eim 中文输入
elim 即时通信
elpa emacs的app store
erc irc
smart-tab auto complete everything by TAB
org2blog 写博客
rinari ruby IDE
better-registers 使用register更方便

Linux软件清单

操作系统为Arch Linux 使用频率最高的软件为git+emacs+vi+firefox+musca+bash

名称 说明 频率
emacs 任何事
vi 文本编辑
firefox 网页浏览
opera 网页浏览,当跑不动firefox时
pidgin 即时通信
rxvt-unicode Terminal
bash shell
musca 窗口管理(使用musca-mm-git)
qtcreator C++ IDE
wine win32模拟器
gappproxy 翻墙
f…gate 翻墙,需要wine
mplayer 视频播放
audacious 音频播放
amule p2p下载
rtorrent BT下载
virtualbox 虚拟机
gimp 图片编辑
git 版本控制
zathura pdf阅读

perl code to change ip

I write this script for a real project. It changes ip of every node in a local network. All the information related to our customer is deleted.

if you see the test() function and you will notice “assert” lines. Yes, I believe in TDD (Test Drivern Development).

  1 #!/usr/bin/perl
  2 
  3 # File name: chgip
  4 # Description : change ip of every node in the local network
  5 require "assert.pl";
  6 use File::Basename;
  7 
  8 #
  9 # Globals
 10 #
 11 use vars qw/ %opt /;
 12 
 13 # valid input:
 14 # 192.168.30.151 node1 -> change /etc/hosts ;$k= node1;
 15 # 192.168.30.151 node1 eth0 none-> NOT change /etc/hosts;$k=node1 eth0
 16 # 192.168.30.151 node1 eth1 none-> NOT change /etc/hosts;$k=node1 eth1
 17 # 192.168.30.151 192.168.30.111 -> NOT change /etc/hosts;$k= 192.168.30.111
 18 # 192.168.30.151 node1 eth1 default -> change /etc/hosts;$k=node1 eth1 default 
 19 # 192.168.30.151 node1 eth1 -> "eth1" will be skipped andit equals 
 20 #                          192.168.30.151 node1
 21 #                         cat /etc/hosts/ and you will see
 22 #                         "150.245.178.31 node1 node1.dcinema.com", 
 23 #                         we will avoid confilict with such lines in /etc/hosts
 24 
 25 $USE_SSH=1;
 26 $USE_TELNET=2;
 27 ############################
 28 # main
 29 ############################
 30 $NDEBUG=0; # NDEBUG  NOT debug version
 31 
 32 &init();
 33 if($opt{h}){
 34   usage();exit 1;
 35 }
 36 
 37 $NDEBUG=$opt{d}?0:1;
 38 
 39 if($NDEBUG){
 40 my @pipein;
 41 
 42 if(&is_interactive()){
 43   if($#ARGV==-1){
 44     usage();exit 1;
 45   }
 46   @pipein=split(/[\r\n]+/,$ARGV[$#ARGV]);
 47 }
 48 else{
 49   @pipein=;
 50 }
 51 
 52 #print "pipein[0]=$pipein[0]\n"; #debug
 53 
 54 if(!$opt{a} && !$opt{p}){
 55   $opt{a}=$USE_SSH;
 56 }
 57 
 58 my $arp;
 59 if($opt{a}){
 60   $arp=&get_hosts_from_arp();
 61 }
 62 my $h=&who_will_change(\@pipein);
 63 
 64 my $pingable;
 65 if($opt{p}){
 66   $pingable=&who_are_pingable($h);
 67 }
 68 
 69 my %whose_ip_changed=();
 70 
 71 my $local_restart_later=0;
 72 #for my $it (keys %$h){ print "key=$it:val=$h->{$it}\n"; } #debug 
 73 my $pingcmd=&get_pingcmd();
 74 for my $k (sort keys %$h){
 75   my @a=split(/[\s\t]+/,$k);
 76   my $s=`hostname`;chomp($s);
 77   if($a[0] eq $s){
 78     $local_restart_later=1;
 79   }
 80 
 81   if($opt{a}){
 82     if(!exists($arp->{$a[0]})){ if($s ne $a[0]){ next; } }
 83   }
 84 
 85   if($opt{p}){
 86     if(!exists($pingable->{$a[0]})){ next; }
 87   }
 88   my $netcard="eth0";
 89   if($#a==2){
 90     if($a[2] eq "default" || $a[2] eq "none"){
 91       $netcard=$a[1];
 92     }
 93   }
 94   #print "netcard=$netcard\n"; #debug
 95   print "changing ip of $netcard on $a[0] ... ";
 96   my $try_telnet=0;
 97   $ret=&change_ip($a[0],$h->{$k},"/etc/sysconfig/network-scripts",
 98                   $netcard,\$try_telnet);
 99   if($ret){
100     print "failed!\n";
101   }
102   else{
103     $whose_ip_changed{$a[0]}=1;
104     print "succeeded\n";
105   }
106   if($try_telnet){
107     if($opt{a}){
108       $arp->{$a[0]}=$USE_TELNET;
109     }
110 
111     if($opt{p}){
112       $pingable->{$a[0]}=$USE_TELNET;
113     }
114   }
115 }
116 
117 #change /etc/host, every node need it
118   my %nodes_restarted=();
119   system("cp -f /etc/hosts /tmp/hosts.new >/dev/null 2>&1");
120   &change_etc_hosts("/tmp/hosts.new",$h);
121 
122 #distribute /etc/hosts
123   for my $k (keys %$h){
124     my @a=split(/[\s\t]+/,$k);
125     my $s=`hostname`;chomp($s);
126     if( $s eq $a[0]){
127       #print "s=$s;k=$k****\n"; #debug
128       #you need reinstall ssh key, if you change anyone's ip and you want 
129       #ssh it again 
130       next;
131     }
132 
133     #if we cannot connect to the node, how can we send it /etc/hosts
134     if($opt{a}){
135       if(!exists($arp->{$a[0]}) || $arp->{$a[0]}==$USE_TELNET )
136       {
137         next;
138       }
139     }
140 
141     if($opt{p}){
142       if(!exists($pingable->{$a[0]}) || $pingable->{$a[0]}==$USE_TELNET )
143       {
144         next;
145       }
146     }
147 
148     my @a=split(/[\s\t]+/,$k);
149     #it's ridiculous to restart same pc again and again!
150     if(!exists($nodes_restarted{$a[0]})){
151       my $ret=0;
152       print "distributing new /etc/hosts to $a[0] ... ";
153       $ret=system("scp /tmp/hosts.new root\@$a[0]:/etc/hosts >/dev/null 2>&1");
154       if($ret){
155         print "failed!\n";
156       }
157       else{
158         print "succeeded\n";
159       }
160 
161 
162       #http://groups.google.com/group/linux.gentoo.user/browse_frm/thread/
163       #       677f23ea20dd85a/506384edeba2c270?lnk=st&q=ssh+network+restart&rnum=5&hl
164       #       =zh-CN#506384edeba2c270
165       #print `cat /etc/hosts|grep node1`; #debug
166       #nohup return too quickly, before ssh starts, we've already changed /etc/hosts
167       my $restart_cmd="/etc/init.d/network restart;/etc/init.d/rmiregistry restart;";
168       if($a[0]=~/node[0-9]+/){
169         $restart_cmd.="restart_some_scripts;";
170       }
171       elsif($a[0]=~/ekfp[0-9]+/){
172         $restart_cmd.="fp_restart;";
173       }
174       #print "restart_cmd=$restart_cmd\n"; #debug
175       $ret=system("nohup ssh root\@".&get_ip($a[0])." '$restart_cmd' >/dev/null 2>&1 &");
176       $nodes_restarted{$a[0]}=1;
177     }
178     else{
179       $nodes_restarted{$a[0]}+=1;
180     }
181   } #end of for
182 
183 #print("local_restart_later=$local_restart_later\n");#debug
184 system("cp -f /tmp/hosts.new /etc/hosts >/dev/null 2>&1");
185 #print `cat /etc/hosts|grep node1`; #debug
186 if($local_restart_later ){
187   my $restart_cmd="/etc/init.d/network restart >/dev/null 2>&1;";
188   my $s=`hostname`;chomp($s);
189   print "restarting $s ... ";
190   if($s=~/manager.*/ ){
191     $restart_cmd.="restart_some_scripts >/dev/null 2>&1;";
192   }
193   else{
194     $restart_cmd.="restart_some_scripts >/dev/null 2>&1;";
195   }
196   #assume this is a cms or tms
197   my $ret=system($restart_cmd);
198   if($ret){
199     print "failed!\n";
200   }
201   else{
202     print "succeeded\n";
203   }
204 
205 }
206 
207 print "finished to change ip!\n";
208 system("rm nohup.out >/dev/null 2>&1");
209 system("rm ~/.ssh/known_hosts>/dev/null 2>&1");
210 
211 if($opt{a}){
212 print "updating arp cache ... ";
213 for my $k (keys %whose_ip_changed){
214   my $pingcmd=&get_pingcmd();
215   system("$pingcmd $k >/dev/null 2>&1");
216 }
217 print "finished\n";
218 }
219 }#if(0)
220 
221 ############################
222 # test
223 ############################
224 if(!$NDEBUG){
225   &test();
226 }#if(0)
227 
228 sub test
229 {
230 
231 if(1){
232   my $hostname=`hostname`;chomp($hostname);
233   my $pipein;
234   if(&is_interactive()){
235     @pipein=split(/[\r\n]/,"127.0.0.9 $hostname");
236     my $h=&who_will_change(\@pipein);
237     my $pingable=&who_are_pingable($h);
238     assert(exists($pingable->{$hostname}));
239   }
240   else{
241     @pipein=;
242     my $h=&who_will_change(\@pipein);
243     my $pingable=&who_are_pingable($h);
244     print "*********who are pingable**********begin\n";
245     for my $k (keys %$pingable){
246       print "key=$k\n";
247       print "val=$pingable->{$k}\n";
248     }
249     print "*********who are pingable**********end\n";
250   }
251 }
252 
253 if(1){
254   my $id=&unique_id();
255   print "id=$id\n";
256   assert( $id ne "");
257 }#if(0)
258 
259 if(1){
260   my $id="test_set_one_card";
261   print "file written by set_onecard=$id\n";
262   `echo "IPADDR=192.168.30.100" > $id`;
263   `echo "NETWORK=192.168.30.0" >> $id`;
264   `echo "BROADCAST=192.168.30.255" >> $id`;
265   `echo "GATEWAY=192.168.30.1" >> $id`;
266   &set_one_card("11.168.30.100",$id);
267   open(F,$id);
268   my @lines=;
269   close(F);
270   chomp($lines[0]);assert($lines[0] eq "IPADDR=11.168.30.100");
271   chomp($lines[1]);assert($lines[1] eq "NETWORK=11.168.30.0");
272   chomp($lines[2]);assert($lines[2] eq "BROADCAST=11.168.30.255");
273   chomp($lines[3]);assert($lines[3] eq "GATEWAY=11.168.30.1");
274   `rm $id`;
275 }
276 if(1){
277   my $hostname=`hostname`;chomp($hostname);
278   my $ip=get_ip($hostname);
279   print "ip=$ip\n";
280   assert(&is_valid_ip($ip));
281   my $rubbish=get_ip("rubbish");
282   assert( $rubbish eq "rubbish");
283 }
284 
285 
286 if(1){
287   my $p="127.0.0.9 node1\n0.0.0.0 node2";
288   my @pipein=split(/[\r\n]+/,$p);
289   my $h=&who_will_change(\@pipein);
290   assert($h->{'node1'} eq '127.0.0.9');
291   assert($h->{'node2'} eq '0.0.0.0');
292   `echo "1.1.1.1 node1" > hosts`;
293   `echo "1.1.1.1 node2 node2.dcinema.com" >> hosts`;
294   `echo "1.1.1.1 node3" >> hosts`;
295   &change_etc_hosts("hosts",$h);
296   open(F,"<hosts");
297   my @lines=;
298   close(F);
299   chomp($lines[0]);assert($lines[0] eq "127.0.0.9 node1");
300   chomp($lines[1]);assert($lines[1] eq "0.0.0.0 node2 node2.dcinema.com");
301   chomp($lines[2]);assert($lines[2] eq "1.1.1.1 node3");
302 }#if(0)
303 
304 if(1){
305   my $hostname=`hostname`;chomp($hostname);
306   print("hostname=$hostname\n");
307   my $p="11.168.30.10 $hostname";
308   my @pipein=split(/[\r\n]+/,$p);
309   my $h=&who_will_change(\@pipein);
310   assert($h->{$hostname} eq '11.168.30.10');
311   `echo "DEVICE=eth0" > ifcfg-eth0`;
312   `echo "IPADDR=192.168.30.10" >> ifcfg-eth0`;
313   `echo "NETWORK=192.168.30.0" >> ifcfg-eth0`;
314   `echo "NETMASK=255.255.255.0" >> ifcfg-eth0`;
315   `echo "ONBOOT=yes" >> ifcfg-eth0`;
316   `echo "TYPE=Ethernet" >> ifcfg-eth0`;
317   `echo "GATEWAY=192.168.30.1" >> ifcfg-eth0`;
318   `echo "BROADCAST=192.168.30.255" >> ifcfg-eth0`;
319   use Cwd;
320   my $d=getcwd;
321   print "cwd=$d\n";
322   my $pingcmd=&get_pingcmd();
323   for my $k (keys %$h){
324     my $ret=0;
325     if(system("$pingcmd $k")!=0){
326       #meanless to ping self
327       if($s ne $a[0]) { next; }
328     }
329 
330     # $k: node1
331     #     node1 eth1
332     #     eckp1 ech1 default
333     my $netcard="eth0";
334     my @a=split(/[\s\t]+/,$k);
335     if($#a==2){
336       $netcard=$a[1];
337     }
338     my $try_telnet;
339     $ret=&change_ip($a[0],$h->{$k},$d,$netcard,\$try_telnet);
340   }
341   open(F,"<ifcfg-eth0");
342   my @lines=;
343   close(F);
344   chomp($lines[0]);assert($lines[0] eq "DEVICE=eth0");
345   chomp($lines[1]);assert($lines[1] eq "IPADDR=11.168.30.10");
346   chomp($lines[2]);assert($lines[2] eq "NETWORK=11.168.30.0");
347   chomp($lines[3]);assert($lines[3] eq "NETMASK=255.255.255.0");
348   chomp($lines[4]);assert($lines[4] eq "ONBOOT=yes");
349   chomp($lines[5]);assert($lines[5] eq "TYPE=Ethernet");
350   chomp($lines[6]);assert($lines[6] eq "GATEWAY=11.168.30.1");
351   chomp($lines[7]);assert($lines[7] eq "BROADCAST=11.168.30.255");
352 }
353 
354 if(0){
355   my ($name,$dir,$suffix)=fileparse($0);
356   print "name=$name;dir=$dir;suffix=$suffix;\n";
357   assert(-e $dir."chg_jnior_ip.exp");
358   my $pingcmd=&get_pingcmd();
359   my $e1ip=&get_ip("node1-auto");
360   if(!system("$pingcmd node1-auto")){
361     print "ip of node1-auto=$e1ip\n";
362     assert($e1ip ne "192.168.30.52");
363     assert(system("$pingcmd 192.168.30.52"));
364     if(system($dir."chg_jnior_ip.exp node1-auto 192.168.30.52")){
365       assert(0);
366     }
367     sleep(8); #wait jnior
368     assert(!system("$pingcmd 192.168.30.52"));
369     assert(system("$pingcmd node1-auto"));
370     if(system($dir."chg_jnior_ip.exp 192.168.30.52 $e1ip")){
371       assert(0);
372     }
373     sleep(8); #wait jnior
374     assert(system("$pingcmd 192.168.30.52"));
375     assert(!system("$pingcmd node1-auto"));
376   }
377   else{
378     print "cannnot connect to node1-auto!\n";
379   }
380 }
381 
382 if(1){
383   my ($network,$broadcast)=&get_netprop("192.168.1.1");
384   assert($network eq "192.168.1.0");
385   assert($broadcast eq "192.168.1.255");
386 }
387 
388 
389 if(1){
390   my $h=&get_hosts_from_arp();
391   foreach my $k (keys %$h){
392     print "key=$k;val=$h->{$k}\n"; #debug
393   }
394 }
395 
396 }
397 
398 sub a()
399 {
400   if(!$NDEBUG){
401     assert($_[$[]);
402   }
403 }
404 
405 ############################
406 # subroutines
407 ############################
408 sub get_ip{
409   my $hostname=shift;
410   my $pingcmd=&get_pingcmd();
411   my $ip=`$pingcmd $hostname`;
412   #PING node1 (192.168.30.111) 56(84) bytes of data.
413   #64 bytes from node1 (192.168.30.111): icmp_seq=0 ttl=64 time=0.129 ms
414   my @a=split(/[\r\n]+/,`$pingcmd $hostname`);
415   chomp($a[0]);
416 # on cygwin, the first line is empty
417   if($a[0] eq ""){ $a[0]=$a[1]; }
418   my @v=split(/[\s\t]+/,$a[0]);
419   $v[2]=~s/[\(\)\[\] ]*//g;
420   if(!&is_valid_ip($v[2])){
421     return $hostname;
422   }
423   return $v[2];
424 }
425 
426 sub is_interactive {
427   return -t STDIN && -t STDOUT;
428 }
429 
430 
431 sub who_will_change
432 {
433   my $ls=shift;
434   my %h=();
435   my @rlt=grep(/^[0-9\.]+.*/,@$ls);
436   # change ip address to 127.0.0.1 is crazy idea!
437   my @rlt=grep(!/^127\.0\.0\.1.*/,@rlt);
438   foreach (@rlt){
439     chomp($_);
440     if($_ eq ""){next;}
441     my @a=split(/[\s\t]+/);
442     if($#a == 0){next;}
443     my $k=$a[1];
444     for(my $i=2;$i<=$#a;$i++){
445       $k.=" ".$a[$i];
446     }
447     $h{$k}=$a[0];
448   }
449   return \%h;
450 }
451 
452 
453 sub change_etc_hosts
454 {
455   my $f=shift;
456   my $h=shift;
457   open(F,$f);
458   @lines=;
459   close(F);
460   for my $k (keys %$h){
461     #print "k=$k\n"; #debug
462     my $need_replace=0;
463     my $replaced=0;
464     my @a=();
465     for(my $i=0;$i<=$#lines;$i++){
466       @a=split(/[\s\t]+/,$k);
467       if( $#a == 2 && ($a[2] eq "none") ){
468         next;
469       }
470 
471       if(&is_valid_ip($a[0])){
472         next;
473       }
474 
475       $need_replace=1;
476       $_=$a[0];
477       if($lines[$i]!~/^[\d\.]+[\s\t]+$_[\s\t\r\n]+/){
478         next;
479       }
480 
481       $lines[$i]=~s/^[\d\.]+/$h->{$k}/g;
482       $replaced=1;
483 
484       #print "line=".$lines[$i]; #debug
485     }
486 
487     if( $need_replace && !$replaced ){
488       push(@lines,"$h->{$k} $a[0]\n");
489     }
490   }
491   #foreach (@lines){print "line=$_";}; #debug
492   open (F,">$f");
493   print F (@lines);
494   close(F);
495 }
496 
497 sub get_pingcmd
498 {
499   my $s=`uname`;chomp($s);
500   if($s eq "Linux"){
501     return "ping -c3";
502   }
503   return "ping";
504 }
505 
506 sub set_one_card
507 {
508   my $addr=shift;
509   my $ifcfg=shift;
510   my @p=split(/\./,$addr);
511   open(F,"<$ifcfg");
512   my @lines=;
513   for(my $i=0;$i<=$#lines;$i++){
514     if($lines[$i]=~/IPADDR=.*$/){
515       $lines[$i]="IPADDR=$addr\n";
516     }
517     elsif($lines[$i]=~/NETWORK=.*$/){
518       my @orig=split(/=/,$lines[$i]);
519       chomp($orig[1]);
520       my @o=split(/\./,$orig[1]);
521       $s="$p[0].$p[1].$p[2].$o[3]";
522       $lines[$i]="NETWORK=$s\n";
523 
524     }
525     elsif($lines[$i]=~/BROADCAST=.*$/){
526       my @orig=split(/=/,$lines[$i]);
527       chomp($orig[1]);
528       my @o=split(/\./,$orig[1]);
529       $s="$p[0].$p[1].$p[2].$o[3]";
530       $lines[$i]="BROADCAST=$s\n";
531     }
532     elsif($lines[$i]=~/GATEWAY=.*$/){
533       my @orig=split(/=/,$lines[$i]);
534       chomp($orig[1]);
535       my @o=split(/\./,$orig[1]);
536       $s="$p[0].$p[1].$p[2].$o[3]";
537       $lines[$i]="GATEWAY=$s\n";
538     }
539   }
540   close(F);
541   open (F,">$ifcfg");
542   print F (@lines);
543   close(F);
544   return 0;
545 }
546 
547 sub get_netprop
548 {
549   my $addr=shift;
550   my @p=split(/\./,$addr);
551   my $network="$p[0].$p[1].$p[2].0";
552   my $broadcast="$p[0].$p[1].$p[2].255";
553   return ($network,$broadcast,);
554 }
555 
556 # Return: 
557 sub change_ip
558 {
559   my $k=shift;
560   my $addr=shift;
561   my $dir=shift;
562   my $netcard=shift;
563   my $tr=shift;
564 
565   my $ret=0;
566   my $s=`hostname`;chomp($s);
567   my ($network,$broadcast)=&get_netprop($addr);
568   if( $s eq $k){ #local
569     #print "s=$s;k=$k\n"; #debug
570     $ret=&set_one_card($addr,"$dir/ifcfg-$netcard");
571     if($ret){ &a(0); return $ret;}
572     return $ret;
573   }
574   #remote
575   my $u=&unique_id();
576   #print "k=$k\n"; #debug
577   $ret=system("scp root\@$k:$dir/ifcfg-$netcard /tmp/$u.ifcfg-$netcard 
578               >/dev/null 2>&1");
579   if($ret){ # maybe jnior
580     $$tr=1;
581     my ($name,$dir,$suffix)=fileparse($0);
582     &a(-e $dir."chg_jnior_ip.exp");
583     $ret=system($dir."chg_jnior_ip.exp $k $addr >/dev/null 2>&1");
584     return $ret;
585   }
586   else{
587     $$tr=0;
588   }
589   $ret=&set_one_card($addr,"/tmp/$u.ifcfg-$netcard");
590 
591   if($ret){ &a(0); return $ret;}
592   $ret=system("scp /tmp/$u.ifcfg-$netcard root\@$k:$dir/ifcfg-$netcard 
593               >/dev/null 2>&1");
594   system("rm -f /tmp/$u.ifcfg-$netcard >/dev/null 2>&1");
595   if($ret){ &a(0); return $ret;}
596   return $ret;
597 }
598 
599 sub unique_id()
600 {
601   my $sessionId  ="";
602   for($i=0 ; $i< 32 ;)
603   {
604     $j = chr(int(rand(127)));
605     if($j =~ /[a-zA-Z0-9]/)
606     {
607       $sessionId .=$j;
608       $i++;
609     }
610   }
611   return $sessionId;
612 }
613 
614 sub is_valid_ip
615 {
616   my $ip=shift;
617   if( $ip !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ ){
618     return 0;
619   }
620   my @a=split('\.',$ip);
621   #print "$a[0] $a[1] $a[2] $a[3]\n";#debug
622   if($a[0]>255){ return 0;}
623   if($a[1]>255){ return 0;}
624   if($a[2]>255){ return 0;}
625   if($a[3]>255){ return 0;}
626   return 1;
627 }
628 
629 sub who_are_pingable
630 {
631   my $h=shift;
632   my %p=();
633   for my $k (sort keys %$h){
634     my @a=split(/[\s\t]+/,$k);
635     my $s=`hostname`;chomp($s);
636     if($a[0] eq $s){
637       print "test $a[0] ... connectable\n";
638       $p{$a[0]}=$USE_SSH;
639       next;
640     }
641     my $pingcmd=&get_pingcmd();
642     print "test $a[0] ... ";
643     if(system("$pingcmd $a[0] >/dev/null 2>&1")!=0){
644       print "NOT connectable\n";
645       next;
646     }
647     else{
648       print "connectable\n";
649     }
650 
651     $p{$a[0]}=1;
652   }
653   return \%p;
654 }
655 
656 sub get_hosts_from_arp
657 {
658   my @a=grep(!/incomplete/,split(/[\r\n]/,`arp -a`));
659   my %h=();
660   foreach my $l (@a){
661     my @s=split(/[\s\t\(\)]+/,$l);
662     $h{$s[0]}=$s[1];
663   }
664   return \%h;
665 }
666 
667 sub usage
668 {
669   my ($name,$dir,$suffix)=fileparse($0);
670   print STDERR << "EOF";
671 USAGE
672         $name [OPTION] "ip hostname [netcard default/none]"
673 
674 DESCRIPTION
675         Change IP of remote hosts.
676         -h    : this (help) message
677         -d    : self test and print debugging messages to stderr
678         -a    : use `arp -a` to verify remote hosts reachable
679                 `$name "ip host"` (without either -a or -p)  
680                 equals `$name -a "ip host"`
681         -p    : use `ping` to verify remote hosts reachable
682 
683         netcard      : the name of the netcard
684         default/none : default - the changed ip will be recorded in /etc/hosts
685                        none    - contrary to "default"
686 
687 EXAMPLE
688         echo 192.168.30.151 manager|$name
689         echo 192.168.30.151 manager eth1 none|$name 
690         echo 192.168.30.151 manager eth1 none|$name  -p
691         cat /etc/hosts|$name -a
692         $name "192.168.30.151 manager"
693         $name "192.168.30.151 manager eth0 default"
694 
695 AUTHOR
696         Chen Bin 
697 
698 EOF
699 }
700 sub init()
701 {
702   use Getopt::Std;
703   my $opt_string = 'hdap';
704   getopts( "$opt_string", \%opt ) or usage();
705 }
706