2013年12月6日 星期五

Bash 內建的 getopts 與外部的 getopt 的使用方式

通常在 GNU/Linux 系統底下使用 command 時都會有帶一些額外的參數,像是以下的例子:

$ echo `seq 0 9` | xargs -n1 | wc -l

利用 `seq 0 9` 產生出 0 到 9 的數字,然後 echo 輸出,再用 xargs -n1 利用空白分開每個數字插入換行符號,然後再導到 wc -l 去計算行數。(其實寫成 `seq 0 9 | wc -l` 也有同樣的效果,利用 hexdump -C 就可以看出其中的不同)

其中的 wc -l 也可以寫成 wc --lines 這樣更清楚、更容易理解。

如果說我們想要利用 Bash shell 寫一個同樣的功能,就可以寫成下面這樣:

#!/bin/bash

while getopts l name; do
    case "$name" in
        ('l')
            line=1
            ;;
    esac
    shift
done

if [ -n "$line" ]; then
    awk 'END { print NR }' "$@"
fi

跟原本的 wc -l 提功同樣功能只是換成使用 awk 這個指令來計算行數。

然而 Bash 內建的 getopts 無法直接提供 --lines 這樣的使用方式,於是就可以改用外部的 getopt 指令來達成。

#!/bin/bash

set -e
eval set -- $(getopt -o l -l "lines" -- "$@")
set +e

while :; do
    case "$1" in
        ('-l'|'--lines')
            line=1
            ;;
        ('--')
            shift
            break
            ;;
    esac
    shift
done

if [ -n "$line" ]; then
    awk 'END { print NR }' "$@"
fi

如果說要利用 Bash 內建的 getopts 寫一個 xargs -n1 這樣的功能,則可以寫成以下這樣:

#!/bin/bash

while getopts n: name; do
    case "$name" in
        ('n')
            max="$OPTARG"
            ;;
    esac
    [ "$OPTIND" = 2 ] && shift
    [ "$OPTIND" = 3 ] && shift 2
done

if [ -n "$max" ]; then
    awk "{for (i = 1 ; i <= NF ; i++) { if (i % $max == 0) printf \"%s\\n\", i; else printf \"%s \", i } }" "$@"
fi

如果要支援 xargs --max-lines=1 這樣的使用方式,則可以利用外部的 getopt 改寫成以下這樣:

#!/bin/bash
# filename: xargs.sh

set -e
eval set -- $(getopt -o "n:" -l "max-lines:" -- "$@")
set +e

while :; do
    case "$1" in
        ('-n'|'--max-lines')
            max="$2"
            shift 2
            ;;
        ('--')
            shift
            break
            ;;
    esac
done

if [ -n "$max" ]; then
    awk "{for (i = 1 ; i <= NF ; i++) { if (i % $max == 0) printf \"%s\\n\", i; else printf \"%s \", i } }" "$@"
fi

這樣就可以使用 `xargs.sh --max-lines=1` 了。

2013年12月4日 星期三

Debian/Ubuntu 無人值守的系統更新

Debian/Ubuntu 上面有一個套件叫做 unattended-upgrades - automatic installation of security upgrades 可以用來設定系統的自動更新,這對於一些特別講究系統安全的伺服器特別有用。

以 Ubuntu 12.04 為例,安裝完成及在下面 APT::Periodic::Unattended-Upgrade 打開之後,預設上只會做安全更新,但是我想要它做一般性的系統更新,於是就修改 /etc/apt/apt.conf.d/50unattended-upgrades

// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
        "${distro_id}:${distro_codename}-security";
        "${distro_id}:${distro_codename}-updates";
//      "${distro_id}:${distro_codename}-proposed";
//      "${distro_id}:${distro_codename}-backports";
};

// Do automatic removal of new unused dependencies after the upgrade
// (equivalent to apt-get autoremove)
Unattended-Upgrade::Remove-Unused-Dependencies "true";

// Automatically reboot *WITHOUT CONFIRMATION* if a
// the file /var/run/reboot-required is found after the upgrade
Unattended-Upgrade::Automatic-Reboot "true";

簡單說就是包含使用 precise-updates 上面的更新,更新之後再用 apt-get autoremove 把多餘的套件移除掉,如果有些更新需要重開機才會生效的話,就重開機吧。

然後再修改一下 /etc/apt/apt.conf.d/10periodic

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "7";
APT::Periodic::Unattended-Upgrade "1";

意思是每日檢查更新,如果有更新的話先下載,每週自動清除不需要的套件快取,使用無人值守自動更新。

關於 linux kernel 的更新,有一個腳本程式 /etc/kernel/postinst.d/apt-auto-removal 產生 /etc/apt/apt.conf.d/01autoremove-kernels,用來保留當下正在使用、或是最新安裝、或是上一份使用的 linux kernel。

至於詳細的運作細節則可以參考 /etc/cron.daily/apt 裡面的內容。

2013年12月2日 星期一

VirtualBox Guest 裡面共用資料夾的自動掛載

VirtualBox 有提供一個方便的功能,能將 Host 的某個資料夾自動掛載到 Guest 裡面使用,只是自動掛載的權限不是預設的使用者。

sylee@vbox:~$ mount
/dev/sda1 on / type ext4 (rw,errors=remount-ro)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
none on /sys/fs/fuse/connections type fusectl (rw)
none on /sys/kernel/debug type debugfs (rw)
none on /sys/kernel/security type securityfs (rw)
udev on /dev type devtmpfs (rw,mode=0755)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620)
tmpfs on /run type tmpfs (rw,noexec,nosuid,size=10%,mode=0755)
none on /run/lock type tmpfs (rw,noexec,nosuid,nodev,size=5242880)
none on /run/shm type tmpfs (rw,nosuid,nodev)
vbox on /media/sf_vbox type vboxsf (gid=999,rw)
sylee@vbox:~$ id
uid=1000(sylee) gid=1000(sylee) groups=1000(sylee),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),111(lpadmin),112(sambashare)

這時候只要參考以前寫過的『如何解決在 Ubuntu 上面安裝 VirtualBox 後缺少 vboxusers 群組權限的問題』,將預設的使用者加入到 vboxsf 這個群組裡面,再重新登入後,就可以自由地使用 /media/sf_vbox 底下的檔案了。

sylee@vbox:~$ sudo usermod -a -G vboxsf sylee

隨意拿了 linux-generic-lts-quantal_3.5.0.44.50_amd64.deb linux-signed-generic-lts-quantal_3.5.0.44.50_amd64.deb linux-signed-image-generic-lts-quantal_3.5.0.44.50_amd64.deb linux-image-3.5.0-44-generic_3.5.0-44.67~precise1_amd64.deb linux-signed-image-3.5.0-44-generic_3.5.0-44.67~precise1_amd64.deb 來測試。

使用 vboxsf 來複製檔案。

real    0m0.661s
user    0m0.004s
sys     0m0.284s

使用 sshfs 來複製檔案。

real    0m2.276s
user    0m0.000s
sys     0m0.103s

sshfs 掰~

2013年9月25日 星期三

Debian/Ubuntu 上好用的 Spotify 網路音樂串流軟體

有高興看到有原生支援 Debian/Ubuntu 的網路音樂軟體。

在 Ubuntu 上面使用的方法如下:

$ echo "deb http://repository.spotify.com stable non-free" | sudo tee /etc/apt/sources.list.d/spotify.list
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 94558F59
$ sudo apt-get update
$ sudo apt-get install spotify-client

然後就可以執行 Spotify 來使用了。

目前的 Spotify 似乎無法自動偵測到正體中文的環境,所以要手動去偏好設定裡面調整。

最後一點小小的想法,看到許多好用的軟體都會在推出的時候,都會同時支援跨平台的 Windows/Mac/Linux 桌面系統,還有 Android 與 iOS 的移動平台,也許 Linux 桌面的使用族群相對稀少,也許支援 Linux 並沒有什麼高獲利可言,但是卻會讓人感受到該軟體對於使用者的誠意在哪裡,套一句從 Even Wu 那邊看到的:「正確 > 進度,是我的理念。因為正確會有未來的尾速!」。

2013年9月18日 星期三

使用 Ubuntu Juju 中的 Local Provider (Linux Container) 心得

Ubuntu Juju 是一套 Canonical 設計給使用 Ubuntu 做為雲端平台的一套工具。

基本概念是輕鬆地使用一堆專家們精心寫好的 Juju Charms,無痛地將網路服務部署到使用 Ubuntu 的雲端平台上面。

Ubuntu Juju 目前支援的雲端平台有 Amazon Web Services (AWS), Microsoft Azure, HP Cloud, OpenStack, MAAS, Local,而 Ubuntu Juju 的工具本身可以在 Ubuntu, Mac OS X, Windows 上面安裝使用。

而 Ubuntu Juju 的 Local Provider 就是利用 Linux Container (LXC) 來在自己的主機上面架設使用 Ubuntu Juju,以下就是介紹 Local Provider 的設定方法。(窮人省錢的雲端解決方案)

首先當然要有一個 Ubuntu 12.04 以上的系統,然後先要加入 ppa:juju/stable 這個 PPA 然後更新索引:

$ sudo apt-add-repository ppa:juju/stable
$ sudo apt-get update

接下來安裝所需要的軟體套件。

如果是 Ubuntu 12.04 的話執行:

$ sudo apt-get install juju-local linux-image-generic-lts-raring linux-headers-generic-lts-raring

然後重新啟動系統來使用 linux-lts-raring 的 kernel

如果是其它比 Ubuntu 12.04 新的版本的話執行:

$ sudo apt-get install juju-local

然後檢查一下 MongoDB 有沒有支援 SSL (這點很重要,一定要支援 SSL 否則就無法使用):

$ mongod -h | grep ssl
  --sslOnNormalPorts      use ssl on configured ports
  --sslPEMKeyFile arg     PEM file for ssl
  --sslPEMKeyPassword arg PEM file password

還有要檢查一下 LXC 的 DHCP server 有沒有被執行起來 (這點也很重要,不然 LXC 的網路就無法正常運作):

$ ps aux | grep lxc-dnsmasq
dnsmasq       1640  0.0  0.0  26080   900 ?        S    10:13   0:00 dnsmasq -u lxc-dnsmasq --strict-order --bind-interfaces --pid-file=/var/run/lxc/dnsmasq.pid --conf-file= --listen-address 10.0.3.1 --dhcp-range 10.0.3.2,10.0.3.254 --dhcp-lease-max=253 --dhcp-no-override --except-interface=lo --interface=lxcbr0 --dhcp-leasefile=/var/lib/misc/dnsmasq.lxcbr0.leases --dhcp-authoritative
user    12013  0.0  0.0  13660   952 pts/2    S+   15:47   0:00 grep --color=auto lxc-dnsmasq

接下來就可以產生 Juju 的設定檔了:

$ juju init 
A boilerplate environment configuration file has been written to /home/user/.juju/environments.yaml.
Edit the file to configure your juju environment and run bootstrap.

然後修改一下 ~/.juju/environments.yaml 將 "default: amazon" 改成 "default: local",接下來就可以執行 `sudo juju bootstrap` 來產生 Local Provider 所需要的環境。

`sudo juju bootstrap` 所做的動作就是產生 ~/.juju/local-cert.pem 及 ~/.juju/local-private-key.pem 還有 /etc/init/juju-*.conf 的檔案來執行一個 MongoDB Server 給 Juju 使用。

接下來執行 Ubuntu Juju 的資料就會全部儲存在 ~/.juju/local/ 目錄底下了。

$ sudo tree .juju                                                                                                                                                                              
.juju
├── charmcache
│   ├── cs_3a_precise_2f_mysql-27.charm
│   └── cs_3a_precise_2f_wordpress-16.charm
├── environments.yaml
├── local
│   ├── agents
│   │   └── machine-0
│   │       └── agent.conf
│   ├── db
│   │   ├── admin.0
│   │   ├── admin.ns
│   │   ├── journal
│   │   │   ├── j._0
│   │   │   ├── lsn
│   │   │   ├── prealloc.1
│   │   │   └── prealloc.2
│   │   ├── juju.0
│   │   ├── juju.ns
│   │   ├── mongod.lock
│   │   ├── presence.0
│   │   ├── presence.ns
│   │   └── _tmp
│   ├── log
│   │   └── machine-0.log
│   ├── server.pem
│   ├── shared-storage
│   ├── storage
│   │   ├── bootstrap-verify
│   │   ├── provider-state
│   │   └── tools
│   │       ├── juju-1.14.0.1-precise-amd64.tgz
│   │       └── juju-1.14.0.1-raring-amd64.tgz
│   └── tools
│       └── 1.14.0.1-raring-amd64
│           ├── downloaded-url.txt
│           ├── FORCE-VERSION
│           └── jujud
├── local-cert.pem
└── local-private-key.pem

13 directories, 26 files

像是如果要架設一台 WordPress Blog Server 就只需要執行以下四行指令:

$ juju deploy wordpress
$ juju deploy mysql
$ juju add-relation wordpress mysql
$ juju expose wordpress

因為建構這些服務需要一些時間去網路上抓取資料回來安裝設定,所以馬上執行 `juju status` 會看到以下輸出,表示動作還沒有完成:

$ juju status
environment: local
machines:
  "0":
    agent-state: started
    agent-version: 1.14.0.1
    dns-name: 10.0.3.1
    instance-id: localhost
    series: raring
  "1":
    instance-id: pending
    series: precise
  "2":
    instance-id: pending
    series: precise
services:
  mysql:
    charm: cs:precise/mysql-27
    exposed: false
    relations:
      cluster:
      - mysql
      db:
      - wordpress
    units:
      mysql/0:
        agent-state: pending
        machine: "2"
  wordpress:
    charm: cs:precise/wordpress-16
    exposed: true
    relations:
      db:
      - mysql
      loadbalancer:
      - wordpress
    units:
      wordpress/0:
        agent-state: pending
        machine: "1"

過一會後再執行 `juju status` 就可以看到全部完成的結果了。

$ juju status                                                                                                                                                                     
environment: local
machines:
  "0":
    agent-state: started
    agent-version: 1.14.0.1
    dns-name: 10.0.3.1
    instance-id: localhost
    series: raring
  "1":
    agent-state: started
    agent-version: 1.14.0.1
    instance-id: user-local-machine-1
    instance-state: missing
    series: precise
  "2":
    agent-state: started
    agent-version: 1.14.0.1
    instance-id: user-local-machine-2
    instance-state: missing
    series: precise
services:
  mysql:
    charm: cs:precise/mysql-27
    exposed: false
    relations:
      cluster:
      - mysql
      db:
      - wordpress
    units:
      mysql/0:
        agent-state: started
        agent-version: 1.14.0.1
        machine: "2"
        public-address: 10.0.3.158
  wordpress:
    charm: cs:precise/wordpress-16
    exposed: true
    relations:
      db:
      - mysql
      loadbalancer:
      - wordpress
    units:
      wordpress/0:
        agent-state: started
        agent-version: 1.14.0.1
        machine: "1"
        open-ports:
        - 80/tcp
        public-address: 10.0.3.69

然後就可以連到 10.0.3.69 這個 IP 就可以開始使用 WordPress 了。 :-)

如果想要清掉這些服務,只要執行下面一行指令:

$ sudo juju destroy-environment
WARNING: this command will destroy the "local" environment (type: local)
This includes all machines, services, data and other resources.

Continue [y/N]? y

這樣就會將之前用 `sudo juju bootstrap` 產生的環境整個清除乾淨。

$ tree .juju
.juju
├── charmcache
│   ├── cs_3a_precise_2f_mysql-27.charm
│   └── cs_3a_precise_2f_wordpress-16.charm
├── environments.yaml
├── local-cert.pem
└── local-private-key.pem

1 directory, 5 files

x11-touchscreen-calibrator 自動校正觸控螢幕的小程式

現在的筆記型電腦很多都搭配著觸控螢幕的功能,在 X Window System 上面使用時會遇到一些問題,像是接上外接螢幕後、或是變更螢幕解析度後,使用觸控螢幕時游標不再跟著手指頭觸控到的地方了。

可是我找來找去都沒有可以自動校正的應用程式,於是就開始了這個小小的專案。

專案首頁在 http://fourdollars.github.io/x11-touchscreen-calibrator/

目前這個程式已經放到 Debian 官方套件庫裡面了,也準備了一個 Ubuntu PPA 來使用。

在 Debian/Ubuntu 上面使用方式很簡單,就是安裝它後重新啟動 X Window System 就會自動在背景執行校正動作。

如果有使用上的問題請回報到 https://github.com/fourdollars/x11-touchscreen-calibrator/issues

2013年8月28日 星期三

在 Ubuntu 上面 Linux Container 的使用心得

Linux Container (以下簡稱為 lxc) 是一個輕量級的虛擬化系統,介於 VirtualBox/VMWare/... 以及打過類固醇的 chroot 之間,簡單來說 lxc 建立了一個 chroot 的環境,但是這個 chroot 的環境卻可以做資源控制,像是 CPU 用量、硬碟空間、記憶體空間、網路還有其它資源等等。

我們可以在上面安裝其它 Linux Distribution 的環境,看看 /usr/share/lxc/templates 這個目錄底下有支援 Arch, Debian, Fedora, openSUSE, Ubuntu 還有其它。

lxc 也可以用來建立雲端作業環境,又或者用來玩 Steam(TM) 上面的遊戲。

參考 LXC LXC 這兩份線上文件也許是個不錯的入門起點。

在 Ubuntu 上面使用只要安裝 lxc 這個軟體套件就可以了。

sudo apt-get install lxc

如果是在 Ubuntu 12.04 上面,建議使用 precise-backports 裡面的 lxc 效果會比較好,如果想要在 x86 上面建立 arm 的虛擬環境就要借助安裝使用 qemu-user-static 這個軟體套件。

sudo apt-get install qemu-user-static

在使用 lxc 之前可以先執行 `lxc-checkconfig` 來檢查環境。

然後就可以開始建立一個 lxc 的實體了,例如想要建立 Ubuntu 12.04 i386 就可以執行以下指令。

sudo lxc-create -t ubuntu -n myLXC -- -r precise -a i386

當中的 precise 可以換成 quantal/raring/saucy/... 而 i386 則可以換成 amd64/armel/armhf/... 然後 myLXC 則是這個 lxc 實體的名稱。

等待一段時間後,等到它建立完成就可以執行以下指令在背景啟動它。

sudo lxc-start -n myLXC -d

如果想要登入就可以執行:

sudo lxc-console -n myLXC

如果想要離開就要使用特殊的指令組合 <Ctrl+a q>

`sudo lxc-list`
可以看到每個 lxc 的情況。
`sudo lxc-stop -n myLXC`
停止 myLXC 這個 lxc 實體。
`sudo lxc-destroy -n myLXC`
刪除 myLXC 這個 lxc 實體。
`sudo lxc-start-ephemeral -o myLXC -d`
從 myLXC 建立並啟動一個暫時用完即丟的 lxc 實體

大概就是這樣。:-)

P.S. 執行 `sudo SUITE=sid MIRROR=http://ftp.tw.debian.org/debian/ lxc-create -t debian -n sid` 可以用來建立一個 Debian sid 的環境。