2011/12/14

cvs や svn のリビジョン番号

プログラムを作成する際、 特にまだ未完成で頻繁に修正とコンパイルを繰り返している間は、 どのリビジョンのプログラムを実行しているのか知りたい事はよく有る。
そんな時に、例えば実行時オプションとして -v を指定すると cvs や svn のリビジョン番号とコミットした日時が表示されると便利だ。
しかし、多くの場合において -v オプションを解析し実行するだろう main 関数が含まれるファイルが最終修正されているとは限らない。 従って main 関数の含まれるファイル中に

static	char	*id = "$Id$";
	
などと定義しておいて commit 時に $Id$ を展開したとしても、 必ずしも最新のリビジョンにはならない場合が多い。

そこで -v オプションのハンドラ部分では

static	char	*id = "revision: " REVISION ", " COMMITED ;
	
と記述としておいて make 時に自動で REVISIONCOMMITED を全てのソース/ヘッダファイル中の cvs や svn の $Id$ キーワードから取得して設定する方法を検討してみる。

build 時に必ず実行する必要がある処理なので Makefile に記述して、 make 中で自動的に実行してしまうのが楽。

    :
# 変数定義
PROGRAM  = myprog
OBJECTS  = sub.o pthread.o common.o util.o
HEADER   = myprog.h const.h config.h
 
# 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンを取得する
REVISION = $(shell awk '/\$$Id/{ if($$4 ~ /[[:digit:]]/) \
            if(max < $$4) max = $$4 } END{ print max }' \
            $(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c)
# 全ソース/ヘッダ中の `$Id' 行から最大のリビジョンのコミット日時を取得する
COMMITED = $(shell awk '/\$$Id.*'$(REVISION)'/{ \
            print $$5 " " $$6; exit }' \
            $(OBJECT:.o=.c) $(HEADER) $(PROGRAM).c)
# コンパイルオプション(REVISIONとCOMMITEDを定義)
CFLAGS   = -g -Wall -Wextra -Werror -Wno-unused-parameter -I.-O2 \
            -DREVISION=\""$(REVISION)"\" -DCOMMITED=\""$(COMMITED)"\"
    :
    :
# ターゲット(デフォルトターゲットは touch と $(PROGRAM)に依存
all: touch $(PROGRAM)

# $(PROGRAM).o ファイルの削除($(PROGRAM).o ファイルは必ずコンパイルする)
touch:
    rm $(@).o

$(PROGRAM): $(OBJECTS)
    $(CC) $(CFLAGS) -o $(@) $^ 

$(OBJECTS): $(HEADER)
    :
	
これでどのファイルを commit した場合でも 必ず最新のリビジョンとコミット日付が -v で表示される。

当初ターゲット touchtouch $(PROGRAM).c としていたが、 とある方よりの指摘で rm $(PROGRAM).o に変更した。
ご指摘感謝です > N.S. さま

2011/11/26

shell での while ループにおける問題

シェルスクリプトでコマンドの出力をループ処理する場合は パイプ | を使用するのが一般的な手法だと思うが、 パイプ | を使うとループが別なプロセスとして実行されるので ループ内部で設定したシェル変数がループ外部で参照できないという問題があり、 (存在するとすれば) shell script 業界では割と FAQ 的な問題だ。
何が問題かと言って posix で定義されていないから シェルの実装に依存しているのが一番の問題だったりするのだろう。
この問題に対しては、シェルビルトインの exec (1) を利用して ディスクリプタを複製して while ループの入力を標準入力にしてしまうのが 一番汎用的で柔軟性のある対応だと思うのだが、 シェルを愛する面々は 皆同じ様な苦労をしているのだ と改めて認識した。

当初は一時ファイルを作成する方法を考えついたのだが、 自分的にはなるべく一時ファイルを使いたくなかったので ファイルから入力する部分をバッククォートによるコマンド実行に置き換え等、 色々と試行錯誤した結果ヒアドキュメントを使う方法に落ち着いた。

# これでは正常に動作しなかった
exec 3<&0  0<`${command}`

while read line
    :
	

勿論一時ファイルを作成しても良いのだが、 ヒアドキュメントを利用する事で一時ファイルが不要になる分だけ 処理的に美しいかなと思う (完全に好みの問題)。
  1#!/bin/sh
  2# コマンドの出力をバッククォートしてヒアドキュメントとして使用する
  3
  4command="/path/to/command"
  5count=0
  6
  7# exec 3<&0: 標準入力 (FD0) を FD3 に複製
  8# exec 0<<EOF: ヒアドキュメントを標準入力として使用
  9exec 3<&0  0<<EOF
 10`${command}`
 11EOF
 12
 13while read line
 14do
 15    :
 16    count=`expr ${count} + 1`
 17done
 18
 19# exec 0<&3: FD3 に複製した標準入力 (FD0) を復帰
 20# exec 3<&-: FD3 をクローズ</i>
 21exec 0<&3 3<-
 22
 23echo ${count}
    

もう一点は子プロセスで親プロセスの変数値を変更する方法。
こちらはプロセスが別になってしまうとファイルを使うしかないので、 同じプロセス内で関数呼び出しとして対応する方法が汎用的ではないだろうか。

  1#!/bin/sh
  2# eval を利用して呼び元の変数を設定する
  3
  4default="デフォルト値"
  5
  6sub()
  7{
  8
  9    local   _val
 10
 11    :
 12    _val="設定したい値"
 13
 14    eval "${1}='${_val:-${default}}'"
 15
 16}
 17
 18value="元の値"
 19
 20sub value
 21
 22echo ${value}
    

ちなみに上のリンク元にある parent.sh の中の処理で ${tmp_file} の内容を for ループで eval しているが、 これはシェルビルトインの . もしくは source を利用して . ${tmp_file} としても同一の結果が得られるので楽だと思う。

  1#!/bin/sh
  2
  3g1="aa"
  4g2="bb"
  5
  6tmp_file=`mktemp /tmp/ps.XXXXXX`
  7export tmp_file
  8
  9./child.sh    # child.sh で、上で設定した変数 g1, g2 の値を変更する
 10
 11# for v in `cat $tmp_file`; do
 12#   eval $v
 13#   done
 14# ${tmp_file} の内容は <tt>name='value'</tt> 形式が保証されているので
 15# ファイル自体を `.' コマンドで読み込むだけで
 16# 行毎に eval を実行しなくてもシェル変数に代入された値が利用できる
 17
 18if [ -f ${tmp_file} ]
 19then
 20    . ${fmp_file}
 21    rm -f ${tmp_file}
 22fi
 23
 24echo $g1 #=> cc に変更された
 25echo $g2 #=> dd に変更された
    
ブログに直接コメントしたかったのだが、 はてなのアカウントが無いので申し訳ないがここで意見させて頂く。

2011/11/24

こどもの国

朝から好天に恵まれてぽかぽかな冬の休日を無駄にするのは勿体ないので、 かねてから気になっていた「こどもの国」に家族で出かけてみた。
行く前は「こどもの国」という名称から 『どうせ地方にありがちな子供向けのしょぼい遊園地の類だろう』等と 高をくくっていたのだが、実際に行ってみると大違い。 東京都と神奈川県にまたがる広大な敷地が、 自然の地形や環境上手に活かさて大人でも十分楽しめる遊園となっていて、 良い意味で期待が裏切られた。
あまりに広すぎるので一日では回りきれず 全体の半分程度を結構な駆け足で巡っただけなのだが、 それでも地面に自由に落書きできる広場や 本格的な牧場やポニーへの乗馬体験、 伊豆のサイクルスポーツセンターを彷彿とさせる様な 様々な変わり種自転車を集めた施設など盛りだくさんの内容だった。

Image: img_1475.jpg

母子で乗馬初体験

しかも牧場では定番のソフトクリームや 首都圏で随一生産されるらしい牛乳も楽しめる。

Image: img_1494.jpg

カーナンバー #27 の J.Alesiを彷彿とさせるドライビング

休日は道路も駐車場も混雑するだろうと電車で出かけたのだが、 2 度の乗り換えがあるが 1 時間かからない程度なので これからも是非出かけてみようと思う。

2011/11/16

メモリリークチェッカー

c で daemon 動作するサーバを作っていると malloc (3) などで動的に確保したメモリの free (3) 忘れによる いわゆるメモリリークを防ぐ事が非常に重要になってくる。
この様なメモリリークを防止するために役立つ 様々なツールが世の中には沢山存在しており それぞれ非常に有用ではあるのだが、 いざ利用しようと思うと割と面倒な作業が発生したり、 自分の求めている機能に対して明らかにオーバースペックだったりするので、 自分の用途に合わせて簡単に使える『俺様ツール』を作ってみた。

動作原理は簡単で malloc (3) や free (3) したアドレスを ログに出力して、 後からログを解析してメモリの確保と解放が対になっていない部分を 抽出するだけである。
そのためにまずは元となるソースコードを修正して、 malloc (3) や free (3) を独自に作成した wrapper 関数に置換えて 確保、解放したアドレスをログに出力する機能を組込む必要があるのだが、 malloc (3) などはエラー処理を含めて共通化するために あらかじめ wrapper 関数化されている場合が多いと思うので、 この部分はフォーマットを決めるだけで割りと簡単に追加できると思う。
今回は syslog (3) を利用して malloc (3)、strdup (3)、 free (3) で確保/解放するアドレスを出力した。

  1/*
  2 * malloc() の wrapper
  3 */
  4void    *mymalloc(size_t size)
  5{
  6
  7    void    *p;
  8
  9    if(!(p = malloc(size)))
 10        /* エラー処理 */;
 11    syslog(LOG_DEBUG, "malloc: %p\n", p);
 12
 13    return(p);
 14
 15}
 16
 17/*
 18 * strdup() の wrapper
 19 */
 20char    *mystrdup(const char *s)
 21{
 22
 23    char    *p = NULL;
 24
 25    if(s){
 26        if((p = strdup(s)))
 27            syslog(LOG_DEBUG, "strdup: %p\n", p);
 28        else
 29            /* エラー処理 */;
 30    }
 31
 32    return(p);
 33
 34}
 35
 36/*
 37 * free() の wrapper
 38 */
 39void    myfree(void *p)
 40{
 41
 42    if(p){
 43        free(p);
 44        syslog(LOG_DEBUG, "free: %p\n", p);
 45    }
 46
 47}
    

次に出力されたログの解析処理であるが、 確保したメモリのアドレスをキーとしたハッシュテーブルを利用したいので 入力行の解析やハッシュテーブルが簡単に利用できる言語として 今回はコマンドライン版の php (1) を利用してみた。
もちろん awk (1) を駆使したり perl (1) を利用しても問題ない。

  1<?php
  2    /*
  3     * Copyright (c) 2011 Mitzyuki IMAIZUMI, All rights reserved.
  4     *
  5     * $Id: memcheck.php 1677 2011-10-12 06:58:03Z mitz $
  6     */
  7
  8    /* 冗長指定 */
  9    if($argv[1] == "-v"){
 10        $verbose = 1;
 11        array_shift($argv);
 12    }
 13
 14    if($fp = fopen($argv[1], "r")){
 15        $line = 0;
 16        while($buf = fgets($fp, 1024)){
 17            $buf = ltrim($buf);
 18            $line++;
 19            /*
 20             * malloc か strdup か free を含む行の場合
 21             * コマンドを配列 $p[0] に、アドレスを配列 $p[1] に格納する
 22             */
 23            if(preg_match("/^(malloc|strdup|free):\s(.*)/", $buf, $p))
 24                /* 解放処理 */
 25                if($p[1] == "free")
 26                    /* 確保済みテーブルに解放アドレスが存在する場合 */
 27                    if($alloc[$p[2]]){
 28                        if($verbose)
 29                            printf("%s: % 8d -> % 8d\n", $p[2], $alloc[$p[2]], $line);
 30                        $alloc[$p[2]] = 0;
 31                    }
 32
 33                    /* 確保済みテーブルに解放アドレスが存在しない場合 */
 34                    else
 35                        printf("%s: unknown free at % 8d\n", $p[2], $line);
 36                else
 37                /* アドレスをキーとしたハッシュテーブルに行番号を格納 */
 38                    $alloc[$p[2]] = $line;
 39
 40        }
 41        fclose($fp);
 42
 43        foreach($alloc as $k => $v)
 44            if($v)
 45                printf("%s: NOT free (% 8d)\n", $k, $v);
 46    }
 47?>
    
このスクリプトを実行する事により malloc(3) もしくは strdup(3) で確保したメモリが解放されていない場合、 もしくは確保していないメモリを解放した場合が簡単に発見可能である。

これらは自分が必要とする機能のみを簡単に実装したものであり、 例えば解放していない事を意図しているメモリまで警告されてしまう等、 汎用的に使える事を目指したツールでは勿論ない。 ただ、日頃発生しうる面倒な作業がちょっとの工夫で 多少なりとも楽になるだろう例の一つとして公開してみた次第である。

2011/11/28 追記

awk (1) 版の方が汎用的なので簡単に作成してみた。 処理内容は上記 php (1) 版と同じである。
  1#!/bin/sh
  2
  3if [ "${1}" = "-v" ]
  4then
  5    verbose=true
  6    shift
  7fi
  8
  9awk '
 10    /^(malloc|strdup)/{
 11        alloc[$4] = NR;
 12    }
 13    /^free/{
 14        if(alloc[$4]){
 15            if("'${verbose:-false}'" == "true")
 16                printf("% 5d %s: % 5d\n", NR, $4, $alloc[$4]);
 17            alloc[$4] = 0;
 18        }
 19        else
 20            printf("% 5d %s: unknown free.\n", NR, $4);
 21    }
 22    END{
 23        for(i in alloc)
 24            if(alloc[i])
 25                printf("% 5d %s: NOT free.\n", alloc[i], i);
 26    }
 27' ${1} | sort ${2}
    

2011/10/30

Dear my Princess


May your Birthday, bring loads of joyful and sweet memories in your life. May you have a great and successful year ahead. Happy Birthday.

2011/10/18

電源関係の小物たち

いつも鞄に入れて持ち歩いている電源達関係の小物

Image: RIMG0456.JPG

電源達

  1. Apple 純正の AC アダプタ
  2. 普段から MacBookAir を持ち歩いているので、 Apple 純正の AC アダプタは必須 (IBM のバンドでまとめてあるのは愛嬌です)。

  3. USB 電源アダプタ
  4. 携帯電話や BlueTooth 機器など USB から充電できる機器が増えているので、 100V コンセントから USB の 5V を出力するアダプタも何かと便利。

  5. HyperMac
  6. 特許の関係で入手が不可になった HyperMac とそのケーブル。 これは MacBookAir からは通常の電源に見えるので、 純正のエアラインアダプタとは異なり充電もできる優れもの。 このおかげでお世辞にも長いとは言えない MacBookAir の駆動時間が 飛躍的に向上出来るのでモバイルには必須とも言えるアイテム。
    Apple も特許が大切なのは判るんだけど、 こういう素晴らしい商品はライセンスするとか対応を考えて欲しいと思う。

  7. Griffin の USB Mini-Cableset。
  8. Image: RIMG0365.JPG

    Griffin の USB Mini-Cableset
    一般的な USB の A 端子コネクタを Mini-USB、Micro-USB、 そして iPod 用の端子に変換するためのケーブルセットなのだが、 短めのケーブルなので収納に困らず邪魔にもならない。 普段から一緒に持ち歩いている iPod や Jawbone の BlueTooth ヘッドセットなど、 殆どの携帯機器の接続や充電が可能になるので非常に便利。

2011/09/17

Mac OS X Lion on VMware Fusion 4

すっかり世間の話題からは乗り遅れた感があるが、 そろそろ Mac OS X Lion を使ってみたくなってきた。
しかし、現在使用している Snow Leopard から 操作性が大きく変更になったと噂で聞いているので、 自宅でメイン環境として利用している MacBook Pro や 職場で生活環境、かつ開発環境として利用している iMac 27″ に Lion を導入する事は躊躇していた。
しかし、Mac OS X Lion をゲスト OS として正式にサポートした VMware Fusion 4 が発売されたので、 まずは VMware Fusion 4 の仮想環境に Mac OS X Lion をインストールして 操作性などを検証してみる。

少なくとも現状では旧バージョンの VMware Fusion からの 優待アップグレードなどは存在しない様なので新規購入する必要がある様だ。

I have a previous version of Fusion (1.x, 2.x, or 3.x.). Am I eligible to purchase an upgrade to Fusion 4?

There is no upgrade option for VMware Fusion 4. You may purchase a full license from our VMware online store. Licenses cost $49.99 USD (for a limited time only).

VMware Knowledge Baseより

期間限定ではあるが新規購入でも 49.99 USD と格安なので アップグレード並の低価格ではあるが…。

VMware Fusion は日本では以前より act2 が正規代理店として発売しているが、 MacUpdate のクーポン を利用して直接 VMware 社のサイトから USD の決済でダウンロード購入すると 10 USD ディスカウントとなり 39.99 USD で購入が可能となるので、 迷わず VMware 社サイトのオンラインストアにてダウンロード版を購入する。
ちなみに VMware Fusion は完全に国際化されているので、 日本で購入しなくてもインストーラからちゃんと日本語で表示される。

円高なので約 3,000 円程度で最新の仮想環境が入手できるのは素晴らしい。
Mac OS X Lion は AppStore からこちらもダウンロード販売で購入する。
こちらは円高の恩恵は受けられなかったのだが、 元々安価で提供されているのでそれほど気にならない。

VMware Fusion 4 を起動して新規仮想マシンアシスタントを起動したら、 インストールメディアの選択画面でダウンロード購入した Mac OS X Lion のインストーラを指定すれば簡単にインストールでき、 Mac OS X Lion の動作する検証用の仮想環境が構築できる。

肝心の Mac OS X Lion の操作性だが、 今は慣れていないのでまごつく事も多々あるのだが、 慣れれば良いのかな?というのが現状の感想。
もう暫く仮想環境で Lion の使い方に慣れたら 実マシンを Lion に移行してみようかとは思っている。

2011/09/09

Bowers & Wilkins C5

普段使いのヘッドフォンのケーブルが断線してしまったので、 発表されたばかりの Bowers & Wilkins 初のカナル型ヘッドフォン C5 を 試聴もしないまま購入する事にした。 購入時は日本未発売で円高の後押しもあったのでアメリカから個人輸入したのだが、 国内で購入するよりは随分と安く済んだ。
故障時の保証や言葉の問題など、 海外から直接個人輸入する事にはデメリットもあるので万人に勧められはしないが、 金額以外のメリットを享受できる場合もあるので 悪くない選択肢の一つだとはおもう。

Image: RIMG0352.JPG

そこそこ高級感のあるパッケージ

新品の状態で聴いてみた印象は音場は結構広がりがある感じだが、 新品故の固さというかドンシャリ感が強い感じ。
とは言っても聴き疲れするタイプの音質ではないし、 今の段階でも上品な感じの低音がしっかり鳴っているので、 今後のエージングに期待してみる。

Image: RIMG0358.JPG

マイクロ多孔質フィルタとセキュアループ

外観の特徴にもなっている筐体外側のマイクロ多孔質フィルターの効果なのか 装着した時の耳への圧迫感が随分軽減されていて、 カナル型なのにも関わらず長時間装着していても苦にならない快適さだと思う。
もう一つ特徴的な外観を形成しているセキュア・ループ・デザインは、 耳たぶの内側にあてる事で装着時のフィット感を高める造りとなっている。 簡単にサイズも調節可能だし見た目も特徴的なので悪くない。
ケーブルの途中に組み込まれたリモコンは 手持ちの iPod や MacBook Pro の iTunes を操作できるので意外と便利だ。
リモコンはボタンが 3 個なので 再生・停止とボリューム操作だけだと思っていたのだが、 ちゃんとマニュアルを読むと 2 回クリックで次の曲、 3 回クリックで前の曲へのスキップも可能だった。

ピンクノイズなどで暫くエージングした後も第一印象とそうは変わらない感じだが、 固めにドンシャリと響いていたカドが丸まったと思う。 上品に響く低音と抜けの良い高音、それから広い音場とあいまって とても優しく聴き疲れしない良質な音になっていると思う。

2011/08/12

Mac で Planex URS-03 (USB-Serial 変換アダプタ) を利用する

Planex URS-03 もメイカーは Mac 用のドライバを提供していないが、 利用しているチップセットのメイカー (Prolific Technology Inc. 社) の Web サイト から Mac 用のドライバをダウンロードしてインストールした上で、 ドライバの Info.plist ファイルに URS-03 の Vendor ID と Product ID を登録する事で正しく利用できる様になる。
URS-03 の場合は /dev/tty.usbserial という デバイスとして認識されるので、 このデバイスを利用して通信する事ができる様になる。

URS-03 の Vendor ID と Product ID はアダプタを接続した状態で、 以下のコマンドを実行して PL2303 を検索する事で取得可能である。

# /usr/sbin/ioreg -l | less
	
X code がインストールされている場合は /Developer/Applications/Utilities/USB Prober.app を実行して Conposite device from ATEN International Co. Ltd. を検索すれば、 Device Descriptor セクションから取得可能である。

URS-03 の Vendor ID は 1367(10)、 Product ID は 8200(10) なので、 Info.plist の内容を以下の様に修正する。

  1<?xml version="1.0" encoding="UTF-8"?>
  2<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
  3<plist version="1.0">
  4    <dict>
  5        <key>CFBundleDevelopmentRegion</key>
  6        <string>English</string>
  7        <key>CFBundleExecutable</key>
  8        <string>ProlificUsbSerial</string>
  9        <key>CFBundleGetInfoString</key>
 10        <string>ProlificUsbSerial v2.2.0, Copyright 2006 Prolific Technology Inc.</string>
 11        <key>CFBundleIdentifier</key>
 12        <string>com.prolific.driver.PL2303</string>
 13        <key>CFBundleInfoDictionaryVersion</key>
 14        <string>6.0</string>
 15        <key>CFBundleName</key>
 16        <string>Mac OS X Driver for Prolific USB-to-Serial Bridge Device</string>
 17        <key>CFBundlePackageType</key>
 18        <string>KEXT</string>
 19        <key>CFBundleShortVersionString</key>
 20        <string>2.2.0</string>
 21        <key>CFBundleSignature</key>
 22        <string>????</string>
 23        <key>CFBundleVersion</key>
 24        <string>2.0.0</string>
 25        <key>IOKitPersonalities</key>
 26        <dict>
 27                <key>067B_2303</key>
 28                <dict>
 29                        <key>CFBundleIdentifier</key>
 30                        <string>com.prolific.driver.PL2303</string>
 31                        <key>IOClass</key>
 32                        <string>com_prolific_driver_PL2303</string>
 33                        <key>IOProviderClass</key>
 34                        <string>IOUSBInterface</string>
 35                        <key>bConfigurationValue</key>
 36                        <integer>1</integer>
 37                        <key>bInterfaceNumber</key>
 38                        <integer>0</integer>
 39                        <key>idProduct</key>
 40                        <integer>8200</integer>              <!-- 変更 -->
 41                        <key>idVendor</key>
 42                        <integer>1367</integer>              <!-- 変更 -->
 43                </dict>
 44        </dict>
 45        <key>OSBundleLibraries</key>
 46        <dict>
 47                <key>com.apple.iokit.IOSerialFamily</key>
 48                <string>1.0.4</string>
 49                <key>com.apple.iokit.IOUSBFamily</key>
 50                <string>1.8</string>
 51                <key>com.apple.kpi.iokit</key>
 52                <string>10.0.0</string>
 53                <key>com.apple.kpi.libkern</key>
 54                <string>10.0.0</string>
 55                <key>com.apple.kpi.mach</key>
 56                <string>10.0.0</string>
 57        </dict>
 58    </dict>
 59</plist>
    
これで kextload (8) を実行してドライバをロードすると URS-03 が無事に利用可能となる。

ただし、本来サポート対象のものを無理矢理利用しようとしているので、 URS-03 を接続した際にカーネルモジュールを自動でロードできない。 そこで 起動時に任意のコマンドを実行する 機能で起動時にカーネルモジュールをロードする設定を追加すると URS-03 を接続するだけで利用可能になる。

2011/07/07

パイプコマンド中の終了ステータス取得

シェルスクリプトでは頻繁に複数のコマンドをパイプで連結して使用するが、 パイプの途中のコマンドの終了ステータスは通常では参照できない。
そこでリダイレクトを利用してパイプの途中のコマンドの 終了ステータスを取得する方法を考えてみる。

  1exec 3>&1
  2ret1=`{ { command1; echo $? 1>&3; } | command2; } 3>&1`
  3ret2=$?
    
exec を利用してあらかじめ FD3 を利用可能にしておき、 command1 の終了ステータスを FD3 に出力している。
command2 の実行後に FD3 の出力を FD1(標準出力) に変更しているので、 ret1 は標準出力から command1 の終了ステータスが取得できる。 command2 の終了ステータスはそのまま $? を参照して ret2 に格納される。

2011/07/04

awk (1) 小技

先日参加した勉強会で awk (1) 関連の発表があったので、 ちょっとした小技を紹介してみる。

system() を利用する際の注意
awk の中で外部コマンドを実行する場合は 組み込み関数 system() が利用できるが、 この関数を利用して外部コマンドを実行すると 入出力 stream がオープンされるので、 ファイルディスクリプタが消費されてしまう。
従って awk 内で何度も system() を実行すると、 オープンできるディスクリプタの上限を超過してしまい、 awk の実行が中断されてしまう可能性がある。
その場合 close() を利用して system() がオープンした stream を閉じれば良いのだが、 awksystem() では stream を明示的に扱わない。 そこで system() で実行したコマンドを close() の引数で指定する事で stream を閉じる事ができる。
  1awk '{
  2    :
  3    # コマンドを生成
  4    command = sprintf("%s %s %s", command, arg1, arg2);
  5    # コマンドを実行して結果を取得
  6    buf = system(command);
  7    # stream をclose
  8    close(command);
  9    :
 10}'
            
関数内ローカルな変数の使用
nawkfunction を利用する事で 内部関数を作成する事ができるが、 関数内でローカルな変数を明示的に定義する事ができない。 そこで関数宣言の仮引数部に変数を定義する事で、 関数内ローカルの変数として利用できる。
  1awk '{
  2        :
  3    # 関数 foo の定義
  4    #   num: ループ数
  5    # 以下ローカル変数
  6    #   i: ループウンタ
  7    function    foo(num,        i)
  8    {
  9        # i はローカル変数扱いなので呼出元の i は影響を受けない
 10        for(i=0; i<1num; i++)
 11            :
 12        }
 13    :
 14    foo(num);
 15        :
 16    }'
            
但し関数呼び出し時と関数定義時に引数の数が等しくないので、 後に混乱しないためにコメントで明記するなどの処置は必要。

2011/07/01

Keyboards

現在職場で愛用中のキーボード達。
改めて見ると個人所有のキーボードが沢山あって驚いた。

PFU Happy Hacking Keyboard PD-KB02

Image: RIMG0044.JPG

PFU Happy Hacking Keyboard PD-KB02

Happy Hacking Keyboard としては2代目の製品かな? いちばん古くから愛用しているキーボード。
メンプレンスイッチのキータッチと シリンドリカル・カーブドスカルプチャの構成は 今でもいちばん気に入っていて、 FreeBSD 端末に接続されている。 プログラムを書くときは、このキーボードと FreeBSD と vi が必須。

PFU Happy Hacking Keyboard Professional 2 PD-KB400WN

Image: RIMG0045.JPG

PFU Happy Hacking Keyboard Professional 2 PD-KB400WN

USB 接続タイプの新世代 Happy Hacking Keyboard。
静電容量無接点方式で押下圧が 45g なので、 キータッチは PD-KB02 に比べると若干軽く感じるが、 打鍵音が若干「カチャカチャ」気味なのが気になる。 シリンドリカル・ステップスカルプチャなので、 手を自然に置いた時の感じが PD-KB02 とは若干異なり 微妙な違和感を感じる。

Apple Wireless Keyboard MB167LL/A

Image: RIMG0043.JPG

Apple Wireless Keyboard MB167LL/A

薄型 Bluetooth 接続の Apple Wireless Keyboard で、 AA セルを 3 本利用する初代タイプ。
酸化皮膜処理のアルミニウムボディは非常に美しいのだが、 パンタグラフタイプなのでキータッチが軽すぎ、 しかもストロークが短いので打鍵感が頼りなく感じてしまい、 キー入力時に底打ちしてしまうのが微妙に使いづらい感じ。 以前は 27インチの iMac のメインキーボードとして活躍してた。 現在は自宅の TV に接続されている Mac MINI のキーボード。

Lenovo ThinkPad USB TrackPoint Keyboard 55Y9003

Image: RIMG0046.JPG

Lenovo ThinkPad USB TrackPoint Keyboard 55Y9003

かつての IBM Space Saver Keyboard の流れをくむ トラックポイントが搭載された薄型のキーボード。
こちらもパンタグラフタイプなのでキータッチが軽く、 ストロークも短めなので本家 IBM Space Saver Keyboard ほどは打鍵感が良くない。

Apple USB Keyboard

Image: RIMG0047.JPG

Apple USB Keyboard

USB 接続の Apple 社純正フルキーボード。
メンプレンスイッチの打鍵感は素晴らしく、 シリンドリカル・カーブドスカルプチャなので違和感を感じない。
テンキーが付いているフルキーボードは邪魔なのだが、 Macintosh 独特の特殊キー(コマンドキー)をクリックしながら 電源をオンにするといった特殊な用途のために USB 接続の純正キーボードが必要な場合がまれにあるので 手元に置いてある。

Fujitsu FKB8745-T101

Image: RIMG0041.JPG

Fujitsu FKB8745-T101

一部では割と有名な富士通高見澤製のキーボード。
メンプレンスイッチのキータッチと シリンドリカル・カーブドスカルプチャというのは PD-KB02 と同じ構成だ。
そもそも PD-KB02 も富士通高見澤製なので 打鍵感なども殆ど変わらない。 PD-KB02 にファンクションキーと カーソルキーを付けた様なものだと思う。
Windows マシンを操作する場合はカーソルキーやファンクションキーが あった方が楽なので手元に置いてあったのだが、 今は手元に Windows マシンがなくなってしまったので、 サーバルームのコンソール用に利用している (良いキーボードなので若干勿体ないな)。

PFU Happy Hacking Keyboard Type-S PD-KB400WNS

Image: RIMG0294.JPG

PFU Happy Hacking Keyboard Type-S PD-KB400WNS

前述の Happy Hacking Keyboard Professional の進化版。
従来のモデルに比較するとキータッチが格段に良くなっている。 カチャカチャ音が完全に消されていて 静かで心地よい音になっているので、 静かなオフィスでも気にせずにタイピングに集中できるし 何よりも打鍵音に安っぽさがなくなって非常に良い感じ。
しかも各キーの軸が安定していてブレる感じがないので キー入力時に全く不快感を感じなくなった。 Type-S という名称だけど全然別モノな感じでとても良い。 現在は iMac に接続されているが、 今の所 PD-KB02 と同じ位気に入った。
問題は高価な事かな。

2011/06/30

Bicycle

最近近所のお友達がちゃんとした自転車に乗るようになり、 一緒に遊ぶ度にその子の自転車を借りては乗っていた。
地面に足は付かないがペダルには届くので なかなか上手にペダルをこいで前に進んでいる姿を見ていると、 もう三輪車では間違いなく物足りない様なので 少し (随分) 早めの誕生日プレゼントとして自転車を買った。

Image: img_1204.jpg

a.n.design-works

アニメなどのキャラクターが描かれた自転車よりも、 シンプルで飽きのこないデザインの自転車が良いなと思い ネットで色々と調べてみると、 a.n.design-works というメイカーの自転車が 子供用とは思えない程シンプルで落ち着いたデザインだったので即決。

Image: img_1208.jpg

CV14

朝起きて自転車を目にした娘はそのまま室内で乗り回す程気に入った様だ。
これから暫くは幼稚園から帰ってくると乗り回す日々なんだろうが、 くれぐれも車には注意して安全に遊んで欲しいと心から願っている。

2011/06/27

MacBook Air のアップデート不具合

Mac OS X のアップデート 10.6.8 がリリースされていたので、 手元にある MacBook Air (Late 2010) でもアップデートを実施した。
MacBook Pro(Eary 2011) や職場の iMac 27″ (Late 2009) では 何も問題が発生しなかったのだが、 何故か MacBook Air だけは問題が発生してしまった。

10.6.8 へのアップデートは一見正常に終了したかに見えたが 最初の再起動ではログイン画面が表示されるまでに非常に時間がかかり、 なんとなく「イヤな予感」がしていたのだが、 ログイン画面が表示された瞬間に「イヤな予感」が的中した事を知った。
ログイン画面は白い半透明のフィルターがかかった様に表示されており、 マウスカーソルの移動はできてもクリックやキー入力は全くできなかった。
しょうがないので電源ボタンの長押しでシャットダウンして再度起動してみると、 やはりログイン画面が表示されるまでに非常に時間がかかり ログイン画面は今度は普通に表示されたのだが、 先ほど同様マウスカーソルの移動のみが可能でクリックやキー入力は 全く出来ない状況に変わりはなかった。

PRAMクリア
そこで電源スイッチを入れた直後に 『Command』+『Option』+『P』+『R』を同時に押して、 もう一度起動音が聞こえるまでキーを押し続ける PRAM クリアを実施してみるが状況は改善されない。
シングルユーザモード
次に電源スイッチを入れた直後に 『Command』+『S』を同時に押してシングルユーザモードで起動し、 Applejack を実行して ファイルシステムの整合性検証 (fsck)や アクセス権の修復などを実施してみるが状況は全く改善されない。
セーフブート
最後は起動音と同時に 『Shift』キーを押して起動する セーフブートを試みると何とかセーフモードで起動できたので、 ソフトウェアアップデートを確認してみると 10.6.8 が適用されていない状況だった。 そこで Apple のサイトから 10.6.8 のアップデータをダウンロードして 手動でインストールしてみたところ無事に起動できた。

PRAM クリアやアクセス権の修復では復旧できない障害はめずらしいが、 セーフモードで起動して回復できたので良しとする。
同じ症状で困っている人がいるかもしれないので記事として残しておきます。

2011/06/19

父の日

娘が幼稚園で作った父の日のプレゼント。

Image: RIMG0276.JPG

父の日のプレゼント

娘からプレゼントを貰えるなんて感激。
改めて成長しているんだなと実感する喜びの瞬間。

2011/06/16

太陽のたまご♪

今年も恒例の宮崎産の完熟マンゴー。

Image: IMG_1173.JPG

太陽のたまご

今年は娘もしっかりと食べる様になったので 1 個増やして 4 個買いました。
素晴らしい太陽と自然の恵みが詰まっています。

2011/06/15

php の proc_open() を利用した openssl コマンドの実行

php ネタ

openssl (1) で暗号化されたファイルを復号化する処理を php で実装する必要があった。
pear を探せばその手のモジュールは多分あるだろうと思ったが、 標準で含まれないモジュールに依存したくなかったので 外部コマンドの openssl を利用する方法を試してみた。

php では入力、もしくは出力のみの外部コマンド実行は popen() を利用できるが、 入出力双方向が必要なので proc_open() を利用する。
最初は proc_open() した openssl の入力側パイプに 暗号化されたデータを全て出力 (fwrite()) した後で 出力側パイプから復号化されたデータを読み出し (fread()) ていたのだが、 入力データがある程度のサイズを越えると openssl はデータ読込みの途中でデータを出力しないと 読込み (もしくは処理) をブロックしてしまう様なので、 fwrite() の後に fread() を実行する様に修正した。
この時、当初は stream_select() を利用してパイプの出力側からの 入力可否を監視する様にしたのだが、 stream_set_bloking() を利用してパイプの出力側を 非ブロックモードにする事で、 パイプから読込めない場合でも fread() が即リターンするので stream_select() の呼出しによりコードが煩雑になる事が避けられた。

  1<?php
  2    /*
  3     * Copyright (c) 2011 Mitzyuki IMAIZUMI. All rights reserved.
  4     *
  5     * $Id: decrypt.php 3 2011-06-16 16:15:48Z mitz $
  6     */
  7
  8    define("OPENSSL",    "openssl enc -d -des3 -pass pass:%s");
  9    define("BLOCSIZE",   "4096");
 10
 11    /*
 12     * ファイルの復号化
 13     *   $1: ファイル名
 14     *   $2: サイズ
 15     *   $3: パスフレーズ
 16     */
 17    function    decrypt($file, $size, $pass)
 18    {
 19
 20        $desc = array(
 21            0 => array("pipe", "r"),                /* stdin:  pipe */
 22            1 => array("pipe", "w"),                /* stdout: pipe */
 23            2 => array("file", "/dev/null", "w")    /* stderr: /dev/null */
 24        );
 25
 26        if(($fp = fopen($file, "r+"))){
 27            if($data = fread($fp, $size)){
 28                if(preg_match("/^Salted_/", $data)){
 29                    /* 暗号化されている場合 */
 30                    if($pp = proc_open(sprintf(OPENSSL, $pass), $desc, $pipe)){
 31                        /*
 32                         * `openssl enc -d …' を実行する。
 33                         *
 34                         * ファイルの内容はすでに $data に格納されているので
 35                         * BLOCSIZE 単位で openssl の標準入力に出力する。
 36                         */
 37
 38                        stream_set_write_buffer($pipe[0], 0);
 39                        stream_set_blocking($pipe[1], 0);
 40
 41                        $buf = "";
 42                        
 43                        while($size > 0){
 44                            /*
 45                             * 1 ブロック出力
 46                             * substr() は開始位置に負の値を指定すると
 47                             * 文字列の終端を起点とした開始位置からの
 48                             * 部分文字列が取得できる。
 49                             */
 50                            fwrite($pipe[0], substr($data, 0 - $size, BLOCSIZE));
 51                            $size -= BLOCSIZE;
 52                            /*
 53                             * openssl からの読み出し処理
 54                             * 非ブロッキングなので、
 55                             * 読めない場合は即座に fread() から戻る。
 56                             *
 57                            $buf .= fread($pipe[1], BLOCSIZE);
 58                        }
 59                        fclose($pipe[0]);
 60
 61                        while(!feof($pipe[1]))
 62                            $buf .= fread($pipe[1], BLOCSIZE);
 63                        fclose($pipe[1]);
 64
 65                        proc_close($pp);
 66                    }
 67                    /*
 68                     * 復号化したデータの出力
 69                     * 入力ファイルを書き換える
 70                     */
 71                    fseek($fp, 0);
 72                    ftruncate($fp, 0);
 73                    fwrite($fp, $buf);
 74                }
 75            }
 76            /* 暗号化されていない場合はそのまま close() する */
 77            fclose($fp);
 78        }
 79
 80    }
 81
 82?>
    

2011/06/16 追記

ブロック転送のロジックを整理して最適化した。
データサイズ $size をループの制御変数とする事で 余計な変数や転送ブロック数の計算を削除し、 openssl への出力データの部分文字列切り出し処理で substr() を利用する際に負の値を指定して 開始位置を文字列後端からの位置で指定する様に変更した。

2011/05/24

シェル変数への情報セット

例えば wget (1) コマンドへ proxy サーバの情報を伝えるためには http_proxy シェル変数に URL を http://[ID[:pass@]]FQDN[:port] 形式でセットする必要があるが、 proxy 情報が以下の形式でファイル格納されていれば 1 行で簡単にシェル変数に設定できる。

proxy.example.com                           # プロキシサーバの FQDN
8080                                        # プロキシサーバのポート番号
user                                        # プロキシサーバの認証 ID 
passwd                                      # プロキシサーバの認証パスワード
	
  1#!/bin/sh
  2
  3setproxy()
  4{
  5
  6    local   _var
  7
  8    _var=${1}
  9
 10    if [ -f ${2} ]
 11    then
 12        set -- `sed 's/#.*//g' ${2}`
 13        test -n "${1}" && eval "${_var}=\${1:+http_proxy=http://\${3:+\$3\${4:+:\${4}}@}\${1}\${2:+:\${2}}}"
 14    fi
 15
 16}
 17
 18setproxy http_proxy ファイル名
 19sh -c "${http_proxy} wget …"
 20    :
    

2011/05/22

レアチーズケーキ

遙か 15 年以上昔にバイト先で作っていたレシピを思い出しながら、 週末にレアチーズケーキを作った。
基本的なレシピはフィラデルフィアのクリームチーズに同梱されている リーフレットの通りに作るのだが、 いくつか独自の手順を加える事で格段になめらかな舌触りのチーズケーキになる。

Image: RIMG0253.JPG

ブルーベリージャムとブランデーをたらして

  1. ゼラチンは水と白ワインを同量混ぜたものに浸しておくと ゼラチンの持つ臭みが消える。
  2. ヨーグルトではなくサワークリームを使うと ほどよい上品な酸味となる。
  3. クリームチーズ、サワークリーム、そして砂糖を混ぜる時には 消える寸前のとろ火にかけてゆっくり加熱しながらよく混ぜる。
  4. 火を止めたらレモン果汁 1 個分を加えて更によく混ぜる。
  5. 生クリームを 6 分立て程度にホイップする。
  6. 冷ましたクリームチーズを裏ごしして生クリームに混ぜる。
  7. ゼラチンを湯煎して溶かしてクリームチーズに手早く混ぜる。
あとは型に流し込んで冷やせば美味しいクリームチーズケーキの出来上がり。
カシスやブルーベリーなどのジャムと、 風味付けに少量のブランデーをたらして召し上がれ。

2011/05/19

Mac でシリアルターミナル

ルータなどのネットワーク機器の設定を行う場合や、 ディスプレイを接続しないでサーバ運用しているマシンの作業など、 シリアル接続をする必要は少なくなったとは言えまだまだ作業する機会はある。
しかし Mac Book (というか現在発売している殆どの Laptop) には 既にシリアル端子などは装備されていないので、 USB シリアル変換アダプタを利用して接続する必要がある。

メーカーが公式に Mac 対応を表明しているアダプタもあるが、 手持ちの Arvel SRC06-USB は 利用しているチップセットのメイカー (Future Technology Devices International Ltd. 社) の Web サイト から Mac 用のドライバをダウンロードしてインストールすると 正しく利用できる様になった。
ドライバをインストールした後でアダプタを接続すると、 /dev/tty.usbserial-FTEHW36L というデバイスが自動で生成されたので、 このデバイスを利用して通信する事ができる様になる。

通信に利用するターミナルソフトは 標準でインストールされている cu (1) や screen (1) を利用するのが手軽だ。
screen (1) を利用する場合は以下の様に起動すると接続できる。 切断する時は コマンド文字+k を入力すればよい。

$ screen /dev/tty.usbserial-FTEHW36L 9600
    
ここでは指定していないがフロー制御やパリティチェックの有無、 ストップビット長などシリアル通信で必要と思われる設定は一通り指定できる。
普段からターミナルで screen (1) を利用している場合は ~/.screenrc defkanji utf などと記述してあるだろうが、 シリアル接続の相手が EUC や MS 漢字コードのなどの場合は ~/.screenrc に以下の記述をして コマンド文字+Eコマンド文字+U を入力する事で screen の表示コードが変更できるので便利だろう。
bind E encoding euc
bind S encoding sjis
bind U encoding utf
	

ファームウェアの転送など XMODEM を利用したファイル転送が必要な場合は、 内部で kermit (1) を呼び出して様々な転送処理が実行できる 多機能な通信端末 minicom (1) をインストールすると便利になると思う。

2011/05/18

Imperia Pasta Machine

遙か昔、当時創刊されたばかりの雑誌 Brutus に パスタマシンが掲載されているのを見かけて漠然と良いなと思っていたのだが、 30 年余の年月を経て念願かなって入手する事ができた。
昔の事すぎて詳しくは覚えていないのだが、 当時の金額で 10 万円ほどのプライスタグがついていて、 パスタマシンというのは非常に高価なものだと 30 年もの間 何の疑いもなく信じ込んでいた。
しかし改めて調べてみると意外と安いものだと気がついたので、 今回 Imperia 社製の自家製パスタマシンを購入してみた。

Image: RIMG0241.JPG

Imperial SP-150

ホームベーカリーのメニューにパスタ生地があるので、 デュラム小麦粉のセモリナを使えばパスタ生地が割と簡単に作れる。
とりあえず今週末は自家製のパスタでペペロンチーノを作ってみる予定なのだが、 今から出来上がりが楽しみでしょうがない。

2011/05/16

ナチューラ ヌメ革マウスパッド

土屋鞄製造所 で作成・販売している ナチューラ ヌメ革マウスパッド。

Image: RIMG0238.JPG

以前からずっと欲しいと思っていたのだが、 先日やっと注文できたものが週末に届いた。
植物性のタンニンで丁寧になめした革で作られたマウスパッドなので、 これから使い込むにつれて革の持つ風合いが 良い感じで出てくるだろうと思うと今からワクワクする。

同じ素材で Happy Hacking Keyboard 用の 30cm 幅の パームレストも是非作って下さい。

2011/05/13

ディスク容量のチェックツール

随分と時間が開いてしまったが、友人の awk (1) の勉強支援の第 4 段。 ディスクの容量をチェックして警告メイルを送信するためのスクリプト。

df (1) の内容を簡単に解析して、 ルートパーティションの使用率が指定した値以上の場合は警告メイルを送信し logger (1) を利用して syslog にも警告を出力する。

  1#!/bin/sh
  2#
  3#   All rights reserved, copyright (c) 2011, Mitzyuki IMAIZUMI
  4#   $Id: rdf,v 1.1 2011/05/13 17:38:09 mitz Exp $
  5#
  6
  7myname=${0##*/}
  8tmpfile=${TMP:-/tmp}/${myname}.$$
  9logger=/usr/bin/logger
 10df="LANG=C df -h"
 11
 12# 警告メイルの宛先
 13to=root@example.com
 14
 15# 警告処理
 16warning()
 17{
 18
 19    LANG=C
 20    subject="##### [ ${1}: Disk Usage ] #####"
 21    ${logger} "${subject}"
 22    cat << EOF | mail ${to} -s "${subject}"
 23
 24${1}
 25
 26`date`
 27
 28`cat ${2}`
 29EOF
 30
 31}
 32
 33# 終了時に一時ファイルを削除
 34trap 'rm -r ${tmpfile}; exit' 0 1 2 3 9 13 15
 35
 36# メイン処理
 37
 38# tee(1) を使って df(1) の出力を awk(1) と一時ファイルの両方に出力
 39${df} | tee ${tmpfile} |
 40    awk '{
 41        # 最後のフィールドが '/' の場合 (ルートパーティション)
 42        if($NF == "/")
 43            # 最後の直前のフィールドが使用率なので閾値との比較結果をリターン
 44            exit($(NF-1) < '${1:-70}')
 45    }' && warning `hostname` ${tmpfile}
    

2011/04/22

国立商店 オイルドレザースリーブ

普段から毎日持ち歩いている MacBook Air なので、 鞄の中で美しいアルミニュームボディーが傷ついたりしない様に 国立商店の職人が作るオイルドレザースリーブ を購入した。

Image: RIMG0232.JPG

職人が作るオイルドレザースリーブ

オイルドレザーなので持った時の肌触りがしっくりと手に馴染み、 内側に貼られた肉厚フェルトが MacBook Air をしっかりと保護してくれる感じで安心感がある。 国立商店の製品は以前購入した MacBook 13″ 用のケースに続き 2 個目となるので、 手元に届く前から品質と素晴らしさは信頼していたのだが、 改めて手に取るとその信頼を凌駕する程の製品でより一層嬉しくなる。

2011/04/20

MocBook Air

Image: RIMG0208.JPG

MocBook Air late 2010

以前白ポリカーボネート製の MacBook の名刺入れをお願いしたのだが、 MacBook Air を購入したのでそちらの分もお願いしていた物が届いた。

Image: RIMG0210.JPG

MacBook Air と MocBook Air

以前届いた MocBook と並べると最高に可愛くて素敵である。 MocBook のデスクトップは娘の誕生日でお願いしたので、 MocBook Air のデスクトップ画像は奥さんの誕生日にしてみた。 キーボードは勿論英語キーボードの画像に変更して頂けた。
MacBook Air は普段から持ち歩いているので MocBook Air 名刺入れも届いてからは毎日持ち歩いているのだが、 仕事柄人に会う機会が少ないので名刺交換も稀なので 自慢する機会が少ないのがちょっと悲しい(そこか)。

Image: RIMG0219.JPG

MocBook と MocBook Air

この様な素晴らしいアイテムを手作りで作成して頂けて非常に感謝している。
最近購入した MacBook Pro の分も是非欲しいなと思う今日この頃 (デスクトップは自分の誕生日にしよう♪)

残念ながら諸処の事情で当面新規の受付は中止しているとの事なので、 作者の方のお名前や Web ペイジの URL は伏せさせて頂く。

2011/04/19

Mac で簡易バックアップ

Mac は rsync (1) が標準で搭載されていて 簡単に差分転送する事ができるので、 簡易的なバックアップが手軽に実施できて便利である。

以下のコードはリモートサーバのディスクを samba 形式で mount (8) し、 rsync (1) で同期するためのスクリプト。
バックアップ先のディスクを samba 形式でマウントするので、 バックアップ先が Windows パソコンでも動作する。
Finder の共有から一度バックアップ先のディスクにアクセスして パスワードをキーチェーンに記憶させると、 以降はパスワードの問い合わせがなくなるので便利だろう。
バックアップ先が unix 系のサーバの場合であれば、 mount (8) しないで rsync (1) に 直接リモートサーバを指定しても良い。 その場合 -e オプションを指定して ssh (1) 経由で転送する事が望ましい。

  1#!/bin/sh
  2
  3# バックアップ対象(= バックアップ先ディレクトリ)
  4target="Pictures Music"
  5# バックアップ先サーバ
  6Server="<u>リモートサーバ</u>"
  7# バックアップ元ディレクトリを格納しておき eval(1) で展開する
  8Pictures="Pictures"
  9Music="Music/iTunes/iTunes Music"
 10
 11# "-f" オプションを指定しない場合は実際には転送しない
 12test "$1" = "-f" || dry=n
 13
 14for i in ${target}
 15do
 16    src="${HOME}/`eval echo '$'${i}`"
 17    dst="/Volumes/${i}"
 18    
 19    mkdir -p ${dst}
 20    mount -t smbfs //${Server}/${i} ${dst}
 21    rsync -avz${dry} --delete --exclude '._*' --exclude .DS_Store "${src}/" "${dst}"
 22    umount ${dst}
 23done
    

2011/04/14

貢ぐ

今まで購入してきた Apple 製品をつらつらと思い出してみる。
iPod 以外の Apple 製品を購入する様になったのは本当にこの数年なのだが、 今ではすっかり Windows を駆逐して Apple 一色になっている。

iPod

iPod Shuffle 第1世代 1GB
iPod nano 第1世代 4GB
iPod nano 第2世代 8GB
iPod nano 第4世代 16GB

Macintosh

Mac mini
Mid 2007 Intel® Core™ 2 Duo T5750 2.0 GHz 4 GB へ換装 200 GB / 7200 rpm へ換装
MacBook 13″ Early 2008 Intel® Core™ 2 Duo T8300 2.4 GHz 4 GB 500 GB / 7200 rpm へ換装
MacBook Air 11″ Late 2010 Intel® Core™ 2 Duo L9400 1.86 GHz 4 GB 128 GB SSD
MacBook Pro 15″ Earyl 2011 Intel® Core™ i7 2720QM 2.2 GHz 8 GB 512 GB SSD
MacBook Air 11″ Mid 2013 Intel® Core™ i7 4650U 1.7 GHz 8 GB 512 GB SSD

Software

Mac OS X 10.6 Snow Leopard 2009 5 License Pack

Miscellaneous

Migty Mouse 2007
Wireless Keyboard 2007 3 AAA cell
Time Capsule Earyl 2010 1TB
Magic Mouse 2011
Magic Trackpad 2011

2011/04/10

入園式

娘の幼稚園の入園式が無事に終了した。
今年は偶然入園式が日曜日だったので参加できたのだが、 娘の成長する姿というのは見ていて微笑ましさと、誇らしさ、 そして若干の寂しさが共存している不思議な感情になる。
「幼稚園の入園式は泣き声があちこちで聞こえて来るので 感動に浸っている余裕はない」 と聞いていたのだが、 確かにあちこちから子供達の泣き声が響いてきた。 一番大きな声で泣いていたのが自分の娘だったのが…微妙な感じではあるが(笑)

ご多分にもれず入園式会場の看板の前で家族で記念撮影などしてみる。
暖かな春の日差しの中で制服を着て少し誇らしげだけど、 幾分緊張もしているわが娘の姿がとても愛おしい。

2011/03/01

Facebook に届いたメッセージ

Facebook に怪しさ満点なメッセージが届きました。

Hello Imaizumi,

I have been in search of a foreigner with this last name "Imaizumi", that will be faithful to me, so when I saw your name I was pushed to contact you and see how best we can assist each other in this business. I am Mr. Bernard Awotwi, a Banker here in GHANA. I believe it is the wish of God for me to come across you name now. I am having an important business discussion I wish to share with you which I believe will interest you because, it is in connection with your last name and you are going to benefit from it. I write you this proposal in good faith, believing that I can trust you with the information I am about to reveal to you.


Let me know your mind on this and please do treat this information as TOP SECRET and DO NOT respond to this on facebook for same security reason. I have more to write you about the details once I receive your urgent response strictly through my personal Email address: xxx@hotmail.com

Regards,
Bernard Awotwi

そんなに私の名字が『信用できる』んでしょうか(笑)。 というかガーナの『銀行員』な方が赤の他人(しかも彼|彼女から見たら外国人)を 名字だけで信用しちゃって良いんでしょうかね(笑)。 さらには神のご意思とか(笑)。
しかも FaceBook での返信をせずに『セキュリティ上の理由から』 hotmail にメイルしてって(笑)。
怪しすぎてワクワクしちゃいますが、 これってひと昔〜ふた昔も前に流行った「ブラックドル」でしょうかね。
暫くは突っ込み所満載な内容に笑わせて貰ったので、 この楽しさを是非共有したいのと骨董品的に懐かしいのとで blog にしちゃいました。

2011/02/28

重複メイル削除ツール

友人の awk (1) の勉強支援の第 3 段。 以前 Software のペイジで公開したスクリプトの中身。

1 メール 1 ファイルの形式(mh 形式)で格納されたメイル本文のうち Message-Id: ヘッダが重複するものを抽出して削除するツールで、 sort (1) や uniq (1) だけを利用しても実装可能だが、 可読性と拡張性、実装の手間等を考えて簡単に awk (1) で実装した。
Message-Id: ヘッダから取得したメッセージ ID をインデックスとして ファイル名を連想配列に格納しており、 既にメッセージ ID が連想配列のインデックスとして存在している場合は メッセージ ID が重複したものとしてファイル名を標準出力に出力するだけの 非常に簡単な内容のスクリプトだ。

  1#!/bin/sh
  2#
  3#   All rights reserved, copyright (c) 2009, Mitzyuki IMAIZUMI
  4#   $Id: DupmailMac,v 1.1 2009/09/30 09:25:09 mitz Exp $
  5#
  6
  7exec 2> /dev/null
  8
  9# Mac のメールボックスのデフォルトロケーション
 10basedir="${HOME}/Library/Mail"
 11
 12find ${basedir} -name "*.mbox" -a -type d |
 13while read folder
 14do
 15    if [ -d "${folder}/Messages" ]
 16    then
 17        awk '{
 18            # 大文字小文字を区別しないために全部小文字に変換
 19            if(tolower($1) ~ /^message-id:/){
 20                # 入力行を `:' で分割する (line[2]: message-id)
 21                split($0, line, ":");
 22                # message-id から余計なスペース、 <、> を削除
 23                gsub("[<> ]", "", line[2]);
 24
 25                if(message[line[2]] != "")
 26                    # message-id に対応するファイルがある場合はファイル名を表示
 27                    print FILENAME;
 28                else
 29                    # message-id に対応するファイルがない場合はファイル名を格納
 30                    message[line[2]] = FILENAME;
 31            }
 32        }' ${folder}/Messages/* |
 33        # ファイルの削除
 34        sort | uniq | xargs rm
 35    fi
 36done
    

2011/02/25

awk を利用した構文解析ツール

友人の awk (1) の勉強支援の第 2 段。 今回はちょっと複雑な処理なので追うのが大変かも?
このスクリプトは随分以前(1996年頃)に作成したのだが、 プログラムによって微妙に異る複数の設定ファイルの中身を 解析するために作成したそこそこ汎用の構文解析機だ。 本来は perl (1) などで記述したかったのだが、 よんどころない事情で awk (1) により実装した。

以下に示す構造の状態遷移テーブルで状態(status)とキーワード(token)を定義し、 それぞれの status の時に出現する入力データ中の token により 定義されていてば外部コマンドを実行して次の status への遷移を繰り返す。

状態遷移デーブルは 1カラム目が '#' の行、タブ、 スペースのみの行は無視する。
`%syntax' で始まる行が token の定義となり、 次の行以降が status の時に出現する token 毎の定義で、 実行するコマンドと遷移する status、 もしくはシンタックスエラー(error)を記述する。
予約された状態値として初期状態を示す `start'と エラー状態を示す `error' が定義されており、 エラー状態では標準エラー出力にメッセージを出力後終了する。
オプションとして開始時に 1 度だけ実行される初期処理コマンドを `%start' で始まる行に、 終了時に 1 度だけ実行される終了処理コマンドを `%end' で始まる行にそれぞれ定義できる。

%syntax token1 token2 ... *
start status1:command1 error ... status2
status1 status3:command3 error ... statusN:commandN

:
statusN error error ... start
%start command parm ....
%end command parm ....

status 定義行ではその status に遷移した際に実行するコマンドを `:' に続けて記述でき、 コマンドの引数には以下の特殊文字が指定できる。 全ての特殊文字の置き換えを終了するとシェルを通してコマンドを実行する。
%
現在のトークンに置き換えられる
,
スペースに置換えられる
$0
直前のコマンドのリターン値に置き換えられる
$1$N
このコマンドの第 3 引数以降に置き換えられる

実際に使用した状態遷移テーブルの例を示す。 下で示す形式の設定ファイルの解析を実施するためのもので、 開始状態から入力データに応じて状態値を遷移させながら解析処理を実施する。

# 最初に実行されるコマンド
%start      ${path}/do.start

# 最後に実行されるコマンド
%end        ${path}/do.end $0

# token 定義
%syntax     {           }      ,           =          *

# 状態遷移テーブル
start       error       error  error       error      name:${path}/do.name,%,$0,$1,$2
name        keyword     error  error       error      error
keyword     error       start  error       error      continue:${path}do.keyword,%,$0,$1,$2
continue    error       error  error       equal      error
equal       error       error  error       error      next:${path}/do.val,%,$0,$1,$2
next        error       start  keyword     error      error
	
解析させた設定ファイルの形式。
名称1 {
	キーワード1 = 値1,
	キーワード2 = 値2,
	  :
	キーワードN = 値N
}

  :

名称M {
	キーワード1 = 値1,
	キーワード2 = 値2,
	  :
	キーワードN = 値N
}
	
この定義ファイルの解析を実行すると 以下の順にコマンドを実行する事と等価な処理が実施できる。
$ ${path}/do.start
$ ${path}/do.name 名称1 $? 引数1 引数2
$ ${path}/do.keyword キーワード1 $? 引数1 引数2
$ ${path}/do.val 値1 $? 引数1 引数2
$ ${path}/do.keyword キーワード2 $? 引数1 引数2
$ ${path}/do.val 値2 $? 引数1 引数2
    :
$ ${path}/do.keyword キーワードN $? 引数1 引数2
$ ${path}/do.val 値N $? 引数1 引数2
    :
$ ${path}/do.name 名称M $? 引数1 引数2
$ ${path}/do.keyword キーワード1 $? 引数1 引数2
$ ${path}/do.val 値1 $? 引数1 引数2
$ ${path}/do.keyword キーワード2 $? 引数1 引数2
$ ${path}/do.val 値2 $? 引数1 引数2
    :
$ ${path}/do.keyword キーワードN $? 引数1 引数2
$ ${path}/do.val 値N $? 引数1 引数2
$ ${path}/do.end $?
    

そしてスクリプト本体。 今見返すと冗長な記述などもあるが敢えてそのままにしておく。

  1#!/bin/sh
  2#
  3# Copyright (c) 1996 Mitzyuki IMAIZUMI, All rights reserved.
  4#
  5# $Id: parser,v 1.7 1996/02/01 19:33:18 mitz Exp $
  6#
  7# 名称 
  8#   parser - 状態遷移テーブルに基づいてシンタックスをチェック
  9#
 10# 構文
 11#   parser config input [引数…]
 12# 
 13# 引数
 14#   config
 15#       状態遷移テーブル
 16#   input
 17#       入力ファイル
 18#   引数…
 19#       各状態で実行するコマンドの引数
 20#
 21
 22# パラメタチェック
 23test $# -lt 2 -o ! -f $1 -o ! -f $2 && exit 255
 24
 25trap '' 1 2 3 5 9 13 15
 26
 27conf=${1}; file=${2}; shift 2
 28
 29for i
 30do
 31    parm="${parm},${i}"
 32    shift
 33done
 34
 35# %syntax 行から token を取得(最後の token は除外
 36token=`
 37    awk '{
 38        if($1 == "%syntax"){
 39            for(i=2; i<NF; i++)
 40                printf("%s", $i);
 41            exit
 42        }
 43    }' ${conf}`
 44
 45# 入力ファイルの token 前後にスペースを付加する
 46sed 's/['${token}']/ & /g' ${file}    |
 47
 48awk '
 49    #
 50    # 初期処理
 51    #   状態遷移テーブルのリード
 52    #
 53    BEGIN{
 54
 55        argc = split("'${parm}'", argv, ",");       # 引数を格納
 56        argv[1] = 0;
 57
 58        while(getline < "'${conf}'" > 0){
 59            if(/^#/ || /^[ \t]*$/)                  # コメント行/空行
 60                continue;
 61            if($1 == "%start"){                     # 初期処理定義行
 62                $1 = "";
 63                prolog = $0;
 64            }
 65            else if($1 == "%end"){                  # 終了処理定義行
 66                $1 = "";
 67                epilog = $0;
 68            }
 69            else if($1 == "%syntax")                # トークン定義行
 70                for(i=2; i<NF; i++)
 71                    item[i-1] = $i;
 72            else                                    # 状態遷移定義行
 73                for(i=2; i<NF; i++)
 74                    if(p = index($i, ":")){
 75                        data[$1 item[i-1]] = substr($i, 0, p-1);
 76                        command[$1 item[i-1]] = substr($i, p+1);
 77                    }
 78                    else
 79                        data[$1 item[i-1]] = $i;
 80        }
 81        if(prolog != "") 
 82            argv[1] = exec(prolog, "");
 83
 84        status = "start";
 85
 86    }
 87
 88    #
 89    # トークンチェック
 90    #
 91    function  isitem(item, token,       i)
 92    {
 93
 94        for(i in item)
 95            if(item[i] == token)
 96                return 1;
 97
 98        return 0;
 99
100    }
101
102    #
103    # コマンド実行
104    #
105    function  exec(command, token,      buf, i)
106    {
107
108        gsub("%", token, command);
109        for(i=0; i<argc; i++){
110            buf = sprintf("\\$%d", i);
111            gsub(buf, argv[i+1], command);
112        }
113        gsub(/,/, " ", command);
114
115        i = system(command);
116        close(command);
117
118        return i;
119
120    }
121
122    #
123    # メイン処理
124    #
125    {
126
127        # コメント行スキップ
128        if(/^#/) 
129            continue
130
131        for(i=1; i<NF; i++){
132            if(isitem(item, $i)){
133                format = command[status $i]
134                status = data[status $i]
135            }
136            else{
137                format = command[status]
138                status = data[status]
139            }
140            if(status == "error"){
141                printf("%s: %d: syntax error \"%s\"\n",
142                    "'${file}'", NR, $i) | "'cat' >2"
143                ret = 255
144                exit
145            }
146            else if(format != "")
147                if(format == "exit"){
148                    ret = argv[1]
149                    exit 
150                }
151                else
152                    argv[1] = exec(format, $i)
153        }
154
155    }
156
157    #
158    # 終了処理
159    #
160    END{
161        if(epilog != "")
162            exec(epilog, "")
163
164        exit ret
165    }
166'
    

2011/02/24

awk スクリプト

知り合いが awk (1) の勉強をしたいと言ってるので、 支援がてら過去に作成したスクリプトをさらしてみる。
このスクリプトは linux 上で稼働している あるプログラムが取得した共有メモリの残骸を掃除するスクリプトで、 ipcs (8) で取得した共有メモリ一覧のうち、 3 件連続しているキーが存在する場合 ipcrm (8) をコールして 共有メモリを削除する処理を行う。
短いスクリプトながら、外部コマンドの呼出し (system())、 コマンドからの値取得 (getline())、 そして関数内ローカルスコープの変数定義など awk (1)の持っている機能を結構活用しているので、 多少なりとも参考にして貰えるとうれしい。

  1#!/bin/sh
  2#
  3# Copyright (c) 2010 Mitzyuki IMAIZUMI, All rights reserved.
  4#
  5# $Id: shmrm,v 1.7 2010/05/08 09:03:15 mitz Exp $
  6#
  7# 共有メモリ削除処理
  8#
  9
 10LANG=C /usr/bin/ipcs -m | sort |
 11
 12awk '
 13    #
 14    # 初期処理
 15    #
 16    BEGIN{
 17
 18        max = 0;                        # 削除対象キーインデックス
 19        num = 0;                        # 削除対象共有メモリインデックス
 20        ipcrm = "/usr/bin/ipcrm";       # ipcrm コマンド
 21
 22    }
 23
 24    #
 25    # 16進数 -> 10進数変換
 26    #    cmd, dec はローカル変数として利用するために仮引数宣言とする
 27    #
 28    function    h2d(hex,            cmd, dec)
 29    {
 30
 31        # printf(1) を利用して 16進数を 10進数に変換
 32        cmd = sprintf("printf '%%d' %s", hex);
 33        # printf(1) の実行結果を getline で変数 dec に取得
 34        cmd | getline dec;
 35        close(cmd);
 36
 37        return dec;
 38
 39    }
 40
 41    #
 42    # 3連続するキーかのチェック
 43    #    start, i はローカル変数として利用するために仮引数宣言とする
 44    #
 45    function    cont3(num,          start, i)
 46    {
 47
 48        start = h2d(key[num]);
 49
 50        for(i=1; i<3; i++)
 51            if(h2d(key[num+i]) != start + i)
 52                return 0;
 53
 54        return 1;
 55
 56    }
 57
 58    #
 59    # コマンド実行
 60    #
 61    function    exec(key, arg)
 62    {
 63
 64        system(sprintf("%s -%s %s", ipcrm, arg, key));
 65
 66    }
 67
 68    #
 69    # 共用メモリ/セマフォの削除
 70    #
 71    function    shmrm(key, num)
 72    {
 73
 74        # 共有メモリを削除
 75        exec(key, "M");
 76
 77        # セマフォを削除
 78        if(num % 3 == 2)
 79            exec(key, "S");
 80
 81    }
 82
 83    #
 84    # メイン処理
 85    #
 86    {
 87
 88        # owner が root で権限が 666 かつ nattch が 0 の共用メモリを検索
 89        if($3 == "root" && $4 == "666" && $6 == 0)
 90            key[max++] = $1;
 91
 92    }
 93
 94    #
 95    # 終了処理(ある意味こっちがメイン)
 96    #
 97    END{
 98
 99        if("'$1'" == "-f"){
100            # 強制モード -- 3連続していないキーでも削除
101            for(n in key)
102                shmrm(key[n], 2);
103        }
104        else {
105            # 通常モード -- 3連続しているキーが削除対象なので抽出する
106            for(i=0; i<max; i++)
107                if(cont3(i))
108                    for(j=0; j<3; j++)
109                        target[num++] = key[i+j];
110
111            # 削除対象の共有メモリ/セマフォを削除
112            for(n in target)
113                shmrm(target[n], n);
114        }
115
116    }
117'
    

2011/02/17

ssh

ssh (1) コマンドは接続先のホスト情報をローカルに保存していて、 接続するたびに接続先が正しいホストかを確認する機能を持っている。 これはホスト名の詐称や DNS poisoning などにより 正しくない接続先に接続してしまうという事態を防止するためには 非常に便利で有用な機能ではあるが、 接続先のシステムが再インストールなどでホスト情報が変更になってしまった場合、 ssh で接続しようとすると

$ ssh ホスト名
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that the RSA host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX.
Please contact your system administrator.
Add correct host key in /home/ユーザ/.ssh/known_hosts to get rid of this message.
Offending key in /home/ユーザ/.ssh/known_hosts:NN
RSA host key for ホスト名 has changed and you have requested strict checking.
Host key verification failed.
$
    
などと表示されて接続できなくなってしまう。
画面に表示された通り known_hosts ファイルの該当行を削除すれば 再度接続できるのだが、 これを自動で行うためのスクリプトを作成してみた。

  1#!/bin/sh
  2cat << EOF | ex -s ${HOME}/.ssh/known_hosts
  3`ssh $* 2>&1 | sed -n '/^Offending key/s/.*:\(.*\)/\1/p' | tr -d '\r'`d
  4wq
  5EOF
    
PATH の通ったディレクトリに sshfix の様な名前で保存し 実行権限を付与しておくと、 ssh で接続を試みて上記エラーが発生した際に実行する事で known_host の該当行を削除するので再度接続できる様になる。

$ ssh ホスト名
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!</i>
   :
$ sshfix !$
$ ssh !$
        
くれぐれも本当に正しい接続先か確認してから利用しなければならない。

2011/02/12

シェルスクリプト自身のパス取得

シェルスクリプトを作成していて、 たまにスクリプト自身のパスを取得したい場合がある。
以下の処理でシェルの種類に依存せず(とは言っても bourne shell 系のみだが) シェルスクリプト自身のパスを取得する事ができる筈(多分)。
PATH 環境変数に従って実行された場合は ${0} にスクリプトの絶対パスが格納されているので ${0}dirname (1) を取得すれば良いが、 相対パス指定で実行された場合は 1度 cd (1) した上で pwd (1) を実行してカレントディレクトリを取得する必要がある。
すべてのケースで "( cd ${0%/*} `dirname ${0}` && pwd )" しても同じ結果を得られるのだが、 サブシェルの実行や特に pwd (1) の実行はシステムに負荷をかけるので、 効率を考え ${0} の値を確認している。
expr (1) の実行も結構負荷がかかるからあまり意味ないかな…

  1#!/bin/sh
  2# ${0} の dirname を取得
  3# cwd=${0%/*}
  4cwd=`dirname ${0}`
  5
  6# ${0} が 相対パスの場合は cd して pwd を取得
  7expr ${0} : "/.*" > /dev/null || cwd=`(cd ${cwd} && pwd)`
  8    :
    

2011/03/17 追記

$ sh foo
	
この様に実行された場合 ${0} には foo が 格納されているので ${0%/*} では foo に展開されてしまい cd (1) がエラーとなる問題があったので、 dirname (1) を利用する様修正しました。

2011/02/10

Happy Birthday My Dearest !

Image: RIMG0177.JPG

Happy! Happy!

Happy Birthday to my better half! You always make me happy, Thank you for your love. Sweets are forever!

2011/02/09

ファイル暗号化/復号化 wrapper スクリプト

openssl (1) を実行してファイルを暗号化/復号化する処理を 簡易実行するための簡単な シェルスクリプトによる wrapper を作成した。
暗号化/復号化は openssl (1) の enc コマンドに 暗号化アルゴリズムに aes-128-cbc を指定して実施している。
指定されたファイルの先頭 8 バイトを参照して Salted__ の場合は暗号化されていると仮定し復号化、 それ以外は暗号化する処理をデフォルトで実行するが、 実行時オプション -d-e を指定する事で 強制的に暗号化/復号化を指定する事も可能だ。
-p オプションでパスワードを指定してバッチ処理も実行できるが、 コマンド実行履歴等にパスワードが残るのでお勧めできない。
暗号化/復号化が成功した場合は元のファイルを置き換える。

例によって必要に迫られれて適当に作成したスクリプトなので このスクリプトを利用した事で生じるあらゆる損害に対して責は負えない。

2011/02/07

HyperMac

アメリカの eBay オークションで落札した HyperMac キットが届いた。
HyperMac は MacBook などに電源を供給し充電も可能な外部バッテリーだが、 MagSafe という Apple が特許を持っているコネクタの利用に関する問題で 昨年 11月2日 をもって発売を中止してしまっていた。
現在は Apple 純正の Airline アダプタを経由する事で利用するタイプを 発売している様だが、 Airline アダプタとのコネクタが巨大で不格好、 しかも充電ができない等 HyperMac の特徴を全く活かせていないので却下。
当然新品は入手できないので国内外のオークションなどに 出品されるのを待っていたのだが、 今回 eBay オークションに 150Wh の HyperMac が出品されたので、 本当は一番小型の 60Wh のモノが欲しかったのだが この機会を逃すと又当面入手できない可能性もあると思い落札した。

Image: HyperMac.jpg

今となっては貴重になった HyperMac 付属の MagSafe ケーブル

eBay の出品者はアメリカ国内への発送しか受け付けていなかったので、 今回はアメリカ国内の転送業者を経由して日本に転送してもらった。 アメリカ国内への送料は出品者負担だったので、 転送業者による日本への転送送料+関税+手数料が余計にかかった費用だが、 転送業者の手数料は $7 だったのでそれほど悪くないだろう。

単語の羅列に近いほんのちょっとの英語力とPayPal アカウント、 それから多少の度胸があれば 日本では滅多に入手できないものを手に入れる事が出来てしまう とっても便利な世の中だ。

2011/02/01

Magic Mouse のスクロール

職場の iMac では Apple 純正の Magic Mouse を利用している。
BetterTouchTool と合わせて非常に便利に使っているのだが、 google map を利用している時などに不用意にスクロールしてしまい 敏感すぎるセンサーが却って邪魔になってしまう事がある。 Safari 用には google map を表示している時に Magic Mouse のスクロールをオフにするという機能拡張を見つけたが、 普段愛用している firefox には同じ機能を持つアドオンを探せなかった。 かといって firefox のアドオンは自作した事がないので勝手もわからない。
丁度、某有名匿名掲示板で環境設定パネルのユニバーサルアクセスを automater を利用して操作するという話題が出ていたので、 投稿されていたソースコードを参考にしながら簡単なスクリプトを作って見た。

automater を起動してアプリケーションのテンプレートを選択、 AppleScript を実行で表示されるエディタに以下のコードを貼り付けて、 アプリケーションとして保存すれば実行できる様になる。 実行するたびに Magic Mouse のスクロールオン・オフがトグルされるので、 ショートカットを Dock に登録したり Butler などのランチャから起動すると便利だと思う。

  1tell application "System Preferences"
  2    activate
  3    set current pane to pane "com.apple.preference.mouse"
  4end tell
  5
  6tell application "System Events"
  7    tell process "System Preferences"
  8        tell window 1
  9            tell group 1
 10                click checkbox "スクロール"
 11            end tell
 12        end tell
 13    end tell
 14end tell
 15
 16tell application "System Preferences" to quit
    

automater や AppleScript を書いたのは初めてで仕様も良く把握していないので、 無駄な処理やおかしな記述があるかも知れない。 おかしな部分があれば是非指摘して頂けるとありがたい。 なお、 個人的に適当に作ったスクリプトなので他の環境での動作は保証できないし、 勿論このスクリプトを利用した事で生じるあらゆる損害に対して責は負えない。

2011/01/31

丑やの革製 MacBook Air ケース

先日注文した丑やの革製 MacBook Air ケースが届いた。
専用設計で職人が手作りしているだけあって、 縫製も非常に丁寧な仕上がりだしサイズもピッタリで納めた時に良い感じ。
新品なので革が馴染んでいないのだが、 これから使い込むにつれてとても良い風合いになると思うので 今からとても楽しみだ。

Image: RIMG0150.JPG

こんな高価なケースなんて馬鹿げているとは思うが、 反面、Mac だからしょうがないかと(半ば自棄気味に)諦めてもいる。

2011/01/22

温泉ツーリング to 南伊豆

週末を利用して友人と南伊豆の温泉に一泊で ツーリング してきた。
勿論、バイクは反社会的な 2 ストローク 2 台(しつこい)。

まずは定番の東名高速海老名でお茶休憩。 日頃の行いの良さを象徴するかの様に気持ちよく晴れ渡った青空の向こうには 雪化粧した富士山もくっきり綺麗によく見える。 そのまま小田原厚木〜西湘バイパスを経由して海沿いの国道 135 号線へ。 1 月という事もあり、とりあえず往路は比較的温暖な海沿いを 南下するという軟弱コースを選択したので、 渋滞にもめげずにひたすら南下南下南下…。
東伊豆の海沿い、特に日頃の行いの良さを象徴するかの様な (大事な事なので2回…ry)天気の良い午前中は海面にキラキラと反射する 太陽の光がとても綺麗で抜群の眺望ではあるのだが、 単調すぎて若干物足りなさを感じてしまう部分は否めない。 まぁ、軟弱コースを主張した張本人なので文句は言うまい(言えない)。

昼食は稲取の「徳造丸」という網元料理のお店で海鮮丼を堪能。 普段は行列したり待ったりしてまで食事をする方ではないのだが、 ここは多少待ってでも食べる価値はあった。 生しらす、生桜エビ、生ぼたんえびなどがふんだんに載っている ここのお店の海鮮丼は量もおいしさも大満足。

Image: RIMG0116.JPG

徳造丸の海鮮丼

食事の後は再度単調な道を延々と南下…
イチ押しでお勧めされた外浦海岸にある「万宝」という干物屋さんでは干物を物色。 たしかにとても美味しそうな干物が沢山並んでいる。 バイクだと大きな荷物が運べないのだが、 宅配便で送ることにしたので美味しそうな干物を際限なく買ってしまう。

そうこうしているうちに無事に宿に到着。 古民家の宿というだけあって見た目も古びてて良い感じ

Image: RIMG0123.JPG

古民家の佇まいにそぐわない2台

予定よりは相当早く到着したので、 のんびりと露天風呂につかりながら疲れをほぐしていると食事の時間。
格安プランだったのであまり期待をしていなかったのだが、 とても美味しい夕食+オプションで金目鯛の煮付けに大満足 (というか食べ過ぎ)。 食事を撮影しようとカメラを持っていたのだが すっかり忘れて食事をむさぼり食ってしまったので写真なし。 まぁ、写真撮影を忘れる位に美味しかったという事で。
食事の後は温泉に行こうとか考えていたのだが、 満腹+良い感じの疲れ具合に動く気にもなれずに 9 時過ぎには就寝。
呆れる程早寝をしたので、 さすがに翌朝は6時頃には自然と目が覚め朝食前に温泉にのんびりつかる (のんびりし過ぎて若干湯あたり気味だったのは内緒)。

ビュッフェ形式の食事を堪能したらいざ出発。
昨日天気が良くて暖かかったのでついつい色気(?)を出して 復路は城ヶ崎から遠笠山経由で伊豆スカイラインを通る事にしたのだが、 路肩には雪が残っているわ、路面は濡れているわ、 日陰ではところどころ路面も凍結しているわで ワインディングを楽しむどころではなく山伏峠で下界に退散。 単調だろうと何だろうと暖かいのがエライと再認識。 勿論往路で軟弱な海沿いルートを主張した自分はやはりエライと(以下略)

Image: RIMG0136.JPG

伊豆スカイライン 亀石峠

熱海で昼食後は往路同様高速道路をひたすら走り無事に帰宅。
走行距離 350 km 程度なので 1 泊ツーリングとしては短い距離だが、 やはりこの時期はバイクで走るものじゃないと改めて実感した次第(軟弱もの)。 次の遠出は 4 月過ぎて暖かくなってからで良いな。

2011/01/21

MacBook Air に vtun 導入

某所で vtun を利用した vpn サーバを構築しているので、 MacBook Air に vtun クライアントを導入して vpn 接続出来る様にする。 vtun ver.3 は MacPort からも導入できるのだが、 諸処の事情により接続先のサーバが vtun ver.2 で運用されているので 今回は MacPort ではなく手作業にてインストールする事にする。

vtun に必要な圧縮関係の lzo ライブラリは MacPort でも導入できるのだが、 依存関係が多く古い perl までインストール要求するので 今回はこちらも手作業にて導入する。 vtun が依存しているのは lzo の 1.0 系なので、 LZO のオフィシャルサイト から lzo 1.0 系の最新版である lzo-1.8.tar.gz をダウンロードして展開し、 MacPort の流儀に合わせて --prefix=/opt/local を指定、 共有ライブラリを有効にするために --enable-shared を指定して configure を実行し make する。

$ ./configure --prefix=/opt/local --enable-shared
$ make
$ sudo make install clean
    

次に vtun ver.2 のソースを Sourceforge からダウンローし展開する。
vtun ver.2 のソースはそのままでは Mac 上ではコンパイルできないので 以下のパッチを適用する。

diff -cr vtun.orig/auth.c vtun/auth.c
*** vtun.orig/auth.c	2002-12-17 06:40:44.000000000 +0900
--- vtun/auth.c	2011-01-20 18:22:49.000000000 +0900
***************
*** 66,73 ****
  #include <openssl/blowfish.h>
  #include <openssl/rand.h>
  #else /* YAY - We're MAC OS */
! #include <sys/md5.h>
! #include <crypto/blowfish.h>
  #endif  /* __APPLE_CC__ */
  
  void gen_chal(char *buf)
--- 66,73 ----
  #include <openssl/blowfish.h>
  #include <openssl/rand.h>
  #else /* YAY - We're MAC OS */
! #include <openssl/md5.h>
! #include <openssl/blowfish.h>
  #endif  /* __APPLE_CC__ */
  
  void gen_chal(char *buf)
diff -cr vtun.orig/lfd_encrypt.c vtun/lfd_encrypt.c
*** vtun.orig/lfd_encrypt.c	2002-12-17 06:40:46.000000000 +0900
--- vtun/lfd_encrypt.c	2011-01-20 18:23:21.000000000 +0900
***************
*** 53,60 ****
  #include <openssl/md5.h>
  #include <openssl/blowfish.h>
  #else /* YAY - We're MAC OS */
! #include <sys/md5.h>
! #include <crypto/blowfish.h>
  #endif  /* __APPLE_CC__ */
  
  #define ENC_BUF_SIZE VTUN_FRAME_SIZE + 16 
--- 53,60 ----
  #include <openssl/md5.h>
  #include <openssl/blowfish.h>
  #else /* YAY - We're MAC OS */
! #include <openssl/md5.h>
! #include <openssl/blowfish.h>
  #endif  /* __APPLE_CC__ */
  
  #define ENC_BUF_SIZE VTUN_FRAME_SIZE + 16 
  	
パッチ適用後、これも --prefix=/opt/local を指定、 導入した lzo 関係のパスを指定するために --with-lzo-headers--with-lzo-lib をそれぞれ指定して configure を実行し make する。

$ ./configure --prefix=/opt/local --with-lzo-include=/opt/local/include --with-lzo-lib=/opt/local/lib
$ make
$ sudo make install clean
    

これで vtun はインストールできたが、 Mac OS X は標準では vtun が利用するトンネルデバイス /dev/tunX が存在しないので Sourceforge から Mac OS X 用の TUN/TAP ドライバをダウンロードしてインストールする。
インストール後にシステムを再起動するか /Library/Extensions/tun.kext をロードする事で /dev/tunX が生成されるので、 /opt/local/sbin/vtund を実行して vpn 接続できるか確認する。

$ sudo kextload /Library/Extensions/tun.kext
$ sudo /opt/local/sbin/vtund -f /opt/local/etc/vtund.conf ホスト サーバアドレス
$ ifconfig tunX
tunX: flags=8851<UP,POINTOPOINT,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    inet XXX.XXX.XXX.XXX --> YYY.YYY.YYY.YYY netmask 0xff000000 
    open (pid PPP)
    

vpn 接続ができればこの様なスクリプトを作成して root 権限で実行すると vpn 接続の開始・終了が出来る様になる。

  1#!/bin/sh
  2base="/opt/local"
  3
  4vtund="${base}/sbin/vtund"
  5conf="${base}/etc/vtund.conf"
  6opt="ホスト サーバアドレス
  7
  8start()
  9{
 10
 11    test -x ${vtund} -a -f ${conf} && ${vtund} -f ${conf} ${opt}
 12
 13}
 14
 15stop()
 16{
 17
 18    ps ax | sed -n '/[v]tund/s/ *\([0-9]*\) .*/\1/p' | xargs kill
 19
 20}
 21
 22case $1 in
 23    start )
 24        start;;
 25    stop )
 26        stop;;
 27    restart )
 28        stop && start;;
 29    * )
 30        echo "Usage: ${0##*/} [start][stop][restart]" 1>&2
 31        exit 255;;
 32esac
    

2011/01/20

Esperance DV のメモリディスクで firefox の起動を高速化

Esperance DV を利用して Mac 上にメモリディスクを作成している。
最初は firefox のキャッシュをメモリディスク上に格納していたのだが、 プロファイル情報を全てメモリディスクに格納したら起動が非常に速くなった。
firefox のキャッシュデータとプロファイルデータを メモリディスク上に格納するために、 元のディレクトリをメモリディスク上に移動してから、 本来のディレクトリにシンボリックリンクしている。

$ mkdir /Volumes/RamDisk/Application\ Support
$ mv $HOME/Library/Application\ Support/Firefox /Volumes/RamDisk/Application\ Support/.
$ ln -s /Volumes/RamDisk/Application\ Support/Firefox $HOME/Application\ Support
$ mkdir /Volumes/RamDisk/Cache
$ mv $HOME/Library/Cache/Firefox /Volumes/RamDisk/Cache/.
$ ln -s /Volumes/RamDisc/Cache/Firefox $HOME/Library/Cache
    

ただし firefox は多くのプラグインや GreaseMonkey スクリプト等で カスタマイズしてあるので、 何か問題があった場合にメモリディスク上のプロファイルデータが 全て消えてしまうと環境の再構築が非常に面倒になる。 そこで rsync (1) を実行するスクリプトを cron に登録して、 自動的にメモリディスク上のプロファイルデータを 通常のディスクに同期コピーする事で メモリディスク上のプロファイルデータが消えてしまっても 最悪でも1日前までの状態には戻れる様にして運用している。
TimeMachine でバックアップは取得しているのだが、 TimeMachine のシンボリックリンクの扱いが不明なので 安全策として独自に同期コピーを実施している。

  1#!/bin/sh
  2
  3src="${HOME}/Library/Application Support/Firefox/"
  4dst="同期データのバックアップディレクトリ"
  5
  6/usr/bin/rsync -a --delete "${src}" ${dst}"
    

ちなみに Esperance DV を勝手に日本語化してみたのだが、 作者に連絡が撮れないのでひっそりと 公開
インストールされた EsperanceDV.prefPane の中の Resources ディレクトリ (標準だと $HOME/Library/PreferencePanes/EsperanceDV.prefPane/Contents/Resources)に 展開した Japanese.lproj をコピーすると、 「システム環境設定」の 「Esperance DV」の設定画面が日本語化される。

この日本語化は私が勝手に日本語化しただけであり、 オリジナルの作者に許諾を取っていませんので、 日本語化部分に関してはオリジナルの作者に絶対に問い合わせないで下さい。

2011/01/17

シェルスクリプトで疑似乱数を取得する

gnu bash には ${RANDOM} というシェル変数が用意されていて、 アクセスするたびに疑似乱数(風)の値が取得できる様だ。 しかし posix 準拠の機能ではなく bash 独自の機能なので拡張性がない。 そこで汎用的な疑似乱数取得の方法として awk (1) を利用した方法を使ってみる。

awk (1) には rand() という関数が組込まれているので 乱数生成ができる。 その際に利用される種(seed)は srand() という関数で初期化できるので、 これらを利用する事でそこそこの精度の疑似乱数は取得できる。
たとえば 0 〜 m までの疑似乱数は 取得した値から m の剰余を取ることで取得できる。
ただし awk (1) の rand() は仕様として 0 〜 1 までの桁数不定の少数を返すので、 乱数として利用する場合には整数に変換する必要がでてくる。
整数にするために 10n を乗すればよいのだが、 小数点以下の桁数が不定のために小さすぎる n を乗すると 全ての桁が整数化されずに乱数の精度が低くなってしまい、 逆に大きすぎる n を乗すると値が xxx000 の様になってしまい 剰余を取得しても無意味になってしまう。

そこで awk (1) により疑似乱数を取得する場合は rand() で取得した値の前 2 文字 ("0.") を文字列として除外した値から m の剰余とする事で 0 〜 m までの乱数が取得できる。

  1#!/bin/sh
  2
  3awk 'BEGIN{
  4    srand();
  5    print substr(rand(), 3) % '${1:-1}'
  6}'
    

データの型が存在しない(文字列であり数字である) awk (1) ならではの裏技とも言えるだろう。

2011/01/14

Mobee The Magic Charger

職場の iMac 27″ モデルでは Apple 社の Magic Mouse を利用している。
Magic Mouse は標準状態ではそれほど素晴らしい機能を持ってないが、 サードパーティ製の機能拡張ソフトウェアと併用すると 非常に素晴らしい性能を発揮してくれるマウスであり、 慣れると他のマウスを利用するのが苦痛になる程便利だ。
そんな Magic Mouse なのだが唯一の欠点は電池の持ちが悪い事で、 普通に利用していると一ヶ月保つかどうかという程度である。
しょうがないので eneloop を使っていたのだが、 Mobee The Magic Charger を見つけたので早速導入してみた。

Image: RIMG0093.JPG

無接触充電中

この Mobee The Magic Charger は Magic Mouse 用の専用設計で、 裏蓋と一体になった専用の充電池を Magic Mouse にセットして 専用の充電台の上に載せておくだけで充電ができるという優れもの。
充電台は本体の USB から給電されるので電源も必要なく、 ただ載せておくだけで無接点で充電できるので非常に便利だ。
専用の充電池は電池よりも 10g 程軽量化されているそうなので、 Magic Mouse を持った感じも軽くなり気持ち使いやすくなった気がする。
職場の iMac は 24 時間稼働しているので、 帰宅時に充電台の上に載せておけば普段から充電できて良い感じ。

今の充電台方式だと使っていない時に「意識して充電台に置く」必要があるので、 次はマウスパッドとインテグレートされた充電台が出来ると嬉しい。
そうすれば普段使っている間も充電されているので、 本当に「充電」に意識を向ける必要がなくなって便利な事この上ないと思うのだが。

2011/01/12

docomo N-05B + MacBook Air でデータ通信

昨年末に携帯電話が故障してしまったので機種変更をした。
最新モデルではないのだが随分と新しいモデルに変更したのだが、 携帯電話に予めインストールされているアプリケイションが楽しそうなので、 初めてパケット通信の定額制割引パックに加入してみた。 色々と調べてみると携帯電話をコンピュータに接続してデータ通信を行っても パケット通信の定額制割引の対象となる事が判明したので、 MacBook Air のモバイル通信環境を構築してみたのだが、 なかなか一筋縄ではいかなかった…はぁ

まずは docomo の Web サイトの作りが最悪で、 必要な情報に素直にたどり着けない。
情報は一カ所に集約されておらず無意味に分散されているので 情報を見つける事が非常に困難で網羅的に情報を収集できない。 携帯電話とコンピュータを接続してデータ通信を行うために、 どの様なパケット通信の割引システムに加入すれば良いのか、 どの様な接続方式で接続すれば良いのか、 どの様なプロバイダを選択すれば良いのかといった情報を 探し出すだけで一苦労であり、 情報にたどり着いても今度は定義が不明確な 独自用語の羅列で何を言っているのか判らない。

結局「パケ・ホーダイ ダブル」に加入して「128K通信」で 対応プロバイダ「mopera U」に接続すると 受信最大 128kbps/送信最大 64kbps という石器時代の様な速度ではあるが パケット定額制割引の対象額の範囲内で通信可能だという事が判明した。
「パケ・ホーダイ ダブル」や「mopera U」の申し込みはオンラインで可能なので、 早速申し込んでみるとコレは簡単に申し込みができた。

次に MacBook Air からの接続なのだが、これも一筋縄ではいかなかった。
docomo の Web サイトによると、 手持ちの「FOMA 充電機能付 USB 接続ケーブル 02」を 利用した接続は対応していると明記してある。 そこで mopera U の Web サイトの説明通り「128K通信」を行うための 「ドコモ コネクションマネージャ for Mac」をインストールするが、 「ドコモ コネクションマネージャ for Mac」は 接続した携帯電話を全く認識していない。 調べてみると「ドコモ コネクションマネージャ for Mac」が対応しているのは ごくごく限られた携帯電話のみであり今回の機種には対応していなかった。
「FOMA 充電機能付 USB 接続ケーブル 02」を利用した接続に対応しているのは、 どうやら Microsoft Windows 系の OS のみの様である (どこにも明記されていないのであくまでも想像ではあるが)。

しょうがないので BootCamp からWindows Xp を起動して 「ドコモ コネクションマネージャ」をインストール、 「FOMA 充電機能付 USB 接続ケーブル 02」を利用して携帯電話を接続すると ちゃんと携帯電話を認識して接続までできたので、 APN 情報や CID 番号などの情報を取得した。

MacBook Air と携帯端末との接続は 予定していた「ドコモ コネクションマネージャ for Mac」が使えないので、 REUDO 社が販売している「FOMA モデムドライバー」をダウンロード購入して APN 情報や CID 番号などを手動で設定する事で何とか接続できた。

ちなみに MacBook Air に REUDO 製の FOMA モデムドライバーを利用して接続した docomo N-05B で mopera U に 128K通信するための設定。

システム環境設定」の「ネットワーク」から 「FOMA N05B」を選択して以下の設定を行う。

構成: デフォルト
電話番号: 空欄 利用されないので空欄で可
アカウント名: 任意の文字列 利用されないのだが入力のみは必須
パスワード: 任意の文字列 利用されないのだが入力のみは必須

次に「詳細...」ボタンをクリックして表示される「モデム」タブに 以下の項目を設定する。

製造元: REUDO
機種: FOMA GPRS (GSM/3G) for IP
APN: mpr.ex-pkt.net
CID: 4

APN や CID は携帯電話によっては異なる可能性があり、 間違えるとパケット定額制割引の対象とはならずに 高額なパケット代の請求となる危険性があるので注意しなければならない。
そもそもこの設定で「パケホーダイ ダブル」の適用対象となる 「128K通信」となっているのか不明なのだ。

2011/01/06

htaccess で特定のファイルのみパスワードを要求しない

Web コンテンツを保護するために .htaccess ファイルを用いて Basic 認証などを実施する場合は多い。
通常 .htaccess はディレクトリ単位で有効になってしまうのだが、 あるディレクトリのアクセスを Basic 認証でアクセス制御したいが 特定のファイルについては認証を要求しない設定を行いたい場合は Files ディレクティブと Satisfy ディレクティブで実現できる。

AuthType        Basic
AuthUserFile    認証用ファイル
AuthName        "Enter password"
Require         valid-user
<Files "認証要求しないファイル">
    Satisfy     Any
    Allow       from all
</Files>
	

Satisfy ディレクティブは Allow ディレクティブと Require ディレクティブがどちらも使われている場合の アクセスポリシーを制御する。
Any が指定された場合は AllowRequire のどちらかの条件を満たせばアクセスが許可されるので、 Files ディレクティブで指定したファイルに対しては Allow 指定が有効になり認証なしでアクセスが許可される。

逆にある特定のファイルだけの Basic 認証によるアクセス制御は Files ディレクティブのみで可能である。

AuthType        Basic
AuthUserFile    認証用ファイル
AuthName        "Enter password"
<Files "認証要求するファイル">
    Require     valid-user
</Files>
	

どちらの場合も Files ディレクティブは以下の様に ~ を使う事でファイル名を正規表現で記述できる。

<Files ~ "\.(gif|jpe?g|png)$">
	:
</Files>
	
apache では PCRE - Perl Compatible Regular Expressions を 利用しているので複雑な正規表現の記述ができる様だ。


Copyright © Mitzyuki IMAIZUMI 2008,2009. All rights reserved.