仙石浩明の日記

2007年3月9日

コミュニケーション能力の育成を第一とする教育が格差社会を救う hatena_b

某大学情報系学部が主催した、企業と学生のマッチングイベントに参加させて頂いた。 その学部では、 コミュニケーション能力の育成を最優先に考えているとのこと。 多くの企業は (情報系の) 新卒の学生さんに技術力よりは コミュニケーション能力を求めているというから、 そのニーズに答える形でこのような教育が行なわれるようになったのだろう。

だから責められるべきは大学側ではなく、企業側である。 なぜ情報系の卒業生に、 コンピュータ技術者としての素養ではなく、 コミュニケーション能力を第一に求めてしまうのだろう?

このイベントには、学生さん達による研究発表 (パネル展示) もあったのだが、 研究内容そのものよりも、 いかに分かりやすく説明するか、 さらにいえば、聞きに来た人にいかに満足してもらうか (ひらたく言えば、いかに「わかったつもり」にさせるか) に 重点を置いているという。 企業側参加者に配られたアンケート用紙は、 学生達の対応マナーの良し悪しや、 しゃべり方の巧拙の評価を問う項目ばかりが目立つものだった。 まるでファミレスやスーパーに置いてある「お客様アンケート」のようだったと 言ったら信じてもらえるだろうか?

もちろん私としては、 プレゼンの巧拙なんかには興味がなく、 まして「分かったつもり」状態を何よりも嫌い、 研究内容そのもの (特に発表者その人が研究活動を通して具体的に何を考えたか) に 興味があるわけで、 接客マナーの向上に腐心していた学生さん達には気の毒なのだが、 パネルに書いてあることそっちのけで質問しまくってしまった。

- o -

学生のうちにコミュニケーション能力を身につけていれば、 たしかに就職したときに会社に慣れるのも早いし、 与えられた仕事を要領よくこなすことができるだろう。 技術にのめり込んで人付き合いが下手なまま社会人になるより、 よっぽど明るい未来が待っていそうである。

そもそも技術や学問やその他、フツーの人が興味を持たないようなことに 熱中すること自体が不幸の始まりである。 ちかごろは「ヲタク」が表舞台に登場することも多くなったわけであるが、 富と名声を獲得した「ヲタク」は正に氷山の一角であって、 彼らの成功は累々と築かれた屍の上の砂上の楼閣であることを忘れてはならない。

他の人と違うことに興味を持てば、 当然フツーの人が楽しんでいる娯楽では満足できなくなる。 これはとても大きな不幸である。 新たらしモノ好き、今風の言葉で言えば アーリーアドプタ (early adopter) はとても損をする。 もう少し普及するのを待っていれば安く買えるのに、 どうしてわざわざ値段が高いときに新製品を買うのだろう? 一つ一つの支払額の増分は大したことがないかもしれないが、 塵も積もれば巨額になる。 パソコンの黎明期は、パソコン関連にウン百万円投資した、 などという話は普通に耳にしたが、 今ではパソコン関連に二十万円以上費やすことの方が難しい (しかも何十年も前のウン百万円と言えば今だとその十倍以上の価値がある)。

変わり者が損するのは、物を買うときだけではない。 そもそも社会全体に対するサービスが、 多数派のためのものである。 ロングテールを狙ったサービスなんてのは嘘っぱちで、 ほとんどのサービスは 20% 以下の少数派のことは切り捨てている。 テレビ放送に至っては、 全人口のせいぜい 50% くらいのニーズしか満たしていないはずだが、 どれだけのカネと貴重な電波資源がテレビ放送網の構築に費やされているか 考えてみたことがあるだろうか? 民主主義の原則は「最大多数の最大幸福」である。 多数派に属さない人が切り捨てられて損をするのは当然だろう。 テレビなんて低俗だから見る気がしない、 なんて平然と言っている人の気が知れない。 他の人と同じように振る舞わなければ損をするのである。

血気盛んな青年期を過ぎれば、新しいことには興味を持ちにくくなる。 若いうちにコミュニケーション能力を身につけて、 大勢の人と馴染めるようになっておけば、 フツーの人が手を出さないようなマニアックなことに 興味を持ってしまうリスクは避けられる。 他の人と同じように考え、同じようなことに興味を持ち、 同じような娯楽を楽しむことができれば、 どんなに幸せなことか。

もちろん弊害もある。 新しいことを始める人が現われなければ、当然進歩も止まる。 しかし、世の中こんなに便利になったのだから、 これ以上何を進歩させる必要があるというのだろう? どんなことにも弊害は付き物である。 最大幸福のためには犠牲にしなければならないこともある。

それに、とっぴなことを始める人がいなければ、 誰もがフツーに働いてフツーに労働時間に見合った報酬を受取るだけの社会になる。 労働時間では測りきれない価値を 唐突に生み出す人がいるから富が一部の人に集まって 格差が生まれるのであって、 だれもが平凡な生活をするに留まっていれば格差社会など生まれるはずがない。 みんなが同じように考え、同じように行動し、同じように幸福な画一的な社会、 それこそが格差が無いみんながハッピーな理想社会である。

みんなが同じことをしていては進化がないと言い張る人がいるかもしれないが、 そもそも「進化」とは何だろう? 人間の大脳皮質はなぜ他の動物に比べて極端に大きいのだろう? 科学技術を発展させるため? 決してそんなことはない。 大脳皮質の容量だけを考えれば、縄文時代も現代もさして差はないのである。

なぜ人間の大脳皮質はこんなに大きいのか? それはコミュニケーションのためである。 複雑・高度化した社会 (原始人の社会にも複雑な人間関係があったと考えられているし、 集団生活を営む哺乳類の多くには様々な複雑な行動様式が観察される) をコミュニケーション能力を駆使して要領よく立ち回り、 より多くの子孫を残せた個体のみが遺伝子を次世代に伝えることができる。 平たく言えば、集団の中で異性に「モテる」方向へ、進化圧が働くのである。 人間を人間たらしめる本質はコミュニケーション能力にある。

一口にコミュニケーション能力というが、 情報処理能力としてみると驚異的な能力である。 相手の顔色を素早く読んで、その場の空気に最適な話題を振り、 わずかな声色の変化から相手の思考を読み取る。 我々はほとんど意識すること無く、 相手のわずかな目の動きやしぐさを読み取って コミュニケーションを行なっているが、 「意識せずに行なえる」から大したこと無いと思っては大間違いである。 下らない話に終始する井戸端会議ですら、 現在のコンピュータでは足下にも及ばない超高度な情報処理の賜物なのである。

それが証拠に、 コミュニケーション能力の欠如が、 超人的な能力の開花につながることがある。 つまり本来ならコミュニケーションという高度な情報処理をこなすために あったはずの大脳皮質の一部が、 別の目的に使われてしまうために発症する知的障害の一形態である。

サヴァン症候群(savant(仏語で「賢人」の意) syndrome)とは、 知的障害を伴う自閉症のうち、ごく特定の分野に限って、 常人には及びもつかない能力を発揮する者を指す。 サヴァン症候群の共通点として、 知的障害と共に異常といえるほどの驚異的な記憶力・表現力を持つことが挙げられる。

天才の多くに、 コミュニケーションに関して大なり小なりの難があるのは偶然ではない。 コミュニケーションという超高度な情報処理能力の一部を放棄することによって 確保した「脳の空き容量」を使って、 常人には成し得ない偉業が達成されているのである。 そして忘れてはいけないのは、 天才の多くが不幸なまま一生を終えている点である。 死んだ後に全世界の尊敬を一身に受けたところで後の祭りである。

若いうちからコミュニケーション能力を磨き、 大多数の人が興味を持たないようなことには決して手を出さず、 フツーの人と同じことに考え、同じように振る舞うことこそ、 幸せな一生への最短経路であり、 大多数の人にこのような幸せな人生をおくらせるような教育を行なうことこそが、 格差のない平和な社会への唯一の道であろう。

- o -

大変遺憾なことではあるのだが、 私自身は中学生の時に当時まだ大変珍しかったパソコンに出会ってしまった。 パソコンにのめり込んでいったために、 自身のコミュニケーション能力を伸ばす努力を怠ったのは、 目新しいものに惹かれる若さゆえのあやまちだったと認めざるを得ない。 当時パソコンに興味を持った人は全人口の 0.01% にも満たなかったはずで、 その後マイナー路線を突っ走ってしまった。 通った中学校に、 たまたま「マイコン部」があった不幸な偶然に感謝したい。

(続く)

Filed under: 技術者の成長 — hiroaki_sengoku @ 07:37
2007年2月19日

キャリアにおける「鶏と卵」 ── 「卵」を後回しにして欲しくない hatena_b

私は「やりたいことを仕事にすべき」と常日頃から主張しているのですが、 自分自身のことを振返ると、 必ずしも「やりたいこと」そのものズバリを 最初からやっていたわけではないことに気づきます。

「鶏がさきか、卵がさきかの議論になっちゃうんだけど」なんて枕詞があるが、 こと20代のキャリアにおいては 「まず目の前の仕事で最高の成果をだすことが"さき"で、 自分がやりたいと思う仕事をおっかけるのは"あと"」というのは 私の中では確信に近い。

確かに、何をやるにしても最初は素人であるはずで、 ずぶの素人が「この仕事をやりたい」と言ったところで、 任せてもらえることはレアケースでしょう。 仮に任せてもらえたとしても、 初めてやる仕事で成果を出せるとは限りません。

私は学校を卒業後、某大企業の研究所に就職し、 遺伝的アルゴリズムの研究に取組みました。 当時は (今も?) 大企業の研究所というのは人気職種で、 今から思えば「やりたいこと」というよりは 「人気の職種だから」やってみたいという思いの方が 強かったような気がします。

もちろん嫌々仕事 (研究) をしていたわけではないのですが、 仕事の合間を見つけては、 本業そっちのけで職場のイントラネット環境の整備に没頭してしまいました。 当時はまだ「イントラネット」という言葉すら生まれていない時代で、 何をするにもいちいち不便なネットワーク環境だったので、 いろいろ改善しようと思ったわけですが、 次第にネットワークの方が面白く感じるようになってしまいました。

入社当時に配属された部署というのは正直私があまり関心がある仕事ではなかったが、 当時はまだ"ひよっこ"という意識が強かったので、 とにかく結果をだすことに専念をした。
(中略)
どちらかというと目の前にある仕事をこなし、 そこで結果をだすのが精一杯で、 自分の関心が本当にどこにあるのかということを真剣に考える余裕も正直なく、 とにかくアサインされたプロジェクトで結果をだすことにがむしゃらになった。
同ページから引用

私の場合、配属された部署というのは関心がある仕事だったのですが、 もっと関心のある分野を見つけてしまったため、 本業 (目の前にある仕事) がおろそかになった、という感じでしょうか。 もしあのとき、 「アサインされた仕事で結果をだすことにがむしゃらになって」いたら、 本当に自分に向いていることを見つけ損なっていたのかも知れません。

確かに、他に熱中できることがなければ、 目の前の「本業」にがむしゃらになって取組むことも重要だとは思うのですが、 自分が本当は何に向いているのか、 いろいろ「かじってみる」余裕は持っていて欲しいと思うのです。

プロ野球選手を目指す子供に対して、 「野球を頑張るのはいいけど勉強も忘れずにね」というような忠告なら有用だと思う。 だけど、「プロ野球選手が本当の自分のゴールかどうかなんてわからないんだから、 まずは(アサインされた)目の前のテストで100点を取りなさい。 野球をするのはそれからだ」とは言わないでしょう?

本業以外に熱中できる新分野を見つけた部下に対し、 「新分野を頑張るのはいいけど本業も忘れずにね」という忠告はするけど、 その新分野への取組みも大いに応援する、 KLab(株)はそんな会社でありたいと思っています。 勤務時間の 10% は本業以外のことを好き勝手にやっていい、 もし見込みが出てきて周囲から認められるレベルになったら、 それを本業にしてしまってもいい、という「どぶろく制度」を作ったのも、 熱中できることを見つけるチャンスを逃して欲しくない、という思いからです。

キャリアは偶然によって作られるものだと私も思う。 なので、その偶然をどう活かすかというのは非常に大切になってくる。 でも「偶然のレベル」と「自分のレベル」は等価なので、 偶然を活かす良いスパイラルを生み出すためには、 まず自分のレベルを上げないと始まらない。 なので、今ある仕事の中で自分のレベルを上げることを第一に考えるべきです。

確かにその通りなのですが、 「偶然のレベル」を引き上げることは、 個人一人一人が自身のレベルを引き上げることによってだけでなく、 「偶然」を起りやすくする環境を会社が整えることによっても可能だと思いますし、 それこそが技術者のための技術会社の存在意義なのではないかと思います。

Filed under: 元CTO の日記,技術者の成長 — hiroaki_sengoku @ 09:46
2007年2月9日

組織を強くする技術の伝え方 hatena_b

ふと立ち寄った本屋で、たまたま手にした本:

プログラミングでメシが食えるか!?
―成功するプログラマーの技術と仕事術 (単行本)
小俣 光之 (著)

を読んでみて驚いた。 私がプログラミングについて漠然と考えていたことを、 とてもよく整理した形で説明している。 ふだん私はこの手のコンピュータ関連「読み物」をほとんど読まないのだが、 書いてあることにいちいち共感してしまって、 そのまま一気に読んでしまった。

プログラミングに関して、ここまで私と考えが似通っている本を 今まで読んだことがなかったので、 著者の小俣さんにメールを送ったところ程無く返信があり、 直接お会いして色々お話することができた

メールの中で小俣さん曰く、 「本書は批判的なコメントが多い中、共感いただけて本当にうれしいです」。 例えば Amazon のカスタマーレビューには、

内容が古すぎます, 2007/1/29
この本の前半はプログラミングスキルの説明をしているのですが、 いかんせん内容が古すぎます。
(中略)
それと本書では複数の開発言語を勉強することを 時間のムダであるかのように書かれていますが、これは大きな誤りです。 優れたプログラムを書くには多くのプログラミング言語を理解し、 その言語に合わせたプログラミングを行なわなければなりません。
タイトルに惹かれて本書を買いましたがその答えはどこにも書かれていませんでした。 まさか最後の章に書かれている「固有技術を磨く」が答えなんでしょうか? 1つの技術に固執すると、その技術が使われなくなったタイミングでメシが食えなくなります。
全体的にがっかりな内容です。

という批判が載っている。 まあ、誰しも自分が考えたいようにしか考えないものだし、 自身の考えが否定されるような本に対しては、 強く反発するのも仕方がないところだと思う。 私自身、私のブログに対し次のような批判的なコメントをもらったことがある。

3. Posted by 20代練習生    2006年07月07日 06:37
こういう記事は老害でしかないと思うんですが。
(中略)
昨今の情報が過剰な状況では、筋道立てて何かを学ぶなんて不可能です。 というか系統立てて学ぶなんて愚かなことです。
あらゆる分野でコミュニティが確立されていて、 何か疑問や問題点があってもそこへポストすれば即座に解決可能です。
こういう時代では、芯の通った骨太で系統的な知識よりも、 断片的で、それ自体では応用も利かないようなぽつぽつとした知識を 多くもっていることの方が価値があると思います。

「優れたプログラムを書くには多くのプログラミング言語を理解し、 その言語に合わせたプログラミングを行なわなければならない」とか、 「断片的な知識を多くもっている方が価値がある」などと思い込んでいる人に、 「その考え方は間違っている」なんて言ってもよりいっそう反発されるだけだし、 そもそも道を誤ったエンジニアを救うよりは、 将来伸びる素質を持ったエンジニアを育てる方が楽しいわけで、 間違った考え方を正してやる義理はないとは思う。

しかしながら、間違いを正してやることにより 伸びる可能性が出てくる人であれば話は別である。 また、その可能性が出てくる人というのが 自分の部下だったり一緒に仕事をする仲間だったりすればなおさらだろう。 というわけで、

組織を強くする技術の伝え方 (新書)
畑村 洋太郎 (著)

を読んだ。「技術は時代とともにダイナミックに変化するが、 その本質部分がきちんと伝わらないと、 大きな変化に対応ができない。 だからこそ『技術を伝える』ことについて徹底的に考え尽すことが必要」と 本書は説く。大変感銘を受けた。特に

技術というのは本来、「伝えるもの」ではなく「伝わるもの」なのです。
(中略)
伝える側が最も力を注ぐべきことは、 伝える側の立場で考えた「伝える方法」を充実させることではありません。 本当に大切なのは、 伝えられる相手の側の立場で考えた「伝わる状態」をいかにつくるかなのです。
第2章 伝えることの誤解 53ページから引用

は、「技術を伝える」ことの本質を見事に言い表しているように思う。 間違った考え方を正してやるには、 まず相手の頭の中に「伝わる状態」をつくらねばならない。

何かを若い人たちに伝えようとしたときに、 「内容が古すぎる」とか「老害」だとか反発する人はいつの時代にもいる。 もちろん古くなって伝えるに値しなくなるものもあるが、 たとえ日進月歩のコンピュータ/インターネットの世界であっても 不易普遍なものはある。 そういったものを「古い」という理由だけで学ぶに値しないと思い込んでいる輩は、 畑村氏が言うところの「偽ベテラン」ないし「偽ベテラン予備軍」なのであろう。

長年やっていれば、誰でもそれなりの技術を習得できます。 極端なことを言うと、 どんなに能力がない人でも そのときの自分の状態にあった程度のことを実践していけば、 その積み重ねの中でやがてはなんらかの技術を習得することができます。
しかし、このようなものは本来、技術の伝達とはいえません。 これを技術の習得というのも不適切で、 ただ単に技術に慣れただけというのが正確な言い方でしょう。
じつはこのように、 経験と慣れだけで技術を獲得してきた人は世の中にたくさんいます。 私はこういう人を「偽ベテラン」と呼んでいます
終章 技術の伝達と個人の成長 170ページから引用

どんどん移り変わる表層的な技術や、 応用は効かないが当座の役には立つ断片的な知識は、 検索一発で探し当てられる便利な世の中だからこそ、 「技術の伝達」の重要性はますます高まっているのだと思う。

Filed under: 技術者の成長 — hiroaki_sengoku @ 07:17
2007年1月22日

Perl の非同期I/Oモジュール POE を使って VPN-Warp relayagent を書いてみました hatena_b

多数の TCP/IP セッションを同時に維持する必要性などから、 非同期I/O が最近流行りのようです。 何をいまさら、という気もするのですが、 いわゆる「最新技術」の多くが 30年前の技術の焼き直しに過ぎない今日このごろなので、 非同期I/O 技術が「再発見」されるのも、 「歴史は繰り返す」の一環なのでしょう。 スレッドが当たり前の時代になってからコンピュータ技術を学んだ人にとっては、 (古めかしい) 非同期I/O が新鮮に映るのかも知れず、 なんだか「ファッションのリバイバル」に似ていますね。

Perl で非同期I/O 処理を手軽に行なうための枠組みとして、 POE: Perl Object Environment というものが あるようです。 POE を使うと、 あたかもスレッドを使っているような手軽さでプログラミングできます。 試しに VPN-Warp の relayagent を POE を使って書いてみました。 オリジナルの relayagent は C 言語で記述した 4000 行を超える プログラムなのですが、 Perl だと 200 行以下で一通り動くものが書けてしまいました (もちろん C 版の機能を全て実装したわけではありません)。

POE を触るのは今回が初めてだったので、 マニュアルをいちいち参照しながら書いたのですが、 なにせわずか 200 行ですから、 開発はデバッグ込みで 1 日かかりませんでした。 改めて Perl の記述性の良さと開発効率の高さに感動したのですが、 これだけ簡潔に書けてしまうと、 relayagent の機能を解説するときの教材としても使えそうです。

というわけで、 今までブラックボックスだった relayagent の中身の解説を試みたいと思います。 これから POE を使ってみようとする人の参考にもなれば幸いです。

VPN-Warp の relayagent とは、 以下の図のようにリレーサーバと Webサーバの両方へ接続して、 リレーサーバから受取ったリクエストを Webサーバへ中継するプログラムです。 http リクエストを受取ってサービスを行なうのですから、 サーバの一種と言えますが、 外部から接続を受付けるわけではなく、 リレーサーバと Webサーバの両方に対してクライアントとして振る舞う点が ユニークと言えるでしょう。

                      リレー            イントラ         イントラ
ブラウザ ─────→ サーバ ←──── relayagent──→ Webサーバ
            https     443番ポート                        80番ポート

http リクエストを受取って Webサーバへ中継するプログラムというと、 proxy サーバを思い浮かべるかも知れません。 proxy サーバはその名の通り、 ブラウザに対してはサーバとして振る舞います:

                                        proxy            イントラ
ブラウザ ──────────────→ サーバ────→ Webサーバ
                                        8080番ポート     80番ポート

proxy サーバが、ブラウザからの接続を受付けて、 それを Webサーバに中継するのに対し、 relayagent は自身では接続を受付けずに中継する、 という違いがお分かりでしょうか? relayagent は接続を受ける必要がないため、 ファイアウォールの内側など、 外部からアクセスできない場所で使うことが可能になっています。

なお、C 版の relayagent はリレーサーバに対して https で接続するのですが、 Perl 版 relayagent (以下 relayagent.pl) は、 説明の都合上 SSL 暗号化の機能を含んでいません。 実際に使うときは、 stone などで SSL 暗号化して リレーサーバに接続する必要があります。

         リレー                         イントラ         イントラ
         サーバ ←──── stone ←── relayagent──→ Webサーバ
         443番ポート       SSL化        Perl 版          80番ポート

例えば stone を

stone -q pfx=relay,5000005.pfx \
      -q passfile=relay,5000005-pass.txt \
      warp.klab.org:443/ssl localhost:12345 &

などと実行しておき、 relayagent.pl はリレーサーバに接続する代わりに、 localhost の 12345 番に接続します。

では、relayagent.pl を順に見ていきましょう。

#!/usr/bin/perl
use POE qw(Component::Client::TCP Filter::Stream);
my $IdleTimerMax = 6;        # 60 sec
&help unless @ARGV == 2;
&help unless shift =~ m/^(\w+):(\d+)$/;
my ($RelayHost, $RelayPort) = ($1, $2);
&help unless shift =~ m/^(\w+):(\d+)$/;
my ($WebHost, $WebPort) = ($1, $2);
my %WebHeap;
my $PollBuf;
my $PollHeap;
my $PollHeader;
my $IdleTimer;
my $DisconectTime = 0;

$RelayHost, $RelayPort は、 リレーサーバのホスト名とポート番号ですが、
前述したように stone 経由でリレーサーバにつなぐために、
$RelayHost = "localhost", $RelayPort = 12345 などとなります。また、 $WebHost, $WebPort は、 中継先となる (イントラの) Webサーバのホスト名とポート番号です。

続いて、リレーサーバへ接続する (直接の接続先は SSL 化を行なう stone ですが、 煩雑になるので以下 「リレーサーバ」 と略記します) ためのコードです:

POE::Component::Client::TCP->new
    ( RemoteAddress => $RelayHost,
      RemotePort    => $RelayPort,
      Connected     => sub {
          $PollHeap = $_[HEAP];
          undef $PollHeader;
          $PollBuf = "";
          $IdleTimer = $IdleTimerMax;
          $PollHeap->{server}->
              put("GET /KLAB/poll HTTP/1.1\r\nX-Ver: realyagent.pl 0.01\r\n\r\n");
      },
      ServerInput   => sub {
          $PollHeap = $_[HEAP];
          $PollBuf .= $_[ARG0];
          &doPoll;
      },
      Filter        => POE::Filter::Stream->new(),
      Disconnected  => \&reconnectPoll,
    );

POE では、非同期に動く処理を、 処理ごとに分けて書くことができます。 各処理のことを「POEセッション」と呼びます。

上記は、リレーサーバへ接続する POEセッションの生成です。 接続先ホストおよびポートを、 それぞれ $RelayHost と $RelayPort に設定しています。

「Connected => sub {」から始まる部分が、 接続に成功したときに実行するコードです。 細かいところはさておき、 接続したら以下のリクエストをリレーサーバに送る、 という点は読み取れるのではないでしょうか。

GET /KLAB/poll HTTP/1.1
X-Ver: realyagent.pl 0.01

同様に、 「ServerInput => sub {」から始まる部分が、 通信相手 (リレーサーバ) からデータを受信したときに実行するコードです。 受信したデータは、 いったん変数 $PollBuf に溜めておいて、 続いて呼び出す doPoll の中で処理を行ないます。

以上からお分かりのように、 リレーサーバへデータを送るときは、
「$PollHeap->{server}->put(送るべきデータ);」を実行し、 リレーサーバからデータが送られてきた時は、 doPoll で受取ります。 とても見通しが良いですね。

各 POEセッションは、スレッドと同様、同一メモリ空間を共有しているので、 他の POEセッションが変更した変数の値を参照できます。 したがってどの POEセッションでもリレーサーバへデータを送ることができますし、 リレーサーバから受信したデータはどの POEセッションでも読むことができます。

続いて、もう一つ POEセッションを作ります。

POE::Session->create
    ( inline_states =>
      { _start => sub {
          $_[KERNEL]->delay( tick => 10 );
        },
        tick => sub {
            if ($IdleTimer > 0) {
                if (--$IdleTimer <= 0) {
                    &sendControl(0, -2);        # keep alive
                }
            }
            $_[KERNEL]->delay( tick => 10 );
        },
      },
    );
$poe_kernel->run;
exit;

この POEセッションは 10秒に一回、 「tick => sub {」から始まる部分を実行します。 見ての通り、$IdleTimer の値を減らしていって、 0 になったら sendControl を実行します。 $IdleTimer は最初 6 ($IdleTimerMax) に設定されるので、 1 分ごとに sendControl を実行する、という意味ですね。

以上 2つの POEセッションは作成しただけで、まだ走り出していません。
その次の「$poe_kernel->run;」が各 POEセッションを走らせるための呼び出しです。 このルーチンは全ての POEセッションが終了するまで返ってきません。

さて、relayagent はリレーサーバとの接続を常時維持していますが、 無通信時間が続くと (通信経路中にあるファイアウォールなどに) 切られてしまう恐れがあるので、 keep alive ブロックを送信しています。 通信が行なわれていない時間を測るためのカウンタが $IdleTimer というわけです。

通信が行なわれない限り $IdleTimer は減り続け、 1 分経過すると sendControl(0, -2) を呼び出して keep alive ブロックを送信します。 sendControl はこんな感じ:

sub sendControl {
    my ($id, $control) = @_;
    $control += 65536 if $control < 0;
    $IdleTimer = $IdleTimerMax;
    if (defined $PollHeap && $PollHeap->{connected}) {
        $PollHeap->{server}->put(pack("nn", $id, $control));
    }
}

既に説明したように「$PollHeap->{server}->put(データ)」は、 リレーサーバにデータを送る呼び出しですから、 「pack("nn", 0, 65534)」が keep alive ブロックであることが分かります。

「ブロック」というのは VPN-Warp 用語でして、 relayagent とリレーサーバとの通信は、 基本的にこの「ブロック」を単位にして行ないます。 ブロックは次のような可変長のデータです。

    ┌───┬───┬───┬───┬───┬─≪─┬───┐
    │セッションID│ データ長  │  可変長データ   │
    └───┴───┴───┴───┴───┴─≫─┴───┘
          2バイト         2バイト      「データ長」バイト

「セッションID」および「データ長」は、ビッグエンディアンです。 つまり上位バイトが先に来ます。 データ長が 0 ないし負数の場合は、 「可変長データ」の部分は 0 バイトになります。

データ長が 0 ないし負数であるブロックは、 コントロール用のブロックで、 以下の意味を持っています:

データ長意味内容
0EOFWebセッションの終了を要求
-1ErrorWebセッションの異常終了を要求
-2Keep Alive無通信状態が続いたときに送信
-3X OFFWebセッションのデータ送信の一時停止を要求
-4X ONWebセッションのデータ送信の再開を要求

ブラウザ送ったリクエストを Webサーバに届け、 Webサーバのレスポンスをブラウザに返す一連の通信のことを、 ここでは「Webセッション」と呼ぶことにします。 つまり、 VPN-Warp が提供する仮想的な通信路 (トンネル) 上のセッションです。

VPN-Warp セッション

ブラウザがリレーサーバと通信するときの TCP/IPセッションと、 relayagent と Webサーバが通信するときの TCP/IPセッションを対応づけるのが、 セッションID です。 「セッション」という言葉が何度も出てきてややこしいですが、 「セッションID」の「セッション」は、 「Webセッション」の意味です。

リレーサーバと relayagent との間は、 複数の Webセッションを一本の TCP/IPセッションに相乗りさせるので、 そのとき各 Webセッションがこんがらないようにするために ブロックにはセッションID がつけられている、というわけです。

では、次はいよいよ relayagent の中核ルーチンである doPoll です:

sub doPoll {
    do {
        if (! defined $PollHeader) {
            if ($PollBuf =~ /\r\n\r\n/) {
                $PollHeader = $`;
                $PollBuf = $';
            }
        }
        return unless defined $PollHeader;
        my ($id, $len, $data) = unpack("nna*", $PollBuf);
        return unless defined $id && defined $len && $len ne "";
        if ($len > 32767) {
            $len -= 65536;
            $PollBuf = $data;
            if ($len == -1) {
                &closeWeb($id);
            }
        } elsif ($len > 0) {
            return unless defined $data && length($data) >= $len;
            ($data, $PollBuf) = unpack "a${len}a*", $data;
            &reqWeb($id, $data);
        } else {        # len == 0
            $PollBuf = $data;
            &closeWeb($id);
        }
    } while ($PollBuf);
}

前述したように、relayagent はリレーサーバに接続したとき、 まず
「GET /KLAB/poll HTTP/1.1」から始まるリクエストヘッダを送ります。 するとリレーサーバは、 次のようなレスポンスを返します:

HTTP/1.1 200 OK
X-Customer: nusers=5&type=1&expire=1169696110&digest=3f6977eceb8c2c43e28e6026b08ba900

そしてこの後 (doPoll において「defined $PollHeader」が真のとき)、 リレーサーバと relayagent は、 前述したブロックを送受信することになります。

「my ($id, $len, $data) = unpack("nna*", $PollBuf);」の部分が、
リレーサーバから受信したブロックを、
「セッションID ($id)」 「データ長 ($len)」 「可変長データ ($data)」 に分解している処理ですね。 続いてブロックの処理が行なわれますが、 コントロールブロックに関する処理は割愛して、 可変長データが付いているブロックの処理を見ていきましょう。 ここで受信した可変長データは、 ブラウザが送信した http リクエストを 2048バイトごとに分割したものです。

つまりリレーサーバは、 ブラウザから https リクエストを受取るたびに「セッションID」を割り振ります。 そして、リクエストをブロックに分割して relayagent へ送信し、 逆に relayagent から受取ったブロックを 同じセッションID ごとに連結して、 http レスポンスとしてブラウザへ送信します。

したがって、 relayagent はリレーサーバから受取ったブロックを 同じセッションID ごとに連結して Webサーバへ中継し、 そのレスポンスをブロックに分割してリレーサーバへ送信すればよいことになります。

同じセッションID ごとに連結して Webサーバへ送信する処理が、 reqWeb です:

sub reqWeb {
    my ($id, $req) = @_;
    if (defined $WebHeap{$id} && $WebHeap{$id}->{connected}) {
        $WebHeap{$id}->{server}->put($req);
    } else {
        POE::Component::Client::TCP->new
            ( RemoteAddress => $WebHost,
              RemotePort    => $WebPort,
              Connected     => sub {
                  $WebHeap{$id} = $_[HEAP];
                  $WebHeap{$id}->{server}->put($req);
              },
              ServerInput   => sub {
                  $WebHeap{$id} = $_[HEAP];
                  &sendRes($id, $_[ARG0]);
              },
              Filter        => POE::Filter::Stream->new(),
              Disconnected  => sub {
                  &sendControl($id, 0);
              },
            );
    }
}

「POE::Component::Client::TCP->new」によって、 Webサーバと通信するための POEセッションを生成しています。 この reqWeb を実行しているのは、 リレーサーバとの通信を受け持つ POEセッションでしたが、 この POEセッションが新たに POEセッションを生成している点に注意してください。

新しく生成した POEセッションは、Webサーバと接続したとき (Connected)、
「$WebHeap{$id}->{server}->put($req);」を実行して リクエスト ($req) を Webサーバに送信します。 そして Webサーバからレスポンスを受信したとき (ServerInput)、 sendRes を実行します。

sub sendRes {
    my ($id, $res) = @_;
    $IdleTimer = $IdleTimerMax;
    if (defined $PollHeap && $PollHeap->{connected}) {
        for my $block (unpack "(a2048)*", $res) {
            $PollHeap->{server}->
                put(pack("nna*", $id, length($block), $block));
        }
    }
}

sendRes は Webサーバからのレスポンス ($res) を 2048バイトごとに分割し、 セッションID ($id) とデータ長 (length($block)) を付加した ブロックとしてリレーサーバに送信します。

以上をまとめたのが、relayagent スクリプト です。 ここで解説した機能の他、 http リクエストヘッダの Host: フィールドを書き換える機能も追加しています。

C 版の relayagent に比べると、 http レスポンスの書き換え機能や、 http 以外のプロトコルを通す機能などがない点や、 高負荷時の性能の検証が充分行なえていない点など、 そのまま実運用に使用するには難しい点もありそうですが、 少なくとも プロトタイピングなどの目的 (あるいは教育などの目的) ならば 充分使えそうです。

Filed under: システム構築・運用,プログラミングと開発環境 — hiroaki_sengoku @ 07:13
2007年1月17日

技術者を目指す学生さんたちへ hatena_b

いよいよ就職活動本番ですね。 どのような進路を選ぶにせよ、 あとで後悔することのないよう、 じっくり考えて決めたいものですよね。 でも、ただ単に考えると言っても、 一人であれこれ思い悩むのは感心しません。 「思いて学ばざれば則ち殆し」と言いますから、 ぜひいろいろ見聞きした上で考えて頂きたいと思います。

企業への就職を考える場合、まず気になるのは評価制度のことだと思いますが、 まさにこの評価制度が揺れ動いているのが、いま現在と言えるでしょう。 高度成長期以来、長年用いられてきた年功主義に基づく評価制度の綻びが 誰の目にも明らかになってきてはいるものの、 年功に代わる評価方法を模索し続けているのが 多くの企業の現状だと思います。

どこの企業も新しい人事制度を模索し、成果主義が取り入れられつつありますよね。 (同時にコスト削減という意味合いもありますが)
この成果主義という人事制度ですが、 多分どこの会社でもおおよそこんな感じなのではないでしょうか。
●年初に目標を設定(部署ごとの目標・個人の目標)
●3ヶ月か半年ごとに上司と面談
●成果をアピール
●評価の通知
一見、単なる年功序列よりは凄くまともそうなシステムに見えますよね。 でも、実際には明らかな欠点があると思います。 (評価する側の上司がそもそも年功序列組だという点は除いて)
◆短期間にアピールできるようなものにばかり心奪われるようになる
◆業績アピールに繋がらない日常の雑用や、他人の手伝いは避けるようになる
どちらも当たり前の弊害だと思うのですが、 多くの企業ではこれらの問題について見て見ぬフリをしているのではないでしょうか?

ぜひこういった疑問を、どんどん企業にぶつけていって頂きたいと思います。 就職活動というのは、いろんな企業に対して歯に衣着せぬ質問ができる 唯一と言ってもいい機会なのですから。

「評価」には二つの側面があります。 「評価される側」と「評価する側」です。 上に引用した文章は、 「評価される側」にフォーカスした疑問と言えるでしょう。 もちろん学生さんは就職したら評価される側になるので、 「評価される側」から考えたくなるのは当然だと思いますが、 なにごとにも表と裏があります。 片方の側からだけ考えていては考えを深めることはできません。 ぜひ、自分とは異なる立場の視点も持つ習慣を身につけて頂きたいと思います。

「評価」を両方の側から考えてみれば、 「短期間にアピールできるようなものにばかり心奪われるようになる」というのは 評価される側の理屈に過ぎないことは自明ですよね? つまり暗黙のうちに、 アピールがそのまま通るような「無能な上司」を前提としてしまっています。 もし、「評価する側」が 「業績アピールに繋がらない日常の雑用や、他人の手伝いは避ける」人の 評価を下げるのであれば、 このような弊害を避けることは可能でしょう。

引用した文中に「評価する側の上司がそもそも年功序列組だという点は除いて」と ありますが、 まさにこれこそが問題の本質だと思います。 「除いて」しまっていては考えが深まりません。

どのような人事制度であれ、 「評価する人」と「評価される人」の双方に、 その制度の「精神」が徹底できていなければ機能するはずがありません。 そして、 「評価される人」への徹底は、 そもそも徹底できていなければ評価が下がるので、 否が応にも徹底されるわけですが、 「評価する人」への徹底ができるかどうかは、 「評価する人」を評価する人、つまりその上の上司の責任です。

より具体的に言えば、 「短期間にアピールできるようなもの」「アピールしやすいもの」 ばかりを評価の対象としてしまって、 部下の本当の価値を評価できていない上司を、本当に降格できるのか? という問題でしょう。 年功主義では過去の功労者が上司となるケースが多いようですが、 過去に成果を上げた人が、 現在の部下の成果を評価できるのか? という問題であるとも言えますね。

製品には、どうしても長期的な投資が必要なものがあると思います。
例えば日亜化学の青色LEDだって、中村氏が個人で何年もかけて、 会社の中止命令を無視してやり遂げたと著書にありましたし。
もし青色LED開発中に、 3ヶ月ごとに面談していたらどんな評価がされるんでしょう?
失敗続きで亀のようにのろく、先が見えない実験の繰り返しでしょうから、 それらの失敗が将来大きなリターンになって返って来ることを 強く確信している人でなければ、続けられませんよね。
同ページ」から続けて引用

「技術者の成果」とは何でしょう?

技術者が作ったものから得られる売上でしょうか? もちろん、それだけではないですよね? 「青色LED」のように最終的に莫大な利益を生み出したものは、 とても分かりやすい「成果」ですが、 サーバシステムの運用などのような縁の下の力持ちの仕事だって 立派な成果ですし、 さらに、 自分自身では何も生み出さなくても、 社内の技術者を育てることや、 社内の技術をブログなどで発表して会社の知名度を上げることなども、 立派な成果と言えるでしょう。

したがって「3ヶ月ごとの面談」という制度が問題なのではなく、 きちんと技術者を評価できない上司をそのままにしておくのが問題なのです。 そしてそういう上司をそのままにしている上司の上司も問題ですし、 そのまた上の上司の上司の上司にも責任があります。

こうやって責任の連鎖を上へ上へ登っていくと、 技術者の評価制度が機能するか否かは、 技術者の評価について最終的な責任を負う人がいるのか? という点に行き着くことになります。

技術者を目指す学生のみなさんには、 ぜひこの点 ──この会社では誰が技術者の評価の最終責任を負っているのか?── を押えた就職活動をするようお勧めします。 そしてできれば、「誰が責任者か」だけでなく、 その責任者がどんな考えを持っているのか調べられるものは全て調べ、 さらに疑問点があれば直接会ってでも質問するくらいの勢いで 臨んで欲しいと思います。

KLab株式会社 取締役
兼 最高技術責任者
仙石浩明
Filed under: 元CTO の日記,技術と経営,技術者の成長 — hiroaki_sengoku @ 07:49
2007年1月15日

La Fonera (FON ソーシャルルータ) で VPN-Warp を使う hatena_b

La Fonera (FON ソーシャルルータ) って知っていますか?

FONは世界最大のWiFiコミュニティです。 誰もが「世界中どこからでもインターネットに無料で接続したい!」という 望みを持っているはずです。 そのようなメンバーが助け合ってWiFiを広めて行こう!ということを コンセプトに私たちは活動しています。 元々簡単なアイディアで始まったFONコミュニティ。 メンバーが作るWiFiインフラを用いて、 WiFiを世界中のどこからでも楽しめるようにしましょう!。
参加は簡単!FON取り扱い店でLa Foneraを購入して接続してスタートするだけ!

La Fonera は、FON のアクセスポイントであると同時に、 普通の (プライベートな) 無線LAN アクセスポイントとしても利用できます。 自宅などで無線LAN アクセスポイントを設置している人は多いと思いますが、 おそらくそのまま La Fonera で置き換えることが可能でしょう。

実際、私はそれまで使ってた 無線LAN ルータ WN-G54/R2 を La Fonera で置き換えてしまいました。 La Fonera が提供する二つのアクセスポイントのうち、 プライベート側を使えば、 自宅LAN に普通にアクセスできて、 いままで使っていた無線LAN ルータに比べて全く遜色ありません。 もちろん WPA2 (Wi-Fi Protected Access 2) 暗号化方式が使えます。

より正確に言うと、 La Fonera のプライベート側アクセスポイント (MyPlace) は、 自宅LAN とは異なるセグメントになります。 有線LAN と無線LAN 相互で自由に通信するためには、 多少の設定変更 (/etc/firewall.user に 2行ほど追加) が必要になります。

私の場合 WPA に対応していない古い無線LAN 端末 (Windows98マシン^^;) も 持っていて、 自宅の無線LAN を WEP から WPA2 に変更した (自宅LAN といえど、WEP を使うのはちょっと抵抗がありますよね?) 時点で、 お蔵入りにしていました。 La Fonera が提供するもう一つのアクセスポイント (パブリック側) を使えば、 直接自宅LAN へはアクセスできないもののインターネットへは接続できるし、 インターネット経由で自宅LAN に戻ってくる (もちろんインターネットからアクセス可能な部分に限定されますが) ことも可能なので、 La Fonera の導入によって古い無線LAN 端末も再び利用できるようになった、 というオマケがつきました。

さて、 この La Fonera で VPN-Warp が利用可能になったらどうなるでしょうか? 無線LAN ルータが VPN ゲートウェイの機能も持つわけです。 つまりインターネットに接続できる環境ならどこからでも、 自宅LAN へ手軽にアクセスできるようになります。

ここで La Fonera は固定 IP アドレスを持つ必要がないばかりか、 そもそもグローバルアドレスを持つ必要性すらない、 という点がミソです。 必要なことは、La Fonera からインターネットへ接続可能、ということだけです。 La Fonera をどこに設置しようと、 その設置した LAN に外部からアクセスすることができます。

以下は、La Fonera で VPN-Warp を使うようにするための手順です。 現状は多少の Linux の知識が必要となってしまうのですが、 要は relayagent プログラムを La Fonera にインストールするだけのことなので、 適切なインストーラさえ作ればいくらでも簡単な手順になることでしょう。 なので、難しそうだとあきらめるのではなく、 関心がある方はご連絡頂ければと思います。

まず La Fonera に ssh あるいはシリアルコンソールで ログインすることが必要となります。 おそらくこれが最大の難関でしょう (^^;)。

senri:/home/sengoku % ssh fonera
root@fonera's password:


BusyBox v1.1.3 (2006.11.21-19:49+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

 _______  _______  _______
|   ____||       ||   _   |
|   ____||   -   ||  | |  |
|   |    |_______||__| |__|
|___|

 Fonera Firmware (Version 0.7.1 rev 2) -------------
  *
  * Based on OpenWrt - http://openwrt.org
  * Powered by FON - http://www.fon.com
 ---------------------------------------------------
root@OpenWrt:~#

ログインさえ可能なら、あとはさほど難しくはありません。 まずネットワーク経由で La Fonera に relayagent を インストールするための準備をします。 具体的には、 以下のように /etc/ipkg.conf に「src gcd http://www.gcd.org/fonera」 を追加し、 「ipkg update」 を実行します。

root@OpenWrt:~# echo "src gcd http://www.gcd.org/fonera" >> /etc/ipkg.conf
root@OpenWrt:~# ipkg update
Downloading http://download.fon.com/release/fonera/0.7/packages/Packages
Updated list of available packages in /usr/lib/ipkg/lists/fon
Downloading http://www.gcd.org/fonera/Packages
Updated list of available packages in /usr/lib/ipkg/lists/gcd
Done.
root@OpenWrt:~#

http://www.gcd.org/fonera というのは、 私が最近始めた La Fonera 用の ipkg feed です。 つまり、上記のような設定をしておくと、 La Fonera にいろいろなソフトウェアを ネットワーク経由でインストールすることができるようになります。 もちろん ;-) VPN-Warp の relayagent も、 ここからインストールできます。 インストールに必要なコマンドは、 「ipkg install relayagent」だけです。 このコマンドを打ち込むだけで、 relayagent の実行に必要な OpenSSL ライブラリ等が 自動的にインストールされます。

root@OpenWrt:~# ipkg install relayagent
Installing relayagent (1.0.7) to root...
Downloading http://www.gcd.org/fonera/relayagent_1.0.7_mips.ipk
Installing libopenssl (0.9.8d-1) to root...
Downloading http://www.gcd.org/fonera/libopenssl_0.9.8d-1_mips.ipk
Installing zlib (1.2.3-3) to root...
Downloading http://www.gcd.org/fonera/zlib_1.2.3-3_mips.ipk
Configuring libopenssl
Configuring relayagent
Configuring zlib
Done.
root@OpenWrt:~#

次に、VPN-Warp の証明書をインストールします。 BIGLOBE の VPN ワープのページ などから入手した証明書とそのパスワードを記したファイルを、 La Fonera の /etc/warp ディレクトリへコピーしてください。 以下の実行例では relay,5000005.pfx が証明書のファイル、 relay,5000005-pass.txt がパスワードを記したファイルです。 5000005 というのは私が使用している証明書の番号なので、 実際に取得した証明書の番号で読み替えてください。

senri:/home/sengoku % echo "パスワード" > relay,5000005-pass.txt
senri:/home/sengoku % scp -p relay,5000005.pfx relay,5000005-pass.txt fonera:/etc/warp/
root@fonera's password:
relay,5000005.pfx                             100% 4856     4.7KB/s   00:00
relay,5000005-pass.txt                        100%    9     0.0KB/s   00:00

次が最後の難関で、 VPN-Warp の設定ファイルを作成します。 /etc/warp/relayagent.cfg.sample に設定ファイルのサンプルがあるので、 これを /etc/warp/relayagent.cfg へコピーして、 vi エディタで編集します。

root@OpenWrt:~# cd /etc/warp/
root@OpenWrt:/etc/warp# ls -l
-rw-------    1 root     root            9 Jan  9 04:09 relay,5000005-pass.txt
-rw-------    1 root     root         4856 Jan  9 04:09 relay,5000005.pfx
-rw-r--r--    1 root     root         3290 Jan  9 07:47 relayagent.cfg.sample
root@OpenWrt:/etc/warp# cp relayagent.cfg.sample relayagent.cfg
root@OpenWrt:/etc/warp# vi relayagent.cfg

設定ファイル relayagent.cfg の中に、以下のような部分があるので、
「relay,0000000」の数字の部分を実際に取得した証明書
(私の場合は「relay,5000005」) の番号で置き換えます。

#--------------------------------------------------------------------
#
# -x  PFX 形式 クライアント証明書を指定
# -X  同パスワードファイルを指定
#
#--------------------------------------------------------------------

-x "/etc/warp/relay,0000000.pfx"
-X "/etc/warp/relay,0000000-pass.txt"

以上で設定ファイルの作成が完了しました。 あとは La Fonera を再起動する (裏面のリセットスイッチを押す) だけです。 再起動する代わりに、起動スクリプトを実行することによって relayagent を起動することもできます。

root@OpenWrt:~# /etc/init.d/N50relayagent start
root@OpenWrt:~#

では、ブラウザで https://warp.klab.org へアクセスしてみましょう。 La Fonera にインストールした証明書と同じ証明書、あるいは その子証明書を使ってアクセスしてください。 La Fonera の Web 設定ページが表示されたら成功です。

Web 設定ページが表示されるのは、 先ほど作成した設定ファイル /etc/warp/relayagent.cfg に、

#--------------------------------------------------------------------
#
# <relay サーバ名:ポート番号>  <転送先ホスト名:ポート番号>
#
# ※-p オプション指定時は下記の意味となる
#
# <プロキシサーバ名:ポート番号> <転送先ホスト名:ポート番号>
#
#--------------------------------------------------------------------

warp.klab.org:443 localhost:80

と書いてあるからです。 「localhost:80」だから La Fonera 内蔵の WWW サーバ (つまり設定ページ) ですね。 「localhost:80」の部分を、 自宅LAN 内の適当なサーバの「アドレス:ポート」で置き換えれば、 そのサーバにアクセスできますし、 通常の VPN-Warp と全く同様に、 任意のサーバに接続するように設定することもできます。

Filed under: La Fonera,システム構築・運用 — hiroaki_sengoku @ 07:02
2007年1月9日

La Fonera 用の ipkg feed を始めました hatena_b

ネットワーク経由で La Fonera にパッケージをインストール

La Fonera (FON ソーシャル ルータ) のベースとなっている OpenWrt は、 パッケージ管理システムとして ipkg を利用している。 La Fonera 用の ipkg feed を 作ってみた。 この feed を使うには、 以下のように /etc/ipkg.conf に 「src gcd http://www.gcd.org/fonera」 を追加し、 「ipkg update」 を実行する。

root@OpenWrt:/# echo "src gcd http://www.gcd.org/fonera" >> /etc/ipkg.conf
root@OpenWrt:/# ipkg update
Downloading http://www.gcd.org/fonera/Packages
Updated list of available packages in /usr/lib/ipkg/lists/gcd
Done.

すると、feed のリスト (パッケージ名の一覧) が /usr/lib/ipkg/lists/gcd に保存される。 あとは、「ipkg install パッケージ名」などと実行するだけで、 パッケージをネットワーク経由で La Fonera にインストールできる。

例えば stone をインストールするには、 次のように「ipkg install stone」と実行するだけでよい。

root@OpenWrt:~# ipkg install stone
Installing stone (2.3c-2.2.4.12) to root...
Downloading http://www.gcd.org/fonera/stone_2.3c-2.2.4.12_mips.ipk
Installing libopenssl (0.9.8d-1) to root...
Downloading http://www.gcd.org/fonera/libopenssl_0.9.8d-1_mips.ipk
Installing zlib (1.2.3-3) to root...
Downloading http://www.gcd.org/fonera/zlib_1.2.3-3_mips.ipk
Installing libpthread (0.9.28-8) to root...
Downloading http://www.gcd.org/fonera/libpthread_0.9.28-8_mips.ipk
Configuring libopenssl
Configuring libpthread
Configuring stone
Configuring zlib
Done.

stone の実行に必要な、libopenssl, zlib, libpthread パッケージの インストールも自動的に行なわれている。 このようにパッケージの依存関係も管理してくれるので便利なのであるが、 残念ながら現行の La Fonera 上の ipkg にはバグがある。 インストールしたパッケージをアンインストールしようとして、

ipkg remove stone

などと実行してはいけない。 /usr/bin/stone だけでなく、 /usr/bin ディレクトリまでもが削除されてしまう。 なぜこんなバグがあるかというと、 mini_fo の rmdir の実装にバグがあるからだ。 すなわち、空でないディレクトリに対して rmdir を行なっても、 ディレクトリが削除されてしまうことがある。 試しに、あまり重要そうでないディレクトリに対して rmdir してみる:

root@OpenWrt:/www/images# ls -a table
.                 bottom-right.gif  logo.gif          top-left.gif
..                bottom.gif        right.gif         top-right.gif
bottom-left.gif   left.gif          sidebar.gif       top.gif
root@OpenWrt:/www/images# rmdir table
root@OpenWrt:/www/images# ls table
ls: table: No such file or directory

La Fonera のファイルシステムは mini_fo (mini fan-out) を使っている。 つまり squashfs を read only でマウントした「上」に、 jffs を重ねてマウントしている。 前述の例で言えば、 /www/images/table ディレクトリは squashfs 上にあり、 jffs 上には /www/images/table ディレクトリは存在しない。 「rmdir table」などファイルシステムに対する変更は、 「上」に重ねられた jffs に対して行なわれるが、 ディレクトリが空か否かの判断まで、 「上」のファイルシステムに対してのみ行なっているのだろう、 jffs 上では /www/images/table ディレクトリの中にはファイルが存在しない (ディレクトリが存在しないので当然だが) ため、 rmdir がエラーにならずにディレクトリの削除が行なわれてしまう。

La Fonera の ipkg (busybox に含まれている) のソース ipkg_remove.c を 確認してみると、

    for (iter = installed_dirs.head; iter; iter = iter->next) {
            file_name = iter->data;
 
            if (rmdir(file_name) == 0) {
                 ipkg_message(conf, IPKG_INFO, "  deleting %s\n", file_name);
                 removed_a_dir = 1;
                 str_list_remove(&installed_dirs, &iter);
            }
    }

などとなっている。 つまりディレクトリが空かどうかは判断せず、 いきなり rmdir してみて成功すればヨシとしている。 このため、「ipkg remove パッケージ名」を実行すると、 空でないディレクトリまで削除してしまうのだろう。

空でないディレクトリに対して rmdir を実行したら ENOTEMPTY を返すのがスジであるので、 mini_fo 側を修正すべきだとは思うのだが、 とりあえず sh スクリプト版の ipkg を拾ってきて、 rmdir するまえに中身の確認をするように変更し、 /usr/bin/ipkg を この修正済み sh スクリプトで置き換えてみた。

root@OpenWrt:~# wget http://www.gcd.org/fonera/ipkg-0.9-1.32.sh
Connecting to www.gcd.org[60.32.85.216]:80
ipkg-0.9-1.32.sh     100% |*****************************| 27925       00:00 ETA
root@OpenWrt:~# chmod 755 ipkg-0.9-1.32.sh
root@OpenWrt:~# mv ipkg-0.9-1.32.sh /usr/bin/ipkg

などと置き換えるとよいだろう。 これで安全にパッケージをアンインストールできるようになる。

root@OpenWrt:~# ipkg remove stone
ipkg_remove: Removing stone...
ipkg_remove: Warning: Not removing the following directories since they are not
empty:
 /usr /usr/bin
Done.

/usr および /usr/bin ディレクトリが空でなかったので削除できなかった旨が、 表示されている。

La Fonera の自動アップデートのまとめ

La Fonera の自動アップデートは、 cron から呼び出される /bin/thinclient プログラムによって行なわれている。 このアップデートは ipkg とは独立した形で行なわれているので、 ipkg でパッケージのインストールを行なう場合は、 この自動アップデートで何が行なわれているか把握しておく必要がある。 確認できたアップデートについてまとめてみた。

0.7.0 rev 2 -> 0.7.0 rev 3 (upgrade.fon)
修正: /usr/lib/webif/validate.awk
0.7.0 rev 3 -> 0.7.0 rev 4 (upgrade.fon)
[WiFi] 暗号方式のデフォルトを WPA+TKIP に変更
修正: /etc/init.d/rcS /sbin/ifup /www/cgi-bin/webif/*.sh
変更: /lib/modules/2.4.32/wlan.o /lib/modules/2.4.32/ath_ahb.o
追加: /etc/hotplug.d/iface/10-ppp_hack
0.7.0 rev 4 -> 0.7.1 rev 1 (upgrade.fon) 2006-11-21 0.7.1 rev 1
[Web interface] 多言語サポート
[Web interface] ポートフォワード機能
[NTP] ntpclient による時刻同期
修正: /bin/thinclient /etc/functions.sh /usr/lib/webif/*
変更: /usr/bin/webif-page
追加: /etc/config/ntpservers /etc/config/openports /etc/config/webif /etc/init.d/N45ntpclient /usr/lib/webif/lang /usr/sbin/adjtimex /usr/sbin/ntpclient /www/cgi-bin/webif/adv_pf.sh /www/cgi-bin/webif/language.sh
0.7.1 rev 1 -> 0.7.1 rev 2 (upgrade.fon) 2007-01-04 01:00 JST
[Web interface] 任意のコマンドを実行させられてしまう脆弱性を修正
[NTP] crontab に複数の ntpclient 呼び出しが登録されてしまうバグを修正
修正: /etc/init.d/N45ntpclient /usr/lib/webif/validate.awk /www/cgi-bin/webif/*.sh
変更: /usr/bin/haserl

特に重要なのが、0.7.1 rev 2 で行なわれた、 Web インタフェースにおける脆弱性の修正だろう。 POST した入力データのチェックが厳密になった。 La Fonera に ssh でログインする最も手軽な方法が、 この脆弱性を利用する方法だっただけに、 0.7.1 rev 2 にアップデートする際は注意が必要である。

Filed under: La Fonera — hiroaki_sengoku @ 06:51
2007年1月4日

La Fonera 上で stone を走らせてみる hatena_b

La Fonera (FON ソーシャル ルータ) のファーム ウェアのソース コードは、 Fon blog に書かれているように、 http://download.fon.com/firmware/fonera/latest/fonera.tar.bz2 から取得できる。 これを展開して make を実行すると、 「OpenWrt Configuration」ダイアログが表示される。 現行の La Fonera のファーム ウェアと同じものをビルドするだけなら Configuration の変更は不要。そのまま save して exit すればビルドが行なわれる。

stone を make するには、 libpthread と libopenssl が必要なので、 以下のように設定する。
まず、

     Base system  --->

を選択して、以下のように libpthread を make するように指定する:

<*> libpthread.......................................... POSIX threa

続いて、

    Libraries  --->

を選択して、以下のように libopenssl も make するように指定しておく:

<M> libopenssl..................................... Open source SSL
< >   openssl-util.............................. OpenSSL command lin
<M> zlib................. Library implementing the deflate compressi

「Do you wish to save your new OpenWrt configuration?」 と聞かれるので「Yes」と答える。 するとクロスコンパイル環境とファームウェアのビルドがどんどん進む。

*** End of OpenWrt configuration.
*** Execute 'make' to build the OpenWrt or try 'make help'.

make[2] toolchain/install
make[3] -C toolchain install
make[4] -C toolchain/sed prepare
make[4] -C toolchain/sed compile
make[4] -C toolchain/sed install
make[4] -C toolchain/kernel-headers prepare
make[4] -C toolchain/kernel-headers compile
make[4] -C toolchain/kernel-headers install
make[4] -C toolchain/sstrip prepare
make[4] -C toolchain/sstrip compile
make[4] -C toolchain/sstrip install
make[4] -C toolchain/uClibc prepare
make[4] -C toolchain/binutils prepare
make[4] -C toolchain/binutils compile
make[4] -C toolchain/binutils install
make[4] -C toolchain/gcc prepare
make[4] -C toolchain/gcc compile
make[4] -C toolchain/uClibc compile
make[4] -C toolchain/uClibc install
make[4] -C toolchain/gcc install
make[4] -C toolchain/ipkg-utils prepare
make[4] -C toolchain/ipkg-utils compile
make[4] -C toolchain/ipkg-utils install
make[4] -C toolchain/libnotimpl prepare
make[4] -C toolchain/libnotimpl compile
make[4] -C toolchain/libnotimpl install
make[4] -C toolchain/lzma prepare
make[4] -C toolchain/lzma compile
make[4] -C toolchain/lzma install
make[4] -C toolchain/squashfs prepare
make[4] -C toolchain/squashfs compile
make[4] -C toolchain/squashfs install
make[4] -C toolchain/jffs2 prepare
make[4] -C toolchain/jffs2 compile
make[4] -C toolchain/jffs2 install

「install」と表示されているが、 これは「./staging_dir_mips」ディレクトリへのインストール。 「./staging_dir_mips」にクロスコンパイル環境がインストールされた後に、 これを使ってカーネルやライブラリ等のコンパイルが進む。

make[2] target/compile
make[3] -C target compile
make[4] -C target/utils prepare
make[4] -C target/utils compile
make[4] -C target/utils install
make[4] -C target/linux prepare
make[5] -C target/linux/ar531x-2.4 prepare
make[4] -C target/linux compile
make[5] -C target/linux/ar531x-2.4 compile
make[6] -C target/linux/ar531x-2.4 modules
make[6] -C target/linux/ar531x-2.4 packages
make[4] -C target/image/ar531x compile
make[2] package/compile
make[3] -C package compile
make[4] -C package compile-targets
make[5] -C package/base-files compile
make[5] -C package/bridge compile
make[5] -C package/busybox compile
make[5] -C package/chillispot compile
make[5] -C package/dnsmasq compile
make[5] -C package/dropbear compile
make[5] -C package/foncheckrsa compile
make[5] -C package/haserl compile
make[5] -C package/madwifi compile
make[5] -C package/hostapd compile
make[5] -C package/iptables compile
make[5] -C package/mini_fo compile
make[5] -C package/mtd compile
make[5] -C package/libpcap compile
make[5] -C package/linux-atm compile
make[5] -C package/ppp compile
make[5] -C package/pptp compile
make[5] -C package/iproute2 compile
make[5] -C package/qos compile
make[5] -C package/webif compile
make[5] -C package/wireless-tools compile
make[5] -C package/zlib compile
make[5] -C package/openssl compile

「OpenWrt Configuration」ダイアログで指定しなかった パッケージまで表示されるが、 これは単に make がそのパッケージのディレクトリの Makefile を実行しただけで、 コンパイルなどは何も行なわずに抜けている。

make[2] package/install
make[3] -C package install
make[4] -C package install-targets
make[5] -C package/base-files install
make[5] -C package/bridge install
make[5] -C package/busybox install
make[5] -C package/chillispot install
make[5] -C package/dnsmasq install
make[5] -C package/dropbear install
make[5] -C package/foncheckrsa install
make[5] -C package/haserl install
make[5] -C package/hostapd install
make[5] -C package/iptables install
make[5] -C package/madwifi install
make[5] -C package/mini_fo install
make[5] -C package/mtd install
make[5] -C package/ppp install
make[5] -C package/pptp install
make[5] -C package/qos install
make[5] -C package/iproute2 install
make[5] -C package/webif install
make[5] -C package/wireless-tools install
make[2] target/install
make[3] -C target install
make[4] -C target/image/ar531x clean
make[5] -C target/image/generic/lzma-loader clean
make[4] -C target/utils prepare
make[4] -C target/utils compile
make[4] -C target/utils install
make[4] -C target/linux prepare
make[5] -C target/linux/ar531x-2.4 prepare
make[4] -C target/linux compile
make[5] -C target/linux/ar531x-2.4 compile
make[4] -C target/linux install
make[5] -C target/linux/ar531x-2.4 install
make[4] -C target/image/ar531x compile
make[4] -C target/image/ar531x install
make[5] -C target/image/generic/lzma-loader clean compile

以上で、クロスコンパイル環境とファームウェアのビルドが全て完了。 所要時間は 30分ほど (私の Celeron D 345 マシンの場合)。

ビルドしたクロスコンパイル環境は、 「staging_dir_mips/bin」に PATH を通すだけで使用できる。

stone を make してみる。 まずは CVS レポジトリから最新版を checkout する:

senri % cvs -d:pserver:anonymous@cvs.sourceforge.jp:/cvsroot/stone login
Logging in to :pserver:anonymous@cvs.sourceforge.jp:2401/cvsroot/stone
CVS password:
senri % cvs -z3 -d:pserver:anonymous@cvs.sourceforge.jp:/cvsroot/stone co stone
cvs checkout: Updating stone
U stone/GPL.txt
U stone/Makefile
U stone/README.en.txt
U stone/README.txt
U stone/cryptoapi.c
U stone/dist
U stone/global.h
U stone/logmsg.mc
U stone/md5.h
U stone/md5c.c
U stone/stone.1
U stone/stone.1.ja
U stone/stone.c
U stone/stone.cnf
U stone/stone.sh
U stone/stone.spec

次に make する:

senri % cd stone
senri % make fon-ssl
make CC="mips-linux-uclibc-gcc" SSL_LIBS="-lssl -lcrypto" TARGET=fon ssl_stone
make[1]: Entering directory `stone'
make FLAGS="-DUSE_POP -DUSE_SSL " LIBS=" -lssl -lcrypto" fon
make[2]: Entering directory `stone'
make CC="mips-linux-uclibc-gcc" FLAGS="-Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL " LIBS="-lpthread -lssl -lcrypto" stone
make[3]: Entering directory `stone'
mips-linux-uclibc-gcc  -Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL  -o stone stone.c -lpthread -lssl -lcrypto
make[3]: Leaving directory `stone'
mips-linux-uclibc-strip stone
make[2]: Leaving directory `stone'
make[1]: Leaving directory `stone'

なお、CVS から checkout した最新版でなくても、 例えば stone 2.3c において

senri:/usr/src/stone-2.3c % mips-linux-uclibc-gcc  -Wall -DPTHREAD -DUNIX_DAEMON -DPRCTL -DUSE_POP -DUSE_SSL -o stone stone.c -lpthread -lssl -lcrypto

などとコンパイルすることにより La Fonera 版 stone を make することができる。 つまり特に変更することなく make できるわけで、 いかに La Fonera がフツーの Linux マシンであるかが分かる。

make した stone および、 libpthread, libopenssl を La Fonera へコピーすれば (libopenssl は SSL 版 stone の場合のみ必要)、 La Fonera 上で stone を実行することができる。

root@OpenWrt:~# stone
Jan  4 00:18:08.478674 1024 start (2.3c) [14946]
Jan  4 00:18:08.488958 1024 stone 2.3c  https://www.gcd.org/sengoku/stone/
Jan  4 00:18:08.570372 1024 Copyright(C)2007 by Hiroaki Sengoku <sengoku@gcd.org>
Jan  4 00:18:08.579479 1024 using OpenSSL 0.9.8d 28 Sep 2006  http://www.openssl.org/
Usage: stone <opt>... <stone> [-- <stone>]...
opt:  -h opt            ; help for <opt> more
      -h stone          ; help for <stone>
      -h ssl            ; help for <SSL>, see -q/-z opt
Filed under: La Fonera,stone 開発日記 — hiroaki_sengoku @ 09:32
2007年1月1日

赤外線リモコンを Linux からコントロール hatena_b

あけましておめでとうございます。 今年もよろしくお願いします。

fj.comp.dev.misc に昨年12月26日に投稿された記事 「USB赤外線リモコン on Mac OS X」で、
河野真治 @ 琉球大学情報工学 さん曰く:

パソコン用学習リモコン PC-OP-RS1
昔は、Crossam 2+ と、そのUSB version があったんだけど、 ちょっと高い。もう売ってないし。 これは、5,000円切っている。 もちろん、ドライバはWindows しかありませんが、 シリアルドライバなんて、なんとでもなる。libusb使ってもいいし。

鎌倉にお参りした帰り、たまたま立ち寄った ラゾーナ川崎 のビックカメラにて、 上記 PC-OP-RS1 を見つけたので衝動買い。

BUFFALOの学習リモコンPC-OP-RS1をLinux(fc3)で使う」を参考にしながら、 perl でテストプログラムを書いてみる。 PC-OP-RS1 は普通のシリアルデバイスとして Linux から見えるので、 CPAN の Device::SerialPort を使えば簡単にコントロールできる。 赤外線リモコンの信号を受信するには、

--> 0x72
<-- 0x59
(リモコン受信)
<-- 0x53
<-- リモコンデータ * 240Byte
<-- 0x45

とするだけ。 そして、受信した 240バイトのデータ(固定長)を、

--> 0x74
<-- 0x59
--> 0x31 ※ 1ch=0x31,2ch=0x32,3ch=0x33,4ch=0x34
<-- 0x59
--> リモコンデータ * 240Byte
<-- 0x45

などと PC-OP-RS1 へ送ってやれば、 PC-OP-RS1 から赤外線が発射される。
ビデオの赤外線受信部近くに、PC-OP-RS1 ↓ の送信部を設置した (両面テープでラックに固定)。

  PC-OP-RS1 & VTR

ラック下段にある PC の上の黒い箱状の ↑ モノが PC-OP-RS1 本体 (拡大写真)。

即席で作った perl スクリプト「irrc」(後述) を、 次のように「-r」オプション付で実行しておいて、 PC-OP-RS1 の受光部へ赤外線リモコンを向けてリモコンのボタンを押す。

% irrc -r
ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff

すると、このように 240 バイトのデータが 16進数で表示される。 このデータを irrc スクリプトの先頭部分で、

$Ir{'aPower'} = [
    pack("H*", "ffffffff ... 中略 .... 00feffff"),
    ];

などと定義する(ちなみに上記データは私の自宅のエアコンの電源ON/OFF)。 複数のボタンのシーケンスを定義する場合は、

$Ir{'AB'} = [
    pack("H*", "ボタンA を押したときの送信データ"),
    pack("H*", "ボタンB を押したときの送信データ"),
    ];

などと定義すればよい。 ここで定義したシーケンス名 (上記「aPower」や「AB」) を、 irrc スクリプトの引数として渡せば、 PC-OP-RS1 から赤外線が発射される。

以下、irrc スクリプト全体:

#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Getopt::Std;

my %Ir;
$Ir{'vPower'} = [
    pack("H*", "ffffffffffffffffffffff0700000000007ef0831ff8c00f7e00003f00800ffc00003f00801f00c00700f00300f8c10f7c00003f00801f00e00700f0831f00c00f7ee0033ff8c10ffc00003ff00100fc00007e00001f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
    ];
$Ir{'aPower'} = [
    pack("H*", "ffffffff0f00000080ff000000fc030000f01fc07f000000fe01fc070000e03f000000ff01fe030000f01f000080ff00fe010000f80fe03f000000ff000000fc07f01fc03f000000ff01fc07f80fe03f807f00ff01fc03f80f0000c07f00ff00fe03f80fe01fc07f00ffffffff07000000c03f000000ff010000f80fe01f000000ff00fe030000f01f0000807f00ff010000f80f0000c03f80ff000000fc07f00f0000c07f000000fe03f807f01f000080ff00fe01fc07f00fe03f80ff00fe01fc070000e03f807f00ff01fc03f80fe03f80ffffffff01000000f01f0000000000000000000000000000000000feffff"),
    ];

our ($opt_v, $opt_r, $opt_d, $opt_c);
getopts("vrd:c:") || &help;
my $Verbose = $opt_v;
my $device;
if ($opt_d) {
    $device = $opt_d;
} else {
    $device = "/dev/ttyUSB0";
}
my $ch = 1;
if ($opt_c && $opt_c =~ /^[1-4]$/) {
    $ch = ord($opt_c) - ord('0');
    print STDERR "Ch: $ch\n" if $Verbose;
}
my $port = &openDev;
&sendData($port, "69");                # LED command
&expect("4f", &readData($port, 1));
if ($opt_r) {
    my $data = &receive($port);
    print unpack("H*", $data), "\n";
}
while ($_ = shift @ARGV) {
    if (defined $Ir{$_}) {
        print STDERR "Tx: $_\n" if $Verbose;
        for my $ir (@{$Ir{$_}}) {
            &transmit($port, $ch, $ir);
        }
    } else {
        print STDERR "Unknown command: $_\n";
    }
}
exit 0;

sub openDev {
    my $port = new Device::SerialPort($device) || die;
    $port->user_msg(1);
    $port->error_msg(1);
    $port->baudrate(115200);
    $port->databits(8);
    $port->parity("none");
    $port->stopbits(1);
    $port->handshake("none");
    $port->read_const_time(100); # 0.1 sec
    $port->read_char_time(5);
    $port;
}

sub transmit {
    my ($port, $ch, $data) = @_;
    &sendData($port, "74");                # transmit
    &expect("59", &readData($port, 1));
    &sendData($port, sprintf("3%d", $ch % 10));
    &expect("59", &readData($port, 1));
    $port->write($data) || die;
    &expect("45", &readData($port, 1));
}

sub receive {
    my ($port) = @_;
    &sendData($port, "72");                # receive
    &expect("59", &readData($port, 1));
    &expect("53", &readData($port, 1, -1));
    my $data = &readData($port, 240);
    &expect("45", &readData($port, 1));
    $data;
}

sub sendData {
    my ($port, $str) = @_;
    $port->write(pack("H*", $str)) || die;
}

sub readData {
    my ($port, $len, $timeout) = @_;
    my $i = 0;
    my $j = 0;
    my $data;
    if (! defined $timeout) {
        $timeout = 10;
    }
    while ($i < $len) {
        my ($l, $d) = $port->read(1);
        if ($l > 0) {
            $data .= $d;
            $i += $l;
            $j = 0;
        } else {
            $j++;
            if ($timeout > 0 && $j > $timeout) {
                print STDERR "TIMEOUT to read $len byte\n";
                exit 1;
            }
        }
    }
    if ($Verbose) {
        print STDERR "read: ", unpack("H*", $data), "\n";
    }
    $data;
}

sub expect {
    my ($ex, $d) = @_;
    my $str = unpack("H*", $d);
    if ($str ne $ex) {
        print STDERR "expect $ex, but got $str\n";
        exit 1;
    }
}

sub help {
    print STDERR <<EOF;
Usage: psoprs1.pl [opt] <com>...
opt:   -d <dev>   set device
       -c <ch>    set channel
       -r         receive
       -v         verbose
EOF
    print "com: ", join(" ", sort keys %Ir), "\n";
    exit 1;
}

「irrc vPower」を実行すれば、ビデオの電源を ON/OFF し、 「irrc -c 2 aPower」を実行すれば、エアコンの電源を ON/OFF できる。 もちろんエアコンの電源を Linux から ON/OFF できてもあまり嬉しくないが、 CS/BS 放送や CATV のチューナのチャンネルを Linux から切り替えて、 そのチューナの出力を Linux マシンで予約録画できると便利。

Filed under: ハードウェアの認識と制御 — hiroaki_sengoku @ 17:06
2006年12月26日

ロングテール戦略が格差社会を生む: 機会均等 hatena_b

仮説: ロングテール戦略が格差社会を生む の検証の三回目 (全七回を予定)。

機会均等

「機会均等」には三つの種類がある。

  • 金持ちになるための機会
  • 貧乏にならないための機会
  • 貧困から脱出するための機会

一番目の「金持ちになるための機会」とは、 例えば「アメリカン ドリーム」のようなものである。 ほとんど全ての人は夢想することはあっても本気で目指そうとは思わない。 本気で目指さないのだから達成できなくても、それは当然だろう。 ほとんどの人が、そういった夢を達成できないからといって、 機会が均等でないことの理由にはならない。

二番目の「貧乏にならないための機会」とは、 「安定した生活」のようなものである。 機会なんかなくても、人並みの努力していれば人並みの生活を維持できると、 かつては信じられていた。 昨今の競争社会は、生活水準が悪くなったと感じる人が増えつつあることから、 努力しても報われない社会だと言う人がいる。 果たしてそうだろうか?

むしろ、「人並みの努力」で中流が維持できた時代が特異だったのではないか? 資本の本質は自己増殖である。 富は富を呼び、お金のないところからはどんどんお金が逃げていく。 もちろん富の再配分によって富の集中を緩和するにしても、 鎖国でもしない限りお金の流れは止められない。 ではなぜ '60年代から '80年代にかけて、 多くの人が中流でいられた (一億総中流) かといえば、 社会全体が成長したから。 何年も2桁成長が続く高度成長期だったからこその現象だろう。

昔、「ドラえもん」の漫画に、「ボーナス1024倍」という話があった。 お金を銀行に預けておくと 10年で約二倍になるから、 100年預けておくと 1024倍になる。 ボーナスを銀行に預けてタイムマシンで 100年後におろしに行く、 という話である。 当時はなんとも思わなかったが、 今から考えると 10年で預金が倍になるなんてのは異常である。 もちろん、実質成長率はそこまで高くないにしても、 今から考えると常軌を逸脱した成長率だった。 しかも 100年後に同じ銀行が存続していることになんの疑いも持たなかった、 というのも今から思えばかなり新鮮な発想である。

高度成長期のような例外的な時代でなければ、 「貧乏にならない」ことは容易ではない。 「人並みの努力」だけでなく、 「機会」を見つけ、それを生かすことが必要である。

三番目の「貧困から脱出するための機会」とは、 「健康で文化的な最低限度の生活」のようなものである。 NHK スペシャル 「ワーキングプア II」の 副題は「努力すれば抜け出せますか」であったが、 「最低限度の生活」は憲法で保証されている権利なのであるから、 努力して獲得すべき性質のものではない。 必要なのは機会均等ではなく、無条件の保証であろう。

というわけで、一番重要なのは二番目の機会均等、 「貧乏にならないための機会」の平等である。 「貧乏にならないため」というのが後ろ向きなので、 多くの人がその「機会」をあまり重視していないようだ。 世の中には、この「機会」はいくらでも転がっているのに 多くの人がその機会をつかもうとしない。 だから資本の論理に立ち向かうことなしに、 ずるずると押し流されてしまっている。

しかも、多くの人はその現実を直視したがらない。 自分達より下に「ワーキングプア」がいるから自分達は「下層」じゃないと 思い込みたがる。 そういう人たちに限って、 「ワーキングプアは自己責任だ」などという。 「自己責任」の元、ずるずると貧乏に落ちていっているのは自分達自身だというのに...

政府の『国民生活に関する世論調査』の中で 「生活程度」についての意識調査の結果を見る限り、 バブル崩壊後も日本国民から一億総中流の意識は抜けていない。 「生活の程度は、世間一般から見て、どの程度と思うか?」という 質問に対する回答で、 「下」と答えた者の割合は、 1960年代から2004年に至るすべての年の調査において、1割以下である。

資本の論理に立ち向かう手段はただ一つ、 「資産を築く」ことである。 ここで言う資産とは、何も金融資産だけに限らない。 「将来の収入をもたらすもの」全てが資産であり、 「将来の支出をもたらすもの」全てが負債である。 そして、 資産の要件は「希少性」と「換金性/収益性」である。 最も効率的な資産である「能力」を例にとれば、 「普通の人にはできないことができる」というのが希少性であり、 「その能力に価値を感じてくれる人を見つける」ことができれば換金できる。

「人並みの能力」に希少性はない。 いつでも代わりの人を見つけられるからだ。 代替可能な人材 (replaceable resource) に支払われる賃金は、 下がることはあっても、上がることは稀である。 ベースアップなどというものは高度成長期のみに許された特異現象である。

では、他人より抜きん出た能力を身につける機会とは何か?

「20:80 の法則」 (パレートの法則) というものがある。 「売上の8割は、全従業員のうちの2割で生み出している」などの経験則が知られるが、 じゃ、その 2割の従業員だけでドリームチームを作れば、 すごい会社が作れるかというと残念ながらそうは問屋がおろさない。 「2割の従業員」がふたたび「20:80」に分かれてしまうのである。 精鋭チームを作ったつもりが、 そのチームの中の多数 (8割) は売上にあまり貢献しなくなってしまう。 逆に、ダメな従業員だけを集めたダメダメチームを作っても、 その中の 2割ほどは頭角を現し、チームを率いるようになる。

つまり能力を向上させる最良の方法は、 自分が上位 20% に入ることを目指せるような集団に属することである。 まさに「寧ろ鶏口となるも牛後となるなかれ」。 上位20% に入ることがどうしても無理なら、 それはその集団が向いていないということである。 牛後に甘んじるよりは思い切って飛び出すべきだろう。

機会均等を押し進めようとするなら、

  • 「再チャレンジ支援」より 「ニート部門
  • 「一斉授業」より「習熟度別指導」
  • 「普通科高校」より「専門高校」
  • 「男女共学」より「女子校」
  • 大企業で中間管理職を目指すより、ベンチャーで経営幹部を目指せ
  • 大企業の研究所で主管研究員を目指すより、ベンチャーで CTO を目指せ
  • 雇われプログラマで人月を換金するより、 世界を変えるオープン ソース ソフトウェアを目指せ

「貧乏にならないための機会」はいたるところで見つけられる。 見つけようとする意志さえあれば。

Filed under: 自己啓発 — hiroaki_sengoku @ 07:22
2006年12月22日

La Fonera を無線LAN 端末として使ってみる hatena_b

La Fonera (FON ソーシャル ルータ) は、 無線LAN アクセス ポイントであるが、 中身は普通の Linux マシンなので、 いろんな用途に使うことができる。 一例として無線LAN 端末として使ってみる。
まずは ssh で La Fonera にログイン:

senri:/home/sengoku % ssh -l root 172.16.254.254
root@172.16.254.254's password: 


BusyBox v1.1.3 (2006.09.11-19:54+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.

 _______  _______  _______ 
|   ____||       ||   _   |
|   ____||   -   ||  | |  |
|   |    |_______||__| |__|
|___|

 Fonera Firmware (Version 0.7.1 rev 1) -------------
  * 
  * Based on OpenWrt - http://openwrt.org
  * Powered by FON - http://www.fon.com
 ---------------------------------------------------
root@OpenWrt:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth0
172.16.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

実験に先だってアクセスポイントとしての機能は殺してあるので、 ルーティング テーブルは至ってシンプル。 default route がないので、この時点では当然インターネットへはアクセスできない。

まず wlanconfig コマンドを使って無線LAN デバイスを端末モードで作成し、 続いて接続先アクセスポイント (もう一台の La Fonera ;-) の ESSID および WEP キー (もちろん伏せ字) を設定:

root@OpenWrt:~# wlanconfig ath0 create wlandev wifi0 wlanmode sta
ath0
root@OpenWrt:~# iwconfig ath0 essid "MyPlace"
root@OpenWrt:~# iwconfig ath0 key "6162636465666768696a6b6c6d"
root@OpenWrt:~# ifconfig ath0 up

これだけでアクセスポイントへの接続が完了する。 iwconfig コマンドで確認してみる。

root@OpenWrt:~# iwconfig ath0
ath0      IEEE 802.11g  ESSID:"MyPlace"
          Mode:Managed  Frequency:2.417 GHz  Access Point: 00:18:84:10:XX:XX
          Bit Rate:36 Mb/s   Tx-Power:18 dBm   Sensitivity=0/3
          Retry:off   RTS thr:off   Fragment thr:off
          Encryption key:6162-6364-6566-6768-696a-6b6c-6d   Security mode:restricted
          Power Management:off
          Link Quality=53/98  Signal level=-42 dBm  Noise level=-95 dBm
          Rx invalid nwid:664  Rx invalid crypt:0  Rx invalid frag:0
          Tx excessive retries:0  Invalid misc:0   Missed beacon:0

あとは 「ifconfig ath0 192.168.10.2」 などと手で IP アドレスを設定するか、 あるいは DHCP クライアントを実行すればよい。

root@OpenWrt:~# udhcpc -i ath0
info, udhcpc (v0.9.9-pre) started
debug, Sending discover...
debug, Sending select for 192.168.10.146...
info, Lease of 192.168.10.146 obtained, lease time 43200
deleting routers
adding router 192.168.10.1
adding dns 192.168.10.1
root@OpenWrt:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 ath0
169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth0
172.16.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0
0.0.0.0         192.168.10.1    0.0.0.0         UG    0      0        0 ath0

設定された default route 192.168.10.1 は NAT ルータなので、 これでインターネットへの接続が完了している。試しにアクセスしてみる:

root@OpenWrt:~# telnet mx.gcd.org 25
220 senri.gcd.org ESMTP
help
214 qmail home page: http://pobox.com/~djb/qmail.html
quit
221 senri.gcd.org
Connection closed by foreign host.
root@OpenWrt:~# wget http://www.klab.org/
Connecting to www.klab.org[211.13.209.203]:80
index.html           100% |*****************************| 11992       00:00 ETA
root@OpenWrt:~# ls -la index.html
-rw-r--r--    1 root     root        11992 Dec 22 05:27 index.html
Filed under: La Fonera — hiroaki_sengoku @ 14:44
2006年12月19日

La Fonera (FON ソーシャル ルータ) hatena_b

La Fonera (FON ソーシャル ルータ) を2台購入した (正確に言うと1台は店頭で購入、もう1台は無料キャンペーンで入手)。

入手方法 店頭で購入 無料キャンペーン
Model FON2100E FON2100E
S/N 8638001XXX 8646022XXX
MACアドレス 00:18:84:10:XX:XX 00:18:84:16:XX:XX
ROMバージョン 0.7.0 r3 0.7.0 r4

店頭で購入した物のほうが古いのは、まあ当たり前か。 新しい方のシリアル番号の下のほうの桁が 2万を超えているので、 現在は万単位で製造しているのだろう。 シリアル番号の上の方の桁、 および ROMバージョンが異なるのは、 製造ロットが違うから? もちろん両ルータともネットにつないでおくことにより、 現時点での最新バージョンである 0.7.1 r1 に自動アップデートした。

自動アップデートは、 cron から呼び出される /bin/thinclient プログラムによって行なわれる (/bin/thinclient は起動時にも呼び出される)。

root@OpenWrt:~# /bin/thinclient cron
upgrade.fon          100% |*****************************| 61473       00:00 ETA
This is a FON hotfix v2 archive
Verified OK
Upgrade name: upgrade_0711
Upgrading...

/bin/thinclient は、まず download.fon.com:1937 へ ssh 接続する。 ポート番号が変則的なのは、 間にファイアウォールがはさまっていないか確認するためか? なお、ssh は /etc/dropbear/key にある秘密鍵を用いて DSA 認証を行なう。 この秘密鍵は両ルータとも同じ内容だった。 /bin/thinclient は、次に この ssh セッションにおいて、 次の文字列を送信する。

mode='cron' wlmac='00:18:84:16:XX:XX' mac='00:18:84:16:XX:XX' fonrev='4' firmware='0.7.0'

ここで、「mode=」は /bin/thinclient の実行方法を示している。 cron から呼び出されたときは「mode='cron'」、 起動時に呼び出されたときは「mode='start'」。 「wlmac=」および「mac=」はそれぞれ、 無線(FON_AP)側の MAC アドレスと、 有線(WAN)側の MAC アドレス。 後者の MAC アドレスに 1 加えた値が前者のアドレスになっているようだ (ちなみにプライベート アクセス ポイント(MyPlace)側の MAC アドレスは、 さらに 1 を加えた値)。 「firmware=」および「fonrev=」はバージョンおよびリビジョン番号を示す。 つまり、個体識別ID を FON に送信しているものと考えられる。

すると、download.fon.com:1937 から、 以下のような sh スクリプトが返ってくる。

cd /tmp
wget http://download.fon.com/firmware/update/0.7.0/4/upgrade.fon
/bin/fonverify /etc/public_fon_rsa_key.der /tmp/upgrade.fon

rm -f /tmp/.thinclient.sh

exit

/bin/thinclient は、 この送られてきた sh スクリプトを /tmp/.thinclient.sh に保存した上で実行する。 つまり、 この例の場合だと、「upgrade.fon」をダウンロードして 「/bin/fonverify」に与える。 おそらく upgrade.fon がアップデートのための差分データで、 「/bin/fonverify」が、このデータを認証した上で展開しているのだろう。

アップデートの必要がないときは、 以下のような何もしない sh スクリプトが返ってくる。


rm -f /tmp/.thinclient.sh

exit

0.7.0 r4 と 0.7.1 r1 の差分をとってみた。
新規に追加されたファイル:

/etc/config/ntpservers
/etc/config/openports
/etc/config/webif
/etc/init.d/N45ntpclient

ntpclient を使って時刻あわせするようになったようだ。 アップデート前するは起動時に「2000年1月1日 9:00 JST」にセットされていた。 これでは起動時からの経過時間しかわからないので、 ntpclient を使うように変更したのだろう。

変更されたファイル:

--- /rom/etc/banner        2006-09-13 02:41:30.000000000 +0900
+++ /jffs/etc/banner        2006-11-22 04:07:20.000000000 +0900
@@ -4,7 +4,7 @@
 |   |    |_______||__| |__|
 |___|
 
- Fonera Firmware (Version 0.7.0 rev 4) -------------
+ Fonera Firmware (Version 0.7.1 rev 1) -------------
   * 
   * Based on OpenWrt - http://openwrt.org
   * Powered by FON - http://www.fon.com

--- /rom/etc/fon_revision        2006-09-12 06:43:42.000000000 +0900
+++ /jffs/etc/fon_revision        2006-11-16 05:08:40.000000000 +0900
@@ -1 +1 @@
-4
+1

--- /rom/etc/fon_version        2006-09-12 04:32:01.000000000 +0900
+++ /jffs/etc/fon_version        2006-11-16 05:08:40.000000000 +0900
@@ -1 +1 @@
-0.7.0
+0.7.1

--- /rom/etc/functions.sh        2006-09-12 04:32:01.000000000 +0900
+++ /jffs/etc/functions.sh        2006-11-15 02:44:26.000000000 +0900
@@ -112,3 +112,18 @@
         esac
 }
 
+#
+# This functions forwards a port. The next args are required:
+# $1 = WAN interface
+# $2 = Origin Port
+# $3 = Destination IP
+# $4 = Destination Port
+# $5 = protocol used
+#
+# Example: open_port $WAN 8080 192.168.1.2 80 tcp
+#
+open_port() {
+        iptables -t nat -A prerouting_rule -i $1 -p $5 --dport $2 -j DNAT --to-destination $3:$4
+        pdots=`echo $4 | sed 's/-/:/g'`
+        iptables        -A forwarding_rule -i $1 -p $5 --dport $pdots -d $3 -j ACCEPT
+}

--- /rom/etc/hotfix        2006-09-12 04:32:01.000000000 +0900
+++ /jffs/etc/hotfix        2000-01-01 09:35:00.000000000 +0900
@@ -0,0 +1 @@
+upgrade_0711

--- /rom/etc/init.d/S45firewall        2006-09-12 04:56:53.000000000 +0900
+++ /jffs/etc/init.d/S45firewall        2006-11-17 22:12:29.000000000 +0900
@@ -105,3 +105,7 @@
 }
 # check if the connection is already up and add WAN_HOOK rules automatically
 env -i ACTION=ifup INTERFACE=wan /bin/sh /etc/hotplug.d/iface/20-firewall
+#
+# Forwarded ports
+#
+/etc/config/openports $WAN

--- /rom/etc/sysctl.conf        2006-09-12 04:32:01.000000000 +0900
+++ /jffs/etc/sysctl.conf        2006-11-08 03:04:39.000000000 +0900
@@ -6,3 +6,4 @@
 net.ipv4.tcp_keepalive_time=120
 net.ipv4.tcp_timestamps=0
 net.ipv4.tcp_vegas_cong_avoid=1
+net.ipv4.ip_local_port_range=4096 8192

このあたりはわずかな修正だが...

--- /rom/etc/init.d/rcS        2006-09-12 22:16:13.000000000 +0900
+++ /jffs/etc/init.d/rcS        2006-11-15 02:44:26.000000000 +0900
@@ -21,13 +21,24 @@
         done
         
         while :; do
+                lock -w /var/run/restart-services
+                
+                # just in case
+                lock -u /var/run/network-connection 
+                killall lock
+                
+                # grab the locks again
                 lock /var/run/restart-services
+                lock /var/run/network-connection
+
                 killall N50chillispot
                 killall chilli
                 killall dnsmasq
                 ifup lan_noinet
                 ifup wan
                 /etc/init.d/S45firewall
+                
+                lock -w /var/run/network-connection
                 for i in /etc/init.d/N*; do
                   $i start 2>&1
                 done

これは、起動時に問題が起きることがあることへの対策か?

そして、0.7.1 へのアップデートで一番の目玉が、 /usr/lib/webif 以下の変更、 すなわち設定 Web インターフェースの多言語対応なのだろう。 /usr/lib/webif/lang ディレクトリが追加され、 /usr/lib/webif/lang/jp/fon.txt などのファイルに、 ローカライズのための文字列置換表が追加された。 /etc/config/webif に現在選択している言語が設定される。

日本語で設定できるようになったのはいいことだと思うが、 0.7.1 r1 にアップデートしてから、 FON Maps で濃いグリーンで表示されなくなったような気がする... アップデート前は FON_AP 登録位置に濃いグリーンの円が表示されていたのだが、 アップデート直後から薄いグリーン(直近で非アクティブ)の円で 表示されるようになってしまった。 前述したようにバージョン番号は thinclient で FON へ送信されるが、 0.7.1 r1 だと電波を出しているとは認識してもらえなくなってしまったのか? (thinclient の実行頻度を高めてみても、淡い緑のまま...)

もう一点、気づいた不具合として、 設定 Web インタフェースにて WAN 以外の設定を行なわずに何らかの設定を行なうと、 WAN が使えなくなってしまう、という問題がある。 すなわち、/etc/config/fon において

config network wan
        option mode        ''

などとなってしまう。 これだと WAN (つまり有線インタフェース) に IP アドレスが設定されない。 つまり有線もパブリック アクセス ポイント「FON_AP」も使えなくなってしまう。 エイリアス「eth0:1」には常に 169.254.255.1 が設定されるので、 大事には至らないのだが...

eth0:1    Link encap:Ethernet  HWaddr 00:18:84:16:XX:XX
          inet addr:169.254.255.1  Bcast:169.254.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING PROMISC MULTICAST  MTU:1500  Metric:1
          Interrupt:4 Base address:0x1000

このような状態になってしまったときは、 この 169.254.255.1 へアクセスするか、 プライベート アクセス ポイント「MyPlace」へアクセスして、 設定 Web インタフェースにて WAN の設定を行なえばよい。 例えば「DHCP」を設定すると、 /etc/config/fon の該当箇所が

config network wan
        option mode        'dhcp'
        option ipaddr        ''

となるので、「/sbin/ifup wan」を実行するか、 あるいは再起動すれば DHCP で取得した IP アドレスが WAN に設定される。

Filed under: La Fonera — hiroaki_sengoku @ 14:15
2006年12月14日

年功主義と実力主義 ~大企業とベンチャー~ hatena_b

私が大学を卒業したのはバブル (ネットバブルではなく、その前の前世紀のほう) がはじけた年でした。 当時は今ほどベンチャーは一般的ではありませんでしたし、 研究職を目指していた学生 (含む私) が就職先として大企業の研究所を選ぶのは、 ごく普通のことだったのではないかと思います。 終身雇用を信じていたわけでもありませんでしたが、 かといって自分自身が転職することになろうとは、 あまり考えてもいなかったような時代です。 今から振り返ってみれば「就職」というよりは「就社」という感覚に 近かったのだと思います。

その後の「失われた十年 (20年?)」の間に、 年功主義の綻びが誰の目にも明らかになってきて、 大企業以外の就職先を選ぶ人も増えてきましたし、 転職も一般的になってきました。 とはいえ、 就職先の選択肢が広がってきているわりには、 それぞれの企業がどんなところか実態を知ること無く、 なんとなくイメージで決めてしまっている学生さんも多いのではないでしょうか。 最近になって再び「寄らば大樹」的な傾向が復活しつつあるとも聞きます。

どのような進路を選ぶにせよ、 もっと企業の実態を知ってもらった上で決めて欲しいと 思っていたところ、 先日たまたまそういう機会を頂けたので、 学生さん相手のセミナーでお話ししてきました。

私は大企業の研究所に 8年間勤め、 その後ベンチャー (KLab(株), 当時の社名は (株)ケイ・ラボラトリー) の 立ち上げに参加し、順調に規模を拡大し、現在に至っています。 なので、

  • 大企業の研究所 (前職)
  • スタートアップ (立ち上げ当初のケイ・ラボラトリー)
  • 中規模ベンチャー (現在の KLab)

それぞれについて、実地の体験談をお話したわけです。 幸い、ベンチャーについて、もともと興味を持っていた学生さんもいて、 核心をついた (答えにくいとも言う ^^;) 質問もありました。 大企業とベンチャー、 それぞれの実態について理解を深めてもらえたと思っています。

まじめな東大生、悩みすぎ? 3割がニート不安

よく勉強する半面、将来の進路などに思い悩み、 不安や無気力に苦しむ学生が増えている-。 東大が13日公表した学生生活実態調査で、 こんな東大生の姿が浮かび上がった。
調査は昨秋、学部生約3500人を対象に実施(回収率39%)。 83%の学生が進路や生き方に悩んでおり、 自分がニートやフリーターになる恐れがあると感じている学生も28%に上った。
今朝のSankei WEBから引用

悩んでいるより、まずはいろいろな企業の実態について 見聞きしてみて、じっくり将来のことを考えて欲しいと思います。 私でよければ喜んでお話ししますので、 興味あるかたは是非ご連絡下さい。 ただし、企業の実態をできるだけ正確にお話しする、という主旨なので、 対象は (研究者・技術者を目指す) 学生さんに限定させて頂きたいと思います。

参考までに、セミナーで使ったスライドを FlashPaper で Flash 化したもの ↓ を公開します。 このスライド自体は 10ページしかありませんが、 それは「実態」は口頭でお話ししているからです ;-p。 質疑応答が盛り上がったので、2時間近くかかりました。

More...
Filed under: 元CTO の日記,技術と経営 — hiroaki_sengoku @ 16:48
2006年12月12日

戦略的に会社を利用する hatena_b

成功している中小企業が持つ5つの要素」から引用:

Five Secrets of High Performing Organizations」という報告書(PDF)が 公開されていました。 この報告書はアメリカで10~100人規模の企業300社以上を調査して、 成功している企業が持つ要素を5つにまとめています。 さらに、それぞれの要素を実現するためにはどのような努力をすれば良いかを 解説してありました。
...
4. 戦略的に技術を利用する
本当は何が必要かをわかるまでは課題に対して利用する技術を特定してはいけません。 成功している中小企業は、技術を使う事が目的ではなく、 戦略を達成するために技術を利用する事を知っています。 また、一度使うと決めた技術に対する調査も徹底して行われます。 調査には、技術そのものに対してだけではなく、 効果を最大化するためのトレーニングも含まれます。

誠にごもっとも。 目的は課題を解決することであって、技術はその手段に過ぎない。 いま自社にある技術を中心に考えてしまうと、 それに引きずられ課題を見失うことになりかねない。 だから技術のことはいったん忘れて何が目的なのかしっかり考えなければならない。 目標が明確になった時、 自社が現有する技術と必要な技術との間に乖離があれば、 自社の技術者たちはスキルシフトか職探しを求められる。

合理主義を貫こうとする限り、 この考え方には反駁の余地がない。 技術者が経営方針に振り回され続け、 その結果スキルを充分に伸ばせなかったとしても、 会社は技術者養成機関ではないのだからその責は負わない、 という考え方は首尾一貫している。

問題は、会社が合理主義を貫こうとしている現代においても、 技術者の側が合理主義を貫ききれていないことにある。 「成功している中小企業が持つ5つの要素」の 技術者版「成功している技術者が持つ5つの要素」が必要であろう。

4. 戦略的に会社を利用する

本当は何が必要かわかるまでは 自身の技術 (あるいはこれから身につけようとする技術) に対して 利用する会社を特定してはいけません。 成功している技術者は、 会社に貢献することが目的ではなく、 自らのスキルを向上させるために会社を利用することを知っています。 また、一度入社すると決めた会社に対する調査も徹底して行ないます。 調査には、会社そのものに対してだけでなく、 効果を最大化するための会社の制度および風土なども含まれます。 経営者の技術に対する考え方や、 昇給・昇格などの人事制度や、 どのような人物が自分の上司になるか、 あるいは互いに切磋琢磨できるような同僚がいるかどうかは、 自らのスキルをどれだけ伸ばすことができるかに大きな影響を与えるからです。

経営者にとっては、事業の遂行が目的であって技術はその手段に過ぎない。
技術者にとっては、自らの技術スキルの向上が目的であって 事業はその手段に過ぎない。

スキル向上に役立たなくなった仕事はどんどん捨てるべきである。 なぜなら技術スキルこそが、技術者にとって最大の資産であるからだ。 目先のキャッシュフロー (若いときの給料) にとらわれることなく、 将来のキャッシュフローを生み出すストックをどうやって積み重ねていくかを第一に考えて欲しい。

Filed under: 技術者の成長 — hiroaki_sengoku @ 12:31
2006年12月8日

なぜ人月見積もりが優れているのか hatena_b

人月見積りでは生産性が上がらない。 IPA が警告するまでもなく、 ソフトウェア技術者ならば誰しも 人月見積りに嫌悪感を持っているのではないでしょうか。 生産性を上げれば上げるほど金額が低くなってしまうし、 そもそも開発者の生産性なんて人によって大きく異なる (私の持論は、 「ピンとキリでは 1000倍の差がある」、です) のだから、 「標準的な技術者一人が一ヶ月かかる仕事」なんて基準をおいたところで 意味がありません。

人月見積もりについては、 「人月見積もり、生産性について」に いろいろな意見へのリンクがまとめられているので参考になります。 このように人月見積もりがなぜ問題なのか、 それこそ掃いて捨てるほど主張が繰り返されていますから、 いまさら同じようなことを唱えても仕方がありません。 そこで、ここでは逆にあえて肯定してみることにします。

そもそもこれだけ嫌われ者の「人月見積もり」が なぜいまだに行なわれているか、 そこには「人月見積もり」ならではの「良さ」があるからではないでしょうか?

誰にとっての「良さ」か? それはもちろん見積書を受取る「お客様」にとっての 良さです。 そもそも技術者の仕事なんてのは、 技術のことを知らない「お客様」にとってはよくわかりません。 「この仕事はすごく高度なんだぞーっ」って言われたって、 「ハイそうですか、じゃ沢山お金を払いますね」なんて言ってくれるお客様が いたらぜひお目にかかりたいものです。 ふつうは「どれだけ価値がある仕事なのか、きちんと説明してもらいたい」って 言うでしょう。

じゃ、どうやって説明しましょうか、技術者の仕事の価値を。

お客様にどう説明すれば納得してもらえるか考えるには、 お客様の立場に立つのが一番分かりやすいでしょう。 といっても、我々技術者にとっては、技術者でない人の気持ちを想像するのは ちょっと難しいですよね? こういうときの常套手段として、立場を逆にして考えてみます。 つまり我々技術者がお客で、 技術者でない相手が何かを我々に売りつけようと見積書を出している 状況を想像します。 売り付けるモノは (コモディティでなければ) 何でもいいんですが、 例えば何かの調査レポートにしましょうか。

相手は、このレポートがいかに創造的で革新的で価値があるものであるか、 必死に説明しようとしています。 が、残念ながら我々はその分野には全く疎いので、 どれだけ価値があるのやら、いまいちピンときません。 そもそも疎いからこそレポートを買おうとしているわけで、 ピンと来ないのは当たり前ですよね? さあ、どうしましょう?

そのレポートを活用することによって我々の利益がどのくらい増えるのか、 目に見えるのであれば分かりやすいのですが、 あいにくそのレポートが即、利益につながるわけではありません。 確かにそのレポートが役に立つであろうことは間違いない (だから買おうとしている) のですが、 利益増にどのくらい貢献しそうか、というと主観的に判断せざるを得ません。 でもって、主観ということになると、 どうしても他社の仕事より自社の仕事の 利益貢献度合いのほうを高く見積りたくなるものでしょう。

- o -

御社のレポートが、弊社のビジネスに大変役立つであろうことは 疑いの余地がないのですが、 もちろんレポートが直接利益を生むわけではなく、 弊社としてもいろいろコストをかけていく必要があるわけでして、 御社がこのレポート作成に多大なコストをかける必要があることは 重々承知しているのですが、 最終的な利益を御社と弊社とでどう配分すべきか、というと なかなか難しいものですね~ なにかもっとこう客観的な指標はないでしょうか。

と、いいますと?

例えば、このレポート作成に、何人の人が何ヵ月くらいかかりそうか、とか。

Filed under: 元CTO の日記,技術と経営 — hiroaki_sengoku @ 17:40
« Newer PostsOlder Posts »