ProxyCommand芸

例2

ssh_configというと,manpageの長さのわりに出来ることが貧弱で,何か工夫しようとするとシェルの設定とカップルせざるを得ない残念仕様というイメージがありますが,

ProxyCommand
        Specifies the command to use to connect to the server.  The command string
        extends to the end of the line, and is executed with the user's shell.  In
        the command string, ‘%h’ will be substituted by the host name to connect
        and ‘%p’ by the port.  The command can be basically anything, and should
        read from its standard input and write to its standard output.  It should
        eventually connect an sshd(8) server running on some machine, or execute
        sshd -i somewhere.  Host key management will be done using the HostName of
        the host being connected (defaulting to the name typed by the user).  Set‐
        ting the command to “none” disables this option entirely.  Note that
        CheckHostIP is not available for connects with a proxy command.

        This directive is useful in conjunction with nc(1) and its proxy support.
        For example, the following directive would connect via an HTTP proxy at
        192.0.2.0:

           ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p

考えてみると便利な抜け穴がありました.自分の権限で任意のコマンドを実行できるので,事実上何でもできそうです.

ぴんとこないかもしれませんので二つ例を挙げます.

例1:OpenSSHのバージョンを見て振る舞いを変える

OpenSSHはバージョン5.4から[-W host:port]というオプションで内蔵のnetcatのような機能を使えるようになりました.

Host gateway
HostName gateway.fqdn

Host target.gw
HostName target.fqdn
ProxyCommand ssh gateway -W %h:%p

これのおかげで,踏み台を必要とする多段ログインの際に,踏み台(gateway)の環境を気にすることなく,以下のコマンドでtargetにワンステップでログインできるようになりました.

$ ssh target.gw

しかし世にはOpenSSH 4.3を採用しているCentOS 5.xなど古い環境がまだまだ現存しているため,それらの環境をまたがってssh_configを使いまわそうと思うと,以下のように古いOpenSSHに対応するHostを別途書いておくか,-Wをあきらめて捨てるかするしかありません.

Host gateway
HostName gateway.fqdn

Host target.gw
HostName target.fqdn
ProxyCommand ssh gateway -W %h:%p

Host target.gw-nc
HostName target.fqdn
ProxyCommand nohup ssh gateway nc %h %p
$ ssh target.gw    # OpenSSH>=5.4環境
$ ssh target.gw-nc # OpenSSH<5.4環境

OpenSSHのバージョンによってエイリアスを使い分けるのは面倒です.これを勝手にやってもらうようにしてみます.

Host gateway
HostName gateway.fqdn

Host target.gw
Hostname target.fqdn
ProxyCommand $(PROXYHOST=gateway; MAJ=$(ssh -V 2>&1|cut -b9); MIN=$(ssh -V 2>&1|cut -b11); if [ $MAJ -gt 5 -o $MAJ -eq 5 -a $MIN -ge 4 ]; then echo "ssh $PROXYHOST -W %h:%p"; else echo "nohup ssh $PROXYHOST nc %h %p"; fi)

こうしておくと,以下のコマンド一つでOpenSSHのバージョンが新しければ-Wを,古ければgatewayのncを使うように自動的に選ばせることができます.

$ ssh target.gw

例2:どのネットワークにつながっているか見て振る舞いを変える

たとえば,次のようなケースを考えます.インターネット(WAN)とは切り離されたプライベートネットワーク(LAN)に,アクセスしたいホスト(Target)があるとします.プライベートネットワークにインターネットからアクセスするためには,ゲートウェイGateway)を経由する必要があります.自分(たとえばラップトップPC)はインターネット(たとえば自宅)にいるときもプライベートネットワーク内(たとえば社内)にいるときもあります.

こんな時に想定される梅的な解はおそらく以下のような感じです.

Host *.gw
ProxyCommand ssh gateway -W %h:%p

Host gateway
HostName gateway.fqdn

Host target*
HostName target.fqdn

インターネット,プライベートネットワーク内にいるときに,それぞれ

$ ssh target.gw # インターネットにいる場合
$ ssh target    # プライベートネットワーク内にいる場合

などとアクセスするわけです.ただこの方法は微妙です.自分がどこにいるかによって実行するコマンドを変えなければなりません.

竹的な解はおそらく以下のような感じです.ここでは簡単のため,プライベートネットワークにいるかどうかを,target.fqdnからpingが返ってくるかどうかで判断できるとします.たとえば次のような感じでしょうか.

ssh-target() {
  if test ping -c 1 target.fqdn >/dev/null 2>&1; then
    ssh target
  else
    ssh target.gw
  fi
}

先ののssh_configに加えてこのような関数を用意することで,どこにいるか意識せず以下のコマンドだけでアクセスできます.

$ ssh-target

ただこの方法も微妙です.sshの設定がシェルの設定とカップルしているからです.変な関数を作ってしまったので,autosshなどの便利なラッパを使おうとすると,その都度場当たり的な対応をすることになります.

さて,これを松と呼ぶのは気が引けますが,ProxyCommandでネットワークの判断まで行ってあげることで,ssh_configだけで完結するように書くことができます.

Host *.gw
ProxyCommand $(if test ping -c 1 target.fqdn >/dev/null 2>&1; then PROXYHOST=localhost; else PROXYHOST=gateway; fi; echo "ssh $PROXYHOST -W %h:%p")

Host gateway
HostName gateway.fqdn

Host target*
HostName target.fqdn

こうしておくと,どこにいても

$ ssh target.gw

で接続できるようになります.ProxyCommandはsshのコマンドが発行されるタイミングで評価されるので,autosshにもそのまま食わせてあげることができます.

Debian Squeeze に TeX Live 2012 を入れた

手元の PC で org-latex-export をしようと思ったら,TeX の処理系が入っていなかった.
TeX 環境の構築というのは本当にうんざりするつまらない作業で,避けて通れるならできるだけやりたくないものという印象があるのだけれど,pTeXTeX Live に取り込まれて以来かなりよい方向に向かいつつあるようで,最近はさくっとパッケージ管理システムで入れれば済んでしまう世界らしい.とはいえ Debianリポジトリは相変わらず古めなので,世界が結構幸せになった TeX Live 2012 を入れようと思うと,Wheezy あたりから借りてこないといけない.とりうる方法は四つぐらい考えられる.

  • TUG 本家からもらってきて /usr/local あたりに放り込む
  • apt-pinning で wheezy から借りてくる
  • wheezy に chroot して,そこに apt で入れる
  • VirtualBoxVMWare などの仮想環境に wheezy を入れてそこに apt で入れる

このうち第一の選択肢は,TeX Live 2012 についてではないが以前試した.手続きが面倒くさかったが,今のところ特に問題は起こっていない.きっと TeX Live 2012 のケースでも,Autotools のバージョンを適当に合わせて,必要なパッケージを手で入れてあげればインストールできるだろう.ただ次に述べる理由によってこれは選びたくない.
第二の選択肢は試したことがない.一見すると apt-pinning は手続き的にもっとも簡単な方法であるような気がするが,せっかく「安定版」パッケージの依存関係を壊さないように慎重に管理されている枠組みを逸脱することになるため,特に長期間安定して運用することを目指すシステムではこれは推奨されない.cf. 第2章 Debian パッケージ管理
ということで,今回は第三の選択肢を選んでみる.TeX くらいだと牛刀の感が否めないが,この方法は必ずしも他のアプリケーションと切り離すことが難しい大きな言語の処理系を複数持ちたいときなどに役に立つ.*1もちろん仮想環境にまるっと OS ごと入れてやることも可能だが,カーネルを巻き込まない程度の話であれば chroot の方がリソースを食わなくてパソコンにやさしい.

chroot する環境を作る

Debian は debootstrap という Perl で書かれた bootstrap の仕組みを持っていて,コマンド一つで最小の Debian を作ることができる.さらに schroot という root 権限を持たない一般ユーザが chroot できる素敵な wrapper が用意されているので,安全にいくつもの環境を散歩することができる.

% sudo aptitude install debootstrap schroot

chroot 環境は /var/chroot 以下に作ることにする.パッケージは sudo は入れておいた方がいいが他はどうでもいいかもしれない.

hoge % sudo mkdir -p /var/chroot/texlive.sid.amd64
hoge % sudo debootstrap --arch amd64 --include=sudo,zsh,locales,less,git,tig,vim sid /var/chroot/texlive.sid.amd64 http://cdn.debian.or.jp/debian
hoge % ls /var/chroot/texlive.sid.amd64
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  selinux  srv  sys  tmp  usr  var

続いてホスト側からユーザ情報をコピーする.chroot した中で閉じるように環境を別途構築する手もあるが,ユーザ情報を丸ごと引き継ぐ方がシームレスな環境になるし,設定も簡単なのでそのようにする.
ちなみにハードリンクにしておいた方がホスト側での変更が直ちに反映されて便利だが,ゲスト側でこれらをうかつに変更できなくなるので,個人的にはコピーの方がよいと思う.

hoge % cd /var/chroot/texlive.sid.amd64/etc/
hoge % for FILE in {hosts,passwd,shadow,group,gshadow}; do sudo mv -v $FILE{,.org}; sudo cp -v -a /etc/$FILE .; done

apt-line もいじっておく.

hoge % cat apt/sources.list
deb http://cdn.debian.or.jp/debian sid main
hoge % sudo vi apt/sources.list
hoge % cat apt/sources.list
deb http://ftp.jp.debian.org/debian/ sid main contrib non-free
deb-src http://ftp.jp.debian.org/debian/ sid main contrib non-free

そしてこれが肝心,/etc/debian_chroot というファイルを作っておく.これを用意しておくと,bash を特に弄らず使っている場合は chroot 環境内外でプロンプトが変化するのでどこにいるのかわからなくなることを防げる.

hoge % cat debian_chroot
cat: debian_chroot: No such file or directory
hoge % echo "TeXLive.sid" | sudo tee debian_chroot
TeXLive.sid
hoge % cat debian_chroot
TeXLive.sid

続いて chroot 内から見えると便利なものを bind で mount する.ただしこの状態で sudo rm -rf /var/chroot/texlive.sid.amd64 とかやるとひどいことになるので注意.

hoge % sudo vi /etc/fstab
hoge % cat /etc/fstab
...
/proc /var/chroot/texlive.sid.amd64/proc  proc  defaults  0 0
/dev  /var/chroot/texlive.sid.amd64/dev none  rbind 0 0
/sys  /var/chroot/texlive.sid.amd64/sys none  rbind 0 0
/tmp  /var/chroot/texlive.sid.amd64/tmp none  bind  0 0
/home /var/chroot/texlive.sid.amd64/home  none  bind  0 0
hoge % sudo mount -a
hoge % mount
...
/proc on /var/chroot/texlive.sid.amd64/proc type proc (rw)
/dev on /var/chroot/texlive.sid.amd64/dev type none (rw,bind)
/sys on /var/chroot/texlive.sid.amd64/sys type none (rw,bind)
/tmp on /var/chroot/texlive.sid.amd64/tmp type none (rw,bind)
/home on /var/chroot/texlive.sid.amd64/home type none (rw,bind)

これで chroot する環境は整った./etc/inittab を編集して tty8 を割り当ててあげるなり,いきなり chroot するなりしてもよいが,前者で X なしに頑張るのも,後者で環境変数の引き継ぎに頭を悩ますのもしんどいだけであまり有益ではないので,schroot を使う.

hoge % sudo vi /etc/schroot/schroot.conf
hoge % cat /etc/schroot/schroot.conf
...
[texlive.sid.amd64]
description=TeX Live 2012 (Debian sid)
directory=/var/chroot/texlive.sid.amd64
users=foo

これでユーザ foo で chroot する準備ができた.やってみる.

hoge % cat /etc/debian_version
6.0.6
hoge % schroot -c texlive.sid.amd64 -p
hoge(TeXLive.sid) % cat /etc/debian_version
wheezy/sid

これは適当に設定した zsh を使っているが,何も弄っていない bash を使うとプロンプトはこんな風になる.

foo@hoge:~$ schroot -c texlive.sid.amd64 -p
(TeXLive.sid)foo@hoge:~$ uname -a
Linux hoge 2.6.32-5-amd64 #1 SMP Sun Sep 23 10:07:46 UTC 2012 x86_64 GNU/Linux

chroot した環境内で最低限の環境を整えておく.

hoge(TeXLive.sid) % sudo dpkg-reconfigure locales
hoge(TeXLive.sid) % sudo dpkg-reconfigure tzdata

一段落したので,chroot 環境をバックアップしておく./var/chroot/texlive.sid.amd64 よりも下位に bind で /home やら何やらをマウントしているので,何も考えずにバックアップをしようとすると大変なことになる.もちろんアンマウントしてからバックアップすればよいのだが,/var/chroot/texlive.sid.amd64 自体を bind でさらに他のところにマウントしてしまうのが簡単でよい.こうすれば chroot 下で走らせているプロセスに気を配る必要がない.

hoge % sudo mkdir /var/chroot/tmp
hoge % sudo mount --bind /var/chroot/texlive.sid.amd64 /var/chroot/tmp
hoge % sudo rsync -az --stats -h /var/chroot/tmp/ /var/chroot/texlive.sid.amd64.bak

Number of files: 16485
Number of files transferred: 13264
Total file size: 461.41M bytes
Total transferred file size: 461.38M bytes
Literal data: 461.38M bytes
Matched data: 0 bytes                                                                                                                                                                 
File list size: 348.93K
File list generation time: 0.001 seconds
File list transfer time: 0.000 seconds
Total bytes sent: 230.07M
Total bytes received: 263.15K

sent 230.07M bytes  received 263.15K bytes  4.75M bytes/sec
total size is 461.41M  speedup is 2.00

git やら vim やらを入れても 500 MB に収まってとてもコンパクト.

TeX Live 2012 のインストール

せっかく Debianリポジトリにあるので,そこから入れる.jsarticle.cls などの jsclasses は Debian では okumura-clsfiles という名前でパッケージ化されているが,それが sid では texlive-lang-cjk に発展的に吸収された模様.

hoge(TeXLive.sid) % sudo aptitude install texlive texlive-lang-cjk
The following NEW packages will be installed:
  ca-certificates{a} cpp{a} cpp-4.7{a} dbus{a} ed{a} file{a} fontconfig-config{a} fonts-droid{a}
  fonts-ipaexfont-gothic{a} fonts-ipaexfont-mincho{a} fonts-ipafont-gothic{a} fonts-ipafont-mincho{a}
  ghostscript{a} gsfonts{a} ko.tex-extra-hlfont{a} latex-beamer{a} latex-cjk-all{a} latex-cjk-chinese{a}
  latex-cjk-chinese-arphic-bkai00mp{a} latex-cjk-chinese-arphic-bsmi00lp{a}
  latex-cjk-chinese-arphic-gbsn00lp{a} latex-cjk-chinese-arphic-gkai00mp{a} latex-cjk-common{a}
  latex-cjk-japanese{a} latex-cjk-japanese-wadalab{a} latex-cjk-korean{a} latex-cjk-thai{a}
  latex-fonts-thai-tlwg{a} latex-xcolor{a} libavahi-client3{a} libavahi-common-data{a} libavahi-common3{a}
  libcups2{a} libcupsimage2{a} libdatrie1{a} libdbus-1-3{a} libdrm-intel1{a} libdrm-nouveau1a{a}
  libdrm-radeon1{a} libdrm2{a} libencode-locale-perl{a} libffi5{a} libfile-basedir-perl{a}
  libfile-desktopentry-perl{a} libfile-listing-perl{a} libfile-mimeinfo-perl{a} libfont-afm-perl{a}
  libfontconfig1{a} libfontenc1{a} libfreetype6{a} libgl1-mesa-dri{a} libgl1-mesa-glx{a} libglapi-mesa{a}
  libglib2.0-0{a} libglib2.0-data{a} libgmp10{a} libgraphite3{a} libgs9{a} libgs9-common{a}
  libhtml-form-perl{a} libhtml-format-perl{a} libhtml-parser-perl{a} libhtml-tagset-perl{a}
  libhtml-tree-perl{a} libhttp-cookies-perl{a} libhttp-daemon-perl{a} libhttp-date-perl{a}
  libhttp-message-perl{a} libhttp-negotiate-perl{a} libice6{a} libijs-0.35{a} libio-socket-ip-perl{a}
  libio-socket-ssl-perl{a} libjasper1{a} libjbig0 libjbig2dec0{a} libjpeg8{a} libkpathsea6{a} liblcms1{a}
  liblcms2-2{a} liblwp-mediatypes-perl{a} liblwp-protocol-https-perl{a} libmagic1{a} libmailtools-perl{a}
  libmpc2{a} libmpfr4{a} libnet-dbus-perl{a} libnet-http-perl{a} libnet-ssleay-perl{a} libopenjpeg2{a}
  libpaper-utils{a} libpaper1{a} libpciaccess0{a} libpcre3{a} libpng12-0{a} libpoppler19{a} libptexenc1{a}
  libruby1.9.1{a} libsm6{a} libsocket-perl{a} libsystemd-login0{a} libtie-ixhash-perl{a} libtiff4{a}
  libtimedate-perl{a} liburi-perl{a} libutempter0{a} libwww-perl{a} libwww-robotrules-perl{a} libx11-6{a}
  libx11-data{a} libx11-protocol-perl{a} libx11-xcb1{a} libxau6{a} libxaw7{a} libxcb-glx0{a} libxcb-shape0{a}
  libxcb1{a} libxcomposite1{a} libxcursor1{a} libxdamage1{a} libxdmcp6{a} libxext6{a} libxfixes3{a}
  libxfont1{a} libxft2{a} libxi6{a} libxinerama1{a} libxml-parser-perl{a} libxml-twig-perl{a}
  libxml-xpathengine-perl{a} libxml2{a} libxmu6{a} libxmuu1{a} libxpm4{a} libxrandr2{a} libxrender1{a}
  libxt6{a} libxtst6{a} libxv1{a} libxxf86dga1{a} libxxf86vm1{a} libyaml-0-2{a} lmodern{a} luatex{a}
  mime-support{a} openssl{a} pgf{a} poppler-data{a} preview-latex-style{a} prosper{a} ps2eps{a} python{a}
  python-minimal{a} python2.7{a} python2.7-minimal{a} ruby{a} ruby1.9.1{a} sgml-base{a} shared-mime-info{a}
  swath{a} tcl8.4{a} tex-common{a} tex-gyre{a} texlive texlive-base{a} texlive-binaries{a} texlive-common{a}
  texlive-doc-base{a} texlive-doc-zh{a} texlive-extra-utils{a} texlive-font-utils{a}
  texlive-fonts-recommended{a} texlive-fonts-recommended-doc{a} texlive-generic-recommended{a} texlive-lang-cjk
  texlive-latex-base{a} texlive-latex-base-doc{a} texlive-latex-extra{a} texlive-latex-extra-doc{a}
  texlive-latex-recommended{a} texlive-latex-recommended-doc{a} texlive-luatex{a} texlive-pictures{a}
  texlive-pictures-doc{a} texlive-pstricks{a} texlive-pstricks-doc{a} thailatex{a} tipa{a} tk8.4{a}
  ttf-dejavu-core{a} ttf-marvosym{a} ucf{a} x11-common{a} x11-utils{a} x11-xserver-utils{a} xbitmaps{a}
  xdg-utils{a} xfonts-encodings{a} xfonts-utils{a} xml-core{a} xterm{a}
0 packages upgraded, 201 newly installed, 0 to remove and 0 not upgraded.
Need to get 808 MB of archives. After unpacking 1,565 MB will be used.
Do you want to continue? [Y/n/?]

現代の TeX の図体のでかさを痛感する瞬間.まあともかく,これで chroot 環境下に TeX 処理系が入った.

hoge(TeXLive.sid) % platex -kanji=utf-8 bar.tex
hoge(TeXLive.sid) % platex -kanji=utf-8 bar.tex
hoge(TeXLive.sid) % dvipdfmx bar.dvi

hoge % schroot -c texlive.sid.amd64 -p -- sh -c "platex -interaction=nonstopmode bar.tex && platex -interaction=nonstopmode bar.tex && dvipdfmx bar.dvi"

などとすることができる.

chroot してタイプセットするスクリプト

しかしタイプセットのたびに毎回 chroot するのは面倒なので,その辺を一気にやってしまうスクリプトをこしらえる.
YaTeX などから呼び出すことを想定するとエラー処理をちゃんとやらないと使い物にならないが,org-latex-export から使う場合インタラクティブにエラーに対処する必要はないので捨ててしまっている.
きっともっと筋のいいやり方があるので,誰か教えてください.

#!/bin/sh

SCHROOT='schroot -c texlive.sid.amd64 -p'
PLATEX='platex -kanji=utf-8 -interaction=nonstopmode'
DVIPDFMX='dvipdfmx'

NOPT=`expr $# - 1`
for i in `seq 1 $NOPT`; do
    OPTS="$OPTS $1"
    shift
done
FILE=$1

if [ ! -e "$FILE" ]; then
    echo "$FILE does not exist" >&2
    exit 1
fi

$SCHROOT -- sh -c "$PLATEX $OPTS $FILE; $PLATEX $OPTS $FILE; $DVIPDFMX ${FILE%tex}dvi"

こんなスクリプトを /usr/local/bin/schrootex などという名前で保存しておいて,

;; TeX process
(setq org-latex-to-pdf-process
      '("/usr/local/bin/schrootex %s"))

などと設定してあげると,Org-mode から C-c C-e p で pdf 出力できるようになる.

*1:たとえば Python で書かれた Web フレームワークである Django の 1.2, 1.4 系を Apache テスト環境もろとも共存させようと思ったりしたときに chroot は無難な選択肢になるだろう.

Mobile-OTP を使うと umask がおかしくなる問題を修正する

Mobile-OTP については,スマートフォン/ガラケー/PDA等を使ってワンタイムパスワードを実現するMobile-OTPのススメ - Dマイナー志向に詳しい.一言で言うと,携帯電話のような皆が当たり前のように持ち歩いているデバイスを,時限性のワンタイムパスワード生成器にして,秘密鍵を持たない出先のインセキュアな端末からもスマートに SSH を使いましょう,というお話.

しかし,これを使うと umask がおかしくなる.

これは誤解で,本当は 0077 になっていた.

以下,パッチ.

% diff -u pam_mobile_otp/pam_mobile_otp.c.org pam_mobile_otp/pam_mobile_otp.c
--- pam_mobile_otp/pam_mobile_otp.c.org 2011-02-07 00:33:40.000000000 +0900
+++ pam_mobile_otp/pam_mobile_otp.c     2012-03-11 16:50:10.000000000 +0900
@@ -324,7 +324,7 @@
       }
     }

-    umask(077);
+    mode_t umask_orig = umask(077);
     if ( (fp=fopen(statfile,"w")) == NULL ) {
       _PAM_LOG(LOG_ERR, "cannot write statfile %s",statfile);
       return PAM_AUTHINFO_UNAVAIL;
@@ -336,6 +336,7 @@
     TRACE(stderr,"written new stats - tries: %d, last time: %lu\n",tries,last_time);
     if ( fclose(fp) ) if (!no_warn)
       _PAM_LOG(LOG_INFO, "warning: cannot close file (w) %s",statfile);
+    umask(umask_orig);
   }


@@ -595,7 +596,7 @@
       tries = 0;

       /* write user's status file */
-      umask(077);
+      mode_t umask_orig = umask(077);
       if ( (fp=fopen(statfile,"w")) == NULL ) {
        _PAM_LOG(LOG_ERR, "cannot write statfile %s",statfile);
        return PAM_SERVICE_ERR;
@@ -607,6 +608,7 @@
       TRACE(stderr,"Has reset tries and written new stats - tries: %d, last time: %lu\n",tries,last_time);
       if ( fclose(fp) ) if (!no_warn)
        _PAM_LOG(LOG_INFO, "warning: cannot close file (w) %s",statfile);
+      umask(umask_orig);
     }
   }

こういう変なバグはあるけれど,すばらしく便利なツールなので,泣く泣く秘密鍵を USB メモリに入れて持ち歩いている人は是非一度おためしあれ.

九十九里浜・犬吠埼散歩

計画自体は一昨年からあったものの,諸事情あって去年は実現できなかった,九十九里浜を踏破したあげく犬吠埼で初日の出を見る散歩コースを,J子,M. Kagami と三人で歩いてきた.元旦は生憎の曇りで,残念ながら初日の出を見ることはかなわなかったが,想定していたコースをほぼ歩ききることが出来たので,散歩としては成功であった.

より大きな地図で 初日の出を目指して九十九里 を表示
コースは基本的にはこの地図のような感じ.実際は危険な車道を避けるために何度も地図に書かれていない海岸沿いの道へ外れた.距離はざっくり 90 km というところだろう.
当初は Kagami 宅で一泊し,2011-12-31 の始発で太東岬から歩き出す予定だったが,初日の出に間に合わないことを心配して,七時間ほど前の 2011-12-30 の終電間際に出発した.実際に犬吠埼に着いたのは日の出の四時間前だったので,正しい判断だった模様.
50 km を超える散歩は,70 km 歩いたところで力尽きた一回目の秩父,歩き残した 60 km を歩いた二回目の秩父に続いて,今回で三回目.基本的に山である秩父に比べ,浜辺の近くを歩く今回のルートは距離は長くとも難易度は低め…のはずだったが,九十九里浜から犬吠埼へ向かうルートのアップダウンが予想外に激しかった.銚子ドーバーラインを歩く蛮勇があればまた違ったのだろうが,秩父とは交通量が桁違いだったので,安全な,しかし急峻なルートを歩かざるを得なかった.
前回までの散歩で,食料調達と睡眠確保が最重要課題であることがわかっていた.今回はコンビニや深夜営業しているラーメン店の存在するような地域であったため前者に問題は無かった.後者については,9:00--11:00 に浜辺の芝生で暖かい陽の光を浴びながら仮眠を取ったところまではよかったが,最後犬吠埼で日の出を待つ間,時間が余りすぎてどうしようも無かったため,野外で座り込みながら寝たのは若干強引だった.今回散歩初参加の Kagami に,防寒の程度について十分に伝えられていなかった点は反省しなければならない.
ところで,前回手ぶらで現れ,これ以上ふざけた真似は出来まいと思われたJ子は,今回スーツでの参加で,何というか,いつもの通りであった.

rxvt-unicode の clipboard 連携

Thinkpad like な環境ではあまり気にならなかった center button のコストが,平凡なマウスを使っていると想像以上に大きいことがわかったので,キーボードでコピペできるようにしてみた. Shift + Insert はさすがに論外.
いわゆるクリップボードと, urxvt がコピペに使っているバッファらしきものが別物であることは以前から認識していたが,今回調べてみて明確に別物であることがわかった. command.C の 568 行あたりにハードコーディングされているので,そこを書き換えてもいいのだけれど,もうちょっとデスクトップ環境に歩み寄ってクリップボードを使うことにしてみる.
/foo/bar/urxvt-xsel という名で以下のスクリプトを保存する.どういう訳か $term->selection が non-false の時に system ("xsel | xsel -bi") など外部コマンドを実行しようとすると urxvt ごと凍ってしまう謎仕様を回避するため,ちょっと苦しいことをしている.
raw

#!/usr/bin/env perl
use strict;
use warnings;

sub copy
{
	my ($self) = @_;

	my $elite = $self->locale_encode($self->selection);
	if ($elite) {
		my $EOF = time();
		$EOF .= '0' while ($elite =~ /$EOF/);
		system ("cat<<$EOF | xsel -bi\n$elite\n$EOF\n");
	} else {
		system ("xsel | xsel -bi");
	}

	()
}

sub paste
{
	my ($self) = @_;

	open (XSEL, '-|', qw/xsel -b/) or die $!;
	while (<XSEL>) {
		$self->tt_write ($self->locale_encode ($_));
	}
	close XSEL;

	()
}

sub paste_selected
{
	my ($self) = @_;

	my $elite = $self->locale_encode($self->selection);
	$self->tt_write ($self->locale_encode ($elite)) if ($elite);

	()
}

sub on_user_command
{
	my ($self, $cmd) = @_;

	if ($cmd eq "urxvt-xsel:copy") {
		$self->copy;
	} elsif ($cmd eq "urxvt-xsel:paste") {
		$self->paste;
	} elsif ($cmd eq "urxvt-xsel:paste_selected") {
		$self->paste_selected;
	}

	()
}

実行には xsel が要る. Debian なら

% sudo aptitude install xsel

とかで入る.
続いて ~/.Xdefaults に以下を追記.

URxvt.perl-lib:        /foo/bar/
URxvt.perl-ext-common: urxvt-xsel
URxvt.keysym.C-C:      perl:urxvt-xsel:copy
URxvt.keysym.C-V:      perl:urxvt-xsel:paste
URxvt.keysym.C-M-V:    perl:urxvt-xsel:paste_selected

これによって実現される機能は,

Ctrl + Shift + C
urxvt 上現在選択されている or 過去に選択されていたテキストをクリップボードへコピー
Ctrl + Shift + V
クリップボードから urxvt 上へペースト
Ctrl + Shift + Alt + V
urxvt 上現在選択されている or 過去に選択されていたテキストを urxvt 上へペースト

Irssi から editor を呼び出すだけのプラグイン

Instant Messenger の buffer は貧弱だと相場が決まってはいるが,それにしても irssi は融通が利かないので, editor を呼び出すだけのプラグインを書いた. LimeChat の Ctrl+D を見習って欲しい.

/ed

と叩くと editor が起動し,入力した文章が msg で送られる.

/ed n

とすれば, notice で送られる.これがしたかった.

くぼたんちき

二年前に PHS の位置情報を Google Maps にプロットするものを作ってみたことがあったのだが,ついに電池を浪費することなく自動更新する方法を思いつくことができなかったので,三日ぐらい遊んだのち放ってあった.
いい方法かどうかはわからないが,携帯端末等に頼らない手段を思いついたので,引っ張り出してきてちょっと修正.今の生活スタイルが続く限りは,そこそこ当てになりそうな気がする.
くぼたんちき

くぼたは2時間前には本郷にいたようです.