@roylez: bizarrely trapped

记录root命令到syslog和防止误重启操作

| Comments

一下子又是几个月没写呢。总是觉得没东西值得写到blog上,但是又总觉得有代码需要 码。想想还是把这点小东西写一下吧。

最近一台大家共着用的服务器被人误重启了。本来为了防着这种事情发生,我已经很限制 root权限的分发了。但是偏偏那个人用到的脚本非要root来跑(鄙视一下写脚本的人), 没办法,只好给他了root。结果呢,服务器误重启,大概是他手抖了吧。

既然没办法不给root,那么只好想些别的辙来确保服务器的运行。

记录root命令到syslog

这个是为了可以找苦主。以前的服务器上,大家都是root,重启了,谁跑的命令都不知道 ,太糟糕。事后想找个人骂都找不到。

稍稍google了下,在zshrc里面加上了这么一段

1
2
3
4
5
6
7
zshaddhistory() {
    cmd=${1%%$'\n'}
    print -sr -- $cmd
    LASTCMD="${(pj:\\\n:)${(f)1}}"
    first_arg=${${(z)LASTCMD}[1]}
    ( [[ `id -u $USER` -eq 0 ]] || [[ $first_arg == sudo ]] ) && logger -t "$USER@${TTY/\/dev\/}[$PWD]" $LASTCMD
}

写得很烂,因为我懒。值得注意的是print -sr -- $cmd是输出命令到 ~/.zsh_history而不是屏幕。最后一句很简单,如果执行用户是root,或者命令的第 一个词是sudo,那么就把命令记录到syslog里面去。输出大概是这个样子

Apr  5 06:26:43 bunker root@pts/22[/root]: source .zshrc.local

这只是事后药,而且缺点也很明显,如果用户输入的sudo不在句首,就无法记录了。懒得 管了,咱zsh也就这水准。。。

用alias来防止误重启

好了,怎么防止root用户手一抖就reboot了呢?

失败的方案:

  • 替换/sbin/{reboot,halt,shutdown}:这个太坑人,这些文件不是我们应该碰的。 用包管理装上的文件,除了/etc/底下的,其他都是我不想碰的。

  • 自己写个wrapper脚本,放到路径里面/sbin的前面:先不说脚本的维护,光是路径 里面有同样名字的可执行文件,这就很糟糕了。

我的方案:root用户的zshrc里面加

1
2
3
4
5
6
confirm_yes() {
    sure=$(dialog --stdout  --inputbox "Are you sure that you want to run '$1' command? Type YES to confirm." 10 50)
    [[ $sure == YES ]] && $1
}

for c in reboot halt shutdown; do alias $c="confirm_yes $c"; done

这样用户在输入危险的关机命令的时候,会跳出来一个dialog提示输入大写YES来确认,否 则命令不会被执行。完全不需要另外写wrapper。

参考

http://jablonskis.org/2011/howto-log-bash-history-to-syslog/

ZSH把命令结果显示在screen的分割窗口中

| Comments

一直懒得写这个,一个挺纠结的功能。正好有人让我测试Archlinux下octopress的语法高亮,所以干脆就写了。

这东西是用来在screen里面开一个split的窗口,用以显示当前命令的结果的。比如说想开个man,对照着看,或者说tail -f一个log文件的时候,应该比较合适。开了新窗之后,焦点保留在原来的窗口,可以用c-a c-i跳转到新窗口,这时候按下任意键新的分割窗口就关掉。这是从screen的源代码包里面看到的一过小tips。

似乎很不错的想法。但是,纠结点在于,如果像我这样,本来screen就是当作terminal mutiplexer来用, 那你的所有的窗口都会分割成上下两个!查了下文档,没有找到解决办法。搞不懂irssi的某些用screen来 显示在线人数列表的插件是怎么做到的,居然没有这样的问题。

还是把代码贴一下吧。

1
2
3
4
5
6
7
8
9
10
11
function run-command-in-split-screen() {
    screen -X eval \
        "focus bottom" \
        split \
        "focus bottom" \
        "screen $HOME/bin/screen_run $BUFFER" \
        "focus top"
    zle kill-buffer
}
zle -N run-command-in-split-screen
bindkey "\e\r" run-command-in-split-screen

12月11日在中科大关于zsh的讲座

| Comments

受到朋友之邀,去中科大讲讲Linux。本来他的原意是让我讲点简单的东西,最好是能够勾起一些学生使用Linux的兴趣。不巧的是,USTC LUG在月初正好有个面向新手的install party,简单的已经被人讲过了。这回讲座的对象,于是变成了“稍有经验的Linux用户”,内容方面,也求我稍晚来点有难度的。

想来想去,zsh估计国内没几个人讲过,花了2天写了个beamer的slides,基本上覆盖了我了解的zsh的各个方面。讲的的时候严重超时,原以为30分钟能讲完的,花了一整个小时。presentation skill有待提高,太没时间感了。

这次的演示内容可以从这里下载。我也附上了LyX源文件,有兴趣的朋友可以下载来看看。里面几个引用到的图片视频什么的,就省略掉了,看得明白就行了。

迁移jekyll网站到octopress

| Comments

生命不息,折腾不止。

jekyll已经有好一阵了,很简单,不操心,但是界面总觉得太阳春。自己折腾界面吧,实在没有那个力气和兴趣,不折腾呢,又有些于心不甘。偶然发现了octopress这个项目,非常兴奋,这不正式我所需要的么?

octopress是基于jekyll的一个博客引擎,用markdown格式写文章,然后编译成静态网页。更棒的是,这东西整合了disqus、twitter和google analytics,这基本上是所有我需要的东西的集合了。界面当然是比没包装过的jekyll舒服多了,另外还有专门用来在heroku和github上部署的指南,很贴心。

官方的安装指南说得很明白详细了。下面说说我遇到的一些问题。

  • git clone回来代码,进入目录后rvm报错。

    这个是因为目录里面的.rvmrc指定的ruby版本跟rvm里面安装的不一样。我的.rvmrc里面写的是

      rvm 1.9.2-head
    
  • Archlinux下看不到语法高亮的代码段。

    octopress使用了pygmentize.rb,这个依赖于rubypython。rubypython只支持python 2.x,但是archlinux默认用的是3.x。解决办法是在plugins/pygments_code.rb中加上这么一句:

1
    RubyPython.configure :python_exe => 'python2.7'
  • 即使在_config.yml里加了disqus的信息,也看不到disqus评论

    octopress需要在每个post的YAML front matter里加上comments: true才会显示评论。一条sed命令就搞定

1
    sed -ie '1acomments: true' *

其他似乎就没什么了。哦,另外,别忘了改permalink的格式,否则disqus找不到评论页面的。

说说Linux下支付宝控件

| Comments

其实这个没什么,豆丁大小的事情,但是不吐不快呢。

支付宝,国内网上支付的龙头老大,在好久之前就号称“发布了Linux”下的安全控件,不过我反复几次不死心的折腾,才终于在这个月用上了这东西。太多的pitfall了,测试是铁定没怎么做的,摆明了就是要坑爹的。

如果你在Linux下,用firefox连到支付宝,支付宝会很贴心的弹出一个框,让你安装支付宝安全控件。本来很直观的一个东西,一个tar.gz包,下载下来,解压缩,把里面的文件拷贝到~/.mozilla/plugins,这样就搞定了对吧?错,人家居然只给你一个aliedit.sh!神马玩意啊?打开看开…..ZOMG!他们把插件binary直接cat到sh文本的后面了。整个文件后半截全是乱码!!!

考虑到这是个公司出的,质量上不能跟开源界看齐,那么,就认了(忍了)吧!执行这个shell脚本,刷刷地提示你:插件安装成功!重启firefox,又蹦出来那亲切的安装安全控件的提示:亲,您还没安装支付宝安全控件呢~~~

这摆明了就是坑爹的。

经过多方搜索,我终于发现,archlinux的AUR里面的aliedit是可以用的!?而支付宝官方页面每次提醒你的那个,其实根本就不能用!真是太有创意了,有好的,却把烂的摆在最显眼的地发。

另外aliedit依赖libpng12,或者更准确的说应该是libpng12.so.0,这一点在支付宝在哪里都没有提。我从libpng14做了个符号链接,好了。

综合以上,个人对于支付宝对Linux平台支持有了个大体印象:

  • 这支持只是领导一句话带来的,领导只是摸摸脑袋觉得“应该要支持”,而并没有实际的督促把这件事做好

  • 开发这插件的,最多不超过3个人

  • 开发插件其中一个人比较有发言力,所以才会做出把binary直接cat到脚本后的事情,这个人很骚包

  • 开发插件的人平常用Windows

综上,支付宝的Linux版安全控件,一个打酱油的产物。诚意?没有。不过总好过什么也没有。起码不能手机上网的Linux用户提供了便利,前提是,这些用户有耐心和决心并最终装上了这插件。

一些ssh通用的配置

| Comments

是写~/.ssh/config的,由于文件后面一堆host,不太适合放到github,所以把通用的配置放这里

ServerAliveInterval 180
AddressFamily inet
Compression yes

# make connection faster
# http://blogs.perl.org/users/smylers/2011/08/ssh-productivity-tips.html
GSSAPIAuthentication no

# share ssh session
ControlMaster auto
ControlPath /tmp/ssh_mux_%h_%p_%r

# keep alive for 2h
ControlPersist 2h

用hostapd自己架无线AP

| Comments

确实老是感觉到没什么值得一写的,但是没想到这次居然时隔半年才有一点点觉得改写的东西。

最近买了一只kindle,颇为不错的东西。住的地方的路由器却跟kindle兼容性不太好,kindle怎么也连不上wifi。 怎么办?Kindle3是不支持ad-hoc无线网络的,所以这条路就绝了。我先是想到去买一只无线路由器,蹭网中继邻 居家的信号给kindle用。于是去买了一只Fast FW300R V2。 东西还没寄到,cfy在聊天室里提醒了我:其实,普通的无线网卡是可以拿来做AP用的,master mode,跟路由一样,不是ad-hoc。 这样的话,用无线网卡模拟出来一个路由信号不就可以了吗?只不过我笔记本的内置无线网卡是需要拿去连家里的路由器的,所以我需要一只外置的usb无线网卡。

把路由器处理给了Meaculpa之后就开始选网卡。挑来挑去最后选中了Mercury MW150u

这款网卡采用AR9271芯片,Linux下用ath9k_htc驱动(内核自带)。支持AP模式,支持monitor模式。有monitor模式,就可以和aircrack-ng搭配了破解无线网路密码,有AP模式,就可以用来自架AP。ath9k_htc驱动目前不支持150M模式,不过这已经很次要了。卓越27包邮,杀人放火居家旅行必备。另外值得一提的是, 这款网卡还有个马甲是Fast FW150u。

下面才是正题。

网上配置hostapd很乱,有的已经过时了。我走了不少弯路,花了几个小时才弄好。

  • 误区一:driver写上了网卡自己的驱动,比如ath9k_htc。这个已经out了,只要是符号80211n规范的网卡,都可以写网卡驱动之上更通用的一个叫做nl80211的东东的。
  • 误区二:配置br0来桥接无线网卡。br0在各个不同的发行版里面的配置方法都不一样,唯一相同的是不好配。而且这个我只看到用来桥接有线和无线的。两个无线网卡进行桥接能不能行还不知道。iptables转发规则要简单的多。
  • 误区三:不配dhcp。kindle用静态ip比用dhcp麻烦多了,输几个数字要死人的。
  • 误区四:dhcp没有配DNS。dhcp不配DNS,kindle是会果断拒绝连接的。

配置文件就没什么好说的了。以下以archlinux为例

  1. 安装hostapd,修改/etc/hostapd/hostapd.conf。这是一个WPA-PSK的配置,用hostapd -d /etc/hostapd/hostapd.conf来测试配置是否可用。

     interface=wlan1
     driver=nl80211
     ssid=vlad_is_here
     channel=6
     hw_mode=g
     ignore_broadcast_ssid=0
     auth_algs=1
     wpa=3
     wpa_passphrase=xxxxxxxx
     wpa_key_mgmt=WPA-PSK
     wpa_pairwise=TKIP
     rsn_pairwise=CCMP
    
  2. 起hostapd服务

     sudo /etc/rc.d/hostapd start
    
  3. 为新无线网卡指定IP

     sudo ifconfig wlan1 10.10.10.1 netmask 255.255.255.0
    
  4. 安装配置dhcp,/etc/dhcpd.conf如下

     default-lease-time 600;
     max-lease-time 7200;
    
     subnet 10.10.10.0 netmask 255.255.255.0 {
         range 10.10.10.10 10.10.10.100;
         option routers 10.10.10.1;
         option domain-name-servers 8.8.8.8;
         option ip-forwarding off;
         option broadcast-address 10.10.10.255;
     }
    
  5. 起dhcpd服务

     sudo /etc/rc.d/dhcp4 start
    
  6. 安装iptables,增加转发规则。这里我要把流量转发到wlan0

     sudo iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
    

收工!我发给kindle的书哗哗的就来了。

Rack-jekyll,新的heroku架jekyll方案

| Comments

升级了jekyll到0.10.0,突然发现新版本的jekyll会自动删除_site目录里面的旧文件,比如说.git目录!这不是坑爹吗?我以前的那种用hook自动从_site目录里面push去heroku的方法于是就不好使了。

搜索了下,已经有了专门针对jekyll和heroku的gem,名字就叫做rack-jekyll,改几个地方,再从jekyll目录push了下,好了:

  • config.ru,如今是以Rack app的模式运行,而非Sinatra app了
    require 'rack/jekyll'

    run Rack::Jekyll.new
  • Gemfile,以前的.gems的方法已经过时
    source :rubygems
    gem rack-jekyll
  • 安装bundle
    bundle install
  • 编译站点
    jekyll
  • 推到heroku
    git add .
    git push heroku master

收工!一切都回来了。

华硕UL30A的摄像头

| Comments

买了新笔记本有点时间了,配置稍稍花了点时间,但是一直懒得写。主要是东西都熟了,配置起来没有以前折腾的挫折感,没那么费劲了。乐趣少了很多。总的来说,这个笔记本除了键盘的键位很搞笑以及手感很差之外,其他方面没有让我操什么心。

华硕UL30A的摄像头,可以说是杂牌的

roylez@bender> lsusb G Webcam
Bus 001 Device 002: ID 064e:a136 Suyin Corp. Asus Integrated Webcam [CN031B]

用的是uvcvideo的驱动

roylez@bender> lsmod G video
uvcvideo               61372  0
videodev               64318  1 uvcvideo
v4l1_compat            15578  2 uvcvideo,videodev
v4l2_compat_ioctl32    10212  1 videodev
i2c_core               18790  5 i915,drm_kms_helper,drm,videodev,i2c_algo_bit
usbcore               137570  4 uvcvideo,uhci_hcd,ehci_hcd
video                  19305  1 i915
output                  1940  1 video

但是uvcvideo驱动的摄像头,有可能出现图象上下颠倒的情况,很不幸,我这个就是。按照uvcvideo官方的说法是,有两种解决方案:

  1. 给uvcvideo的开发人Hans写信,具体说明摄像头的相关信息,他会在新版的驱动里面为这个型号的摄像头解决这个问题。

  2. 把电脑倒过来用。

我打算采用第一套方案。

记下用mplayer看摄像头图像的命令

mplayer tv:// -tv driver=v4l2:width=640:height=480:device=/dev/video0 -fps 15 -flip

如果嫌麻烦

mplayer tv://

为笔记本的ACPI事件绑定快捷键

| Comments

我还是Thinkpad X32的时候,一直是用tpb来关联笔记本的acpi事件。省心是省心,不过弄得我一直觉得acpi好神秘的。现在换了Asus UL30A,发现在Arch下音量调节的快捷键都不能用了,没办法,只好自己动手。

起了个acpid,到/etc/acpi下瞟了眼,两个目录eventsactions,一个文件handler.sh。又翻了下里面的文件,大概明白了。

  • events目录里面是你要捕捉的acpi事件,文件格式如下(下面的是我用来定义华硕笔记本快捷键的)

    # 要捕捉的事件,正则匹配 event=hotkey / .* # 执行的脚本,用%e将事件的名字传递 action=/home/roylez/lib/acpi/asus_button.sh %e

  • actions是默认放执行的脚本的目录,但是你不放这里也没什么影响。比如我就把脚本放自己的home了。

  • handler.sh会捕捉其他没有被捕捉的事件,一般不需要改。

于是,我先用acpi_listen捕捉了静音、音量减小和音量加大的acpi事件,记下来,分别是hotkey ATKD 00000032 ...hotkey ATKD 00000031 ...hotkey ATKD 00000030 ...

然后,我写了个叫做asus_button.sh的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/bash
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 定义Asus UL30A acpi事件
script_dir=/home/roylez/bin
export DISPLAY=:0
set $*

# "hotkey ATKD 00000030 ...."
case $3 in
    00000032)   # mute
        su roylez -c "$script_dir/change_volume toggle"
        ;;
    00000031)   # volume down
        su roylez -c "$script_dir/change_volume down"
        ;;
    00000030)   # volume up
        su roylez -c "$script_dir/change_volume up"
        ;;
    0000005c)   # suspend-hybrid
        pm-suspend-hybrid &
        ;;
esac

再然后,我在我的bin目录里面放了个用来改变音量的脚本,就叫做change_volume

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/usr/bin/env ruby
# coding: utf-8
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 更改音量并用notify-send做出通知

$: << File.join(ENV['HOME'], 'lib/ruby')

require "msg.rb"

class VolumeControl
    def initialize
        @step = 5
        @notify = File.file?("/usr/bin/notify-send") ? :show_notification : nil
    end

    def mute_toggle
        set "toggle"
    end

    def set(control)
        `amixer sset Master #{control}`
        show
    end

    def down
        set("#{@step}%- unmute")
        `aplay #{ENV['HOME']}/.sounds/MACSound/Open.wav`
    end

    def up
        set("#{@step}%+ unmute")
        `aplay #{ENV['HOME']}/.sounds/MACSound/Open.wav`
    end

    def show
        @status = `amixer get Master`
        puts @status
        @notify.nil? || send(@notify)
    end

    private

    def show_notification
        volume = mute = nil
        if @status =~ /Playback.*?\[(.*?)%\].*?\[(on|off)\]/m
            volume = $1.to_i
            mute = $2
        end if volume && mute
            status = case volume
                     when 0..33     then    'low'
                     when 34..66    then    'medium'
                     when 67..100   then    'high'
                     end
            status = 'muted' if mute == 'off'
            icon = "/usr/share/icons/Tango/scalable/status/audio-volume-#{status}.svg"
            title = mute == 'off' ? "\n    扬声器已静音" : "\n    现在音量是 #{volume}%"
            msg :icon => icon, :text => '' , :title => title
        end
    end
end

if __FILE__ == $0
    require 'optparse'

    vc = VolumeControl.new

    case ARGV.first
    when 'help'
        puts "#{$0} <command>"
        puts "    help      show this help"
        puts "    show      display current volume info"
        puts "    up        increase volume"
        puts "    down      decrease volume"
        puts "    toggle    toggle mute"
    when "show"
        vc.show
    when "up"
        vc.up
    when "down"
        vc.down
    when "toggle"
        vc.mute_toggle
    end
end

里面用到了Tango的图标主题,以及苹果系统的声音主题(有兴趣的给我发信),然后就是一个叫做msg.rb的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env ruby
# coding: utf-8
#Author: Roy L Zuo (roylzuo at gmail dot com)
#Description: 通用提醒脚本,随机选择仆人

icon_path = File.join( ENV['HOME'], '.icons')
servants = Dir.glob(File.join(icon_path, 'servants', '*.png'))
$icon = servants[rand(servants.length)]
$title = '主人,提醒您一下:'

case ARGV.length
when 1
    $text = ARGV[0]
when 2
    $title, $text = ARGV
when 3
    $icon = File.exist?(ARGV[0]) ? ARGV[0] : \
        File.exist?(File.join( icon_path , ARGV[0])) ? File.join(icon_path,ARGV[0]) : icon
    $title, $text = ARGV[1..-1]
else
    $title = '错误'
    $text  = '参数无效'
end

def msg(kwds={})
    kwds = { icon:$icon, title:$title, text:$text }.merge(kwds)
    kwds[:text] = %Q{<span size="12000" weight="bold">\n#{kwds[:text]}</span>}
    system(%Q{DISPLAY=:0.0 notify-send -i #{kwds[:icon]} '#{kwds[:title]}' '#{kwds[:text]}'})
end

if __FILE__==$0
    msg
end

最后的效果是这样的

volume_change

最后说说感想

  • 最难的地方是su roylez -c。一直没有想到acpi脚本是用root权限执行这个问题,导致notify-send的通知怎么也不能显示,su成我的用户名就好了。
  • 把华硕自带的电源管理键绑成了休眠,比Fn Fx什么的方便多了。合上屏幕就是关闭屏幕,按电源管理就是休眠,按电源就是关机。
  • pm-suspend-hybrid还是个半成品,目前等于suspend to memory。
  • change-volume脚本可以很方便的改用OSD,加一句判断什么的就成,不过我懒得动了。
  • tpb好难看。