☆os-proberでsda10がsda2の前に出てくるのを直す

私はパーティションを制限いっぱいの16まで切っています。
これで困るのは、grub-updateで作成される/boot/grub/grub.cfgのmenuentryの順序です。
grub2のOS選択画面で/dev/sda2のUbuntuより先に/dev/sda10や/dev/sda11が先に表示されてしまいます。
30_os-proberのセクションではos-proberの出力順にしたがってmenuentryが作成されるので、
修正すべきはos-proberの出力順序です。

os-proberの中をのぞいてみるとpartitionsというシェル関数で"/sys/block/*/*[0-9]"という記述があります。
シェルのグロビングのソートは文字列の昇順なので、ここが原因だとわかります。
コマンドの性質上(障害時などは/usrがマウントされない可能性がある)、
スタティックリンクされた/bin,/sbinの下にあるバイナリだけを使用して処理を記述すべきです。
そうするとsortは使えません。もちろんawkやperlもダメです。
ちょっとしたパズル感覚です。

2010/05/22追記
スタティックリンクされたバイナリは/bin/busyboxと/sbin/ldconfig.realだけでした。
しかもbusyboxにはawkやsortも組み込まれています....

幸いsh(dash)のビルトインにprintfがあったので、sed,printf,シェルのグロビングのあわせ技でソートしてみました。
パッチはこれ os-prober.patch です。
tail_num_sortは/usr/share/os-prober/common.shに追加することも考えたのですが、 自分しか使わないと思ったのでos-proberに入れてしまいました。

しかし、Lucidになってからmenuentryの文字列が長すぎです。
OS選択画面でパーティションの部分が見えなくなってしまいました。
仕方がないので"e"を押してどのパーティションか確認してからCtrl+xで起動しています。

修正前のos-proberの出力
/dev/sda10:Ubuntu 10.04 LTS (10.04):Ubuntu:linux
/dev/sda11:Ubuntu 10.04 LTS (10.04):Ubuntu1:linux
/dev/sda2:Ubuntu 9.10 (9.10):Ubuntu2:linux
/dev/sda3:Ubuntu 9.10 (9.10):Ubuntu3:linux
/dev/sda5:Ubuntu 10.04 LTS (10.04):Ubuntu4:linux
/dev/sda6:Ubuntu 10.04 LTS (10.04):Ubuntu5:linux
/dev/sda7:Ubuntu lucid (development branch) (10.04):Ubuntu6:linux
/dev/sda8:Ubuntu 10.04 LTS (10.04):Ubuntu7:linux
/dev/sda9:Ubuntu 10.04 LTS (10.04):Ubuntu8:linux
修正後のos-proberの出力
/dev/sda2:Ubuntu 9.10 (9.10):Ubuntu:linux
/dev/sda3:Ubuntu 9.10 (9.10):Ubuntu1:linux
/dev/sda5:Ubuntu 10.04 LTS (10.04):Ubuntu2:linux
/dev/sda6:Ubuntu 10.04 LTS (10.04):Ubuntu3:linux
/dev/sda7:Ubuntu lucid (development branch) (10.04):Ubuntu4:linux
/dev/sda8:Ubuntu 10.04 LTS (10.04):Ubuntu5:linux
/dev/sda9:Ubuntu 10.04 LTS (10.04):Ubuntu6:linux
/dev/sda10:Ubuntu 10.04 LTS (10.04):Ubuntu7:linux
/dev/sda11:Ubuntu 10.04 LTS (10.04):Ubuntu8:linux
os-prober.patch
diff -ru a/os-prober b/os-prober
--- a/os-prober 2010-02-11 08:06:58.000000000 +0900
+++ b/os-prober 2010-05-04 21:45:47.261264519 +0900
@@ -24,12 +24,32 @@
  return 1
 }
 
+tail_num_sort() {
+ if [ $# -eq 0 ] ; then
+  return 0
+ fi
+
+ if type mktemp >/dev/null 2>&1; then
+  TAILNUMSORT_TMP="$(mktemp -d /tmp/tailnumsort.XXXXXX)"
+ else
+  TAILNUMSORT_TMP=/tmp/tailnumsort.$$
+ fi
+ trap "rm -rf $TAILNUMSORT_TMP" EXIT HUP INT QUIT TERM
+
+ for dev in $@ ; do
+  disk=$(echo $dev | sed -e 's#/##g;s#[0-9]*[0-9]*$##')
+  fname=$(printf '%s%02d' $disk $(echo $dev | sed -e "s#/##g;s#$disk##"))
+  echo $dev > $TAILNUMSORT_TMP/$fname
+ done
+ cat $TAILNUMSORT_TMP/* 2>/dev/null
+}
+
 partitions () {
  # Exclude partitions that have whole_disk sysfs attribute set.
  if [ -d /sys/block ]; then
   # Exclude partitions on physical disks that are part of a
   # Serial ATA RAID disk.
-  for part in /sys/block/*/*[0-9]; do
+  for part in $(tail_num_sort /sys/block/*/*[0-9]); do
    if [ -f "$part/start" ] && \
       [ ! -f "$part/whole_disk" ] && ! on_sataraid $part; then
     name="$(echo "${part##*/}" | sed 's,[!.],/,g')"

☆fsarchiverでパーティションをコピーしてみました

5/2 追記
5/1 当初の手順だと不足がありました。
grub.cfgのsearchの行とlinuxの行でUUIDの齟齬が生じて
結局 linux行が優先され、/dev/sda8をルートとして起動してしまいます。
修正した手順の確認がとれたので修正済みのものを掲載しました。
修正した点は、実行していたにもかかわらず記載していなかった7.update-grubの実行と
本質的に欠けていた6.のところのgrub.cfgの編集です。

Ubuntu 10.04 Lucid Lynxが正式リリースされました。
本格的に弄るのは日本語Rimixが出てからのつもりですが、一応インストールしました。
インストールすれば弄りたくなるのが人情です。
弄っておかしくなった場合や、バグ報告のを考慮すると、あまり弄っていない状態をすぐに作れるようにしておくと便利です。
パーティション丸ごとのバックアップといえばddやpartimageがメジャーですが、ddは敷居が高く、partimageはext4に対応していません。
そこでfsarchiverを使用してバックアップを作成し、これを別パーティションに復元して弄ることにしました。
fsarchiverはLucidからuniverseのリポジトリに入りました。バージョンはまだ0.6.8ですが...
幸いパーティションはMaxの16まで切ってあるので、空きはいっぱいあります。
とりあえず、/dev/sda8にdesktop-i386をインストール。
その後、sda9,sda10,sda11にコピーをしました。
(バックアップからのリストアは1つあたり3分くらいだったので調子に乗って3つもコピーしてしまいました。)

手順の概略
1.とりあえず10.04を/dev/sda8へインストール
2./dev/sda1にインストールしてあったKarmicを起動
3.fsarchiverで/dev/sda8を丸ごとバックアップ
4.fsarchiverで別のパーティション(/dev/sda9)へリストア
5.tune2fsでリストアしたパーティション(/dev/sda9)のUUIDを変更
6.リストアしたパーティション(/dev/sda9)に入っている設定ファイルを修正
7.リストアしたパーティションを起動時に選択可能にするためupdate-grubを実行
8.リブートして/dev/sda9から起動、dpkg-reconfigureでgrub-pcのインストール先を修正

(私の環境では"/home"や"/var"を分けずに"/"の1パーティションだけで運用しています。 "/home"は個別のパーティションにする理由は設定ファイルをシステムごとに別にするためです。 データ用のパーティションは別に用意して$HOMEのしたからシンボリックリンクを張っています。)

以下で3以降の手順を細かく説明します。

3.fsarchiverで/dev/sda8を丸ごとバックアップ

bzip2 -2と同一の圧縮レベルで、圧縮用のスレッドを2つ使用してバックアップ

$ sudo fsarchiver -z5 -j2 savefs sda8.fsa /dev/sda8

4.fsarchiverで別のパーティション(/dev/sda9)へリストア

$ sudo fsarchiver restfs sda8.fsa id=0,dest=/dev/sda9

5.tune2fsでリストアしたパーティション(/dev/sda9)のUUIDを変更

リストアしたパーティションのUUIDはバックアップしたパーティションと同じになっているので変更します。 tune2fsのmanページで見ると-Uのあとにrandomやtimeをしてできそうなのですが、なぜかうまくいきません。 普通にuuidgenの出力を使うことにしました。

$ sudo tune2fs -U $(uuidgen) /dev/sda9
# fstabを書き換えるためにUUIDを確認
$ sudo blkid /dev/sda9
/dev/sda9: UUID="2bd68985-c6cf-4eb5-9c55-aaee3b173656" TYPE="ext4" 

6.リストアしたパーティション(/dev/sda9)に入っている設定ファイルを修正

fstabとgrub.cfgを修正します。
grub.cfgの修正が必要な理由は、update-grubの実行時に走る/etc/grub.d/30_os-proberの中で
/usr/lib/linux-boot-probes/40grub2 が実行されるのですが、これがリストアしたパーティションにある
grug.cfgを参照してるからです。

$ sudo mkdir /mnt/sda9
# fstabのUUIDを書き換える。ついでにduring installationのコメントも/dev/sda9に変更
$ sudo vi /mnt/sda9/etc/fstab
$ sudo vi /mnt/sda9/boot/grub.cfg
fstabの修正内容
@@ -7,4 +7,4 @@
 #                
 proc            /proc           proc    nodev,noexec,nosuid 0       0
 # / was on /dev/sda10 during installation
-UUID=32c50640-99e1-49c1-837f-05701e9e7def /               ext4    errors=remount-ro 0       1
+UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 /               ext4    errors=remount-ro 0       1
grub.cfgの修正内容(書込みは:w!で強制書込みをします)
UUIDの書き換えが必要な箇所は"### BEGIN /etc/grub.d/10_linux ###"から"### END /etc/grub.d/10_linux ###"の間にある
"linux"始まる行です。(ほかの部分は8.のdpkg-reconfigureの時に直ります。)
--- sda10_grub.cfg.org 2010-05-02 20:28:18.581936565 +0900
+++ sda10_grub.cfg.new 2010-05-02 20:31:53.980286836 +0900
@@ -63,22 +63,22 @@
 ### BEGIN /etc/grub.d/10_linux ###
 menuentry 'Ubuntu, with Linux 2.6.32-21-generic' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod ext2
    set root='(hd0,8)'
    search --no-floppy --fs-uuid --set 32c50640-99e1-49c1-837f-05701e9e7def
-   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=32c50640-99e1-49c1-837f-05701e9e7def ro   quiet splash
+   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 ro   quiet splash
    initrd  /boot/initrd.img-2.6.32-21-generic
 }
 menuentry 'Ubuntu, with Linux 2.6.32-21-generic (recovery mode)' --class ubuntu --class gnu-linux --class gnu --class os {
    recordfail
    insmod ext2
    set root='(hd0,8)'
    search --no-floppy --fs-uuid --set 32c50640-99e1-49c1-837f-05701e9e7def
    echo    'Loading Linux 2.6.32-21-generic ...'
-   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=32c50640-99e1-49c1-837f-05701e9e7def ro single 
+   linux   /boot/vmlinuz-2.6.32-21-generic root=UUID=2bd68985-c6cf-4eb5-9c55-aaee3b173656 ro single 
    echo    'Loading initial ramdisk ...'
    initrd  /boot/initrd.img-2.6.32-21-generic
 }
 ### END /etc/grub.d/10_linux ###
 
 ### BEGIN /etc/grub.d/20_memtest86+ ###

7.リストアしたパーティションを起動時に選択可能にするためupdate-grubを実行

$ sudo update-grub

8.リブートして/dev/sda9から起動、dpkg-reconfigureでgrub-pcのインストール先を修正

$ sudo dpkg-reconfigure grub-pc
2つほど設定を訊かれますがデフォルトで進めると下記のような画面が出るので grubのインストール先を選択しなおします。

☆LucidのGnome-Sudokuにパッチをあてる

Lucid(Ubuntu 10.04)のGnome数独(Gnome-sudoku 2.30)はKarmicで悪化した経過時間の表示が修正されています。
Karmicの時はセルの上側にメモ用の数字を入力すると経過時間がやたら増えたのですが、これも直っているようです。
(完全ではないようで、多少修正されています。)
しかし、残念ながら特定の場合にコンフリクトした値が赤くならないバグと終了判定が正しくないバグが残っています。
(すでにGnomeの新しいリビジョンでは修正されているので次のリリースでは修正されているはずです。)
これを修正するパッチを用意しました。

gnome-sudoku.2.30.0-0ubuntu6.patchをダウンロードして下記の要領でパッチしてください。
ファイルは ~/Download ディレクトリにダウンロードした前提で記述しています。

$ cd /usr/lib/python2.6/dist-packages/gnome_sudoku
$ sudo patch -p1 < ~/Download/gnome-sudoku.2.30.0-0ubuntu6.patch

注意点はgnome-games-commonとgnome-sudokuパッケージの両方にダブってsduoku.py,timer.pyが含まれていることです。
実際に使われているのはgnome-games-commonの方のsduoku.py,timer.pyですのでパッチを当てるファイルを間違わないように気をつけてください。

suoku.pyに対するパッチの内容はGnomeで実際に適用されたものではなく、私が書いたものです。
gitの使い方を多少学んだのですが、2.30からいくつかコミットが行われていてどれが該当するのかわからなかったのでorz.
(Gnomeで実際に適用されたパッチの方が良かった気がするのですが)
timer.pyの方の修正はGnomeで行われた修正を取り込みました。
こちらは変更が少ないのでgitで変更したコミットが簡単に特定でしました。

gnome-games$ find ./ -name 'timer.py'
../gnome-sudoku/src/lib/timer.py
gnome-games$ git blame -L '/^        if currently_timing:/,+1' -- ./gnome-sudoku/src/lib/timer.py
f235e03e (Jim Ross 2010-03-31 03:53:55 -0400 142)         if currently_timing:
gnome-games$ git log f235e03e^..f235e03e
commit f235e03e98e960a69627f5b4afacb9f14d599bad
Author: Jim Ross <jimbo@dimensia.com>
Date:   Wed Mar 31 03:53:55 2010 -0400

    sudoku: Fixed mark timing
    
    The problem is, when the main window is not active(timer paused) and
    the auto-save kicks in - the timer always gets restarted in mark_timing().
    
    This patch fixes it by storing the current value of self.is_timing at the st
    of mark_timing() and then checking it before calling resume_timing().
    
    Fixes bug #562782
gnome-games$ git show f235e03e
commit f235e03e98e960a69627f5b4afacb9f14d599bad
Author: Jim Ross 
Date:   Wed Mar 31 03:53:55 2010 -0400

    sudoku: Fixed mark timing
    
    The problem is, when the main window is not active(timer paused) and
    the auto-save kicks in - the timer always gets restarted in mark_timing().
    
    This patch fixes it by storing the current value of self.is_timing at the st
    of mark_timing() and then checking it before calling resume_timing().
    
    Fixes bug #562782

diff --git a/gnome-sudoku/src/lib/timer.py b/gnome-sudoku/src/lib/timer.py
index bd5e2de..c37511a 100644
--- a/gnome-sudoku/src/lib/timer.py
+++ b/gnome-sudoku/src/lib/timer.py
@@ -132,13 +132,15 @@ class ActiveTimer (gobject.GObject):
         self.resume_timing()
 
     def mark_timing(self):
+        currently_timing = self.is_timing
         self.pause_timing()
         if self.active_time < 1:
             self.active_time = 1
         # dirty hack: never let total time be less than active time
         if self.active_time > self.total_time:
             self.total_time = self.active_time
-        self.resume_timing()
+        if currently_timing:
+            self.resume_timing()
 
     def finish_timing (self):
         self.mark_timing()

☆LucidのNautilusに再びエンピツのトグルボタンを

Lucid(Ubuntu 10.04)のNautilus(2.30)では"text location bar"と"button bar"を切り替えるエンピツのトグルボタンがなくなってしまいました。
さるお方にアドバイスを頂いて、どなたかPPAで公開してくれる事を願ってPatchをLaunchPadに投稿したのですが、
今のところPPAにアップしてくれた方はいらっしゃらないようです。
ということで自分でパッケージをビルドしたものを公開してみます。
この投稿に提示した操作により損害が発生しても保障はいたしません。適用は自己責任でお願いいたします。

nautilus_2.30.0-0ubuntu4_i386.deb
nautilus_2.30.1-0ubuntu1_i386.deb

本来はパッケージ名を変えたりすべきなのですだと思うのですが、なにぶん良くわからないのでそのままです。
ダウンロードして下記のようにすれば、元々のパッケージと置き換えられます。

# ファイル名はバージョンに合わせて読み替えてください
$ sudo dpkg -i nautilus_2.30.1-0ubuntu1_i386.deb
$ pkill nautilus
※ログイン時にバックグラウンドでnautilusが起動されているのですが、これがいる限り新しいバイナリのnautilusは起動できません。

ソースからビルドしたい方はUbuntu Weekly Recipe 第16回を参照してビルドできる環境を整えてください。
ビルドできる環境が整ったら下記のようにしてビルドします。
正規の手続きではないと思うのですが、一応パッケージは作成されます。
(最後のほうで"running debsign failed"と出るのですが、パッケージは生成されています。)
ディレクトリの名称は適当に置き換えてください。 パスに日本語が含まれていると失敗するかもしれません。ディレクトリ名は英数字だけにしておくのが無難だと思います。

# ディレクトリ名はnautilusである必要はありません
$ mkdir nautilus
$ cd nautilus
# ソースの取得と展開
$ apt-get source nautilus
# ビルドに必要なパッケージの取得
$ sudo apt-get build-dep nautilus
# パッチのダウンロード
$ wget http://launchpadlibrarian.net/44697678/nautilus-2.30.0_add_location_togglebutton.patch
# ディレクトリはバージョンに合わせて読み替えてください。
$ cd nautilus-2.30.1/
# パッチの適用
$ patch -p1 < ../nautilus-2.30.0_add_location_togglebutton.patch
# ビルドの実行
$ debuild -r fakeroot

パッケージのメタ情報を何もいじらなかったせいか、更新がなくてもアップデートマネージャに更新対象として表示されてしまいます。
バージョンがまったく変わっていないときは手動でチェックを外さないと元に戻ってしまうみたいです。

☆Twitterのタイムラインをマージする

Twitterのタイムラインをマージしたいと思って、下記のようなスクリプトを書きました。
毎度初心者レベルなものですいません。
作った環境はUbuntu Karmic Koala。
libdatetime-format-dateparse-perl パッケージを導入していることが前提です。
導入した覚えがない場合は下記のようにして導入してください。
これを使う理由は☆Twitter の投稿時刻をPerlでローカル時刻にするを見てください。

$ sudo apt-get install libdatetime-format-dateparse-perl

tw_merge.sh と tw_merge_rev.pl をダウンロードして、実行権限をつけ、下記のようにして使います。
~/tmp というディレクトリを作るか、tw_merge.sh の HTML_TMP_DIR の指定を適当に変えてください。
スクリプトで作成したファイルは残ったままになるので、ときどき手動で削除してください。

使用例
$ tw_merge.sh _hito_ mizuno_as pores_n ikunya seotch henrich
$ tw_merge.sh 'cc_jp?max_id=11149920499&per_page=20' 'henrich?max_id=11149420427&per_page=24' 'Say_no?max_id=11150167435&per_page=4'
$ tw_merge.sh 'HPLinuxJP?max_id=11248010182&per_page=7' 'henrich?max_id=11192408823&per_page=2' 'henrich?max_id=11247481244&per_page=3'

ブラウザで結果が表示されます。
結果はTwitterのWeb表示とは逆順になります。
通常と同じ順番が場合はtw_merge_rev.plの下から4行目
「sort keys %TWEET」の部分を「reverse sort keys %TWEET」に修正してください。

最初の例の場合、リストと違って発言の多い人と少ない人でズレがでてきてしまうのが難点です。
揃えたい場合はper_pageでTweetの数を調整します。
古い時期の発言を揃えて見たい場合はmax_idを使います。

max_idやper_pageはあらかじめFirefoxで見たいところを選択して選択部分のソースをみて調べておきます。
(これが一番大変なのですが、簡単な方法が思いつきませんでした。)

一応リストもマージ対象に出来ます。 通常 user_name のところに user_name/list_name としてやればOKです。
(ちょっと表示がおかしくなりますが、読めないことはないです。)
リストが対象に入っている場合は、リストを引数の先頭にしてください。

tw_merge.sh
#!/bin/bash
HTML_TMP_DIR=~/tmp
OUT_HTML=${HTML_TMP_DIR}/TwitRev-`date '+%F-%H%M%S'`-$$.html

i=0
for arg in $*
do
  html=${HTML_TMP_DIR}/`echo $arg | sed -e 's#[/?&]#_#g'`.html
#  HTMLS="$HTMLS $html"
  HTMLS[$i]=$html
  wget -O - http://twitter.com/$arg |sed -e 's#</li>#</li>\n#;' > ${html}
  i=$(($i+1))
done

tw_merge_rev.pl ${HTMLS[*]} > $OUT_HTML
x-www-browser $OUT_HTML &
tw_merge_rev.pl
#!/usr/bin/perl
use DateTime;
use DateTime::Format::DateParse;
my $tzhere = DateTime::TimeZone->new( name => 'local' );

my $thumb_span_tmplt = <<'EOF';
<span class="thumb vcard author">
<a href="http://twitter.com/%s" class="tweet-url profile-pic url">
<img alt="%s" class="photo fn" height="48" src="%s" width="48" />
</a>
</span>
EOF


sub repTime
{
  my $gmt = shift;
  $dt = DateTime::Format::DateParse->parse_datetime($gmt);
  $dt->set_time_zone($tzhere);
}

my $is_header_saved = 0;
my $is_footer_saved = 0;
my (@HEADER, @FOOTER);
my (%TWEET);

for $twhtml (@ARGV){
    open TWHTML, $twhtml or die "$!:$twhtml\n";

    my $screen_name;
    my $url_screen_name;
    my $thumb_span;

    # search start of tweets.
    while(<TWHTML>){
      if(not $is_header_saved){
        s#<head>#<head><base href="http://twitter.com/" />#;
        push @HEADER, $_;
      }
      last if m#<ol id='timeline' class='statuses'>#;

      # build screen_name span
      if(!$screen_name and m#<meta content="(.+)" name="page-user-screen_name" />#){
        $screen_name = $1;
        $url_screen_name = qq(<strong><a href="http://twitter.com/$1">$1</a></strong>);
      }

      # buid thumb span
      if(!$thumb_span and m#<a href="/account/profile_image/.*src="([^"]+)"#){
        my $src_url = $1;
        $src_url =~ s#_bigger\.(\w+)$#_normal.\1#;
        $thumb_span = sprintf $thumb_span_tmplt, $screen_name, $screen_name, $src_url;
      }
    }
    $is_header_saved = 1;

    # save tweets
    OUTER:
    while(1){
      my($id, $tweet);
      while(<TWHTML>){
        last OUTER if m#</ol>#;
        $id = $1 if /id="status_(\d+)"/;
        s#(<span class="published timestamp" data="{time:'([^']+)'}">)[^<]+</span>#$1 . repTime($2) . "</span>"#e;
        $tweet .= $_;
        last if m#</li>#;
      }

      # add $url_screen_name and $thumb_span if not contained.
      if( $tweet !~ m#class="tweet-url screen-name"#){
        $tweet =~ s#<span class="status-body">#$thumb_span <span class="status-body" style="padding-left:48px;">#;
        $tweet =~ s#<span class="status-content">#<span class="status-content"> $url_screen_name#;
      }

      $TWEET{$id} = $tweet;
    }

    if(not $is_footer_saved){
        @FOOTER = $_;
        # save remaining
        while(<TWHTML>){
          push @FOOTER, $_;
        }
        $is_footer_saved = 1;
    }

    close TWHTML;
}

print @HEADER;
for (sort keys %TWEET){
    print $TWEET{$_};
}
print @FOOTER;