2011/04/30

Androidで逆USBテザリングを使う(NAT)

環境: Android:CyanogenMod 6.1.1, PC:Ubuntu 10.10

逆USBテザリングをブリッジ接続で行うと、不便が生じた。

- テザリングを止めるとき、PC側でブリッジ削除スクリプトを実行する必要がある。
- ブリッジ削除スクリプトを実行しないとPC側の通信ができなくなる。

それに、Androidにsshで入りたいわけでもないので、AndroidのIPアドレスがLAN内で見える必要もないし。
ということで、NATを利用する方法を考えた。この方法ならば、いつUSBを抜いてもPC側の通信には影響がない。
ブリッジ接続を紹介していたMathieuさんのコメント欄のところで、原理的に同じことを書いている人がいた。

なお、メーラーが自動受信しないとか、Android Marketのダウンロードが動作しないなどのAndroidの逆USBテザリング時に通信しないアプリが存在する理由で述べた問題があるが、私はAndroidのモジュールを変更して無理やりできるようにした。興味のある方はリンク先を参照してください。


まずPC側のIP Forwardを有効にする。

/etc/sysctl.conf:

net.ipv4.ip_forward=1


sysctl -p で反映。sysctl.confを変更せずに一時的に試すなら、
sysctl -w net.ipv4.ip_forward=1 で。

次にiptablesの設定。

iptables -A INPUT -j ACCEPT
iptables -A OUTPUT -j ACCEPT
iptables -A FORWARD -i usb0 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

# 一部修正 5/6

Ubuntuでiptablesの設定をOS起動時に有効にするには、自分でスクリプトを書く必要がある。私は /etc/rc.local に次のように書いている。

/etc/rc.local:

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


iptables-rusbnat.rules は上記の設定を行った後に iptables-save > iptables-rusbnat.rules で作った。

ここまで一度設定すれば良い。

以下はテザリングを有効にする度にPC側で実行するもの。
Android側ではやることはテザリングの設定を有効にするだけ。
停止するときはAndroidでテザリングの設定を無効にする。PC側は何もする必要がない。

rusbtether-nat:

#!/bin/sh 
set -e
export LANG=C
ROUTE=/system/bin/route
GWA=`LANG=C ifconfig usb0 | grep "inet addr:" | \
    sed 's/ *inet addr:\([0-9\.]\+\).*$/\1/'`

if [ "$GWA" = "" ]; then
    echo exit
    exit 1
fi

ping -I usb0 -c 1 $GWA > /dev/null
# add route
adb shell su -c "$ROUTE add default gw $GWA dev usb0"

DNS=`grep nameserver /etc/resolv.conf | head -n1 | awk '{print$2}'`

# clear dns
for p in net.dns1 net.dns2 net.dns3 net.rmnet0.dns1 \
    net.rmnet0.dns2 dhcp.eth0.dns1 dhcp.eth0.dns2 \
    dhcp.eth0.dns3 dhcp.eth0.dns4 net.eth0.dns1 \
    net.eth0.dns2 net.usb0.dns1 net.usb0.dns2; do
    adb shell su -c "setprop $p ''"
done

# set dns
adb shell su -c "setprop net.dns1 $DNS"

echo Completed


なお、AndroidのDNSを再設定するところはブリッジ接続の方法と変わらないので、テザリング中にWiFi/3GをONしない方が良い。
ONにした場合DNSの設定が変わって名前解決できなくなるが、再度 rusbtether-nat を実行すれば解決できるようになる。

#追記 5/3
Android側に付くアドレス 192.168.42.129 は固定のようだ。com.android.server.connectivity.Tethering にハードコーディングされていた。

Google検索にBloggerの最新記事がヒットする速度

Bloggerで記事を投稿した30秒後に記事中のキーワードで検索をかけたら、その記事がトップに出た。26400件ヒットしたにもかかわらず。
こういう検索アルゴリズムの仕様に少し疑問もあるが、15へぇくらいだ。

2011/04/29

Androidの通信量の節約方法を考える

近いうちに Softbank を解約して b-mobile Fair に移行すべく、通信量を節約する方法を模索している。
今までは自宅でもWiFiを使わずに3Gで通信していたこともあり、月300-400MB程度使っていた。
目標は 平日8MB/日、休日4MB/日。これで120日使うと900MB。1GBまでの残り100MBはたまに使うGoogle Mapやメール添付イメージなどのバッファとして考えている。
b-mobile U300で通信料制限なしという選択肢もあるが、通信速度は私にとってとても重要なことだ。遅いとイラッと来るので。

現在通信量のうち多くの割合を占めるものは以下の3つ。

1) 通勤中のRSSリーダ、Webブラウザ使用。たぶん通信量の8割を占める。
2) Android Marketでのアプリのアップデート、新しいアプリのダウンロード。
3) Gmailの待ち受け、取得、写真送受信。メイン携帯へのメールもGmail(auone)で取得。

- RSSリーダ対策

自動更新はOFFにする。RSSリーダ(NewsRob)は、出勤前にキャッシュしておく。
手動で更新するのは割りと苦にならない性格なので。

会社では技術調査の名目で社内LANに接続したPCにスマフォを接続している。Firewallを越えるにはProxyに対応したアプリが必要。そこで、プロキシに対応した Sparse rss というのがあったが、Google Readerの同期機能がない。残念。
Google Reader にフィードを集約しているので、個人的に対応は必須要件。

結局探したところで、Proxy対応したアプリが使いやすいとは限らないので、Proxy越えをする要rootedな別の方法を選択した。後日検証する予定。

- Webブラウザ対策

Webブラウザを使うときは Opera Mini を使う。Opera Mini はOpera用のサーバが仲介して、参照先のWebページを圧縮してからブラウザに送るため、普通のダイレクトに接続するタイプのブラウザよりも通信量が結構少ない。
画質の設定を最低・携帯表示ONにすると、広告等の画像の多いページの通信量がかなり軽減される。私の場合、ほとんどが文字情報を見たいだけだったりするのでこれで十分。

ただ、Opera Miniは仲介サーバにつながらないことがある。そんなときは仕方なくOpera MobileまたはDolphin Browser Miniを使っている。

#追記 5/8
Opera MobileでOpera TurboをONにすると、Opera Miniと同様に圧縮される。


- Android Market対策

自動更新をOFFにして、自宅でアップデート&インストールする。
アプリのバージョンアップでデグレードすることもあるため、自動更新はリスクが高いと思っている。
なので元々自動更新はしていない。この作業も苦にならない。

- Gmail対策

メーラはK9-MailでIMAP Idleを使用。待ち受けだけだと、それほど通信量は多くなさそうだが、今度念のため計測しておきたい(計測した)。
たまに取った写真を送ったり、送られたりするのが通信量が大きい。
送信するときは SmallPic で小さくしてから。
受信した添付ファイルは、すぐに見る必要のないものは自宅でWiFiやPCで確認する。
K9-Mailは受信するメッセージのサイズを指定できるので、8kにしている。

#追記 5/3
- 広告/情報収集対策

無料アプリの中にはAdMobなどの広告を取得するものがあるので、通信系のアプリではなくても起動する度に広告をダウンロードすることがある。よく起動するアプリならば、広告なしの有料版に変更したほうが通信量を節約できる。
また、アプリ使用状況の情報収集のためにデータを送信するアプリもある。データ送信することをユーザに許可を求めるアプリが送信するのは構わないが、WiFiのときだけという設定がほしい。
パケットを眺めていたら、ToggleSettings無料版はwww.togglesettings.comと通信するのでアンインストールした。CyanogenModのウィジットボタンで事足りてしまったので。有料版は使ったことがないが、どうなのだろうか。

Ubuntuのアップグレードを促すダイアログに驚く

Ubuntuで作業している最中、突然Ubuntu11.04へアップグレードするかどうかを聞くダイアログ(結構大きめ)が表示された。これには驚いた。
さらにそのダイアログのデフォルトのフォーカスが「今すぐアップグレードする」だったことにも驚いた。

このインターフェースは不親切だと思った。
パネルから小さな枠の通知を表示するだけにするとか、他にスマートで効果的な方法があったはず。
さすがにこれはナシだ。

気を取り直して自動起動するアプリからアップデート通知をはずした。

chkconfig対応のsquid init script

Ubuntuでapt-getでインストールしたらinitスクリプトがなかったので。
起動時にDNSテストをスキップする"-D"が指定されていることに注意。

/etc/init.d/squid:

#!/bin/sh
# chkconfig: 345 98 20
# description: squid server
# processname: squid

# description for chkconfig: 345 98 20
#  345: run levels
#  98: priority to start
#  20: priority to stop

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/squid
NAME=squid
DESC="squid server"
PIDFILE=/var/run/$NAME.pid
test -x $DAEMON || exit 0

if [ -f /etc/default/$NAME ]; then
    . /etc/default/$NAME
fi

DAEMON_OPTS="-D"

start_proc() {
    $DAEMON $DAEMON_OPTS
    if [ "$?" -eq 0 ]
    then
        pidof $DAEMON > $PIDFILE
        return 0
    fi
    return 1
}

stop_proc() {
    kill -9 `cat $PIDFILE 2>/dev/null`
    [ "$?" -ne 0 ] && return 1
    rm -f $PIDFILE
    return 0
}

status_proc() {
    killall -0 $DAEMON 2>/dev/null
    return "$?"
}

case "$1" in
    start)
        echo "Starting ${DESC}"
        start_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    stop)
        echo "Shutting down ${DESC}"
        stop_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    restart|force-reload)
        echo "Restarting ${DESC}"
        stop_proc
        if [ $? -ne 0 ]; then
        failure ; echo ; exit 0
        fi
        sleep 1
        start_proc
        [ "$?" -eq 0 ] || echo "failed"
        ;;
    status)
        status_proc
        [ $? -eq 0 ] && echo "$DESC is alive" || echo "$DESC is dead"
        echo
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
        exit 1
        ;;
esac
exit 0

2011/04/28

Android MarketでDownload pausedが発生したときの対処

逆USBテザリングをするために色々弄っていたのが悪かったのか、WiFi接続時にAndroid Marketからアプリをインストールできなくなってしまった。

現象としては、"Download paused" (日本語で"ダウンロードが一時停止しました") が表示され、ずっとダウンロードが終わらない。3Gに切り替えると正常にダウンロード&インストールはできる。
WiFiネットワークにファイアウォールはない。また、logcat にExceptionやそれらしいメッセージは出ないので原因究明のきっかけを掴めない。
最終的には原因不明のまま、メニューのプライバシーにあるデータの初期化を行って回復した。

少し調べたら同じ現象の人がいるようで、

  • 再起動する。
  • Marketアプリのキャッシュ、データをクリアする。
  • Marketアプリにアカウントを再登録する。
  • Google Talkに接続する。

を試してみた。しかし、現象は変わらず。
Titanium Backupでデータの復元はできるので、結局Factory Resetを選択した。

#追記 5/8
  • Google Talkに接続して、すでにログインしている場合はログアウト・ログインする。
    Google TalkとAndroid Marketの認証・通信の仕組みは共有されているのだろうか。
    PCからMarketサイトでインストールを実行した後、WiFiに接続しているAndroidでダウンロードが始まらないときは、これをやると始まる。
  • /cacheの容量が少ないとダウンロードが開始しないようだ。/cacheのパーティションサイズを変更したとき、大きなファイルはダウンロード開始しなかった。不足しているなら不足しているというエラーを出してくれればいいのに、エラーが出ない。

Bloggerにテキストファイルを投稿するスクリプト

このブログのほとんどの記事をLinuxからGoogleCLで投稿している。
記事のタイトルとタグをいちいちコマンド引数に渡すのが面倒なので、投稿するファイルから拾うスクリプトを作成して使っている。

実行方法:
$ blogger-post file1.txt file2.txt ...

投稿ファイルのサンプル (UTF-8):

記事のタイトル
tag1,tag2
本文
本文
本文


blogger-post:

#!/usr/bin/perl
use utf8;

my $blog = "My Blog";
my $tf = "/tmp/blogger-post.tmp";

while (my $f = shift) {
    print "Posting $f ...\n";
    open(IN, "<:utf8", $f) || die "Cannot open $f : $!\n";
    my $title = <IN>;
    chomp $title;
    (length($title) > 0) || die "Title is empty\n";
    my $tags = <IN>;
    chomp $tags;
    open(OUT, ">:utf8", $tf) || die "Cannot write to $tf : $!\n";
    while (<IN>) {
        print OUT;
    }
    close IN;
    close OUT;
    my $r = system("google blogger post --blog=\"$blog\" ".
        "--draft -n \"$title\" -t \"$tags\" $tf");
    unlink $tf;
    exit 1 if ($r ne 0);
}


$blog は対象のブログ名。

2011/04/26

HTC Desireのパーティションサイズを任意に変更する

AlphaRev 1.8のCM7は /cache が5MBなのだが、これが小さすぎるようだ。
Android Marketは一時ダウンロードディレクトリとして /cache を使用しており、5MB以上の.apkをダウンロードできない。現象としては、Marketアプリでずっとダウンロードが完了しない状態になる。
これは困るのでパーティションをまた変更した。

今度はXDAのCustom MTD Partitionsを見て、system/cache を130/20に設定。再度CM6.1.1を焼いた。
ATOK TrialをMarketからインストールできるようになった。

ついでに使用しない.apkを/system/appから削除。私はカレンダー同期を使用しないが、アカウントの設定でカレンダーの同期をOFFにしているはずなのに、裏でサービスが動いてコネクションまで張っていたので削除した。

2011/04/25

Androidで逆USBテザリングを使う(ブリッジ接続)

ここに記述されている方法よりも、NATで行う方法の方が使い勝手が良いので、逆USBテザリングをしたい方は先にそちらを参照してください。


WiFiの代わりに3Gよりかは高速な接続方法として、USBテザリングを利用できないか調べたら、こちらのMathieuさんがやり方を紹介していた。ただし要rooted。
AndroidからPC側のブリッジを介して通信できる。

ブラウザ、メールは使えた。ただしアプリ的な問題がある。
K9-Mailを使っているのだが、"WiFiと3Gが無効のときはメール取得を停止する"機能が実装されているようで、受信メニューを手動で選択しないと受信してくれない。
もちろん普通は正しい動作。待機系のアプリは大抵このような動作をするのではなかろうか。
(Androidの逆USBテザリング時に通信しないアプリが存在する理由で調べてみた)

以下、使ったスクリプト。PC側はrusbtether-start で逆USBテザリング開始。rsubtether-stop で元に戻す。Android側は開始前にUSBテザリングの設定をON、終了後にOFFすれば良い。
最初、ping www.google.com が通らないので調べたらDNSが別のWiFiネットワークで設定されたものだった。setprop net.usb1 を実行して上手く行った。他にもMathieuさんのブログのコメント欄にいくつか情報がある。

WiFi/3GがONの状態だと、WiFi/3Gのインターフェースから取得したDNSも見に行ってしまう。
これをやるときはWiFi/3GはOFFにした方が良い。

#追記4/29
さらに検証したら3GがOFFのときでも、net.rmnet0.dns1, net.rmnet0.dns2 を使って名前解決を試みる現象を確認した。そのため、net.usb1 を設定する前に全てのDNS設定をクリアするようにした。クリアしたDNSはWiFiまたは3GをONにしたときDHCPでのアドレス取得と共に戻る。テザリング中にWiFi/3GをONにしてからまたOFFにしてしまうと、WiFi/3GのDNS設定が行われて名前解決ができなくなるので注意。
また、スクリプトに一部誤りがあったので修正。

#追記4/30
Android側でUSBテザリングを停止すると、ブリッジを構成しているPC側のeth0も通信ができなくなってしまう。USBを抜いたり、テザリングを停止した後は必ず rusbtether-stop を実行する必要がある。


rusbtether-start:

#!/bin/sh
sudo ifconfig eth0 0.0.0.0
sudo ifconfig usb0 0.0.0.0
sudo brctl addbr br0
sudo brctl addif br0 eth0
sudo brctl addif br0 usb0
sudo ifconfig br0 up
sudo dhclient br0

# Clear DNS
for p in net.dns1 net.dns2 net.dns3 net.rmnet0.dns1 \
    net.rmnet0.dns2 dhcp.eth0.dns1 dhcp.eth0.dns2 \
    dhcp.eth0.dns3 dhcp.eth0.dns4 net.eth0.dns1 \
    net.eth0.dns2 net.usb0.dns1 net.usb0.dns2; do
    adb shell su -c "setprop $p ''"
done

adb shell su -c "netcfg usb0 dhcp"
# Replace DNS
adb shell su -c "setprop net.dns1 192.168.3.254"
# Show the address attached to usb0
adb shell su -c netcfg


rusbtether-stop:

#!/bin/sh
sudo ifconfig eth0 down
sudo ifconfig usb0 down
sudo ifconfig br0 down
sudo brctl delbr br0
sudo ifconfig eth0 up
sudo dhclient eth0

Logitec LAN-PW150N/Rで頻繁に切断したときの対処

HTC DesireをLAN-PW150N/Rで接続していると2-4分置きに切断する。
ルータのログを見ると以下のメッセージが出力されていた。

[XX:XX:XX:XX:XX:XX] has been aged-out and disassociated

別のマシンからDesireに対して1分置きにpingしてやると切断しない。

ルータモードでもAPモードでも症状は同じ。
MACがaged-outするということはarpテーブル関連なのだと思うが、ルータの設定にはそれらしきものはない。
"Age Out Timer"の設定ができるLogitecのルータはあるようだが、このルータではできない。
DTIMピリオドという項目を1に変更してみても効果なし。
安価でお手軽な物だから仕方ないのか。

Desire側での対処ができないかと、Wifi Keep Alive、REGPON wifi KeepAlive を使ってみたが改善しない。
唯一有効だった外部マシンからの定期pingはやりたくないし、
もう新しいルータを購入するしかないかと考えている。

#追記 5/10
ファームウェアを 1.09 から 1.11 に更新したら10分~1時間置きの切断になった。
これならまあ使えるかも。

2011/04/24

ソースコードをHTMLへ変換する

ソースコードをHTMLへ変換するツールをいくつか試したが、しっくり来るものがない。
タブやスペースを"&nbsp;"に変換してくれて、monospaceフォントで出力してほしい。それと可変幅のテーブルで囲わずにページ幅のhrで挟んでほしい。
探すのが面倒なので以下のスクリプトを作った。
以下のスクリプトのHTMLは、以下のスクリプト自体で変換して貼り付けたもの。

blogger-code:

#!/usr/bin/perl

print "<hr style=\"border: 2px #9999ff solid;\" />";
print "<pre><span style=\"font-family: 'Courier New', ".
    "Courier, monospace;\">";
while (<>) {
    s/\t/    /g;
    s/&/&amp;/g;
    s/</&lt;/g;
    s/>/&gt;/g;
    s/  /&nbsp;&nbsp;/g;
    print;
}
print "</span></pre>";
print "<hr style=\"border: 2px #9999ff solid;\" />\n";

HTC DesireにCyanogenMod 6.1.1を入れる

CyanogenMod 7.0.0 (Android 2.3.3) から CyanogenMod 6.1.1 (Android 2.2.1) に変更した。
やはり今は2.2じゃないとアプリが追いついてないので、生活ができない。
パーティションサイズを変更したので、以前から使っていたアプリを入れても、/dataにまだ160MBの空きがある。

RADIOはCyanogenMod 7.0.0を入れたときすでに最新に更新済み。
アプリは Titanium Backup でバックアップ&レストアした。
しかし、OS Monitor / Simeji / Opera Mini など他にもいくつかのアプリケーションが起動しなくなった。
adb logcat で確認したら、起動しないアプリは UnsatisfiedLinkError が出てた。
起動しないアプリを一度アンインストールしてから、Android Marketから再インストールしたら動いた。

HTC Desireのパーティションサイズを変更する

S-OFFへの変更とパーティションサイズの変更をした。
app2sd-extを使うかどうか迷ったが、内部メモリの大半を眠らせておくのはもったいない気がしたので。

AlphaRev r1.80のBravo CM7を使用。
fastboot をHTCからダウンロードした。

まずS-OFFについてはAlphaRev 1.8 HBOOT reflash utilityの .iso をダウンロード。VMWare Player で Ubuntu Linux タイプのVMを作って起動し、VM にUSBを接続した。10分もかからず終了。
パーティションサイズの変更は、AlphaRevのページのFAQの手前に書いてあるやり方でサクッと終了。

これで/dataが302.6MBになった。

HTC Desire - CyanogenMod 7.0.0のバッテリー消費量

6時間ほぼスリープ状態で100%=>89% だった。

常駐アプリの状況
- K9-Mail (IMAP Idle 2アカウント, メール3件取得)
- NewsRob (100件くらいのrssを自動取得)
- なまず速報β (速報なし)


あと、3G回線。

2011/04/23

付箋紙アップレットのデータを抽出する

Ubuntu Desktopに標準でインストールされるアップレット"付箋紙"(Sticky Notes)のデータは、~/.gnome2/stickynotes_applet にXML形式で保存されている。

これを抽出して一覧で出力するスクリプトを作った。

こんな感じ。

$ list-stickynotes
2011年04月20日 猫の餌を買う
2011年04月21日 4/23 食事会 大宮 12:00


1つの付箋が"タイトル<タブ文字>内容"の1行で出力される。
付箋の内容に改行がある場合はスペースに変換。エンコーディングはUTF8。
"付箋紙"は内容を記述してから10秒ぐらいしないと、ファイルに保存されないことに注意。

list-stickynotes:

#!/usr/bin/perl
package StickyNotesHandler;
use base qw(XML::SAX::Base);

my $elm;

sub start_element {
    my ($self, $data) = @_;
    $elm = $data->{Name};
    return if ($elm ne 'note');
    my $na = $data->{Attributes};
    my $t = $na->{'{}title'}->{Value};
    print "${t}\t";
}

sub end_element {
    return if ($elm ne 'note');
    print "\n";
    $elm = undef;
}

sub characters {
    my ($self, $data) = @_;
    return if ($elm ne 'note');
    my $c = $data->{Data};
    if ($c eq "\n") { $c = ' '; }
    print "$c";
}

#-----------------------------------
package main;

use HTML::Entities;
use XML::SAX;
use Env qw(HOME);

binmode STDOUT => "utf8";

my $fi = "${HOME}/.gnome2/stickynotes_applet";
my $p = XML::SAX::ParserFactory->parser(
    Handler => StickyNotesHandler->new);

$p->parse_uri($fi);

HTC DesireにCyanogenMod 7.0.0を入れる

HTC Desire (Softbank X06HT)の内部メモリが少ない。X06HTに限らないと思うが、プリインストールされた使わないかつ消せないアプリが多すぎて、無駄にリソースを消費している。なので、CyanogenMod-7 for Desire GSM V7.0.0を入れた。

・root化 ... unrevoked 3.22

この時点で最新の3.32だとfarmwareが新しすぎるというエラーが出るので、3.22を使用。
最新版ダウンロードへのリンクである、
http://downloads.unrevoked.com/recovery/3.32/reflash.tar.gz
の3.32の部分を3.22に手動で変更したらダウンロードできた。

・RADIO 32.54.00.32U_5.14.05.17

RADIOはunrevoked 3.22で入れたままのバージョンだと動かない。最新のものを入れた。
RADIOの更新に失敗するとスマフォが文鎮化することがあるそうで。少し緊張しつつ繰り返し再起動するのを見守った。

・CyanogenMod-7 for Desire GSM V7.0.0

XDA Developersの投稿を参考にした。Introduction通りにやったら起動した。最初wipeするのを忘れたらsplashがループして先に進まなかった。

CyanogenMod-7 は Android 2.3.3。空きメモリは前と同じアプリを入れた後で70MBだった。正規ROMではアプリを入れた状態で空きが20MB。

少し触ってみた感じでは、

満足な点
- 正規ROMよりもきめ細かい設定ができる。
- ロック画面上で使用可能なアプリが選択できる。音楽再生ウィジットを置いた。
- Android標準のテザリング機能を利用できる。

不満な点
- Android 2.3 対応アプリが少ない。まだこれで生活するのは不便だった。アプリのメインアクティビティからサブアクティビティが表示されないものが結構ある。これにより一部のアプリの一部の設定変更できないので困る。アプリ / CyanogenMod / Android 2.3のいずれの問題なのかはわからない。
- たまにフリーズする。再起動が必要になった。
- 以前から使っていたお天気系のウィジットがウィジットの選択に出てこなくなった。原因不明。

Androidバージョン分布の調査によると2011/4/1の時点で2.3系は2.5%(2.3:0.8%、2.3.3:1.7%)。DesireとXperiaが今年中に2.3へアップデートすることが発表されているため、アプリの実質的な対応はそれからになるだろう。

テザリングについては、たまに使うのでうれしい。httpsも有料アプリ不要だし。速度はLinux PCに接続してBNR Speed Testで計測した結果、Down 364kbps / Up 232kbps だった。
なお、近いうちにSoftbankを解約してb-mobile Fairに変更するつもりなので、Softbank回線でのテザリングにおけるグレーな感じとはおさらばに。

Android 2.2 から 2.3 でバッテリ消費量が改善されたそうで、正規ROMの無駄アプリが排除されたことによる効果も合わせて、消費量がどの程度かを調べたい。

次はCyanogen 6.1 Froyoを試す。

Linuxで固定IPアドレスを設定する

固定IPアドレスの設定の仕方をいつも忘れるのでメモ。

CentOS系: /etc/sysconfig/network-scripts/ifcfg-eth#

DEVICE=eth0
IPADDR=192.168.3.10
NETMASK=255.255.255.0
HWADDR=00:11:22:33:44:55
ONBOOT=yes



resolv.conf:

nameserver 192.168.3.253
nameserver 192.168.3.254
search localdomain



Debian系: /etc/network/interfaces

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
address 192.168.3.10
network 192.168.3.0
netmask 255.255.255.0
gateway 192.168.3.254
dns-nameservers 192.168.3.253 192.168.3.254
dns-search localdomain


(dns-nameservers, dns-search は resolvconf 使用時)

2011/04/20

JPAでbooleanをマッピングする

環境: Hibernate 3.6.3, PostgreSQL 8.4.7

JPAでbooleanをDBのBOOLEAN型にマッピングできるのかを試したら、普通にできた。


@Entity
public User {
    @Id
    private int id;
    @Column
    private boolean valid;

    public boolean isValid() {
        return valid;
    }
 
    public void setValid(boolean valid) {
        this.valid = valid;
    }



できて当然といえば当然だけどね。ちょっとうれしい。

しかし、BOOLEAN型ではない型を使ってブール値を保存しているような既存システムのDBに対してJPAでマッピングするには、値を変換する処理を書く必要があるようだ。

変換処理はWikibooksのJava Persistence/Basic Attributesここで紹介されている。これは面倒でスマートではない。

そんな面倒な変換処理をAnnotationで生成してくれる、Boolean Magic Converterというのを作っている人がいた。あるプロジェクトの一部品だが、BooleanMagicProcessorFactory.jar だけ個別にダウンロードできるようになっている。
こんな感じで書ける。


@BooleanMagic(trueValue = "T", falseValue = "F",
columnName = "VALID", ifNull = ReturnType.FALSE)
private transient Boolean valid;


Hibernateではorg.hibernate.annotations.Typeで次の型がある。

@Type(type="yes_no") ... Y/N
@Type(type="true_false") ... T/F

1/0はないのか...と思った。一応 org.hibernate.usertype.UserType を実装すれば、1/0を@Typeで指定できるようになりそうだが、いずれにせよ他のアプリケーションサーバで動かすことになったときHibernate依存だと困るので避けたい。

2011/04/19

Struts2 ConventionとTilesのJSP検索パス

環境: Struts 2.2.1.1, Tiles 2.2.2

Struts2 ConventionとTilesを一緒に使うときの話。

Conventionが.jsp を検索するパスはデフォルトで /WEB-INF/content (constant: struts.convention.result.path) だが、Tilesは Context Root から検索する。Tilesの方はソースを追ってみた限りでは、templateに共通のプリフィックスを付加するような処理はなく、templateの値がそのままdispatchされるようだ。

できる限り設定の追加は避けたいので、Tiles のパスを長々と書くことにした。

tiles.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC
"-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">

<tiles-definitions>

<definition name="/main.tiles" template="/WEB-INF/content/main.jsp">
<put-attribute name="body" value="/WEB-INF/content/body.jsp" />
<put-attribute name="footer" value="/WEB-INF/content/footer.jsp" />
</definition>

</tiles-definitions>



template attribute の /WEB-INF/conent を書かずに済む方法があれば、すっきりするのだが。

update-alternativesコマンドを使う

Ubuntu 10.10 Server x86_64 にaptitudeでgcc-4.4をインストールした。
/usr/bin/gcc-4.4, cpp-4.4 はあるが、/usr/bin/gcc, cpp はないので、update-alternatives で追加。
"gcc" "cpp" がないとconfigure時に面倒なので。


# update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 90
# update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-4.4 90


ld は
/usr/bin/ld -> /usr/bin/ld.bfd
という symlink になっていた。
goldの方が速いので、/usr/bin/ld.gold を使うように変更することにした。
ld もデフォルトでは /etc/alternative に入っていない。


# update-alternatives --install /usr/bin/ld ld /usr/bin/ld.bfd 1
# update-alternatives --install /usr/bin/ld ld /usr/bin/ld.gold 2
# update-alternatives --list ld
/usr/bin/ld.bfd
/usr/bin/ld.gold


今担当しているプロジェクトのフルビルドにかかる時間が、52秒から50秒に短縮。もともとリンクにそれほど時間のかかるプロジェクトではないから、こんなものだろう。

2011/04/18

GoogleCLでDeprecationWarningが発生したときの対処

環境: Ubuntu 10.04, Python 2.6.5, GoogleCL 0.9.13

GoogleCLでgoogle blogger listすると次のエラーが出た。


/usr/lib/pymodules/python2.6/atom/http.py:225: DeprecationWarning: socket.ssl() is deprecated.  Use ssl.wrap_socket() instead.
ssl = socket.ssl(p_sock, None, None)
/usr/lib/pymodules/python2.6/atom/http.py:226: DeprecationWarning: FakeSocket is deprecated, and won't be in 3.x. Use the result of ssl.wrap_socket() directly instead.
fake_sock = httplib.FakeSocket(p_sock, ssl)
Traceback (most recent call last):
File "/usr/bin/google", line 849, in
main()
File "/usr/bin/google", line 835, in main
run_once(options, args)
...
...
File "/usr/lib/python2.6/httplib.py", line 755, in send
self.sock.sendall(str)
AttributeError: sendall


これはGoogle Data APIs Python Client Libraryを最新にすることで解決した。Ubuntu 10.04のaptで入るpython-gdataは1.2.4。元サイトの最新版は2.0.14。ちなみにUbuntu 10.10のaptでは2.0.8だったので問題は発生しなかった。

1.2.4のままでも /usr/lib/pymodules/python2.6/atom/http.py を以下のように修正したら動いた。

@@ -42,6 +42,7 @@
import atom.http_interface
import socket
import base64
+import ssl

@@ -222,12 +223,10 @@
raise ProxyError('Error status=%s' % str(p_status))

# Trivial setup for ssl socket.
- ssl = socket.ssl(p_sock, None, None)
- fake_sock = httplib.FakeSocket(p_sock, ssl)

# Initalize httplib and replace with the proxy socket.
connection = httplib.HTTPConnection(proxy_url.host)
- connection.sock=fake_sock
+ connection.sock=ssl.wrap_socket(p_sock, None, None)
return connection

WindowsのVMWare Player同士でシリアルポートを接続する

ホストOSとして使えるのがWindowsしかなく、VMWare Player上のLinuxカーネルをデバッグしたときの設定。

デバッグする側のVMをdebugger vm、デバッグされる側のVMをdebuggee vmとする。VMに仮想シリアルポートを名前付きパイプで追加して、debugger vmの仮想シリアルポートはサーバ、debugee vmの仮想シリアルポートはクライアントにする。
最近のVMware Playerは.vmxの設定をUIからできるので楽。

以下の設定はUIが無かったころのもの。
なお、ホストOSがLinuxのときはserial0.fileNameを/tmp/vm-ttyとかにすればOK。

- debugger vm の .vmx:


serial0.present = "TRUE"
serial0.startConnected = "TRUE"
serial0.yieldOnMsrRead = "TRUE"
serial0.pipe.endPoint = "server"
serial0.tryNoRxLoss = "FALSE"
serial0.fileType = "pipe"
serial0.fileName = "\\.\pipe\com1"
serial0.autodetect = "TRUE"



- debuggee vm の .vmx:


serial0.present = "TRUE"
serial0.startConnected = "TRUE"
serial0.yieldOnMsrRead = "TRUE"
serial0.pipe.endPoint = "client"
serial0.tryNoRxLoss = "FALSE"
serial0.fileType = "pipe"
serial0.fileName = "\\.\pipe\com1"
serial0.autodetect = "TRUE"

LinuxのVMWare PlayerでホストOSからゲストOSのLinuxをデバッグする

LinuxでVMware Player上のLinuxをデバッグするときの設定。
仮想シリアルポートをソケットとして追加する。

.vmx:

serial0.present = "TRUE"
serial0.fileType = "pipe"
serial0.fileName = "/tmp/vm-tty-pipe"
serial0.pipe.endPoint = "server"
serial0.yieldOnMsrRead = "TRUE"



あとはVirtualBoxのときと同じ。

/boot/grub/menu.lst(/boot/grub/grub.conf) の
kernel /vmlinuz-2.6..... というところに、以下を追加。

kgdbwait kgdboc=ttyS0,115200



Host OSで
$ socat -d -d /tmp/vm-tty-pipe pty

socatのログで /dev/pts/# の部分を確認。

2011/04/18 20:50:05 socat[23989] N PTY is /dev/pts/5



gdbの設定
.gdbinit:

file ./vmlinux
set remotebaud 115200
target remote /dev/pts/5 ... socatで確認した番号

2011/04/17

Evernoteからデータを移した

Evernoteにあったメモをこのブログに移した。

Evernoteのエディタがブラウザ版/アプリ版が共に使いずらいと思っていた。
私としては直接HTMLを編集したい。

なので、ありきたりにブログへ移行することにした。
Linuxで作業することが多いのだが、GoogleのサービスはLinuxで動作するコマンドラインツールGoogleCLが公開されているのがかなりうれしい。

Windowsの"送る"フォルダを開く

「ファイル名を指定して実行」で
shell:sendto
shellで開くのは他にもたくさんあるけど、使ったことがあるのはこれだけ。

Windows XPでスクリーンロックのショートカットを作成する

%windir%\system32\rundll32.exe user32.dll,LockWorkStation

VMware Player 3D accelarationの設定

どの程度動くのか興味があってやってみた。
Host OSには当然及ばないが、かなり頑張ってる方だと思う。

なお最新のVMware Playerならば.vmxを直接編集せずにUIから設定できる。

.vmx:

#Enabled 3D Accelaration
mks.enable3d = TRUE
#VRAM 64MB
svga.vramSize = 67108864


あと、ゲームをするときなどはマウスが外に出ないよう、以下の設定をしておくと良い。

#Disable mouse integration
vmmouse.present = FALSE

VMware Playerのキーバインドを変更する

ある環境にVMware Playerをインストールしたとき、日本語キーボードで"_"を入力できなかったり、上矢印キーの動作が変なことがあった。以下の設定で直った。

/etc/vmware/config:

xkeymap.language = jp109
xkeymap.keycode.37 = 0x01d # Control_L
xkeymap.keycode.64 = 0x038 # Alt_L
xkeymap.keycode.97 = 0x073 # kana_RO
xkeymap.keycode.100 = 0x079 # Henkan
xkeymap.keycode.104 = 0x11c # KP_enter
xkeymap.keycode.105 = 0x11d # Control_R
xkeymap.keycode.106 = 0x135 # devide
xkeymap.keycode.107 = 0x137 # Print
xkeymap.keycode.108 = 0x138 # Alt_R
xkeymap.keycode.110 = 0x147 # Home
xkeymap.keycode.111 = 0x148 # Up
xkeymap.keycode.112 = 0x149 # Prior
xkeymap.keycode.113 = 0x14b # Left
xkeymap.keycode.114 = 0x14d # Right
xkeymap.keycode.115 = 0x14f # End
xkeymap.keycode.116 = 0x150 # Down
xkeymap.keycode.117 = 0x151 # Next
xkeymap.keycode.118 = 0x152 # Insert
xkeymap.keycode.119 = 0x153 # Delete
xkeymap.keycode.132 = 0x07d # backslash
xkeymap.keycode.133 = 0x15b # Super_L
xkeymap.keycode.135 = 0x15d # Menu
xkeymap.keycode.211 = 0x073

Ubuntu / VMWare Server 2.0の管理ユーザ設定

Ubuntuではrootでログインできない。したがって初期状態でVMware Infrastructure Web Accessにログインできない。

そこで、rootグループに属する別のユーザを追加する。
/etc/vmware/hostd/authorization.xml の
<ACEDataUser>root</ACEDataUser> のrootを、rootグループの別のユーザへ変更する。

VirtualBoxでLinuxカーネルをデバッグする

VirtualBoxの[Settings]->[Serial Ports]で"Port Mode"を"Host Pipe"にして、"Create Pipe"にチェック。
Pathに適当なパス(ここでは/tmp/vm-tty-pipe)を入力。

カーネルブート時のパラメータに次の文字列を追加。
kgdbwait kgdboc=ttyS0,115200

host os で
$ socat -d -d /tmp/vm-tty-pipe pty:

ちなみにsocatを使わないで直接 /tmp/vm-tty-pipe をgdbに渡す方法は不可。

gdbの設定
.gdbinit:

file ./vmlinux
set remotebaud 115200
target remote /dev/pts/0 ... 0 はsocatで出力された番号とする

VirutlaBoxで仮想ディスクをコピーする

cpコマンドなどでファイルをコピーした場合、VDIに記録されているUUIDを変更する。

$ VBoxManage internalcommands sethduuid original.vdi


以下のコマンドでも cp+sethduuid と同じ

$ VBoxManage clonevdi VDI1 VDI2


コピーしたときに、"udev: renamed network interface eth0 to eth2"と表示されて、
/dev/eth* が存在しない場合、/etc/udev/rules.d/70-persistent-net.rules
の中のMACアドレスを新しいものに変更する。

VMware Playerで仮想ディスクへの変更を削除する

.vmxを手動で変更。

ide0:0.mode = " independent-nonpersistent "
snapshot.action = " autoRevert "
snapshot.disabled = "TRUE"

ブラウザからサイトの表示速度を測定する

「ウェッブサイトの表示速度を測定するフリーツール集」

chkconfig対応のsvnserv init script

/etc/init.d/svnserv:

#!/bin/sh
# chkconfig: 345 98 20
# description: Subversion server
# processname: svnserve

# description for chkconfig: 345 98 20
# 345: run levels
# 98: priority to start
# 20: priority to stop

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/bin/svnserve
REPOSITORY=/var/svn/repos
NAME=svnserve
DESC="Subversion server"
USER=repos
PIDFILE=/var/run/$NAME.pid
test -x $DAEMON || exit 0

if [ -f /etc/default/$NAME ]; then
. /etc/default/$NAME
fi

DAEMON_OPTS="-d -r $REPOSITORY $OPTIONS"

start_proc() {
sudo -u $USER $DAEMON $DAEMON_OPTS
if [ "$?" -eq 0 ]
then
pidof $DAEMON > $PIDFILE
return 0
fi
return 1
}

stop_proc() {
kill -9 `cat $PIDFILE 2>/dev/null`
[ "$?" -ne 0 ] && return 1
rm -f $PIDFILE
return 0
}

status_proc() {
killall -0 $DAEMON 2>/dev/null
return "$?"
}

case "$1" in
start)
echo "Starting ${DESC}"
start_proc
[ "$?" -eq 0 ] || echo "failed"
;;
stop)
echo "Shutting down ${DESC}"
stop_proc
[ "$?" -eq 0 ] || echo "failed"
;;
restart|force-reload)
echo "Restarting ${DESC}"
stop_proc
if [ $? -ne 0 ]; then
failure ; echo ; exit 0
fi
sleep 1
start_proc
[ "$?" -eq 0 ] || echo "failed"
;;
status)
status_proc
[ $? -eq 0 ] && echo "$DESC is alive" || echo "$DESC is dead"
echo
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0

ssh tunnilingでsvn+ssh

.subversion/config:

[tunnel] 
            ssh5022 = ssh -p 5022 -i /path/to/id_rsa.pub 



% svn co svn+ssh5022://localhost/proj/trunk 

chkconfig対応のHudson init script

/usr/local/hudson/hudson:

#!/bin/sh
/usr/lib/jvm/java-6-sun/bin/java $HUDSON_JVM_OPTS -jar /usr/local/hudson/hudson.war


/etc/init.d/hudson:

#!/bin/sh
# chkconfig: 345 98 20
# description: Hudson
# processname: java

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/hudson/hudson
NAME=hudson
DESC="Hudson"
USER=repos
PIDFILE=/var/run/$NAME.pid
LOG=/var/log/$USER/hudson_stdout.log
HTTP_PROXY_HOST=www-proxy.sra.co.jp
HTTP_PROXY_PORT=80
export HUDSON_JVM_OPTS="-Xmx128m -server -Dhttp.proxyHost=$HTTP_PROXY_HOST -Dhttp.proxyPort=$HTTP_PROXY_PORT -Dhttps.proxyHost=$HTTP_PROXY_HOST -Dhttps.proxyPort=$HTTP_PROXY_PORT"

test -x $DAEMON || exit 0

if [ -f /etc/default/$NAME ]; then
. /etc/default/$NAME
fi

get_pid() {
PID=`ps ax|grep hudson.war|grep -v grep|awk '{print $1}'`
}

start_proc() {
cd /tmp
sudo -u $USER $DAEMON 1>$LOG 2>&1 &
if [ "$?" -eq 0 ]; then
sleep 1
get_pid
echo $PID > $PIDFILE
return 0
fi
return 1
}

stop_proc() {
kill -9 `cat $PIDFILE 2>/dev/null`
[ "$?" -ne 0 ] && return 1
rm -f $PIDFILE
return 0
}

status_proc() {
get_pid
[ "$PID" != "" ];
return $?
}

case "$1" in
start)
echo "Starting ${DESC}"
start_proc
[ "$?" -eq 0 ] || echo "failed"
;;
stop)
echo "Shutting down ${DESC}"
stop_proc
[ "$?" -eq 0 ] || echo "failed"
;;
restart|force-reload)
echo "Restarting ${DESC}"
stop_proc
if [ $? -ne 0 ]; then
failure ; echo ; exit 0
fi
sleep 1
start_proc
[ "$?" -eq 0 ] || echo "failed"
;;
status)
status_proc
[ $? -eq 0 ] && echo "$DESC is alive" || echo "$DESC is dead"
echo
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2
exit 1
;;
esac
exit 0

CUnit

オリジナルのCUnitよりも、Google が拡張したCUnitの方が導入が簡単。
Google版にはJUnitのXML形式のレポートを生成する拡張がある。
試したバージョンはオリジナルの2.1-0、Google版の1.1.1。

・オリジナルのCUnit 2.1 のビルド方法

$ dos2unix *
$ libtoolize
$ aclocal
$ autoconf
$ automake --add-missing
$ ./configure --prefix=/usr/local --enable-curses
$ make


・Google版CUnitでJUnitのXML形式のレポートを生成する方法

CU_automated_enable_junit_xml(CU_TRUE);
CU_automated_run_tests();


・Google版CUnitでHudsonのレポートを表示するには

Hudson でレポートを表示するにはJUnit形式で出力して、
以下のスクリプトを通して修正する必要がある。

cunit-to-junit:

#!/usr/bin/perl
while (<>) {
    next if (/<cunit_testsuites>/);
    next if (/<\/cunit_testsuites>/);
    next if (/<cunit_footer>/);
    next if (/<\/cunit_footer>/);
    print;
}


・Google版CUnit サンプル

#include <stdio.h>  
#include <CUnit/CUnit.h>
#include <CUnit/Console.h>
#include <CUnit/CUCurses.h>
#include <CUnit/Automated.h>
#include <CUnit/Basic.h>
#include "config.h"

void setup_test_suite();

void test_01() {
FILE *fp;
fp = fopen("test.log", "w");
CU_ASSERT_PTR_NOT_NULL(fp);
fprintf(fp, "PARAMS=%s %s %s\n", PARAM_A, PARAM_B, PARAM_C);
fclose(fp);
}

void test_02() {
}

void test_03() {
if (strcmp(PARAM_B, "B3") == 0) {
CU_FAIL("PARAM_B is B3");
}
}

enum {
MODE_CONSOLE,
MODE_CURSES,
MODE_AUTOMATED,
MODE_BASIC,
};

int main(int argc, char **argv) {

int mode = MODE_CURSES;

if (argc >= 2) {
char *arg = argv[1];
if (strcmp(arg, "-c") == 0) {
mode = MODE_CONSOLE;
} else
if (strcmp(arg, "-r") == 0) {
mode = MODE_CONSOLE;
} else
if (strcmp(arg, "-a") == 0) {
mode = MODE_AUTOMATED;
} else
if (strcmp(arg, "-b") == 0) {
mode = MODE_BASIC;
} else {
fprintf(stderr, "uknown option: %s\n", arg);
return 1;
}
}

CU_initialize_registry();
CU_automated_enable_junit_xml(CU_TRUE);
CU_set_output_filename("cunit");
setup_test_suite();

switch (mode) {
case MODE_CONSOLE:
CU_console_run_tests();
break;
case MODE_CURSES:
CU_curses_run_tests();
break;
case MODE_AUTOMATED:
CU_automated_run_tests();
break;
case MODE_BASIC:
(void)CU_basic_run_tests();
break;
}

CU_cleanup_registry();

return 0;
}

void setup_test_suite() {
CU_pSuite suite1;

suite1 = CU_add_suite("TestSuite1", NULL, NULL);
CU_add_test(suite1, "test_01", test_01);
CU_add_test(suite1, "test_02", test_02);
CU_add_test(suite1, "test_03", test_03);
}

wineの日本語フォントを変更する

wineで表示される日本語を見やすくする設定方法。
小林さんが提供しているIPAモナーフォントをダウンロードして、
fonts ディレクトリからttfを取り出す。

ttf を /usr/share/fonts/ipa に配置する。
$ sudo chmod 644 /usr/share/fonts/ipa/*
を忘れずに。

$ cd ~/.wine/drive_c/windows/Fonts/
$ ln -s /usr/share/fonts/ipa/*.ttf .

~/.wine/user.reg へ追加:

[Software\\Wine\\Fonts\\Replacements]
"MS Gothic"="IPA \x30e2\x30ca\x30fc \x30b4\x30b7\x30c3\x30af"
"MS Mincho"="IPA \x30e2\x30ca\x30fc \x660e\x671d"
"MS PGothic"="IPA \x30e2\x30ca\x30fc P\x30b4\x30b7\x30c3\x30af"
"MS PMincho"="IPA \x30e2\x30ca\x30fc P\x660e\x671d"
"MS UI Gothic"="IPA \x30e2\x30ca\x30fc UI\x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \x30b4\x30b7\x30c3\x30af"="IPA \x30e2\x30ca\x30fc \x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \x660e\x671d"="IPA \x30e2\x30ca\x30fc \x660e\x671d"
"\xff2d\xff33 \xff30\x30b4\x30b7\x30c3\x30af"="IPA \x30e2\x30ca\x30fc P\x30b4\x30b7\x30c3\x30af"
"\xff2d\xff33 \xff30\x660e\x671d"="IPA \x30e2\x30ca\x30fc P\x660e\x671d"

[Software\\Wine\\X11 Driver]
"ClientSideAntiAliasWithCore"="Y"
"ClientSideAntiAliasWithRender"="Y"

autofsの設定

/etc/auto.master:

/fs             /etc/auto.misc --timeout=600 --ghost


--ghost はマウントせずにエントリを表示するオプション。
tab補完を効かせたいので付けた。

/etc/auto.misc:

# ntfs
ntfshdd         -fstype=ntfs            :/dev/sda2

# ssh
sshsvr       -fstype=fuse,follow_symlinks,rw,nodev,nonempty,noatime,allow_other,max_read=65536,uid=xxx,gid=yyy     :sshfs\#xxx@sshsvr\:

# smb
smbsvr     -fstype=smbfs,credential=/etc/credential.xxx,uid=xxx,gid=yyy,iocharset=utf8    ://smbsvr/dir

coreファイルのパスを変更する

coreファイルは一ヶ所にまとまっていてほしいので保存先パスを変更した。

$ cat cat /proc/sys/kernel/core_pattern
core

# echo /var/core/core.%e.%p > /proc/sys/kernel/core_pattern

書式指定子は以下のとおり

%p - pid
%u - uid
%g - gid
%s - signal number
%t - UNIX time of dump
%h - hostname
%e - executable filename
%% - output one "%"
%<NUL> - "%" is dropped
%<OTHER> - both are dropped


init scriptで起動するプロセスはinit script内か/etc/defaultでulimit -c unlimitedすることを忘れずに。


Linuxのプロセス数制限

・システム全体の最大プロセス数

/etc/sysctl.conf: kernel.threads-max

・ユーザ当たりの最大プロセス数

/etc/security/limits.conf: nproc

・ulimit -u

Ubuntuの特殊ディレクトリ名を変更する


アプリケーションが日本語対応してくれるのは有難いけど、日本語ディレクトリ名はいや。
次のファイルを編集する。中身は見れば分かる。

    ~/.config/user-dirs.dirs

変更後の設定に合わせてmv/mkdir/rmを実行。

git diffのpagerで日本語文字化け対策

LANG=ja_JP.UTF8な環境で、

git config --global core.pager "nkf -w | LESSCHARSET=utf-8 less"

LESSCHARSET=utf-8 less は lv でも良い。

gitのコミットメール設定

push時にメール送信する。

hooks/post-receive:

#!/bin/sh
sh /usr/local/bin/post-receive-email $*


以下、オリジナルからSubject内容を変更したもの。

/usr/local/bin/post-receive-email:

#!/bin/sh
#
# Copyright (c) 2007 Andy Parkins
#
# An example hook script to mail out commit update information.  This hook
# sends emails listing new revisions to the repository introduced by the
# change being reported.  The rule is that (for branch updates) each commit
# will appear on one email and one email only.
#
# This hook is stored in the contrib/hooks directory.  Your distribution
# will have put this somewhere standard.  You should make this script
# executable then link to it in the repository you would like to use it in.
# For example, on debian the hook is stored in
# /usr/share/doc/git-core/contrib/hooks/post-receive-email:
#
#  chmod a+x post-receive-email
#  cd /path/to/your/repository.git
#  ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
#
# This hook script assumes it is enabled on the central repository of a
# project, with all users pushing only to it and not between each other.  It
# will still work if you don't operate in that style, but it would become
# possible for the email to be from someone other than the person doing the
# push.
#
# Config
# ------
# hooks.mailinglist
#   This is the list that all pushes will go to; leave it blank to not send
#   emails for every ref update.
# hooks.announcelist
#   This is the list that all pushes of annotated tags will go to.  Leave it
#   blank to default to the mailinglist field.  The announce emails lists
#   the short log summary of the changes since the last annotated tag.
# hooks.envelopesender
#   If set then the -f option is passed to sendmail to allow the envelope
#   sender address to be set
# hooks.emailprefix
#   All emails have their subjects prefixed with this prefix, or "[SCM]"
#   if emailprefix is unset, to aid filtering
#
# Notes
# -----
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
# give information for debugging.
#

# ---------------------------- Functions

#
# Top level email generation function.  This decides what type of update
# this is and calls the appropriate body-generation routine after outputting
# the common header
#
# Note this function doesn't actually generate any email output, that is
# taken care of by the functions it calls:
#  - generate_email_header
#  - generate_create_XXXX_email
#  - generate_update_XXXX_email
#  - generate_delete_XXXX_email
#  - generate_email_footer
#
generate_email()
{
    # --- Arguments
    oldrev=$(git rev-parse $1)
    newrev=$(git rev-parse $2)
    refname="$3"

    # --- Interpret
    # 0000->1234 (create)
    # 1234->2345 (update)
    # 2345->0000 (delete)
    if expr "$oldrev" : '0*$' >/dev/null
    then
        change_type="create"
    else
        if expr "$newrev" : '0*$' >/dev/null
        then
            change_type="delete"
        else
            change_type="update"
        fi
    fi

    # --- Get the revision types
    newrev_type=$(git cat-file -t $newrev 2> /dev/null)
    oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
    case "$change_type" in
    create|update)
        rev="$newrev"
        rev_type="$newrev_type"
        ;;
    delete)
        rev="$oldrev"
        rev_type="$oldrev_type"
        ;;
    esac

    # The revision type tells us what type the commit is, combined with
    # the location of the ref we can decide between
    #  - working branch
    #  - tracking branch
    #  - unannoted tag
    #  - annotated tag
    case "$refname","$rev_type" in
        refs/tags/*,commit)
            # un-annotated tag
            refname_type="tag"
            short_refname=${refname##refs/tags/}
            ;;
        refs/tags/*,tag)
            # annotated tag
            refname_type="annotated tag"
            short_refname=${refname##refs/tags/}
            # change recipients
            if [ -n "$announcerecipients" ]; then
                recipients="$announcerecipients"
            fi
            ;;
        refs/heads/*,commit)
            # branch
            refname_type="branch"
            short_refname=${refname##refs/heads/}
            ;;
        refs/remotes/*,commit)
            # tracking branch
            refname_type="tracking branch"
            short_refname=${refname##refs/remotes/}
            echo >&2 "*** Push-update of tracking branch, $refname"
            echo >&2 "***  - no email generated."
            exit 0
            ;;
        *)
            # Anything else (is there anything else?)
            echo >&2 "*** Unknown type of update to $refname ($rev_type)"
            echo >&2 "***  - no email generated"
            exit 1
            ;;
    esac

    # Check if we've got anyone to send to
    if [ -z "$recipients" ]; then
        case "$refname_type" in
            "annotated tag")
                config_name="hooks.announcelist"
                ;;
            *)
                config_name="hooks.mailinglist"
                ;;
        esac
        echo >&2 "*** $config_name is not set so no email will be sent"
        echo >&2 "*** for $refname update $oldrev->$newrev"
        exit 0
    fi

    # Email parameters
    # The email subject will contain the best description of the ref
    # that we can build from the parameters
#    describe=$(git describe $rev 2>/dev/null)
#    if [ -z "$describe" ]; then
#        describe=$rev
#    fi

    committer=$(git rev-list --pretty=format:%an ${rev}^..${rev} | tail -n 1)

    generate_email_header

    # Call the correct body generation function
    fn_name=general
    case "$refname_type" in
    "tracking branch"|branch)
        fn_name=branch
        ;;
    "annotated tag")
        fn_name=atag
        ;;
    esac
    generate_${change_type}_${fn_name}_email

    generate_email_footer
}

generate_email_header()
{
    # --- Email (all stdout will be the email)
    # Generate header

    cat <<-EOF
    To: $recipients
    Subject: ${emailprefix} $short_refname ${change_type}d by ${committer}
    X-Git-Refname: $refname
    X-Git-Reftype: $refname_type
    X-Git-Oldrev: $oldrev
    X-Git-Newrev: $newrev

    The $refname_type, $short_refname has been ${change_type}d
    EOF
}

generate_email_footer()
{
    SPACE=" "
    cat <<-EOF


    hooks/post-receive
    --${SPACE}
    EOF
}

# --------------- Branches

#
# Called for the creation of a branch
#
generate_create_branch_email()
{
    # This is a new branch and so oldrev is not valid
    echo "        at  $newrev ($newrev_type)"
    echo ""

    echo $LOGBEGIN
    # This shows all log entries that are not already covered by
    # another ref - i.e. commits that are now accessible from this
    # ref that were previously not accessible
    # (see generate_update_branch_email for the explanation of this
    # command)
    git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
    git rev-list --pretty --stdin $newrev
    echo $LOGEND
}

#
# Called for the change of a pre-existing branch
#
generate_update_branch_email()
{
    # Consider this:
    #   1 --- 2 --- O --- X --- 3 --- 4 --- N
    #
    # O is $oldrev for $refname
    # N is $newrev for $refname
    # X is a revision pointed to by some other ref, for which we may
    #   assume that an email has already been generated.
    # In this case we want to issue an email containing only revisions
    # 3, 4, and N.  Given (almost) by
    #
    #  git rev-list N ^O --not --all
    #
    # The reason for the "almost", is that the "--not --all" will take
    # precedence over the "N", and effectively will translate to
    #
    #  git rev-list N ^O ^X ^N
    #
    # So, we need to build up the list more carefully.  git rev-parse
    # will generate a list of revs that may be fed into git rev-list.
    # We can get it to make the "--not --all" part and then filter out
    # the "^N" with:
    #
    #  git rev-parse --not --all | grep -v N
    #
    # Then, using the --stdin switch to git rev-list we have effectively
    # manufactured
    #
    #  git rev-list N ^O ^X
    #
    # This leaves a problem when someone else updates the repository
    # while this script is running.  Their new value of the ref we're
    # working on would be included in the "--not --all" output; and as
    # our $newrev would be an ancestor of that commit, it would exclude
    # all of our commits.  What we really want is to exclude the current
    # value of $refname from the --not list, rather than N itself.  So:
    #
    #  git rev-parse --not --all | grep -v $(git rev-parse $refname)
    #
    # Get's us to something pretty safe (apart from the small time
    # between refname being read, and git rev-parse running - for that,
    # I give up)
    #
    #
    # Next problem, consider this:
    #   * --- B --- * --- O ($oldrev)
    #          \
    #           * --- X --- * --- N ($newrev)
    #
    # That is to say, there is no guarantee that oldrev is a strict
    # subset of newrev (it would have required a --force, but that's
    # allowed).  So, we can't simply say rev-list $oldrev..$newrev.
    # Instead we find the common base of the two revs and list from
    # there.
    #
    # As above, we need to take into account the presence of X; if
    # another branch is already in the repository and points at some of
    # the revisions that we are about to output - we don't want them.
    # The solution is as before: git rev-parse output filtered.
    #
    # Finally, tags: 1 --- 2 --- O --- T --- 3 --- 4 --- N
    #
    # Tags pushed into the repository generate nice shortlog emails that
    # summarise the commits between them and the previous tag.  However,
    # those emails don't include the full commit messages that we output
    # for a branch update.  Therefore we still want to output revisions
    # that have been output on a tag email.
    #
    # Luckily, git rev-parse includes just the tool.  Instead of using
    # "--all" we use "--branches"; this has the added benefit that
    # "remotes/" will be ignored as well.

    # List all of the revisions that were removed by this update, in a
    # fast forward update, this list will be empty, because rev-list O
    # ^N is empty.  For a non fast forward, O ^N is the list of removed
    # revisions
    fast_forward=""
    rev=""
    for rev in $(git rev-list $newrev..$oldrev)
    do
        revtype=$(git cat-file -t "$rev")
        echo "  discards  $rev ($revtype)"
    done
    if [ -z "$rev" ]; then
        fast_forward=1
    fi

    # List all the revisions from baserev to newrev in a kind of
    # "table-of-contents"; note this list can include revisions that
    # have already had notification emails and is present to show the
    # full detail of the change from rolling back the old revision to
    # the base revision and then forward to the new revision
    for rev in $(git rev-list $oldrev..$newrev)
    do
        revtype=$(git cat-file -t "$rev")
        echo "       via  $rev ($revtype)"
    done

    if [ "$fast_forward" ]; then
        echo "      from  $oldrev ($oldrev_type)"
    else
        #  1. Existing revisions were removed.  In this case newrev
        #     is a subset of oldrev - this is the reverse of a
        #     fast-forward, a rewind
        #  2. New revisions were added on top of an old revision,
        #     this is a rewind and addition.

        # (1) certainly happened, (2) possibly.  When (2) hasn't
        # happened, we set a flag to indicate that no log printout
        # is required.

        echo ""

        # Find the common ancestor of the old and new revisions and
        # compare it with newrev
        baserev=$(git merge-base $oldrev $newrev)
        rewind_only=""
        if [ "$baserev" = "$newrev" ]; then
            echo "This update discarded existing revisions and left the branch pointing at"
            echo "a previous point in the repository history."
            echo ""
            echo " * -- * -- N ($newrev)"
            echo "            \\"
            echo "             O -- O -- O ($oldrev)"
            echo ""
            echo "The removed revisions are not necessarilly gone - if another reference"
            echo "still refers to them they will stay in the repository."
            rewind_only=1
        else
            echo "This update added new revisions after undoing existing revisions.  That is"
            echo "to say, the old revision is not a strict subset of the new revision.  This"
            echo "situation occurs when you --force push a change and generate a repository"
            echo "containing something like this:"
            echo ""
            echo " * -- * -- B -- O -- O -- O ($oldrev)"
            echo "            \\"
            echo "             N -- N -- N ($newrev)"
            echo ""
            echo "When this happens we assume that you've already had alert emails for all"
            echo "of the O revisions, and so we here report only the revisions in the N"
            echo "branch from the common base, B."
        fi
    fi

    # The diffstat is shown from the old revision to the new revision.
    # This is to show the truth of what happened in this change.
    # There's no point showing the stat from the base to the new
    # revision because the base is effectively a random revision at this
    # point - the user will be interested in what this revision changed
    # - including the undoing of previous revisions in the case of
    # non-fast forward updates.
    echo ""
    echo "Summary of changes:"
    git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev

    echo ""
    if [ -z "$rewind_only" ]; then
        #echo "Those revisions listed above that are new to this repository have"
        #echo "not appeared on any other notification email; so we list those"
        #echo "revisions in full, below."

        echo ""
        echo $LOGBEGIN
        git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
        git rev-list --pretty --stdin $oldrev..$newrev

        # XXX: Need a way of detecting whether git rev-list actually
        # outputted anything, so that we can issue a "no new
        # revisions added by this update" message

        echo $LOGEND
    else
        echo "No new revisions were added by this update."
    fi
}

#
# Called for the deletion of a branch
#
generate_delete_branch_email()
{
    echo "       was  $oldrev"
    echo ""
    echo $LOGEND
    git show -s --pretty=oneline $oldrev
    echo $LOGEND
}

# --------------- Annotated tags

#
# Called for the creation of an annotated tag
#
generate_create_atag_email()
{
    echo "        at  $newrev ($newrev_type)"

    generate_atag_email
}

#
# Called for the update of an annotated tag (this is probably a rare event
# and may not even be allowed)
#
generate_update_atag_email()
{
    echo "        to  $newrev ($newrev_type)"
    echo "      from  $oldrev (which is now obsolete)"

    generate_atag_email
}

#
# Called when an annotated tag is created or changed
#
generate_atag_email()
{
    # Use git for-each-ref to pull out the individual fields from the
    # tag
    eval $(git for-each-ref --shell --format='
    tagobject=%(*objectname)
    tagtype=%(*objecttype)
    tagger=%(taggername)
    tagged=%(taggerdate)' $refname
    )

    echo "   tagging  $tagobject ($tagtype)"
    case "$tagtype" in
    commit)

        # If the tagged object is a commit, then we assume this is a
        # release, and so we calculate which tag this tag is
        # replacing
        prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)

        if [ -n "$prevtag" ]; then
            echo "  replaces  $prevtag"
        fi
        ;;
    *)
        echo "    length  $(git cat-file -s $tagobject) bytes"
        ;;
    esac
    echo " tagged by  $tagger"
    echo "        on  $tagged"

    echo ""
    echo $LOGBEGIN

    # Show the content of the tag message; this might contain a change
    # log or release notes so is worth displaying.
    git cat-file tag $newrev | sed -e '1,/^$/d'

    echo ""
    case "$tagtype" in
    commit)
        # Only commit tags make sense to have rev-list operations
        # performed on them
        if [ -n "$prevtag" ]; then
            # Show changes since the previous release
            git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
        else
            # No previous tag, show all the changes since time
            # began
            git rev-list --pretty=short $newrev | git shortlog
        fi
        ;;
    *)
        # XXX: Is there anything useful we can do for non-commit
        # objects?
        ;;
    esac

    echo $LOGEND
}

#
# Called for the deletion of an annotated tag
#
generate_delete_atag_email()
{
    echo "       was  $oldrev"
    echo ""
    echo $LOGEND
    git show -s --pretty=oneline $oldrev
    echo $LOGEND
}

# --------------- General references

#
# Called when any other type of reference is created (most likely a
# non-annotated tag)
#
generate_create_general_email()
{
    echo "        at  $newrev ($newrev_type)"

    generate_general_email
}

#
# Called when any other type of reference is updated (most likely a
# non-annotated tag)
#
generate_update_general_email()
{
    echo "        to  $newrev ($newrev_type)"
    echo "      from  $oldrev"

    generate_general_email
}

#
# Called for creation or update of any other type of reference
#
generate_general_email()
{
    # Unannotated tags are more about marking a point than releasing a
    # version; therefore we don't do the shortlog summary that we do for
    # annotated tags above - we simply show that the point has been
    # marked, and print the log message for the marked point for
    # reference purposes
    #
    # Note this section also catches any other reference type (although
    # there aren't any) and deals with them in the same way.

    echo ""
    if [ "$newrev_type" = "commit" ]; then
        echo $LOGBEGIN
        git show --no-color --root -s --pretty=medium $newrev
        echo $LOGEND
    else
        # What can we do here?  The tag marks an object that is not
        # a commit, so there is no log for us to display.  It's
        # probably not wise to output git cat-file as it could be a
        # binary blob.  We'll just say how big it is
        echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
    fi
}

#
# Called for the deletion of any other type of reference
#
generate_delete_general_email()
{
    echo "       was  $oldrev"
    echo ""
    echo $LOGEND
    git show -s --pretty=oneline $oldrev
    echo $LOGEND
}

send_mail()
{
    if [ -n "$envelopesender" ]; then
        /usr/sbin/sendmail -t -f "$envelopesender"
    else
        /usr/sbin/sendmail -t
    fi
}

# ---------------------------- main()

# --- Constants
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"

# --- Config
# Set GIT_DIR either from the working directory, or from the environment
# variable.
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
if [ -z "$GIT_DIR" ]; then
    echo >&2 "fatal: post-receive: GIT_DIR not set"
    exit 1
fi

#projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
# Check if the description is unchanged from it's default, and shorten it to
# a more manageable length if it is
#if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
#then
#    projectdesc="UNNAMED PROJECT"
#fi

recipients=$(git config hooks.mailinglist)
announcerecipients=$(git config hooks.announcelist)
envelopesender=$(git config hooks.envelopesender)
#envelopesender="git@local"
#emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
GIT_DIR_NAME=$(cd $GIT_DIR; basename `pwd`)
emailprefix="[${GIT_DIR_NAME}]"

# --- Main loop
# Allow dual mode: run from the command line just like the update hook, or
# if no arguments are given then run as a hook script
if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
    # Output to the terminal in command line mode - if someone wanted to
    # resend an email; they could redirect the output to sendmail
    # themselves
    PAGER= generate_email $2 $3 $1
else
    while read oldrev newrev refname
    do
        generate_email $oldrev $newrev $refname | send_mail
    done
fi



lsの色を変更する

lsコマンドの色出力について、デフォルトの設定が気に入らないので変更した。
.bashrc で dircolors を呼ぶ箇所に ~/.dir_colors を追加。


eval "`dircolors -b ~/.dir_colors`"
alias ls='ls --color=auto'


dircolors -b > ~/.dir_colors で作成したファイルを編集。
以下、サンプル。


# Configuration file for dircolors, a utility to help you set the
# LS_COLORS environment variable used by GNU ls with the --color option.
# Copyright (C) 1996, 1999-2008
# Free Software Foundation, Inc.
# Copying and distribution of this file, with or without modification,
# are permitted provided the copyright notice and this notice are preserved.
# The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the
# slackware version of dircolors) are recognized but ignored.
# Below, there should be one TERM entry for each termtype that is colorizable
TERM Eterm
TERM ansi
TERM color-xterm
TERM con132x25
TERM con132x30
TERM con132x43
TERM con132x60
TERM con80x25
TERM con80x28
TERM con80x30
TERM con80x43
TERM con80x50
TERM con80x60
TERM cons25
TERM console
TERM cygwin
TERM dtterm
TERM eterm-color
TERM gnome
TERM gnome-256color
TERM jfbterm
TERM konsole
TERM kterm
TERM linux
TERM linux-c
TERM mach-color
TERM mlterm
TERM putty
TERM rxvt
TERM rxvt-cygwin
TERM rxvt-cygwin-native
TERM rxvt-unicode
TERM screen
TERM screen-256color
TERM screen-bce
TERM screen-w
TERM screen.linux
TERM vt100
TERM xterm
TERM xterm-16color
TERM xterm-256color
TERM xterm-88color
TERM xterm-color
TERM xterm-debian
# Below are the color init strings for the basic file types. A color init
# string consists of one or more of the following numeric codes:
# Attribute codes:
# 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
# Text color codes:
# 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
# Background color codes:
# 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
#NORMAL 00 # no color code at all
#FILE 00 # regular file: use no color at all
RESET 0 # reset to "normal" color
DIR 34 # directory
LINK 04;34 # symbolic link. (If you set this to 'target' instead of a
# numerical value, the color is as for the file pointed to.)
HARDLINK 04;31 # regular file with more than one link
FIFO 40;33 # pipe
SOCK 01;35 # socket
DOOR 01;35 # door
BLK 40;33;01 # block device driver
CHR 40;33;01 # character device driver
ORPHAN 40;31;01 # symlink to nonexistent file, or non-stat'able file
SETUID 37;41 # file that is setuid (u+s)
SETGID 30;43 # file that is setgid (g+s)
CAPABILITY 30;41 # file with capability
STICKY_OTHER_WRITABLE 30;43 # dir that is sticky and other-writable (+t,o+w)
OTHER_WRITABLE 34 # dir that is other-writable (o+w) and not sticky
STICKY 37;47 # dir with the sticky bit set (+t) and not other-writable
# This is for files with execute permission:
EXEC 35

vimrcをさらす

tab pageをよく使うのでF2,F3にタブ切り替えを割り当てている。


set novisualbell
set ai
set number

map <F2> :tabprevious<CR>
map <F3> :tabnext<CR>
map <F4> :cn<CR>

set fileencodings=utf-8,ucs-bom,iso-2022-jp-3,iso-2022-jp,eucjp-ms,euc-jisx0213,euc-jp,sjis,cp932

" taglist.vim
let Tlist_WinWidth=50

"for c-lang
au BufNewFile,BufRead *.c set sw=4
au BufNewFile,BufRead *.c set ts=8
au BufNewFile,BufRead *.txt syntax off

" format.vim
let format_join_spaces=2
let format_allow_over_tw=0
set formatoptions=mq

set tags+=,../tags,../../tags,../../../tags,../../../../tags,../../../../../tags

" modeline
set modeline
set modelines=10

set maxmempattern=1000
set clipboard=unnamed

LVMミラーボリュームの復旧

PV: /dev/sdb /dev/sdc
VG: vg1
LV: lvm

/dev/sdcが壊れて、/dev/sddに取り替えることを想定。


# lvconvert -m 0 vg1/lvm 
# lvs -a -o +devices
LV VG Attr LSize Origin Snap% Move Log Copy% Convert Devices
lvm vg1 -wa-ao 1.80t /dev/sdb(0)
# vgreduce vg1 /dev/sdc
# pvcreate /dev/sdd
# vgextend vg1 /dev/sdd
# lvconvert -m 1 /dev/vg1/lvm /dev/sdb /dev/sdd --alloc anywhere

Ubuntu 10.10 64bit Adobe AIR 2.6 で日本語入力できるようにする

私はTwitterクライアントのSpazを利用している。Adobe AIRで作られているのだが、Ubuntu 10.10 x86_64では日本語入力ができなかった。以下が対処法。
元情報のページのリンクが切れているので、ここから再掲。


$ cd /tmp
$ sudo apt-get install ia32-libs
$ wget -c http://airdownload.adobe.com/air/lin/download/latest/AdobeAIRInstaller.bin
$ chmod +x AdobeAIRInstaller.bin
$ ./AdobeAIRInstaller.bin
$ wget http://frozenfox.freehostia.com/cappy/getlibs-all.deb
$ sudo dpkg -i getlibs-all.deb
$ getlibs -p libibus2 ibus ibus-gtk
$ echo '"/usr/lib32/gtk-2.0/2.10.0/immodules/im-ibus.so"' > gtk.immodules.32
$ echo '"ibus" "IBus (Intelligent Input Bus)" "ibus" "" "ja:ko:zh:*"' >> gtk.immodules.32
$ sudo mv gtk.immodules.32 /usr/lib/gtk-2.0/2.10.0/


お気に入りのvim plugin

taglist, gtags

  タグジャンプ用。私はExuberant CtagsとGNU Globalを同時に使用している。
  Cを書くときに必須。

format

  テキストファイルの整形。

yanktmp

  一時ファイルが保存先のコピー/ペースト。何かと便利。

eregex

  正規表現拡張。perlの正規表現に慣れてるもので。

qbuf

  バッファ一覧・編集。
  タグジャンプしまくってバッファがやたらと増えたときに使用。

rsyslogが動作しないときの対処

rsyslog が動作しないときは

- rsyslog -d でデバッグログを見て原因を探る。

- rsyslog -c0 (互換モード)で起動する。ただしUbuntu 10.10では rc scriptが/lib/init/upstart-jobへのリンクなので、別のものに置き換えないとオプションを変更できない。

シェルスクリプトでパスワード入力などで入力文字を表示させない方法

たまに使う。

stty -echo
read pass
stty echo

linux でbeep音を無効にする

/etc/modprobe.d/blacklist.conf に次の行を追加。

    blacklist pcspkr

以前は手動で追加していたが、最近のUbuntu環境を見たら最初から追加されていた。
コメントが書いてあった。

# ugly and loud noise, getting on everyone's nerves; this should be done by a
# nice pulseaudio bing (Ubuntu: #77010)

うん、そうだよね。

ssh tunnelingでgitosis

$ ssh -f -N -L 5022:remotehost.net:22 user@gateway.com

~/.ssh/config へ以下の設定をすると"ssh -p 5022 localhost"のかわりに
"ssh remotehost" で接続できる。ssh tunnelでgitosisへアクセスするときはこれが必要。

Host remotehost 
Hostname localhost
HostKeyAlias remotehost.net
Port 5022

vimで日本語文字コードを自動判別

.vimrc:
set fileencodings=ucs-bom,iso-2022-jp-3,iso-2022-jp,eucjp-ms,euc-jisx0213,euc-jp,sjis,cp932,utf-8

.vimrcにfileencodingsを記述せずに、後からset fileencoding=euc-jpなどとしても切り替わらない。

format.vimをencoding=utf-8で使う

vim使いとしてはうれしいパッチがこれ
少なくともKaoriya版vim7.2では必要。

USBにインストールしたLinuxのブート


grub> root(hd0,0)
grub> kernel /boot/vmlinuz-2.6.xx ro root=(hd0,0)  ... TAB補完が効いた
grub> initrd /boot/initrd.img-2.6.xx                           ... TAB補完が効いた
grub> boot

Linux LiveCDからgrubを再設定する

何かしてLinuxをブートできなくなり、grubを再設定する必要が生じたときの話。
LiveCDでブート後、grubの設定ファイルが保存されているパーティションをmount。
ここでは /dev/sda1 とする。

# mount /dev/sda1 /mnt
# /mnt/sbin/grub
grub> root (hd0,0)
grub> setup (hd0)
grub> quit
# reboot

Ubuntu 9.04 カーネル再構築で unable to mount root fs が発生したときの対処

起動時に "unable to mount root fs on unknown-block(0,0)" が発生した。
initrd を作成していなかったのが原因。
以下のコマンドで作成する。
# mkinitramfs -o initrd.img-2.6.29 2.6.29
そして、grubの設定ファイルにinitrdの設定を追加した。

yum で "Metadata file does not match checksum" が発生したときの対処方法

DBが壊れているかもしれなので、
# yum clean all

fastestmirror を使っている場合、
# yum makecache --disableplugin=fastestmirror

WindowsでHTC Desire(X06HT)をadbで接続する設定

(AndroidSDK)\usb_driver\android_winusb.inf:
[Google.NTx86] に次の文を追加

;HTC Desire 
%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C87
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C87&MI_01

2011/04/16

Eclipse WTPのTomcatが出力するログを設定する

環境: Eclipse 3.6 Helios, Tomcat 6.0, JDK 6

Eclise WTPで起動するTomcatが使用するlogging.propertiesはJDKデフォルトのファイルである。
OpenJDK 6 Linuxの場合は /etc/java-6-openjdk/logging.properties。

これを変更するには、Run Configurations の Apache Tomcat で、VMの引数を追加する。

    -Djava.util.logging.config.file="logging.propertiesのパス"

Hibernateのデバッグログを出力したかったので、次の行を追加した。

    org.hibernate.level=FINEST

EclipseでTomcatの起動タイムアウトを設定する

環境: Eclispse 3.6 Helios, Tomcat 6.0

EclipseでTomcatをデバッグ起動し、起動完了する前にどこかブレークポイントで止めておくと、45秒でタイムアウトになりデバッグが終了してしまう。初期化処理をデバッグするときに困った。

メッセージ:
Server Tomcat v6.5 Server at localhost was unable to start within 45 seconds.
If the server requires more time, try increasing the timeout in the server editor.

これを回避するには、Server Viewで目的のサーバをダブルクリックしてServer Editorを開き、Timeouts - startの項目を編集すれば良い。とりあえず600秒にしておいた。

Struts2 Convention Pluginとsubmitタグ

環境: struts2-2.2.2.1

今ではdeprecatedなCodebehind Pluginではできたのだが、Struts2 Convention Pluginはsubmitタグのmethod attributeを見てくれない。

Actionクラスのexecute()以外に実行したいメソッドがある場合は、@Actionで指定するポリシーのようだ。リクエストパラメータでメソッドの区別をするのではなく、URLで区別するということだ。
理由は知らないけど、Codebehindではsubmitの値次第で、意図しないpublicメソッドが呼べてしまうしね。
@Actionならば、指定したメソッドだけが呼び出し対象になる。

それでConvention Pluginでは次のとおり。
submitのactionを指定して、その値を@Actionの引数に対応させる。
submitが1つのときはformのactionに指定しても良い。
ただし複数のsubmitが存在する場合は、submitのactionを使ってそれぞれ異なるURLを指定する。

.jsp

<s:form> 
<s:submit action="hoge" value="Hoge">
<s:form>


Action Class

@Action("hoge") 
public String hoge() {
...
}