分类 Linux 下的文章

本来树莓派(Raspberry Pi 2 Model B)在家中安静地连上Internet,并定时下载网盘上的电脑,就已经很实用了。但是发现花生壳推出针对树莓派的内网端口及socket5映射软件,就试玩了一下:

树莓派花生壳(内网版)攻略
http://service.oray.com/question/2680.html

该软件的作用就是把处于内网环境的树莓派的端口或socket5映射到外网,实现外网直接访问树莓派,可玩性一下子提高几个等级。但是由于是免费版,拥有N多限制,包括流量、响应时间、可映射端口数量等等。查了相关资料,有网友说其实是利用了ssh隧道来实现的,既然这样,为什么不自己部署一个?安全性及相关限制都可以自己设置的。

所需条件
1)外网可访问的服务器或VPS(下文简称VPS),并且可运行SSH服务,最好是Linux系统
2)可访问VPS的树莓派(下文简称RPi)
3)域名,非必须

原理
RPi通过ssh客户端主动连接VPS的ssh服务,并开启VPS上的端口,映射到PRi的端口。为了方便创建连接,VPS上的ssh服务需要无密码访问,这里采用了证书登录。由于网络断掉后,ssh客户端的连接也会断掉,所以采用autossh代替ssh客户端。

关于ssh服务
参考以下文章:

1)此文章说明了很多ssh服务的安全相关配置,可用作VPS上的ssh服务配置参考。
SSH 安全性和配置入门
https://www.ibm.com/developerworks/cn/aix/library/au-sshsecurity/

2)详细说明了如果配置证书登录ssh服务
ssh证书登录(实例详解)
http://www.cnblogs.com/ggjucheng/archive/2012/08/19/2646346.html

3)介绍ssh隧道的使用
SSH Tunnel解决无公网IP 80被封等问题
http://blog.bbzhh.com/index.php/archives/60.html

4)介绍用autossh的使用
autossh在Ubuntu上的配置 ssh 隧道
http://yuanxiao.sinaapp.com/pages/122/132/377/article_index.html

配置
1)RPi上,创建证书。需要输密码时,直接按回车跳过。

ssh-keygen -t rsa

这里采用了rsa方式,默认生成两个文件:公钥(id_rsa.pub)和私钥(id_rsa)。把公钥(id_rsa.pub)文件,上传到VPS。

2)VPS上,生成授权文件,把自制的公钥都放入去。

cat id_rsa.pub >> ~/.ssh/authorized_keys

3)VPS上,安装并配置sshd。配置文件路径/etc/ssh/sshd_config,比较重要的配置项如下:

# 禁用root账户登录,增加安全性,非必要
PermitRootLogin no

# 是否让 sshd 去检查用户家目录或相关档案的权限数据,
# 避免使用者将某些重要文件的权限设错,而可能导致一些问题发生。
# 例如使用者的 ~/.ssh/ 权限设错时,某些特殊情况下会不许用户登入
StrictModes no

# 是否允许用户自行使用成对的密钥系统进行登入行为,仅针对 version 2。
RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      %h/.ssh/authorized_keys

# 禁用密码登录,增加安全性
PasswordAuthentication no

配置完成后,记得重启sshd服务,使配置生效。

4)VPS上,安装TCP Echo服务,供autossh判断网络隧道连通情况。默认运行端口为7。

apt-get install openbsd-inetd

安装后,需要修改配置文件/etc/inetd.conf,在文件末尾增加以下内容:

echo        stream    tcp    nowait    root    internal

然后就可以启动服务了。

5)RPi上,用autossh连接VPS并进行端口映射

# 安装autossh
sudo apt-get install autossh

# 创建port_forward.sh文件
cat >> port_forward.sh << EOF
#!/bin/bash
export AUTOSSH_PIDFILE=/var/run/autossh.pid
export AUTOSSH_POLL=60
export AUTOSSH_FIRST_POLL=30

# -f是后台运行
# -M 后台管理端口:VPS的echo服务端口
# -NR VPS端口:本机地址:本机端口,本例把RPi端口80映射到VPS端口8080
# 后面的就是登录相关信息
autossh -f -M 4567:7 -NR 8080:localhost:80 user@vps_name.net -p 22 -i /path/to/id_rsa 

# 设置可执行权限
chmod 755 port_forward.sh

# 运行
./port_forward.sh

6)最后就可以直接访问http://vps_name.net:8080就可以访问RPi端口80上的服务了。

经过一段时间的研究和实践,终于把树莓派(Raspberry Pi 2 Model B)作为家庭服务器用起来了。简单来说,就是树莓派作为无线路由、网盘下载机和提供DLNA服务,Chromecast作为播放器在电视机播放多媒体内容,用Android手机作为遥控器控制一切。

树莓派
1)无线路由
参考前面的Bolg
Setup Raspberry Pi 2 as Wireless Router
http://blog.foxail.org/index.php/archives/661/

2)网盘下载机
这里选择了百度盘,其空间足够大(免费2TB空间),有Linux的客户端(第三方开发的)。树莓派上安装客户端:
https://github.com/GangZhuo/BaiduPCS

然后设置个定时同步下载,就可以自动下载电影了。下载前,还会上传当前的下载状态及硬盘空间使用情况到网盘,方便查看。

3)安装USB存储设备
参考以下文章,增加自动挂载USB存储设备:
树莓派自动挂载usb移动存储设备
http://rpi.linux48.com/usbstorage.html

该脚本在Debian 8上有问题,修改了一下,如下:

KERNEL!="sd*", GOTO="media_by_label_auto_mount_end"
SUBSYSTEM!="block",GOTO="media_by_label_auto_mount_end"
IMPORT{program}="/sbin/blkid -o udev -p %N"
ENV{ID_FS_TYPE}=="", GOTO="media_by_label_auto_mount_end"
ENV{ID_FS_LABEL}!="", ENV{dir_name}="%E{ID_FS_LABEL}"
ENV{ID_FS_LABEL}=="", ENV{dir_name}="Untitled-%k"
ACTION=="add", ENV{mount_options}="relatime,sync"
ACTION=="add", ENV{ID_FS_TYPE}=="vfat", ENV{mount_options}="iocharset=utf8,uid=1000,gid=1000,umask=000"
ACTION=="add", ENV{ID_FS_TYPE}=="ntfs", ENV{mount_options}="iocharset=utf8,uid=1000,gid=1000,umask=000"
ACTION=="add", RUN+="/bin/mkdir -p /media/%E{dir_name}", RUN+="/bin/mount -o $env{mount_options} /dev/%k /media/%E{dir_name}"

ACTION=="remove", ENV{dir_name}!="", RUN+="/bin/umount -l /media/%E{dir_name}", RUN+="/bin/rmdir /media/%E{dir_name}"
LABEL="media_by_label_auto_mount_end"

如果是U盘,直接插上就可以了。但如果是移动硬盘,要考虑树莓派的电流不足的问题,会导致硬盘mount不上,或者不能写入数据。不得不换了个2A的USB电源和有源USB Hub。现在算是能顺利运行了,但还是想简化电路和连接方式,后面再考虑。

4)DLNA服务
装个minidlna就可以了。如果需要识别rmvb文件,需要改源码并重新编译。参考这个:
Raspiberry Pi安装minidlna1.1.4并支持rmvb
http://raspi.vanabel.info/wordpress/?p=92

Chromecast
国内真的不推荐用这个,因为要连上google才能使用。为了连上google,花了很多时间去研究,最后采用简单的方案:设置某个固定ip采用代理。本文前面的“无线路由”配置中,包含了相关配置。

关于ROOT。感觉不获取ROOT权限,也不会影响使用。不过个人习惯,ROOT后会增加更多的可能性。并且,升级到最新版系统后,目前不能ROOT了。

检查是否可ROOT:[INFO] Rootable Serial Numbers
http://forum.xda-developers.com/showthread.php?t=2537022

ROOT教程,需要借助硬件Leonardo Pro Micro(基于芯片ATmega32U4):Chromecast ROOT
http://raspberrypihelp.net/motornavigatie-dutch/62-chromecast-root

Android手机
目前安装了connectbot,作为SSH客户端连接树莓派。项目源码:
https://github.com/connectbot/connectbot

DLNA方面,采用BubbleUPnP,可以用本机播放树莓派的多媒体内容,可以把树莓派的多媒体内容推送到Chromecast。Google Play下载:
https://play.google.com/store/apps/details?id=com.bubblesoft.android.bubbleupnp

上星期在公司的服务器上部署了OpenVPN,居然没有想象中的难。记录一下吧。

1)添加软件源
参考:CentOS推荐使用EPEL源
http://blog.51yip.com/linux/1337.html

添加软件源后就方便多了。N多软件直接yum安装。

2)安装OpenVPN
参考1:OpenVPN for CentOS
https://docs.ucloud.cn/software/vpn/OpenVPN4CentOS.html

参考2:CentOS6.4配置OpenVPN
http://www.chenshake.com/centos6-4-configuring-openvpn/

基本就是yum安装OpenVPN及相关软件,然后设置并生成相关证书。客户端使用相关证书即可。

3)设置SELinux
参考:Run OpenVPN on non-standard port with SELinux and Centos 6
http://www.jacobtomlinson.co.uk/2014/12/08/openvpn-non-standard-port-selinux-centos-6/

设置SELinux,使得OpenVPN的自定义端口可以访问。如运行了iptable的话,也要设置该端口可访问。

垂涎于Raspberry Pi 2 Model B的四核性能及低廉的价格,待其跌到200RMB左右时,终于还是入手了一个。

装完Raspbian,立马试了XMBC/Kodi媒体中心。效果还是跟以前一样,不尽人意。无它的,就是视频和音频的编码支持不完善,导致很多视频格式都不支持。(为了解决电视机播放手机视频的问题,入手了Chromecast。但Chromecast也不是个省油的灯,后面再说吧。)于是Pi只能放在后台,作为路由和服务器来使用了。

需求:增强无线网络覆盖范围,即作为桥接的无线路由,手机和Chromecast可以通过其上网,特别Chromecast要可以访问Google。

硬件准备:
1)Raspberry Pi 2 Model B,1台。
2)16GB的TF卡,1个,刷上Raspbian。
3)无线网卡,2个(磊科NW338和水星MW150UH),并且有一个(水星MW150UH)支持Ad-hoc功能,即能开启无线热点功能。
4)USB风扇,1个,用于扇热。温度过高的话,会导致硬件性能下降,就是网络传输速度会变慢。

软件安装及设置:
1)初始化准备
参考以下文章,设置好无线网卡的接口名称。否则每次开机,每个无线网卡的接口名称都可能会改变。
Setting Network Interface Name on Raspbian
http://www.foxail.org/blog/index.php/archives/532/

我设置了“磊科 NW338”为wlan0,“水星 MW150UH”为wlan1,。wlan0用于连接无线路由,wlan1用于开启无线热点。

2)配置无线网卡
MW150UH 可能还需要第三方驱动,这里是已编译好的驱动文件:
(UPDATE) Drivers for TL-WN725N V2 - 3.6.11+ -> 4.1.xx+
https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=62371

确定网卡可以运行后,修改 /etc/network/interfaces 为以下内容:

auto lo
iface lo inet loopback
 
auto eth0
allow-hotplug eth0
iface eth0 inet manual
 
auto wlan0
allow-hotplug wlan0
iface wlan0 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
 
auto wlan1
#allow-hotplug wlan1
iface wlan1 inet static
  address 192.168.11.1
  netmask 255.255.255.0

3)hostapd
安装命令:

sudo apt-get install hostapd

由于采用了 水星 MW150UH ,芯片为RTL8188EU,所以参考了以下文章,使用相关已编译的 hostapd 文件来替换原来的 /usr/sbin/hostapd 。
Access Point (AP) for TP-Link TL-WN725N V2
https://www.raspberrypi.org/forums/viewtopic.php?f=91&t=54946

然后备份配置文件/etc/hostapd.conf,并改为如下内容:

interface=wlan1
driver=rtl871xdrv
ssid=rpi-ap
channel=1
hw_mode=g
auth_algs=1
wpa=2
wpa_passphrase=123456
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
#macaddr_acl=1
#accept_mac_file=/etc/hostapd/hostapd.accept

4)dnsmasq
安装命令:

sudo apt-get install dnsmasq

然后备份配置文件/etc/dnsmasq.conf,并改为如下内容:

interface=wlan1
bind-interfaces
#except-interface=lo
dhcp-range=192.168.11.100,192.168.11.150,255.255.255.0,6h
 
# chromecast bind ip
dhcp-host=aa:bb:cc:dd:ee:ff,my-chromecast,192.168.11.10

5)iptables
设置iptables,实现转发:

sudo iptables -F
sudo iptables -t nat -A POSTROUTING -s 192.168.11.0/24 -o wlan0 -j MASQUERADE
sudo iptables -A FORWARD -s 192.168.11.0/24 -o wlan0 -j ACCEPT
sudo iptables -A FORWARD -d 192.168.11.0/24 -m conntrack --ctstate ESTABLISHED,RELATED -i wlan0 -j ACCEPT

保存iptables配置:

sudo iptables-save > /etc/iptables.rules

设置系统启动时自动加载iptables的配置。修改文件/etc/network/if-pre-up.d/iptables为以下内容:

#!/bin/sh
/sbin/iptables-restore < /etc/iptables.rules

保存该文件后,设置其权限:

sudo chmod +x /etc/network/if-pre-up.d/iptables

6)设置转发
修改文件 /etc/sysctl.conf ,找到net.ipv4.ip_forward,改为:

net.ipv4.ip_forward=1

7)解决Chromecast连上google的问题
为解决这个问题,花了不少时间去探索和尝试。Chromecast就算ROOT了,也不能安装shadowsocks客户端。解决方法是,固定 Chromecast 的IP地址,该IP的所有连接走 Raspberry Pi 上 shadowsocks-libev 的客户端。由于国内支持 Chromecast 的应用几乎没有,所以没必要再考虑访问国内网站的问题。

具体部署步骤,shadowsocks-libev 的官方说明已经详细说明了:
https://github.com/shadowsocks/shadowsocks-libev#advanced-usage

简单记录一下我的操作:
a)安装shadowsocks-libev,主要用到 ss-redir 。直接去GitHub下载源码编译安装吧:
https://github.com/shadowsocks/shadowsocks-libev

b)设置iptables:

# 建立新的链路
sudo iptables -t nat -N SHADOWSOCKS

# 跳过Shadowsocks服务器IP,这里假设为123.123.123.123
sudo iptables -t nat -A SHADOWSOCKS -d 123.123.123.123 -j RETURN

# 跳过内网IP地址
sudo iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
sudo iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN

# 其他IP跳转到 ss-redir 的代理端口
sudo iptables -t nat -A SHADOWSOCKS -p tcp -j REDIRECT --to-ports 12345

# 设置 Chromecast 利用代理访问外网
sudo iptables -t nat -A PREROUTING -s 192.168.11.10/32 -p tcp -j SHADOWSOCKS

设置完,记得保存iptables配置:

sudo iptables-save > /etc/iptables.rules

c)配置并启动 ss-redir,记得本地端口要设置为与上面的一致。

ss-redir -u -c /etc/config/shadowsocks.json -f /var/run/shadowsocks.pid

最后,重启一下Raspberry Pi ,应该所有配置都会生效了。

差不多忙了一天,实现了CentOS上用Shell脚本备份SVN的项目。其实只要svnadmin hotcopy就可以实现svn项目的全量备份,但是想增加log文件,以记录备份是否成功。

一般的备份都不过是三个步骤:1)检查相关文件夹及文件;2)执行备份;3)删除旧的备份文件。

具体的代码如下:

#!/bin/sh
# 获取本文件所在的绝对路径
cd $(dirname $0)
CUR_PATH=$(pwd)
cd - > /dev/null

# 定义用户变量
# 注意:确保TMP_PATH设置为非空! 否则有可能导致根路径下的文件被删除!
# PROJECTS为svn的项目名称的数组
PROJECTS=("project1" "project2")
# SVN_PATH是svn的项目文件的所在路径
SVN_PATH=/opt/svn/projects
# BACKUP_PATH是备份文件的存放路径
BACKUP_PATH=${CUR_PATH}/daily_bak
# TMP_PATH是临时文件的存放路径
TMP_PATH=${CUR_PATH}/tmp
# DATE_STR是当前日期的字符串,备份文件名的组成部分
DATE_STR=$(date +%Y%m%d)

# 初始化相关目录
[ ! -d ${BACKUP_PATH} ] && mkdir ${BACKUP_PATH}
[ ! -d ${TMP_PATH} ] && mkdir ${TMP_PATH}
rm -rf ${TMP_PATH}/*

# 定义log文件
LOG_MSG=("" "[info]" "[warn]" "[error]")
LOG_FILE=${BACKUP_PATH}/svn_bak_log_${DATE_STR}.log
[ -f ${LOG_FILE} ] && rm -f ${LOG_FILE}
touch ${LOG_FILE}

# 函数,把消息记录到log文件的函数
function log()
{
     # $1: 第一个参数,消息类型,正整数,即数组$LOG_MSG的下标
     # $2: 第二个参数,文本,要记录到log文件的消息
     echo "${LOG_MSG[$1]} $2" >> ${LOG_FILE}
}

$(log 1 "Start backup svn $(date +%Y-%m-%d\ %H:%M:%S)")
if [ ! -d ${SVN_PATH} ]; then
    # 如果svn目录不存在,则退出
    $(log 3 "svn path not found: ${SVN_PATH}")
    exit 1
fi

# 执行备份
errInfo=""
for projName in ${PROJECTS[@]};
do
    $(log 0 "")
    $(log 1 "Backup SVN Project: ${projName} $(date +%Y-%m-%d\ %H:%M:%S)")
    bakFile=svn_${projName}_bak_${DATE_STR}.tar.gz
    
    # 复制svn的项目文件
    errInfo=$(svnadmin hotcopy ${SVN_PATH}/${projName} ${TMP_PATH}/${projName}/)
    if [ "$?" != "0" ]; then
        $(log 3 "svn hotcopy failed: ${projName}")
        $(log 0 ${errInfo})
        continue
    fi

    # 若存在同一天备份的文件,则删除
    [ -f ${BACKUP_PATH}/${bakFile} ] && rm -f ${BACKUP_PATH}/${bakFile}

    # 打包压缩备份文件
    cd ${TMP_PATH}
    errInfo=$(tar -zcPf ${BACKUP_PATH}/${bakFile} ${projName}/)
    cd ${CUR_PATH}
    if [ ! -f ${BACKUP_PATH}/${bakFile} ]; then
        $(log 3 "compress backup file failed: ${projName}")
        $(log 0 ${errInfo})
    fi

    # 删除没用的文件
    errInfo=$(rm -rf ${TMP_PATH}/${projName})
    if [ "$?" != "0" ]; then
        $(log 2 "clean files failed: ${projName}")
        $(log 0 ${errInfo})
    fi

    $(log 1 "Backup succeed: ${projName}")
done

# 清除旧的备份,由于是全量备份,所以只保留最新的备份就可以了
# 其中 -mmin 设置为120,即删除两小时前生成的文件,达到保留最新备份的目的
errInfo=$(find ${BACKUP_PATH} -mmin +120 -type f | xargs rm -rf)
if [ "$?" != "0" ]; then
    $(log 2 "clean old backup files failed: ${projName}")
    $(log 0 ${errInfo})
fi

$(log 1 "Backup svn finished")

最后,设置cron,实现自动备份就可以了。

还原备份文件,很简单,也是用svnadmin hotcopy命令,把备份文件复制到svn服务路径下就可以了。

tar -zxPf svn_project1_bak_20150710.tar.gz #解压备份文件
svnadmin hotcopy /backup/path/project1 /new/path/project1

今年4月份,Google drive的接口更新了,导致grive不可用。由于grive的作者已经N久没更新了,于是VPS的日常备份要做修改了。

一开始的方案是换个客户端。找来找去,就只有这个drive项目了:
https://github.com/odeke-em/drive
由于是用go写的,需要装go相关包。感觉很麻烦,于是放弃安装。

最后改为用Dropbox来做同步。优点是可选客户端比Google drive多,且安装方便。缺点就是免费空间小,默认只有2GB,目前还算够用。

Dropbox的Shell脚本客户端,只需要dropbox_uploader.sh文件就够了:
https://github.com/andreafabrizi/Dropbox-Uploader

参考了以下两个教程:
1)自动备份网站并同步到 Dropbox
http://www.lovelucy.info/backup-website-and-sync-to-dropbox.html
2)Linux VPS备份网站数据到Dropbox
https://maoxian.de/2013/01/linux-vps%E5%A4%87%E4%BB%BD%E7%BD%91%E7%AB%99%E6%95%B0%E6%8D%AE%E5%88%B0dropbox/755.html

简单总结一下步骤:
1)下载 dropbox_uploader.sh
2)登录 dropbox.com 并创建一个应用
3)初始化客户端,运行以下命令,并按提示输入App key和App secret,以及相关设置:

./dropbox_uploader.sh info

4)编写备份脚本。其实跟备份到Google drive的脚本差不多。
5)设置自动运行备份脚本,命令是:

crontab -e

备份脚本参考:

#!/bin/bash

# begin settings
# MySQL settings
MYSQL_USER=user #MySQL用于备份数据的用户
MYSQL_PASS=password #用户密码
MYSQL_BIN=/usr/bin
# backup directory and files
CUR_DIR=/opt/backup
TMP_DIR=$CUR_DIR/tmp
BAK_DIR=/opt/dropbox_bak/$(date +%Y%m%d)
BAK_DB_NAME=bak_$(date +"%Y%m%d")_mysql.tar.gz
BAK_FILE_NAME=bak_$(date +%Y%m%d)_file.tar.gz
# delete local backup files 20 days ago
BAK_EXPIRED=/opt/dropbox_bak/$(date -d -20day +"%Y%m%d")
# dropbox client
DROPBOX=$CUR_DIR/dropbox_uploader.sh
DROPBOX_DEL=/$(date -d -30day +%Y%m%d)
# website directory
WEB_DATA="/opt/www/"
# end settings

# Create the directory when not existed
if [ ! -d $BAK_DIR ]; then
    mkdir -p $BAK_DIR
fi
if [ ! -d $TMP_DIR ]; then
    mkdir -p $TMP_DIR
else
    rm -Rf $TMP_DIR/*
fi
cd $TMP_DIR

# begin backup database
DATABASES=`$MYSQL_BIN/mysql -h "127.0.0.1" -u$MYSQL_USER -p$MYSQL_PASS -Bse 'show databases'`

for DB in $DATABASES; do
    # ignore system tables
    if [ "$DB" == "information_schema" -o "$DB" == "performance_schema" -o "$DB" == "mysql" ]; then
        continue
    fi

    # export data to backup file
    $MYSQL_BIN/mysqldump -h "127.0.0.1" -u$MYSQL_USER -p$MYSQL_PASS $DB > $DB.sql
done

# compress and merge all the database backup files
tar zcf $BAK_DB_NAME *.sql
mv -f $BAK_DB_NAME $BAK_DIR/$BAK_DB_NAME
# end backup database

# backup website files
tar zcpf $BAK_FILE_NAME $WEB_DATA
mv -f $BAK_FILE_NAME $BAK_DIR/$BAK_FILE_NAME

# sync files
$DROPBOX upload -s $BAK_DIR /
$DROPBOX delete $DROPBOX_DEL

# delete local old files
rm -Rf $TMP_DIR/*
if [ -d $BAK_EXPIRED ]; then
    rm -Rf $BAK_EXPIRED
fi

dropbox_uploader.sh的操作跟一般的文件操作类似,可以上传指定的文件或文件夹。

好了,剩下的工作就是想办法免费扩容。

使用init脚本,无非就是实现软件服务的开机启动。但是用Debian或Ubuntu时,都是用apt-get来安装软件,而且服务类的软件都自带init脚本,对init脚本变得生疏了。工作中在CentOS上配置了两个开机启动脚本,并发现一个问题。

开机守护进程启动Tomcat6
用了Tomcat这么久,以前居然一直没做个这个配置。Tomcat官方网站推荐用jsvc守护进程来启动Tomcat。

关于jsvc,Apache官网上说得非常清楚了:
http://commons.apache.org/proper/commons-daemon/jsvc.html

关于Unix(包括Linux)系统上Tomcat6守护进程配置:
https://tomcat.apache.org/tomcat-6.0-doc/setup.html#Unix_daemon

这里的步骤简单为:
1)找到jsvc的源码包:tomcat6/bin/commons-daemon-native.tar.gz
2)解压、配置并编译

cd $CATALINA_HOME/bin
tar xvfz commons-daemon-native.tar.gz
cd commons-daemon-1.0.x-native-src/unix
./configure
make
cp jsvc $CATALINA_HOME/bin/

3)配置init脚本。jsvc源码包里有Tomcat5的init脚本,复制过来,按需要进行修改,并放到 /etc/init.d/ 目录下。例如文件名为 tomcat 。jsvc启动Tomcat后,可能根据Tomcat的启动情况来设置返回值(即通过$?获取的值),导致Tomcat正常启动后,jsvc仍会返回1,而不是0。因此,这里改为Tomcat启动后,通过检测相关端口来判断Tomcat是否启动成功并显示启动后的提示信息。脚本示例如下:

#!/bin/sh 
# 
# Startup Script for Tomcat6 
# 
# chkconfig: 345 98 02 
# description: Tomcat Daemon 
# processname: tomcat 
# pidfile: /var/run/tomcat.pid 
# config: 
# 
# Source function library.
. /etc/rc.d/init.d/functions 
# 
prog=tomcat

JAVA_HOME=/usr/local/jdk1.6.0_45
CATALINA_HOME=/usr/local/apache-tomcat-6.0.35
DAEMON_HOME=$CATALINA_HOME/bin
TOMCAT_USER=tomcat
HTTP_PORT=8080

# for multi instances adapt those lines.
TMP_DIR=$CATALINA_HOME/temp
PID_FILE=/var/run/tomcat.pid
CATALINA_BASE=$CATALINA_HOME

CATALINA_OPTS="-server -Xss512k -Xms64M -Xmx512M"
CLASSPATH=\
$JAVA_HOME/lib/tools.jar:\
$CATALINA_HOME/bin/commons-daemon.jar:\
$CATALINA_HOME/bin/bootstrap.jar

start() {
  echo -n $"Starting $prog: "
  
  #
  # Start Tomcat
  #
  $DAEMON_HOME/jsvc $JSVC_OPTS \
  -user $TOMCAT_USER \
  -home $JAVA_HOME \
  -Dcatalina.home=$CATALINA_HOME \
  -Dcatalina.base=$CATALINA_BASE \
  -Djava.io.tmpdir=$TMP_DIR \
  -wait 10 \
  -pidfile $PID_FILE \
  -outfile $CATALINA_HOME/logs/catalina.out \
  -errfile '&1' \
  $CATALINA_OPTS \
  -cp $CLASSPATH \
  org.apache.catalina.startup.Bootstrap
  #
  # To get a verbose JVM
  #-verbose \
  # To get a debug of jsvc.
  #-debug \

  # Check if Tomcat is running on HTTP_PORT
  lsof -i:$HTTP_PORT > /dev/null
  ret=$?
  if [ $ret -eq 0 ]; then
    echo_success
  else
    echo_failure
  fi
  echo
  return $ret
}

stop() {
  echo -n $"Stopping $prog: "
  
  #
  # Stop Tomcat
  #
  $DAEMON_HOME/jsvc $JSVC_OPTS \
  -stop \
  -pidfile $PID_FILE \
  org.apache.catalina.startup.Bootstrap
  
  ret=$?
  if [ $ret -eq 0 ]; then
    echo_success
  else
    echo_failure
  fi
  echo
  return $ret
}

restart() {
  stop
  start
}

case "$1" in
  start)
    start
    ;;

  stop)
    stop
    ;;

  restart)
    restart
    ;;

  *)
    echo "Usage $prog start/stop/restart"
    exit 1;;
esac

4)加入开机启动。利用chkconfig命令:

chkconfig --add tomcat
chkconfig tomcat on

所有Java程序都可以用jsvc来启动,但是,启动的类需要实现init(String[] args)、start()、stop()、destroy()这4个方法。服务在启动时会先调用init(String[] args)方法,然后调用start()方法,在服务停止是会首先调用stop()方法,然后调用destroy()方法。

直接启动Tomcat6
其实init脚本只是个含有特定格式(例如要求实现带start和stop参数的功能)的shell脚本。就是说,可以直接执行Tomcat6自带的启动和停止脚本($CATALINA_HOME/bin目录下的startup.sh和shutdown.sh)。

具体配置参考这个:
http://www.cnblogs.com/lianche/p/3154096.html

init脚本的模板如下:(以memcache为例)

#!/bin/sh
#
# Startup Script for memcached
#
# chkconfig: 345 65 35
# description: memcached
# processname: memcached
# pidfile: /var/run/memcached.pid
# config:
#
# Source function library.
. /etc/rc.d/init.d/functions
#
prog=memcached

FILE_HOME=/usr/local/memcache
PID_FILE=/var/run/memcached.pid

start() {
  echo -n $"Starting $prog: "
  
  $FILE_HOME/bin/memcached -d -m 128 -u root -l 0.0.0.0 -p 11211 -c 512 -P $PID_FILE

  retval=$?
  if [ $retval -eq 0 ]; then
    echo_success
  else
    echo_failure
  fi
  echo
  return $retval
}

stop() {
  echo -n $"Stopping $prog: "

  cat $PID_FILE | xargs kill -9

  ret=$?
  if [ $ret -eq 0 ]; then
    echo_success
  else
    echo_failure
  fi
  echo
  return $ret
}

restart() {
  stop
  start
}

case "$1" in
  start)
    start
    ;;

  stop)
    stop
    ;;

  *)
    echo "Usage $prog start/stop/restart"
    exit 1;;
esac

关于没有生成PID文件的程序
有些程序不像memcached那样把进程id记录到PID_FILE,于是就不能使用kill命令来关闭该进程了。虽然直接调用startup.sh和shutdown.sh来启动和关闭Tomcat也是没有PID_FILE,但是Tomcat会使用端口来关闭自己进程。遇到既没有PID_FILE,也没有像Tomcat那样有特殊的关闭方法的话,就自能自己用ps命令找到对应的进程id,然后kill掉。

以下以“花生壳(phddns)”为例。其中get_pid函数是获取进程号的。本来ps -C phddns -o pid=就可以获取到进程id了。但是由于启动脚本的文件名也是phddns,ps -C phddns会找到脚本所在的进程id,所以要再加上grep来找到/usr/bin/phddns命令的进程。

#!/bin/sh
#
# Startup Script for phddns
#
# chkconfig: 345 56 44
# description: phddns Daemon
# processname: phddns
# pidfile:
# config:
#
# Source function library.
. /etc/rc.d/init.d/functions
#
prog=phddns

get_pid() {
  #get the process id using ps
  echo $(ps -C phddns -o pid,command | grep '/usr/bin/phddns' | awk '{print $1}')
}

start() {
  echo -n $"Starting $prog: "

  PID_NUM=$(get_pid)

  if [ -n "$PID_NUM" ]; then
    echo_failure
    echo
    echo $prog is already running.
  else
    /usr/bin/phddns -c /etc/phlinux.conf -d > /dev/null

    ret=$?
    if [ $ret -eq 0 ]; then
      echo_success
    else
      echo_failure
    fi
    echo
  fi
  return $ret
}

stop() {
  echo -n $"Stopping $prog: "

  PID_NUM=$(get_pid)

  if [ -n "$PID_NUM" ]; then
    kill -9 $PID_NUM

    ret=$?
    if [ $ret -eq 0 ]; then
      echo_success
    else
      echo_failure
    fi
    echo
  else
    echo_failure
    echo
    echo $prog is not running.
  fi
  return $ret
}

restart() {
  stop
  start
}

case "$1" in
  start)
    start
    ;;

  stop)
    stop
    ;;

  restart)
    restart
    ;;

  *)
    echo "Usage $prog start/stop/restart"
    exit 1;;
esac

关于启动顺序
开机启动时,如果服务A依赖服务B来运行时,必须要先启动服务B,再启动服务A。通常在init脚本头(注释)中,用 chkconfig 及其后面3个参数来标记。例如:
chkconfig 345 65 35
345表示在init状态为3、4、5时启动,65表示启动顺序(只支持两位数字,数字越小,启动越早),35表示中止顺序(也只支持两位数字,数字越小,中止越早)。

那么,服务A的chkconfig第2个参数要大于服务B的。

生成的启动顺序,可以参照 /etc/rc.d 文件夹(号代表0到6)内的文件名称。其中K开头的表示中止,S开头的表示启动。

PS. 由于工作上用到了CentOS,相关的配置也用得越来越多。这是N年前想不到的事情。

网上看到关于Shadowsocks的优化,觉得有必要设置下自用的VPS。
shadowsocks 公共代理的必要设置
https://gist.github.com/fqrouter/95c037f9a3ba196fd4dd

但是VPS上的Shadowsocks一般只有自己在用,而且服务器限制了最大访问文件数。那么就剩下iptables值得设置了。

关于Debian的iptables,发现跟RedHat(或者CentOS)的不同。最后还是参照了官方Wiki进行了配置。
https://wiki.debian.org/iptables

PS. 本来大学时就学过iptables了,但是现在用来,发现基本忘干净了。

入手Kobo mini一个月后,选择了放弃,把它转手给更需要它的人。

使用时,深深体会到E-ink屏的低功耗,确实很棒。但是机器本身没有良好的系统与开发平台,更没有良好的网络应用,只能离线看书。这不是我想要的。

国外有开发者移植了Debian上去,也写了E-ink屏与多点触控驱动。但是该系统是在旧版fireware上通过Chroot运行的,如要升级最新fireware,需要定制kernel。搞来搞去,搞不成。

最后甚至考虑放在家里,作为留言板、遥控器之类。但是缺乏中文输入,触摸也不好用,而且现在人手一个智能手机,根本不需要这机器。

E-ink屏的低功耗是非常好的优点,但是应用场合太少了。像Yota Phone这种手机,也不能成为主流。如果再不努力,可能会被其它材质取代。

入手Kobo mini已经快一个星期了。总结一下这家伙吧。

关于机器本身
入手Kobo mini,最主要是便宜,淘宝300不到就能买到。其次是体积小,5吋屏幕,分辨率是800x600,自带WiFi,并且可以换更大的TF卡来实现扩展存储空间。最后就是可以刷Debian。关于刷Debian,刷过后才感受到不是那么美好,这个后面再说。

缺点,首先是没有背光。然后是系统基于Linux开发的,比Android的(例如:Nook Simple Touch)可玩性差很多,开发的难度也高很多。原系统对中文支持差,可以说是不支持。虽然可以外挂字体显示中文文档,但是自带的浏览器却搞不定。并且没有其它可以很好利用网络的应用软件,自带的WiFi基本是没用了。最后就是不支持外部存储扩展,更换内部TF卡要打开后背,很麻烦。当然从一个角度来说,可以更专心地用来看书。特别是刷上Koreader之后。

官方介绍机器的详细配置:
http://www.kobo.com/kobomini#techspecs
Mobile Read的WIKI:
http://wiki.mobileread.com/wiki/Kobo_Mini

系统恢复
机器到手后,点了一下factory reset,机器不断重启且不能进入系统。幸好找到img,刷上就好了。刷好后立马备份一个,以防万一。

把img文件刷到TF卡的教程(英文),关键就是改S/N和把4G的镜像刷入2G卡:
http://www.mobileread.com/forums/showpost.php?p=2597246&postcount=15
img文件下载地址(Google Driver):
https://docs.google.com/file/d/0BxbJNE-fuHAmeGloU3drOXF0WE0/edit

固件升级
这个很简单,机器用USB数据线连上PC,把升级包解压到.kobo目录下,安全断开连接后,系统就自动刷。

固件升级包地址如下(目前mini的最新版本是3.4.1):
http://wiki.mobileread.com/wiki/Kobo_Releases
Kobo 固件升级详解
http://www.by-smart.com/forum.php?mod=viewthread&tid=2828

添加中文支持
机器连接到电脑,建一个fonts文件夹,把喜欢的中文字体复制进去,然后安全断开连接。原生系统看中文书时,选复制进去的中文字体就可以完美支持中文了。但是浏览器的中文显示问题解决不了。

Kobo系列书库乱码及字体替换解决方案
http://www.by-smart.com/forum.php?mod=viewthread&tid=3776

安装Kobo Start Menu
第三方的系统中,Kobo Start Menu算是兼容性比较好的了。而且界面都是调用sh脚本文件 + html文件的方式,方便修改。缺点是界面比较丑,简直就是没有做界面。

Kobo Start Menu官方介绍(英文):
http://www.mobileread.com/forums/showthread.php?t=233259
目前最新版本及安装方法(英文):
http://www.mobileread.com/forums/showpost.php?p=2980759&postcount=591

安装Koreader
个人感觉Kobo上,Koreader是最好的阅读软件了(确实也没几个可以选择了)。

Koreader的介绍
http://www.hi-pda.com/forum/viewthread.php?tid=1078988
Koreader的安装使用贴士——该帖适用于Kobo全系列机型
http://bbs.cooldu.net/forum.php?mod=viewthread&tid=1253
Koreader下载地址
https://github.com/koreader/koreader/wiki/Download
字典下载,zh_CN 简体中文词典
http://abloz.com/huzheng/stardict-dic/zh_CN/

安装Vlasovsoftlauncher
Vlasovsoftlauncher集成了几个游戏(数独、国际象棋、国际跳棋、黑白棋、推箱子等),无聊时可以玩玩。还集成了Coolreader。不过已装了Koreader,就对Coolreader不感冒了。

KOBO系列产品CoolReader安装教程(参考方法二)
http://www.cooldu.net/forum.php?mod=viewthread&tid=1495&page=1
Vlasovsoftlauncher各版本安装包下载链接
http://vlasovsoft.triolan.com.ua/files/builds_en.html
安装方法原帖链接
http://wiki.vlasovsoft.net/doku.php?id=en:pbchess-1.2.6#dokuwiki__top

刷Debian系统
试着刷上该系统,但发现操作起来各种不适应,系统很多地方都没有针对本机器进行优化。最后还是刷回原版系统。

Kobo as a Linux tablet(系统作者的Blog)
https://sites.google.com/site/gibekm/hardware/kobo/kobo-as-tablet
系统作者在论坛上发的帖
http://www.mobileread.com/forums/showthread.php?t=222123
另一个详细安装教程,附Debian镜像下载地址
http://robotsfuckyeahalloneword.svbtle.com/debian-on-kobo-mini

至此,应付看书应该没问题了。但是我希望能运行Debian,或者开发更多的应用。那么,只能继续折腾了。