仙石浩明の日記

2011年1月2日

香港海洋公園のパンダの動画を YouTube に投稿してみた

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

香港海洋公園 (Ocean Park) には四頭のパンダがいる (屋外と屋内に二頭ずつ)。 パンダというと、 いつも寝ている動物というイメージがあったのだが、 午前中に訪れたためかとても活動的で、 パンダの様々な習性・行動を観察することができた。

Giant Panda Le Le

屋外のパンダ館 「大熊猫之旅」 (Giant Panda Adventure) でオスのパンダ 「樂樂」 (LE LE) の動画をデジカメで撮影していたところ、 寝てしまった。 そろそろお昼も近いのでお昼寝タイムなのかと残念に思いつつ、 カメラを回し続けていたら、 少々意外な行動に出た。

パンダの飼育係にとっては日常的な光景なのであろうが、 そもそも活動的に動くパンダを見る機会がある人というのも少ないだろうから、 このようなパンダの行動を公開することは少なからず意義があることと思う。 そこで、 YouTube で公開してみることにした(再生すると音が出るので注意):

私にとって YouTube 初投稿。 残念ながら 「sengoku」 および 「sengoku + 数字」 という ID は取得できなかったので、 「HiroakiSengoku」 という ID を取得し、 投稿してみた。

おまけ:

Giant Panda Le Le Fake

園内を徘徊していた 樂樂 (LE LE) の着ぐるみ。 本物とは似ても似つかないというか、 どうやったらパンダがこんなに怖くなってしまうのか理解できないというか。

Filed under: 香港 — hiroaki_sengoku @ 12:59
2010年12月31日

香港の深水埗で華為 IDEOS U8150-B (EMOBILE S31HW) を買ってみた hatena_b

年末に香港へ行って電脳街を巡るのが毎年の恒例行事になってきた。 香港の電脳街は、 すでに秋葉原を追い越した感じがする。 近頃の秋葉は面白いモノをあまり見かけなくなり、 たまに見かけても、 大抵は香港などで流行ってるモノを単に持ち込んだだけで、 しかも値段は香港の倍以上だったりする。 デフレと言われて久しい日本だが、 PC にしろケータイにしろ値段が高すぎる。 これから発展していくアジアと、 これから衰退していくだけの日本ということなのか。

いつものように深水埗の高登電腦中心を巡回していたら、 大一(香港)有限公司IDEOS U8150-B を HK$1380 で売っているのを見つけた。 U8150-B といえば来年1月中旬にイー・モバイルから発売される予定の Pocket WiFi S (S31HW) の同型機。 ただし、 ファーウェイによれば、 グローバル展開されている 「IDEOS」 と日本向け S31HW は別の製品、 ということらしい。 なぜ日本は 「グローバル展開」 の対象ではないのだろう?

「Pocket WiFi S」は、 音声通話機能とAndroid™ 2.2を搭載したWi-Fiルーターです。 Android™ 2.2搭載端末としては国内最安※1となる端末価格19,800円(税込)での提供と、 国内最軽量※2の重さ約105gサイズを実現しました。

「国内最安」 「国内最軽量」 つまり世界基準では安くもなければ軽くもないということ。 本来 Android は 2.2 froyo 以降からデフォルトで 「ポータブルWi-Fiアクセスポイント」 として使えるのに、 わざわざ 「Wi-Fiルーター」 と呼んでいるあたりにも、 いかに日本が世界標準からずれてきてしまっているかを感じる。

とはいえ、 日本では 19,800円でしか買えない端末が HK$1380 (約 14,500円) で買えるのなら悪くない。 思わず衝動買いしてしまった。 画面が QVGA (2.8" 320x240) だったり、 内蔵カメラが貧弱 (一応 320万画素なのだが...) だったり、 CPU が MSM7225 528MHz だったりと、 Android 端末としては見劣りするものの W-CDMA I/II/V/IX (2100/1900/850/1700MHz) に対応したスマートフォンが 15,000円弱で買えるとなると、 一気に普及が進むヨカン。 ちょうど 2年前、 ここ香港で 4バンド GSM 機を HK$899 (約 10,000円) で買ったのが遠い昔のことのようだ。

中華 Android 端末なんて 「安かろう悪かろう」 だろうなどと思ってはいけない。 カスタマイズしすぎて Android とは別物になりつつある国産スマートフォンより、 よほど素直で使いやすい (そして圧倒的に安い)。 買った当初は値段相応の期待しか持っていなくて、 メイン端末が壊れたときなどの非常用端末くらいの感覚だったのだが、 高解像度の画面 (およびマルチタッチ) を必要としないアプリのほとんどがそのまま動いてしまった。 これなら非常用としてだけでなく、 大きな端末を持ち歩きたくない時とかにも使えそう。 Android 2.2 へアップデートできない一部の国産 Android 端末だと、 動かないアプリが沢山でてきてしまう。

ただし、 多くのアプリ開発者が QVGA などという尋常でない低解像度を想定していないためか、 Android マーケットでダウンロードできないアプリが少なからずある。 そういうアプリも別の Android 端末でダウンロードして得た apk ファイルを、 「adb install」 コマンドなどで IDEOS へインストールしてみると、 ほとんどが特に問題無く使えるようだ。

購入時のファームウェアは、

Androidバージョン2.2
ベースバンドバージョン2210
カーネルバージョン2.6.32.9-perf
huawei@product #1
ビルド番号U8150V100R001C127B818SP05

だったが、 この 「ビルド番号」 だと、 日本の一部(?)のキャリアの SIM でデータ通信できないという問題がある (音声通話は可能)。 例えば、 ドコモの SIM で mopera U を使う場合は 3G 通信可能だが、 ソフトバンクの銀SIM/黒SIM や、 WILLCOM CORE 3G の SIM では接続できなかった。

Huawei のページで U8150 のファームウェアを検索してみると、

ProductSoftware NameRelease Date
U8150U8150 V100R001C02B82 7SP02(Italy Vodafone ) 2010-12-15

が見つかった。 Italy Vodafone と書いてあるあたりが気になるが、 google で検索してみると、 このファームウェアを書込んだら 3G 通信できるようになった、 という旨のページを見つけた:

huaweidevice.comのサイトから、 「U8150 V100R001C02B82 7SP02(Italy Vodafone ) Host Software」 をダウンロードして解凍し、 出てきたファイルをmicroSDにコピーして、 ボリューム上と終話ボタンを押しながら電源を入れればファームアップが始まります。 一度目最後に失敗したら一度電池を抜いてもう一度同じ操作をすれば、ファームアップが正常に完了です。
ファームアップ後は、 まっさらに初期化されるため、 最初から設定しなおしです。 とはいえ、Andorid2.2とマーケットアプリのおかげで、 Googleアカウントを入れれば、 アプリも全部自動で勝手にマーケットからダウンロードして復活してくれるので楽です。 ファームアップ後のrooted も可能です。

さらに、 元のファームウェア V100R001C127B818SP05 も Huawei サイトからダウンロードできるようなので、 最悪の事態になっても元に戻せるだろうと思い、 上記ファームウェアを IDEOS に書込んでみた。 ファームウェアに同梱されている 「U8150 Software Upgrade Guideline」 に書込み方法の説明がある (英語版とイタリア語版の説明書が同梱)。

こばこのひみつ」 に書かれている通り、 書き込みの最後で 「失敗」 するのだが、 いったん電池を抜いて再起動すると正常に起動した。 また RageAgainstTheCage exploit を利用して root 権限を再取得できた。

更新後のバージョンは以下の通り:

Androidバージョン2.2
ベースバンドバージョン22201003
カーネルバージョン2.6.32.9-perf
huawei@product #1
ビルド番号U8150V100R001C02B827SP01

ダウンロードしたファームウェアのファイル名は末尾が 「...7SP02」 だったのに、 ビルド番号が 「...7SP01」 なのが気になるが、 ソフトバンクの銀SIM や WILLCOM CORE 3G SIM で試してみたところ、 正常に 3G 通信できている。

Filed under: Android,香港 — hiroaki_sengoku @ 01:11
2010年12月15日

“自分に向いたこと” を見つけ、自分にしか生み出せない価値を持て hatena_b

今年 8月に、 株式会社ドリームキャリアさんのインタビューを受けました。 ドリームキャリアさんは、 理系学生のための就職情報サイト 「理系ナビ」 を運営している会社です。 いま、 一部の大学で配布中の小冊子 「理系ナビ 10冬号」 の巻頭の 「トップインタビュー」 に掲載されました。 2012年卒 (に限りませんが) の学生さんの就職活動の参考になれば幸いです。

ドリームキャリアさんの許可を得て、 記事全文を転載します:

- o -

“自分に向いたこと” を見つけ、
自分にしか生み出せない価値を持て

KLab株式会社 取締役 CTO 仙石 浩明

会員数 500万人を超えるソーシャルアプリ 「恋してキャバ嬢」 を開発・運営するなど、 ソーシャル、クラウドをキーワードに事業展開を図る KLab株式会社。 その最高技術責任者 (CTO) である仙石浩明氏は、 専門誌でサーバ構築に関する連載を持ったことがあるなど、 業界でも名の知れた人物だ。 自身も IT業界でキャリアを積み、 KLab の取締役として数多くの新卒・中途採用の面接を重ね、 そして入社してきた相当数のエンジニアの仕事ぶりを見つめてきた仙石氏に、 就職先を選ぶ際に気を付けるべきポイントについて話を聞いた。

考える前にやってみる。
そして見つけた自分に向いた仕事

「世の中には、 仕事を楽しんでいない人が多いように感じますね。 どんな仕事をしても良いのですが、 楽しんでほしい。 楽しめないような職業は選んでほしくないのです。 楽しめなければ成長できるはずがない」

これから就職活動を迎える学生に、 このようなメッセージを送るのは KLab株式会社最高技術責任者 (CTO) の仙石浩明氏。 「朝、コンピューターに向かったら熱中してしまい、 気が付いたら夜だった」 というほどのめり込めることを仕事にする仙石氏は、 コンピューターとは中学時代に出会ったという。

ただ、コンピューターばかりにのめり込んでいたわけではない。 高校の時は数学好きで特殊相対性理論にも興味を持った。 哲学にもはまり、 大学で哲学を専攻しようかと真剣に悩んだこともあったが、 最終的には哲学よりも好きだった情報工学を学ぶことに決め、 京都大学工学部に進んだ。

そのまま大学院に進み、 就職活動を迎えた仙石氏は、 日立製作所への入社を選ぶ。 当時、 情報系の研究をするなら NTT の研究所が一番人気。 仙石氏は NEC のインターンシップにも参加していたが、 あまのじゃくな気質を発揮して 「それ以外から選ぼう」 という判断をしたそうだ。

日立に入社した後は、 遺伝的アルゴリズムやネットワーク基盤技術の研究を進める。 だが入社してから、 会社での仕事以上に熱中したのはテニス。 定時であがって、 日没までテニスを続けるような日々だった。

ただ、 そのテニスは半年間しか続けなかった。 「私の場合、 『向いているんだろうか』 と考える前にやってみます。 やってみて向いてないと思えば止めてしまう。 コンピューターの道だけを選んで成功したと人には思われているようですが、 物理、数学、哲学、テニスと試してみて、 止めたものも数限りなくあるのです」 と仙石氏は自身がテニスを止めた理由を説明する。

中途半端に好きな状態で居るのではなく、 興味を持ったのなら、 全力で取り組んでみる。 自分の能力がどれくらいのものかと、 把握できるまでやってみる。 そして向いてない、 止めると決めたらそれ以上は手を出さない。 それを繰り返すことで、 本当に自分が好きなもの、 人並み以上の価値を生み出せるものを見つけ出し、 それを仕事にしていったのが仙石氏のキャリアなのだ。

ほかの人にもできる仕事は最小限に
自分しかやれない仕事にフォーカス

元々、 定年まで働くつもりはなかったというが、 大手企業で働くうちに仙石氏は会社に違和感を覚えるようになる。 上司から指示される仕事は、 ほかの人にでもできるような仕事。 「ほかの人ができることをやっていても差が付かない」 と考えた仙石氏は、 必要最小限で済ませるようになり、 上司から指示されていない自分にしかできない研究に取り組んでいった。

しかし、 そうした働き方が評価されるわけではなく、 仙石氏の給与は年功序列でほかの社員と横一線。 会社から与えられる仕事は、 相変わらず代わり映えしなかった。

そんな時に、 仙石氏のところに1本の電話が掛かってくる。 外資系ヘッドハンティング会社の転職代理人からの転職を勧める電話だったが、 「転職という手段もあるのか」 とその時に初めて意識することになる。

そして転職代理人から薦められたモバイルコンテンツ開発会社に面接へ行ってみたところ、 「新しく研究開発の会社を立ち上げます」 という話を聞く。 当時の仙石氏はベンチャーに興味があったわけではなく、 携帯電話すら持っていないような状況だったが、 「面白いかもしれない。 やったことがないからやってみよう」 と転職を決意する。

そんな経緯で入社したのが KLab の前身となるケイ・ラボラトリー。 CTO に就任した仙石氏は、 世界初の携帯電話向け Javaアプリの開発、 携帯電話端末の技術仕様策定等、 モバイル技術開発会社として業界内で存在感を示す KLab を、 技術面でリードしていくことになる。

“自分にしかできないこと” を
評価してくれる会社の見分け方

仙石氏は自身の経験を踏まえ、 エンジニアが就職先を選ぶのなら、 “自分にしかできないこと” を評価してくれる会社にすべきだと訴える。

「会社は 『この仕事をやってほしい』 と考えて、 誰にでもできる仕事を任せるために人を採用している面もあります。 ですが、 そんな仕事ばかりでは人は成長できません」

自分に向いていること、 やっていて楽しいことを評価してくれる会社。 そんな会社かどうかを見抜くには、 「自分はこんなことを考えている」 と面接で伝えれば良いと仙石氏は助言する。 面接官が興味を持ってくれるのなら、 その会社には見込みがある。 だが、 興味すら示してくれないのなら、 社員一人一人の関心・適性に会社としての興味はないということ。 適材適所ではなく、 会社の都合で仕事が割り当てられると思った方が良いのだとか。

「私なんかは、 大学での研究について話を聞くのが好きですね。 教授に言われてやっている研究ではなく、 自分がやりたいと思って取り組んでいる研究。 その人が今までに熱中したことについて延々とでも話を聞くことで、 ほかの人と違うことをやることに価値を見出せる人かどうか、 簡単に分かります」 (仙石氏)

“自分に向いていること” を
見つけるために設けたどぶろく制度

自分だけにしかできない仕事。 そのための時間を取ってもらうために KLab では 「どぶろく制度」 を設け、 標準労働時間の 10% までなら、 好きなように使えるようにしている。 ただ、 どぶろく制度の目的はそれだけではない。 仙石氏は片っ端から興味を持ったことにチャレンジして “自分に向いていること” を見つけたが、 すべての人がそれを見つけられているわけではない。 「今やっている仕事内容とは違うことに仕事時間を使うことで、 “自分に向いていること” を見つけてほしい。 向いているかどうかを確かめるためには、 転がり込んできたチャンスにチャレンジすることが必要。 『時間がないから』 という理由で動かない人が多いので、 それを言い訳にさせないためにつくったのがどぶろく制度なのです」 と仙石氏は制度の狙いを明かす。

また、 KLab では “自分に向いていること” を突き詰めて “自分にしかできないこと” を身に付けているエンジニアを正当に評価するため、 ダイナミックな人事評価制度を導入している。 基本的に年に一度、 成果と能力に基づいて給与を改定するが、 新卒入社の社員には、 入社から 3年間は半年に一度見直しをかける。 能力のある社員なら、 給与テーブルのグレードを一足飛びで駆け上がっていくため、 入社して数年の間に数百万円の年収差が生まれることもざらにあるのだとか。

質問すること、チャレンジすることで
伸びる 「考える力」

もちろん、 “自分に向いていること” が見つかったからといって、 “自分にしかできないこと” がすぐにできるようになるわけではない。 そのためには自分自身の成長が必要になる。

成長のためには、 「考える力」 が重要だと仙石氏は言う。 例えば、 多くのエンジニアはある程度仕事を覚えると、 あとは惰性で仕事をするようになる。 仕事で分からないところが出てきても、 検索エンジンを使って調べれば、 だいたいの答えが出てきてしまう。 チャレンジが無くなることで、 そのエンジニアの成長はそこで止まってしまうのだ。

そんな仕事スタイルのエンジニアこそ、 誰にでもできる仕事しかできなくなると仙石氏は批判。 「考える力」 とは抽象的な概念なので説明しづらいが、 知りたいことに憶さず質問して興味を持ったことを深掘りしていくこと、 そしてチャレンジすることを通して育つ力だと説明する。

ちなみに KLab では、 自分の興味・関心のあるテーマについて大勢のエンジニア相手に発表させる勉強会を開くことで、 エンジニアとしての成長を促している。 自分が深めたいと考えているテーマを自分の力で考えて発表させることで、 あるいはそれを聞いて質問する力を伸ばさせることで、 社員の成長を促しているのだとか。

格差が進む 20年後のために
自分だけの価値を生めるように

しかし、 なぜそこまで “自分に向いていること” “自分にしかできないこと” にこだわらなくてはいけないのだろうか。 仙石氏はその理由について次のように語る。

「これから就職する学生が迎える将来の社会について、 私はかなり悲観的な見方をしています。 格差社会と言われていますが、 今から 20年後には格差がもっと進むのではないでしょうか。 日本の会社は、 まだまだ昔ながらの年功序列に支えられています。 成果主義が広まっているとは言うものの、 仕事のできる人とできない人とで、 そんなに差は付いていません。 これから就職する学生の皆さんが 40〜50歳になっているころには、 それこそ年収が1ケタ違うのも当たり前にあり得るのではないでしょうか」

実際に 20年前と今とを比べると、 誰にでもできる単純作業のうち、 かなりの部分が技術の進歩によって一掃されてしまった。 現在、 誰にでもできる仕事でお金をもらっている人は、 20年後にどうなってしまうのか。 そう考えていくと、 他人には生み出せない自分だけの価値を持つことが必要なのだと仙石氏は話している。

20年後のあなたは “自分に向いていること” を見つけて、 “自分にしかできないこと” を習得できているだろうか。 本当に成長したいのなら、 「当社が一番適した会社かどうかは分かりませんが、 技術者が会社に依存せず、 自分の価値を築ける一番の会社にしようと、 少なくとも私は本気で考えています」 という仙石氏の居る KLab も、 就職先の選択肢に含めてみてはどうだろうか。

Filed under: 元CTO の日記,自己啓発 — hiroaki_sengoku @ 15:19
2010年12月4日

Android 端末 IS01 のカーネルを入れ替えてみた 〜 さよならデッカード LSM tweets

先週末 IS01 で root 権限が必要なアプリが使えるようになったばかりなのに、 そのわずか 4日後、 スマートフォン@2ch掲示板に以下の書き込みがあり、 カーネル空間への侵入口が明らかにされてしまった。 一番乗りを果たした goroh_kun さんに敬意を表しつつも、 IS01 のプロテクトがこの程度だったことが残念でもある。 「root を取られても大丈夫な作りになっている」 と開発者が豪語し、 しかも IS01 の発売から 5ヶ月間も破られなかったのだから、 さぞかし鉄壁の守りなのだろうと思っていたのに、 こんな分かりやすい穴があったとわ... (負け惜しみ ^^;)。

【ROM焼き】au IS01 root2 〜わたくし達も未来へ〜

...

317 :goroh_kun:2010/12/01(水) 03:14:21 ID:LGLTLBmZ

自動起動仕込むところを大体見つけました。
どなたか協力お願いします。

/data/の直下にlocal.propを置く
ここで、内容をoverrideできます。
サービス起動時のおダイナミックライブラリがある
サービスプログラムを探す。

getpropしてみると、

rild.libpathが/system/lib/libril-qc-1.soといかにも起動時に使っていそう。
rildaemonから使われているプラグインと思われます。

そこで、
local.propの中身を
rild.libpath=/data/lib/libril-wrapper.so
rild.wrapper.cmd=/data/rootkit.boot.sh
rild.libpath2=/system/lib/libril-qc-1.so

で、このプラグインを/data配下から読み出されるようにして、
libril-wrapper.soにて、ダイナミックロード時に
自動実行したいプログラムを指定できるようにします。
で、自動実行したいプログラムをlibril-wrapper.soで実行後
本来のrild用のプラグインをロードして、rildに引き渡すようにします。

これでいけるのではないかと思っているのですが、やるよって方は
いらっしゃいますか?

rild.libpath プロパティは、 RIL (Radio Interface Layer) デーモン rild が参照している。 rild は rild.libpath の値をダイナミックライブラリのパス名として dlopen(3) に渡し、 dlsym(3) でライブラリ内の関数 RIL_Init 呼び出す。 つまり、 rild が起動される前に rild.libpath の値を書き換えておけば、 rild に任意のプログラムを起動時に実行させることが可能になる。

したがって守る側 (つまり開発者の立場) から考えると、 (1) rild.libpath が書き換えられないようにするか (2) 任意のプログラムの実行が脅威にならないよう、 rild 起動前に防御を固めてしまえばよい。

プロパティは、 以下の 4 つのファイルで設定できる。 どれか一つでも改変可能であれば、 (1) の方法は使えない。

  • /default.prop
  • /system/build.prop
  • /system/default.prop
  • /data/local.prop

IS01 の場合、 (たとえ root が奪取されても) /system ディレクトリ下は改変できないようになっている。 また、 /default.prop は initramfs 内のファイルなので、 改変しても再起動すれば揮発してしまう。 だから上記 4ファイルのうち /data/local.prop 以外は改変不可能。

Android では /system ディレクトリ下は read only でも構わないが、 /data ディレクトリ下は Android 動作中に書き換えることが前提となっている。 もちろん /data/local.prop だけは書き換え不可能にするとか、 あるいは再起動時に自動的に /data/local.prop を書き戻す、 という方法も考えられなくもないが、 そもそも論で言えば /data/local.prop は、 システムのプロパティをオーバーライドするための設定ファイルであって、 その本来の趣旨からいえば書き換え不可能にすることは望ましくない。

したがって守るなら (2) の方法が自然。 というか、 /data/local.prop で rild.libpath の値を変更されても、 rild はユーザ空間で動くデーモンなのだから、 本来は 「任意のプログラムの実行」 が可能でも脅威にはならないはず。

ところが IS01 の開発チームは致命的なミスを犯した。

More...
Filed under: Android — hiroaki_sengoku @ 12:59
2010年11月27日

月額8円で運用できる Android 端末 IS01 で、root 権限が必要なアプリを使えるようにしてみた tweets

Android 搭載スマートブック IS01 by SHARP (愛称 「メガネケース」) が、 本体価格 0円 + 契約事務手数料 2835円 + 月々 8円 * 2年しばり = 3027円 で入手できるとネット上で話題になっていたので買ってみた。 メジャーアップデート対応なしと公式に宣告されてしまった IS01 ではあるが、 「ワンセグ放送が視聴できて WiFi 通信もできるポメラもどき」 と割りきったとしても激安なので 「ふたつでじゅうぶんですよ」 と言われつつ 3つ入手した。

「グローバル展開もしていないから xda の助けも得られない」 と総統閣下に嘆かれつつも、 RageAgainstTheCage exploit を利用すれば root になることは一応できる (ベースバンドバージョン / ビルド番号 01.00.07 で確認):

senri:/home/sengoku % adb push rageagainstthecage /data/local/tmp/
senri:/home/sengoku % adb shell
$ cd /data/local/tmp
$ chmod 755 rageagainstthecage
$ ./rageagainstthecage
[*] CVE-2010-EASY Android local root exploit (C) 2010 by 743C

[*] checking NPROC limit ...
[+] RLIMIT_NPROC={1856, 1856}
[*] Searching for adb ...
[+] Found adb as PID 8223
[*] Spawning children. Dont type anything and wait for reset!
[*]
[*] If you like what we are doing you can send us PayPal money to
[*] 7-4-3-C@web.de so we can compensate time, effort and HW costs.
[*] If you are a company and feel like you profit from our work,
[*] we also accept donations > 1000 USD!
[*]
[*] adb connection will be reset. restart adb server on desktop and re-login.
$ 
senri:/home/sengoku % adb shell
# 

ただし rageagainstthecage はタイミングが良くないと失敗するので、 少なくとも数回は繰り返し実行しないと root が取れない (運が悪いと何度実行しても成功しない上に adb で接続できなくなって再起動する羽目に陥ることも)。 IS01 を再起動するたびに rageagainstthecage を実行して root を取り直すのは著しく手間なので、 root が取れたら su コマンドをインストールしておくべき。

インストール先は、 一般ユーザ (shell 権限) でアクセス可能で、 かつ再起動しても消えない (つまり tmpfs とかでない) ボリュームで、 かつ mount 時に nosuid オプションをつけられていないことが必須条件になるが、 幸い IS01 ではフラッシュメモリ /dev/block/mtdblock7 が /sqlite_journals に nosuid 無しで mount されている:

senri:/home/sengoku % adb shell
$ grep -v '\(tmpfs\|nosuid\)' /proc/mounts
rootfs / rootfs ro 0 0
devpts /dev/pts devpts rw,mode=600 0 0
proc /proc proc rw 0 0
sysfs /sys sysfs rw 0 0
/dev/block/mtdblock7 /sqlite_journals yaffs2 rw 0 0
/dev/block/mtdblock5 /system yaffs2 ro 0 0
$ ls -l /sqlite_journals
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settings
drwxrwx--x system   system            1980-01-06 09:00 com.android.providers.settingsex
drw-rw-rw- root     root              2010-11-26 18:51 lost+found

sqlite_journals というディレクトリ名からして SQLite 用のディレクトリと思われるが、 それならなぜ nosuid がつけられていないのだろうか? ガチガチに防御を固めているはずの IS01 にしては珍しい抜け穴の一つ。

ちなみに上記のように /dev/block/mtdblock5 も /system ディレクトリに nosuid オプション無しでマウントされているが、 こちらはカーネルの MTD ドライバで書き込みが禁止されている (SHARP が公開している IS01 のカーネル kernel/drivers/mtd/devices/msm_nand.c 参照) ので、 簡単には変更できそうにない (少なくとも rw で remount するだけでは書込めない)。

というわけで /sqlite_journals に bin ディレクトリを作って ChainsDD の su コマンド (android_system_extras に含まれる) を置いてみる。

# ls -l /sqlite_journals/bin/su
-rwsr-sr-x    1 root     root         26256 Aug 17 17:27 /sqlite_journals/bin/su

ところが!

More...
Filed under: Android — hiroaki_sengoku @ 15:46
2010年11月13日

iモード.net と imoten を使って iモードメールを自動送信する 〜 SubEtha SMTP のバグ hatena_b

iモードメールを Android 端末で送受信するには、 sp モードiモード.net を利用する。 前者は sp モード専用の APN (Access Point Name) spmode.ne.jp に接続しないと利用できない (つまり無線LAN 経由では使えない) 上に、 ドコモが IMEI (International Mobile Equipment Identity, 端末識別番号) をチェックしていて、 契約端末でないとこの APN に接続できない (らしい)。 なので私は後者を利用している。

iモード.net は、 PC から iモードメールを読み書きできる Webメールサービス (月額210円)。 PC の代りに Android 端末上のアプリ (iMoNi が有名) からアクセスすれば、 Android 端末で iモードメールを読み書きできる。 あるいは iモード.net からメールを読み取って別のメールアドレス (例えば Gmail) へ転送するプログラムを (どこかのサーバで) 動かしておけば、 iモードメールを Android 端末で普通のメールとして受信できる。

前者の方法は、 メールの新着をどうやって知るか、 という問題がある。 Android 端末上のアプリが定期的に iモード.net にアクセスして新着をチェックすることになるが、 電池の消耗を考えればせいぜい 15分に一度チェックするのが限界で、 それ以上の頻度でチェックするのは現実的ではない。 つまり最大 15分間は新着を知るのが遅れる。 iMoNi にはドコモから送られてくる WAP PUSH を検知してメールをチェックしにいく機能もあるが、 3G 接続中は (なぜか) WAP PUSH を受け取れないらしい。

それに対し後者の方法は、 転送プログラムは Android 端末上で動くわけではないので、 電池のことを心配せずに iモード.net を頻繁にチェックできる。 iモード.net で取得したメールを Gmail へ転送するようにすれば、 Android 端末は Gmail の着信をリアルタイムで知ることができる。

前者の方法は Android 端末だけで iモードメールを読み書きできるのに対し、 後者の方法は転送プログラムを動かすサーバ (24時間稼働が望ましい) が必要になるので万人向けではないが、 そういったサーバを確保できるのなら、 iモードメールを普通のメールと同様に扱える後者の方法が圧倒的に便利。

というわけで、 私は imoten (imode.netのメールをSMTPで転送するプログラム) を使っている (まだ使い始めて 2日だが)。 1分も遅れずに Android 端末で着信を知ることができるので、 とても便利。

imoten には iモードメールを送信する機能もある。 すなわち imoten が SMTP サーバとして振る舞い、 受け取ったメールを iモード.net へ転送する。

ということはつまり Android 端末から iモードメールを送ることができるのはもちろん、 PC 上のメーラや任意のメール配信プログラムから iモードメールが送ることが可能。 試しにテストプログラムを書いてみた:

#!/usr/bin/perl
use strict;
use warnings;
use Net::SMTP;

my $smtp = Net::SMTP->new('senri.gcd.org', Port => 42525, Debug => 1);
$smtp->auth('imoten', 'xxxxxxxx');
$smtp->mail('sengoku');
$smtp->to('sengoku@gcd.org');
$smtp->data();
$smtp->datasend("To: sengoku\@gcd.org\n");
$smtp->datasend("\n");
$smtp->datasend("A test message\n");
$smtp->dataend();

imoten を動かしているサーバ senri.gcd.org の 42525 番ポートに SMTP 接続してメールを送信するプログラム。 imoten は SMTP PLAIN 認証をサポートしているので、
「$smtp->auth('imoten', 'xxxxxxxx');」 で、
ユーザ 「imoten」、 パスワード 「xxxxxxxx」 (伏字) を指定している。

ところが!

More...
Filed under: Android — hiroaki_sengoku @ 08:19
2010年10月25日

Android 端末 (Nexus One) でアプリを SDカードの ext3 パーティションにインストールする (Apps 2 SD) hatena_b

新しい Android 端末が次から次へと発表される今日このごろ、 1月6日に発表された Nexus One は (まだ一年たっていないのに) 旧型機といった感じになりつつある。 特に端末内部メモリ容量が 200MB しかないのは致命的。 正確に言えば内蔵フラッシュメモリは 512MB だが、 システム側で 300MB ほど使うので、 ユーザが自由に使えるのは残り 200MB 程度となる (もちろんそれとは別に SD カードが使える)。

いまどき 200MB というのはいかにも少ない。 例えば私が Nexus One で使ってるアプリのうち、 サイズの大きいものを一部ピックアップしてみると、

アプリappdata合計
Google Earth20.32MB0.24MB20.56MB
Twitter2.70MB14.57MB17.27MB
Skype10.68MB3.40MB14.08MB
OpenWnn Flick対応版7.68MB4.78MB12.46MB
Adobe Flash Player12.40MB0.00MB12.40MB
Google Map8.09MB1.44MB9.52MB
Graffiti5.14MB0.84MB5.22MB
K-9 Mail2.67MB1.07MB3.74MB

わずか 8個のアプリだけで 100MB 近く使っている。 200MB に収めようと思えば、 インストールするアプリを相当厳選する必要がある (100個程度で限界)。

さすがにこれでは使い物にならないということで、 アプリを SDカード上にインストールできるようにする仕掛け Apps 2 SD (A2SD) が提案されてきた。 A2SD には App2sd など、 いろんな実装があるが、 基本的には SDカードに ext3 (あるいは ext4) パーティションを切り、 /system/ext あたりにマウントして (実はこれが難しい, 後述)、 /data/app などからシンボリックリンクを張る。

つまり、こんな感じ:

# cat /proc/mounts
        ...
/dev/block/mtdblock3 /system yaffs2 ro,relatime 0 0
/dev/block/mtdblock5 /data yaffs2 rw,nosuid,nodev,relatime 0 0
        ...
/dev/block/mmcblk0p2 /system/ext ext3 rw,nosuid,nodev,relatime,errors=continue,data=writeback 0 0
/dev/block/vold/179:1 /mnt/sdcard vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
/dev/block/vold/179:1 /mnt/secure/asec vfat rw,dirsync,nosuid,nodev,noexec,relatime,uid=1000,gid=1015,fmask=0702,dmask=0702,allow_utime=0020,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro 0 0
        ...
# cd /data
# ls -l
drwxrwxr-x    1 system   system        2048 Oct 23 19:33 anr
lrwxrwxrwx    1 root     root            15 Oct 23 20:45 app -> /system/ext/app
lrwxrwxrwx    1 root     root            23 Oct 23 20:46 app-private -> /system/ext/app-private
lrwxrwxrwx    1 root     root            18 Oct 23 20:47 backup -> /system/ext/backup
lrwxrwxrwx    1 root     root            24 Oct 24 11:23 dalvik-cache -> /system/ext/dalvik-cache
drwxrwx--x    1 system   system        2048 Oct 24 11:59 data
drwxr-x---    1 root     log           2048 May 13 08:47 dontpanic
lrwxrwxrwx    1 root     root            17 Oct 23 20:02 local -> /system/ext/local
drwxrwx---    1 root     root          2048 May 13 08:47 lost+found
drwxrwx--t    1 system   misc          2048 Oct 24 14:02 misc
drwx------    1 root     root          2048 Oct 23 19:33 property
drwxrwxr-x    1 system   system        2048 Oct 24 14:20 system
lrwxrwxrwx    1 root     root            22 Oct 23 21:11 tombstones -> /system/ext/tombstones

アプリを Android 端末にインストールすると、 パッケージファイル (*.apk) が /data/app ディレクトリに格納 (「コピー防止」 アプリは /data/app-private ディレクトリに格納) され、 クラスファイル (classes.dex) が /data/dalvik-cache ディレクトリに格納される。 上記のように /data/app および /data/dalvik-cache はそれぞれ /system/ext/app および /system/ext/dalvik-cache へのシンボリックリンクにしてあるので、 内蔵フラッシュメモリ (/data パーティション) の代わりに SDカード (/system/ext パーティション) へ格納される、という仕掛け。

/data/data ディレクトリにはアプリが使用するデータが格納される。 /data/app および /data/dalvik-cache が、 アプリのインストール/アンインストール時のみ書き込みが行なわれるのに対し、 /data/data はアプリ動作中も読み書きが行なわれるわけで、 /data/data を SDカードへ移すとアプリの実行速度が低下したり、 あるいは頻繁な書込みによって SDカードの寿命が短くなったりする恐れがある。 もし /data/data も SD カード上に置こうとするなら、 使っている SDカードが充分高速 (Class6 以上?) で、 かつウェアレベリング (wear leveling) をサポートしているか確認したほうが無難。

私は /data/data は /system/ext へのシンボリックリンクを張らずに /data パーティションをそのまま使用している。 現時点での各パーティションの空き容量は次のような感じ:

# df
/dev:               197552K total,       0K used,  197552K available (block size 4096)
/mnt/asec:          197552K total,       0K used,  197552K available (block size 4096)
/system:            148480K total,  127580K used,   20900K available (block size 4096)
/data:              200960K total,  102148K used,   98812K available (block size 4096)
/cache:              97280K total,    9128K used,   88152K available (block size 4096)
/system/ext:       3690832K total,  269908K used, 3420924K available (block size 4096)
/mnt/sdcard:      11547432K total, 6808720K used, 4738712K available (block size 8192)
/mnt/secure/asec: 11547432K total, 6808720K used, 4738712K available (block size 8192)

私が使ってる SD カードは 16GB Class6 だが、 /data パーティションはまだ 99MB の空きがあるので /data/data を無理に /system/ext へ移動させることもないと判断した次第。

なお、Android 2.2 froyo から OS 標準で、 アプリを SD カードへ移動できるようになった。 しかし対応アプリでないと移動できないし、 現時点では多くのアプリが対応していない。 さらに 「USBストレージをON にする」 と SD カードへ移動したアプリは USB ストレージが ON の間は使えなくなるので注意が必要。

というわけで、 アプリを SDカード上にインストールできるようにするのは、 シンボリックリンクを /data から /system/ext へ張るだけのことなので全く難しくない。 しかしながら、 どうやって SDカード上の ext3 パーティションをマウントするか、 という問題がまだ残っている。

More...
Filed under: Android — hiroaki_sengoku @ 09:56
2010年9月21日

地デジ MPEG-2 TS の PCR/PTS/DTS ラップアラウンド (PCR Wrap-around) 問題を回避して ffmpeg で PS 変換できるようにしてみた hatena_b

(地上)デジタルTV 放送を Linux サーバで予約録画しまくるようになった今日このごろ、 たまに失敗するのが気になっていた。 失敗といっても、 録画した MPEG-2 TS (Transport Stream) ファイルを VLC で再生する分には問題がないが、 ffmpeg を使って MPEG-2 PS (Program Stream) フォーマットへ変換しようとすると途中で映像が止まってしまう (TS 読み込みに失敗しているので他のフォーマットへの変換もおそらく無理)。

おそらく TS のデータに何らかの誤りがあって ffmpeg が映像データを読むのをそこで止めてしまうためだろうと思っていた。 そりゃ放送なんだからノイズが混じることもあるだろうと思い込んでいたのだが、 よほど電波状況が悪くない限りエラー補正が効くだろうから、 ノイズが原因というのはありそうもない話。

なぜ PS へ変換したいかといえば、 TS のままだとファイルサイズが大きすぎる (2時間の映画番組とかだと 12GB を超える) ので、 PS へ変換し (CM カットなどの編集を行なうときは PS の方が便利)、 さらに MPEG-4 などへ変換してサイズを小さくしたいから (もちろん保存する必要がない番組は見たらすぐ消す)。 ところが PS への変換に失敗した番組は TS のまま保存せざるを得ず、 そういうファイルが増えてきて、 2TB のハードディスクもだんだん手狭になってきた。

そこで、 何が原因で TS の読み込みに失敗しているのか、 まずは調べてみようと思ったのだが、 いかんせんファイルがデカすぎる。 12GB もあるとファイルの内容をダンプするのもままならない。 そこで変換が失敗する近辺のデータだけ抜き出して詳しく調べてみようと考えた。 MPEG-2 TS の仕様を調べてみると、 PCR (Program Clock Reference) なるタイムスタンプが 100msec 以下の間隔で MPEG-2 TS には埋め込まれているらしい。 問題の TS ファイルは、 2時間の番組中、 先頭から 1時間35分41秒ほど経過したあたりで読み込みに失敗するので、 PCR の値を手がかりにその近辺のデータを抜き出そうと考えた次第。

まずは PCR の値を表示する perl スクリプト tsdump.pl を作ってみた (後述する tsrenum.pl に -v オプションを与えることによって同じ出力が得られる)。

% ./tsdump.pl -v < 133000_GR23.ts
0008c4a PCR 24:55:00.570 8073051319+138
001a12d PTS 24:55:01.111 8073100071
001a132 DTS 24:55:01.011 8073091062
001d841 PTS 24:55:00.748 8073067367
0022556 PCR 24:55:00.628 8073056524+157
00226d5 PTS 24:55:01.045 8073094065
0026a65 PTS 24:55:00.769 8073069287
0029dcd PTS 24:55:01.078 8073097068
00300f1 PTS 24:55:00.791 8073071207
0033225 PTS 24:55:01.212 8073109080
003322a DTS 24:55:01.111 8073100071
0038f69 PTS 24:55:00.812 8073073127
003bcea PCR 24:55:00.685 8073061729+175
...

「133000_GR23.ts」 が問題の TS ファイル (23チャンネルなので TV東京 ^^;)。 次の 「0008c4a PCR 24:55:00.570 8073051319+138」 という行は、 ファイル先頭から 0008c4a バイト目に PCR があって、 そのタイムスタンプが 「24:55:00.570」 であることを示す。

行末尾の 「8073051319+138」 は生の PCR の値。 つまり、 PCR は 33bit の 「PCR base」 と 9bit の 「PCR extension」 から構成されるが、 それぞれ 8073051319 と 138 であることを示す。 PCR base は 90kHz の解像度、 PCR extension は 27MHz の解像度。 とりあえずここでは後者は無視して、 前者 8073051319 を 90000 (= 90kHz) で割ると 89700.570 秒で、 時分秒に直すと 24:55:00.570 になる。

PCR の他に、 PTS (Presentation Time Stamp) と DTS (Decode Time Stamp) というタイムスタンプも MPEG-2 TS には埋め込まれている。 これらは PCR と違って 90kHz の解像度の 33bit のデータのみ。 MPEG-2 TS では、 PCR の時刻を基準にして、 復号する時刻 (DTS) と再生する時刻 (PTS) を定めている (音声と映像の同期のためにも使われる)。 前述したプログラムの出力において、 2カラム目に PTS, DTS と出力している行は、 それぞれ PTS, DTS の値であることを示す。

で、 PCR の値を表示させてみていきなり気付いてしまったのだが、 この問題の TS ファイルは PCR の値が 33bit の上限値ギリギリ。 33bit の上限は 0x1FFFFFFFF = 8589934591 だから、 PCR は 26:30:43.717 (26時間30分43秒余) までしかカウントできない。 このファイルは冒頭のタイムスタンプが 24:55:00.570 だから、 冒頭から 01:35:43.147 ほど経過したあたりで PCR の値が桁あふれ (オーバーフロー) を起こして 00:00:00.000 へ戻ってしまう (ラップアラウンド)。

これは冒頭から 1時間35分41秒ほど経過したあたりで ffmpeg が止まってしまう症状にピッタリ符合する (2秒ほど差があるのは MPEG-2 復号に要する時間?) ので、 もう原因は PCR のラップアラウンド (PCR Wrap-around) で間違いないと推測。

「VLC で再生する分には問題がない」 と書いたが、 この問題の TS ファイルを VLC で再生して 1時間35分41秒あたり (VLC の場合 TS 再生時は時刻表示を行なわないのでシーンで探さざるを得ず大変) をよく見てみると、 再生が一瞬 (1秒ほど?) 止まっていた。 26時間半に一度、 必ず起こるラップアラウンドなのに、 VLC でも完全な対策は (まだ) 行なわれていないようだ。

原因調査の準備のために作ったスクリプトが、 いきなり原因究明の役に立つとは (^^;) と思いつつ、 tsdump.pl にタイムスタンプの値を付け替える (すなわち 24:55:00.570 から始まるタイムスタンプを 00:00:00.000 始まりにリナンバーする) 処理を付け加えたスクリプト tsrenum.pl を書いてみた (後述する pcr_write, ts_write 関数を追加しただけ)。

% ./tsrenum.pl < 133000_GR23.ts > 133000_GR23_fixed.ts

tsrenum.pl を使ってタイムスタンプを付け替えた 133000_GR23_fixed.ts は、 ffmpeg を使って PS 変換ができるようになった。 原因調査の準備のために作ったスクリプトが、 ほとんどそのまま問題解決の役にも立ってしまった (^^;)^2。

tsrenum.pl は、 標準入力から読み込んだ MPEG-2 TS において最初に現れた PCR/PTS/DTS タイムスタンプを基準 (下記スクリプト中の $pcrOrg) にして、 タイムスタンプを付け替えて標準出力へ書き出す:

#!/usr/bin/perl
use strict;
use warnings;
use POSIX;
use Getopt::Std;
our ($opt_v);
getopts('v') || &help;
my $PacketSize = 188;
my $offset = 0;
my $pcrOrg;
for (;;) {
    my $buf;
    my $len = read(STDIN, $buf, $PacketSize);
    last unless $len > 0;
    my @buf = unpack("C*", $buf);
    die if $buf[0] != 0x47;        # sync byte
    my $ind = (($buf[1] & 0xE0) >> 5);
    my $adp = (($buf[3] & 0x30) >> 4);
    my $pos = 4;
    if ($adp & 0x2) {        # Adaptation field exist
        $pos++;
        my $af = $buf[$pos++];
        if ($af & 0x10) {
            my ($pcr33, $pcr9) = &pcr(\@buf, $pos);
            printf(STDERR "%07x PCR %s %d+%d\n",
                   $offset+$pos, &stime($pcr33), $pcr33, $pcr9) if $opt_v;
            &pcr_write(\@buf, $pos, $pcr33);
            $pos += 6;
        }
        if ($af & 0x08) {
            $pos += 6;
        }
        if ($af & 0x04) {
            $pos++;
        }
    }
    if ($ind & 0x02) {
        if ($buf[$pos] == 0x00 && $buf[$pos+1] == 0x00
            && $buf[$pos+2] == 0x01 && ($buf[$pos+6] & 0xC0) == 0x80) {
            my $flag = $buf[$pos+7];
            $pos += 9;
            if ($flag & 0x80) { # PTS
                my $ts = &ts(\@buf, $pos);
                printf(STDERR "%07x PTS %s %d\n",
                       $offset+$pos, &stime($ts), $ts) if $opt_v;
                &ts_write(\@buf, $pos, $ts);
                $pos += 5;
                if ($flag & 0x40) { # DTS
                    my $ts = &ts(\@buf, $pos);
                    printf(STDERR "%07x DTS %s %d\n",
                           $offset+$pos, &stime($ts), $ts) if $opt_v;
                    &ts_write(\@buf, $pos, $ts);
                    $pos += 5;
                }
            }
        }
    }
    syswrite(STDOUT, pack("C*", @buf), $len);
    $offset += $len;
}

sub stime {
    my ($ts) = @_;
    my $sec = floor($ts / 90000);        # 90kHz
    $ts -= $sec * 90000;
    my $min = floor($sec / 60);
    $sec -= $min * 60;
    my $hour = floor($min / 60);
    $min -= $hour * 60;
    return sprintf("%02d:%02d:%02d.%03d", $hour, $min, $sec, $ts/90);
}

sub help {
    print STDERR <<EOF;
Usage: tsrenum.pl <opt>
opt:   -v            ; verbose
EOF
    exit 1;
}

MPEG-2 TS を標準入力から 1パケットずつ @buf に読み込んで、 PCR を見つけたら
pcr(\@buf, $pos) 関数を呼び出して PCR base の値 $pcr33 を取得し、
pcr_write(\@buf, $pos, $pcr33) 関数で PCR base の値を変更して標準出力へ書き出す。 PTS, DTS についても同様に、 ts(\@buf, $pos) 関数を呼び出して値を取得し、 ts_write(\@buf, $pos, $ts) 関数で変更する。

ISO/IEC 13818-1 の 2.4.3.4節 Adaptation field (20ページ) の Table 2-6 - Transport Stream adaptation field を見ると、 adaptation field の 2バイト目から 33bit が PCR base の値、 続いて 6bit の予約ビット、 その次の 9bit が PCR extension の値であることが分かる。 したがって pcr(\@buf, $pos) 関数は以下のように書ける:

sub pcr {
    my ($br, $pos) = @_;
    my $pcr33 = (($br->[$pos] << 25) | ($br->[$pos+1] << 17)
                 | ($br->[$pos+2] << 9) | ($br->[$pos+3] << 1)
                 | (($br->[$pos+4] & 0x80) != 0));
    my $pcr9 = ((($br->[$pos+4] & 0x01) << 8) | $br->[$pos+5]);
    return ($pcr33, $pcr9);
}

pcr_write(\@buf, $pos, $pcr33) 関数は PCR base の値 $pcr33 から $pcrOrg を減算してから pcr(\@buf, $pos) 関数の逆を行なうだけ:

sub pcr_write {
    my ($br, $pos, $pcr33) = @_;
    $pcrOrg = $pcr33 unless defined $pcrOrg;
    $pcr33 -= $pcrOrg;
    $br->[$pos] =   (($pcr33 >> 25) & 0xFF);
    $br->[$pos+1] = (($pcr33 >> 17) & 0xFF);
    $br->[$pos+2] = (($pcr33 >>  9) & 0xFF);
    $br->[$pos+3] = (($pcr33 >>  1) & 0xFF);
    if ($pcr33 & 1) {
        $br->[$pos+4] |= 0x80;
    } else {
        $br->[$pos+4] &= 0x7F;
    }
}

ISO/IEC 13818-1 の 2.4.3.7節 Semantic definition of fields in PES packet (31ページ) の Table 2-17 - PES packet を見ると、 PES (Packetized Elementary Stream) の 9バイト目から marker bit を挟みつつ 33bit の PTS と DTS が格納されていることが分かる。

したがって ts(\@buf, $pos) 関数と ts_write(\@buf, $pos, $ts) 関数は以下のように書ける:

sub ts {
    my ($br, $pos) = @_;
    return ((($br->[$pos] & 0x0E) << 29)
            | ($br->[$pos+1] << 22) | (($br->[$pos+2] & 0xFE) << 14)
            | ($br->[$pos+3] << 7)  | (($br->[$pos+4] & 0xFE) >> 1));
}

sub ts_write {
    my ($br, $pos, $ts) = @_;
    $pcrOrg = $ts unless defined $pcrOrg;
    $ts -= $pcrOrg;
    $br->[$pos] =   ((($ts >> 29) & 0x0E) | ($br->[$pos] & 0xF1));
    $br->[$pos+1] =  (($ts >> 22) & 0xFF);
    $br->[$pos+2] = ((($ts >> 14) & 0xFE) | ($br->[$pos+2] & 0x01));
    $br->[$pos+3] =  (($ts >>  7) & 0xFF);
    $br->[$pos+4] = ((($ts <<  1) & 0xFE) | ($br->[$pos+4] & 0x01));
}

すべて解決した後で気付いたのだが (^^;)、 ラップアラウンドする TS ファイルを読み込んだときは ffmpeg が的確なエラーメッセージを出力していた:

...
frame= 1328 fps= 21 q=2.0 size=   31898kB time=44.00 bitrate=5938.8kbits/s dup=24 drop=0    
frame= 1341 fps= 21 q=2.0 size=   32002kB time=44.45 bitrate=5898.1kbits/s dup=24 drop=0    
[mpegts @ 0x6253c0]Invalid timestamps stream=0, pts=4078, dts=8589929661, size=53459
*** drop!
*** 1 dup!
*** drop!
*** drop!
*** drop!
*** drop!
*** drop!
adding -2147483648 audio samples of silence
adding -2147483648 audio samples of silence
*** drop!
...

「Invalid timestamps ... pts=4078, dts=8589929661」 すなわち PTS が 00:00:00.045 なのに、 DTS が 26:30:43.663 なので無効なタイムスタンプである、 というエラー出力。 タイムスタンプがラップアラウンドしたときは、 「Invalid timestamps」 とは言えないと思うのだが...

念のため ffmpeg の最新版を 「git clone git://git.ffmpeg.org/ffmpeg/」 で取得してコンパイルしてみたのだが、 「FFmpeg version git-735bbae」 でも同様に 「Invalid timestamps」 エラーが出力された。

Filed under: プログラミングと開発環境 — hiroaki_sengoku @ 08:13
2010年9月4日

中国でプリペイド SIMカード 神州行と如意通を買ってみた hatena_b

週末に休みを追加して北京へ行ってきた。 今回は全日程終日自由行動のパックツアーだったので、 昨年 「連れ回しパックツアー」 で北京へ行ったときには見せてもらえなかったところ (終日バスで連れ回されたので街の日常の風景を見ることができなかった) が見えてきて大変興味深かった。 が、 その内容をここで書いてしまうと、 このブログが中国から読めなくなってしまいそうなので自粛。

実質 3日間の短期滞在だったので、 日本で使ってるソフトバンクのケータイをそのまま持ち込んで 「海外パケットし放題」 でもよかったのだが、 せっかく訪れたのだからと北京でも SIMカードを買ってみた。

China unicom at BCIA

北京首都国際空港 (簡体字だと 「北京首都国际机场」) に着いてすぐ 「电话卡」 (直訳すると 「電話カード」) と書いてある SIMカード自販機が目を引く (写真は出発ロビーの自販機だが、到着ロビーにもある) が、 通話料なしで 100元 (約 1300円) もする。 つまり SIMカードを買っただけではダメで、 通話料をチャージ (中国語では 「充値」) しなければ使えない。 街中なら 50元の通話料込の SIMカードが 100元以下で売っている (つまり SIMカード単体の価格で比較すれば半額以下) ので、 空港での購入は見送った。 SIMカードに限らず、 中国では場所によって値段が大幅に変わってくるので注意が必要。

私は中関村 (簡体字だと 「中关村」) のケータイショップで買ったのだが、 中関村でなくても、 というかむしろ西単とかの繁華街の方が簡単に買える。 「我要SIM」 (正しくは儲値卡) などと紙に書いて店員に見せたら (四声をちゃんと発声しないと通じないので紙の方が早いし正確, もちろん英語は通じない) 電話番号ごとの値段リストを見せてくれた。

More...
Filed under: SIM — hiroaki_sengoku @ 00:34
2010年8月4日

HYBRID W-ZERO3 の解約に続き、ウィルコムADSL も解約した。さよならウィルコム! tweets

事業破綻の報道等で 「顧客 (サービス利用者) の保護」 という言葉をみかけることが多いが、 言語明瞭意味不明ワードの一つだと思う:

PHS の解約が予想以上に進み、 2月に 417万人だった契約者数は 6月末には 388万人に減った。 7月下旬に予定していた裁判所への再建計画提出も、 「環境が変わった」として 10月に延期していた。
管財人らは、顧客を守るためにも、再建には通信会社の協力が必要と判断。 XGPを引き受けるソフトバンクに、PHS事業への支援に加わるよう求めていた。

「顧客を守る」 などと書くと聞こえはよいが、 PHS サービス継続を望む顧客がどれだけいるかなんて、 「PHS の解約が予想以上に進」んでいることから明らかなはず。 現在 388万人(も) 契約者数が残っていると言ったって、 その大半は 2年縛りのせいで辞めるに辞められない人たちであって (私も 6月末時点だと契約者なのでこの 388万人に含まれている)、 現顧客にとって望ましいのはサービス継続じゃなくて 2年縛りを免除してあげることだと思う。

「顧客」 というと 「債権者」 というイメージが強いし、 この報道のように 「顧客を守る」 と書くとますます 「債権者たる顧客を守れ!」 というイメージが強調されるけど、 実状は 「辞めたいけど契約の縛りで払い続けざるを得ない債務者たる顧客」 と 「辞めていく顧客から少しでも多くの資金を回収したい債権者」 という (報道から受けるイメージとは真逆の) 構図であるわけで、 報道がいかに実状をゆがめたイメージを撒き散らしているかの一例だと思う。

私は丸 4年 (なのでちょうど解約可能なタイミングだった)、 ウィルコムの PHS サービスを利用してきた。 また自宅のバックアップ回線として 4年間近くウィルコムADSL を利用してきた。 なぜウィルコムだったかと言えば、 2006年当時まだ珍しかった 「スマートフォン」 W-ZERO3[es] を使いたかったから。 4年前ドコモショップで、 スマートフォンが使いたいから解約すると伝えたとき、 ドコモも近いうちにスマートフォンを出す予定 (hTc Z) と担当者が言っていたのを思い出す。

その後、 W-ZERO3[es], Advanced W-ZERO3[es], HYBRID W-ZERO3 と 3代続けて W-ZERO3 シリーズを使い続けてきたが、 HYBRID W-ZERO3 のあまりのデキの悪さ (そもそも電話の着信音 / バイブレーションが小さすぎて着信に気付けないのだから電話として失格) に幻滅し、 乗り換えを決意した次第。 輝かしい W-ZERO3 の歴史の最後の最後で欠陥品を出してしまうとは (私は使ったことがないが一つ前の WILLCOM 03 も失敗作だった?)、 いったいシャープに何が起こったのか?

iPhone を使いたいがためにソフトバンクに乗り換える人が圧倒的である昨今、 いまさら言うまでもないことだが、 (2006年ごろから?) 通信サービスは端末のオマケに成り下がったとつくづく思う。 いや、 端末ではなくコンテンツのほうがもっと大事だと言う人がいるかもしれないが、 そうなるのはもう少し先 (少なくともあと 3年くらい先)、 端末がコモディティ化した後の話だと思う。 少なくとも現時点では、 どんなに立派で魅力的なコンテンツでも、 それをユーザに届ける窓口である 「端末」 がヘタレであれば何の意味もない。

一般に解約するのは骨が折れる。 多くの場合、解約は電話のみの受付だったり、 解約通知書を郵送しなければいけなかったりと、いろいろ手間がかかる。 Web だけで契約が済んでしまう入会の時とは対照的。 しかも解約の方法は Web を丹念に見ていかないと見つけられなかったりする。 退会率を下げたいという事業者側の気持ちも (職業がら) もちろん分かるのだが、 解約方法を分かりにくくすればするほどサポートコストは増えるしユーザ満足度も下がるわけで、 結局事業者自身の首を絞めることにしかなってないと思う。

特に、 ウィルコムADSL の解約は大変だったので以下にメモ:

More...
Filed under: その他 — hiroaki_sengoku @ 08:07
2010年7月26日

Nexus One で Android 2.2 froyo のマルチタッチを試してみる tweets

Android は 2.1-update1 以降でマルチタッチ (Multi-touch) をサポートしている。 ところがマルチタッチといっても、 ピンチイン/ピンチアウトなどのジェスチャをサポートしているだけのアプリが大半で、 複数のタッチを独立に扱えるアプリはいまだほとんどなく、 iPhone と比べるとその差が際立っている。

どうして Android にはマルチタッチを活用したアプリケーションが無いのだろう? と思ったので、 マルチタッチを試すテストアプリ MultiTouch.java (apk) を書いてみた:

MultiTouch

タッチした位置にタッチの強さに応じた大きさの円を表示するだけの単純なアプリ。 指を移動すれば円も追随する。 Android ではタッチID が順に割り振られるので、 ID が 0 のタッチを赤色の円で、 ID が 1 のタッチを緑色の円で描いている。

プログラム上は ID が 2 のタッチを青色の円で描くことになっているが、 残念ながら現行の Android で同時に扱えるタッチは 2箇所のみ (追記: Samsung Galaxy S は 5箇所のマルチタッチが可能らしい) なので、 3箇所にタッチしても三つ目の円が描かれることはない。 だから例えば iPhone のアプリにあるような鍵盤楽器アプリを作ろうと思っても、 三つ以上の音を同時に鳴らすことはできない。

とはいえ、 2箇所のタッチを独立に扱えれば、 いろいろ応用が効くだろうにと思いつつ、 このテストアプリをいじっていると...

More...
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 08:57
2010年7月17日

Nexus One の近接センサ/環境光センサは、どこにあるのか?調べてみた hatena_b

Nexus One など最近のスマートフォンには、 加速度 (Accelerometer)、 環境光 (照度, Ambient Light)、 磁場 (磁界, Magnetic Field)、 方位 (電子コンパス, Orientation)、 近接 (Proximity) など、 様々なセンサがついている。 いろいろ応用できそうで夢がふくらむが、 携帯電話本来の使い方 (つまり通話すること) において、 使い勝手に直接影響する重要なセンサが近接センサ。

Nexus One や iPhone など全面タッチパネルの携帯電話だと、 (受話器として使うために) 耳に近づけたときタッチパネルが反応しては困る。 そこで近接センサを使って顔が接近してくることを感知し、 タッチパネルを無効にする (ついでにディスプレイをオフにして消費電力を抑える)。

私は Proximity なんて聞くと、 Proximity Warning System (接近警報システム) を思い浮かべてしまうくらいで、 携帯電話用の近接センサがどういうしくみか全く知らなかった。 今年1月の Nexus One の発表の時に近接センサのことを初めて知り、 その時はタッチパネル全体への接近を感知する (静電容量の変化を検知して?) のかと想像したが、 後述するように Nexus One の近接センサはタッチパネルの左上にしかなく、 タッチパネルの下方への接近は感知できないことが分かった。

Nexus One のどこに近接センサが搭載されているか、 センサの感応範囲がどれくらいなのか、 私には見当もつかなかったし、 google で検索してもその手の情報は見つからなかったので、 近接センサが感知した値を表示するだけの簡単なプログラムを書いてみた。

実は、 私にとって初めての android アプリ (^^;)。 しかも、 ここ数年 Java から遠ざかっていたので、 久々に書く Java プログラムだったりする。

お膳立ては Android SDK が全てやってくれるので、 わずか 74行のプログラム。 まず SensorManager#getSensorList メソッドで、 PROXIMITY タイプのセンサを取得し (sensor)、

        sensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
        List<Sensor> sensors
            = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
        Sensor sensor = sensors.get(0);

この sensor の値が変化したときなどにセンサの値を受け取るリスナ (SensorEventListener) を、 SensorManager#registerListener メソッドで登録するだけ。

public class Proximity implements SensorEventListener {
        ...
        sensorManager.registerListener(this, sensor,
                                       SensorManager.SENSOR_DELAY_NORMAL);
        ...
    @Override
    public void onSensorChanged(SensorEvent event) {
        view.update(event.values);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}

近接センサの値が変化するとリスナの onSensorChanged メソッドが呼び出されるので、 新しいセンサ値を描画する (view.update):

        void update(float[] values) {
            Canvas canvas = getHolder().lockCanvas();
            if (canvas == null) return;
            canvas.drawColor(Color.WHITE);
            Paint paint = new Paint();
            paint.setColor(Color.BLACK);
            paint.setTextSize(40);
            float height = paint.getTextSize();
            for (int i = 0; i < values.length; i++) {
                canvas.drawText(" "+values[i], 0, height * (i + 1), paint);
            }
            getHolder().unlockCanvasAndPost(canvas);
        }

Nexus One のタッチパネル左上隅近く (黒枠部分) にセンサがあるらしく、 パネルまで 2cm ほどの距離に物体を近づけると反応する (センサの値が 9.0 から 0.0 へ変化する)。 また、 パネルと並行に物体を動かす場合、 センサの真上から 1cm ほど外れると反応しなくなる。

Proximity Sensor  ←  鈴を近づけたことにより、
近接センサが反応して、
値が 0.0 になっている

赤外線型の近接センサ (赤外線を照射し、近接する物体からの反射光を測定するセンサ) なので、 凸面の物体など赤外線があさっての方向へ反射してしまって受光素子に正しく届かない場合や、 あるいは黒色の物体などあまり反射しない場合などでは、 より近づけないと反応しない。

例えば、 黒く細い丸棒などだと 1cm 以下に近づけないと反応しない。 逆に白い紙 (凹面〜平面) など、 効果的に赤外線を反射し、かつ受光素子に反射光が効率的に届くケースだと、 2cm より遠くても (8cm くらいでも) 反応する。

ちなみに、 Nexus One に搭載されている近接センサは、 Capella MicrosystemsCM3602 という、 環境光センサ付短距離近接センサ (Short Distance Proximity Sensor with Ambient Light Sensor) であるようだ。 名前の通り環境光も測定できる。 おそらく近接センサの受光素子をそのまま使って照度を測定しているのだろう。

前述したプログラムにおいて、 「Sensor.TYPE_PROXIMITY」 を 「Sensor.TYPE_LIGHT」 に置き換えれば、 環境光センサの値を読み取ることができる。

More...
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 08:55
2010年7月8日

Google カレンダーの過去の予定を自動的に削除する方法 hatena_b

ここ 2ヶ月ほど、 Willcom の HYBRID W-ZERO3 から Nexus One への移行を徐々に進めてきた。 Windows Mobile (HYBRID W-ZERO3) は PC とのデータ同期が基本なので、 PC にスケジュールや電話帳など丸ごと入れておけば済むが、 Android (Nexus One) の場合は 「クラウド」 との同期が基本なので一筋縄にはいかない。 つまり 「クラウド」 に全てのスケジュールを置いていいのか? という問題。 私の場合、 「スケジュール」 といいつつ会議の議事メモから個人的な日記まで、 プライベートな情報を全て集積しているので問題がより深刻になる。

ちなみに私は、 1999年に WorkPad 30J を使い始めて以来、 プライベートなデータを PDA / スマートフォンに集積してきた。 今まで使ってきた PDA / スマートフォンをまとめてみる:

購入月機種OS
1999-04 WorkPad 30J PalmOS 3.1J
2000-03 TRGpro PalmOS 3.5
2000-08 Palm m100 PalmOS 3.5.1
2002-09 Zaurus MI-E1 ZaurusOS
2002-12 Linux Zaurus SL-C700 Linux 2.4 Embedix
2003-06 Linux Zaurus SL-C750 Linux 2.4 Embedix
2006-07 W-ZERO3[es] Windows Mobile 5.0
2007-07 Advanced W-ZERO3[es] Windows Mobile 6 Classic
2008-12 HTC P3600 Windows Mobile 5.0
2010-01 HYBRID W-ZERO3 Windows Mobile 6.5 Professional
2010-04 Nexus One Android 2.2 froyo
2010-06 iPhone 4 iOS 4
2010-11 IS01 Android 1.6 donut
2010-12 Galaxy S Android 2.2 froyo
2010-12 IDEOS U8150-B Android 2.2 froyo
2011-03 Nexus S Android 2.3 gingerbread
2011-05 nook color Android 3.0 honeycomb
2011-12 Galaxy Nexus Android 4.0 ice cream sandwich
2012-12 Galaxy S3 Android 4.0 ice cream sandwich
2013-07 Galaxy Mega 5.8 Android 4.2 jelly bean
2013-11 Nexus 5 Android 4.4 kitkat
2014-03 iPhone 5s iOS 7.0
2016-01 Nexus 5X Android 6.0 marshmallow
2016-12 ZenFone 3 ZS570KL Android 6.0.1 marshmallow

WorkPad 30J から HYBRID W-ZERO3 に至るまで、 全て PC とのデータ同期が基本だったし、 それぞれデータ移行ツールが用意されていたので移行は容易だった。 ところが Nexus One で同じような同期を行なうには、 データを PC ではなく Google Calendar へ置かなければならない。

もちろん、 Google Calendar は共有設定さえ行なわなければ他人に読まれることはないだろうし、 「don't be evil」 と言ってるくらいだから、 Google が勝手にユーザのデータを活用する可能性も無い (と信じたい)。

だからといって、 個人的なデータや会社の超機密事項 (議事メモにはそういった情報も含まれる) も洗いざらい Google に預けてしまう、 なんてことは小心な私にはとてもできない。 よく知られているように Google Calendar は 「限定公開 URL」 が漏れるだけで一巻の終わりであるわけで、 漏れることを前提でリスク評価すべき。

というわけで、 議事メモや日記を Google Calendar に置くことはハナからあきらめて、 Google Calendar には直近の予定だけを置くことにした。 万一漏れても、向こう一週間くらいの予定だけであれば、 致命的というほどでもない。

ところが驚いたことに Google Calendar には過去の予定を一括削除する機能がない。 手作業でいちいち消していかない限り、 データは残り続けるようだ。 当たり障りのない 「予定」 でも積み重なればいろいろ見えてくることがあるわけで、 長年にわたって溜った予定データは脅威となりうる。

どうしてこんな超基本機能が無いのだろうと思いつつ google で検索してみると、 見つかるのは 「どうやったら過去のデータを (一括) 消去できるのか?」 という質問のページばかり。 過去データを削除したい、 というニーズは確実にありそうなのに、 なぜ Google は実装しようとしないのか? そういえば gmail も過去のメールを溜め続けるのが基本だし、 Google Calendar に自動的に削除する機能がないのは意図的なのかもしれない。

無い機能は作ってしまえと、 Google Calendar API のドキュメントを眺めてみる。 API を叩くためのクライアントライブラリが用意されているようだ。 が、.NET とか Java とか Python とか PHP とか、 あまり気の進まない (^^;) 言語ばかりが並んでいる。 Perl は無いのかっと思って CPAN を検索したら、 Net::Google::Calendar があっさり見つかった。

マニュアル片手にテストプログラムを書いてみる:

use Net::Google::Calendar;
use DateTime;
my $cal = Net::Google::Calendar->new;
$cal->login('sengoku@gmail.com', 'xxxxxxxx');
for my $event ($cal->get_events('start-max' => DateTime->now
                                - DateTime::Duration->new(days => 7))) {
    $cal->delete_entry($event);
}

たったこれだけ。 最初に login($user, $pass) して、 get_events(%opts) で「予定」データを取り出して、 delete_entry($event) で削除。 get_events の引数で、 開始時刻が一週間以上過去の予定のみ取り出すよう指定している。

ちょっと書き足して、 ユーザID やパスワードを設定ファイルで設定できるようにしたスクリプト gcal_remove を作ってみた。

% gcal_remove -c /path/to/config.yaml -d 7

などと実行すると、 7日間以上前の予定を削除する。

設定ファイル config.yaml は以下のような感じ:

google_user: sengoku@gmail.com
google_passwd: xxxxxxxx
Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 09:44
2010年6月17日

Linode から prgmr.com へ乗り換えてみた hatena_b

今年 2月から使っている Linode (VPS, 仮想サーバ) は、 月額 $19.95 の最小プラン Linode 360 だと RAM 360MB なので WordPress を動かす (Apache+PHP+MySQL) には若干足らない (稀に スラッシング 状態に陥って無反応になる)。 もちろん、 一つ上のプラン Linode 540 (RAM 540MB) にすればいいのであるが、 これだと月額 $29.95 でちょっと高い (私の感覚だと月額 $20 あたりに心理的な壁がある)。

prgmr.com ならば、 RAM 512MB, ディスク 12GB が月額 $12 (年一括払いなら $115.2) で済むので乗り換えてみた。 Linode のようなユーザフレンドリーな Web ユーザインタフェースは無いし、 時々新規申込みの受付を中止したりする (今も RAM 1024MB 以上のプランは受付けていない) が、 逆に言えば Web インタフェースが不要な人 (含む私) にとっては prgmr.com のシンプルさがむしろ望ましく、 レスキュー用のブートイメージなど必要最低限のものは揃っているので、 不便と感じることはない。 また、 いったん申込みが完了してしまえば受付の一時停止は関係ないわけで、 むしろサーバリソースに見合ったユーザ数しか受付けない姿勢は好感が持てる。

Web から Signup すると、 以下のようなメールが届く。

you ordered a xen vps, 512MiB ram, 12GiB Disk 80 GiB transfer Xen VPS,  $12/month username sengoku 

Before I can set you up, I need to know what distro you would like and an OpenSSH format public key (on a *NIX, run ssh-keygen  and send me either the id_dsa.pub or id_rsa.pub file in an attachment)    

on putty/windows, see
http://www.unixwiz.net/techtips/putty-openssh.html#keypair
and scroll down to the screen shot where it says 'openssh format public key for pasting into authorized_keys' - that is what I need.

thanks. 

OpenSSH の公開鍵くらい Web から登録させればいいのにと思いつつ、 「I need to know what distro you would like」 などと聞いてくるということは多少の融通は聞くのかと思い、 パーティションの切り方をついでにリクエストしてみた (6/4 15:46)。 つまり、 自分で作った Linux distro (配布していないから 「distribution」 と呼ぶのは正確性に欠けるのだが、 それなら何て呼んだらいいのだろう?) をインストールしたいから、 最小のパーティション (例えば 2GB) に ubuntu をインストールしておいてもらえるか? などとリクエストしてみた:

Hi,

At Fri, 04 Jun 2010 05:48:05 +0000,
support@prgmr.com wrote:

> Before I can set you up, I need to know what distro you would like and
> an OpenSSH format public key (on a *NIX, run ssh-keygen and send me
> either the id_dsa.pub or id_rsa.pub file in an attachment)

I'd like to use my own linux-based image that is running on
fremont.gcd.org (linode) now.  Is it possible to make two partitions
(plus swap partition) ?  One is for a normal ubuntu, and the other is
for my own image.  I'd like to switch these two OSs by pv-grub.

Please minimize the ubuntu partition in order to maximize my own image.
For example, 2GiB for ubuntu, 1GiB for swap, and 9GiB for me.

I've attached my public key id_dsa.pub at the end of this mail.


#9860.
https://www.gcd.org/sengoku/                Hiroaki Sengoku <sengoku@gcd.org>

すると、 4日後の 6/8 09:03 に返事が来た。 リクエストはあっさり無視されて、 ディスク一杯に ubuntu をインストールした状態の VPS が提供されただけ。 個別対応しないのであれば、 自動化したほうがいいような。

Your vps is setup now, your host server is whetstone.prgmr.com.
Login with your username and ssh key and follow the instructions
at http://book.xen.prgmr.com/mediawiki/index.php/Quickstart
For more information about repartitioning, see
http://book.xen.prgmr.com/mediawiki/index.php/Repartition
The default root password for the vps is password, and you
should receive a bill soon by email. If you have any questions
please email support@prgmr.com. Thanks very much.
Nick Schmalenberger

言われた通り ssh でログインしてみる:

senri:/home/sengoku % ssh whetstone.prgmr.com
The authenticity of host 'whetstone.prgmr.com (65.49.73.105)' can't be established.
RSA key fingerprint is 97:52:07:d4:52:8d:c0:cc:13:c7:fb:5d:5d:1b:ab:b4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'whetstone.prgmr.com,65.49.73.105' (RSA) to the list
of known hosts.
Name                                        ID   Mem VCPUs      State   Time(s)
sengoku                                    xxx   512     1     -b----     11.5

Options for sengoku
1. console
2. create/start
3. shutdown
4. destroy/hard shutdown
5. reboot
6. swap i386/amd64 bootloaders (pvgrub)
7. exit
press the number> 

「6. swap i386/amd64 bootloaders (pvgrub)」 が目を引くが、 PV-GRUB は DomainU 側で実行されるので、 PV-GRUB にて 32bit/64bit の両方のカーネルを立ち上げることができず、 PV-GRUB を実行する前にどちらか一方に決めておく必要がある、 ということのようだ。 デフォルトは (残念なことに) 32bit になっていて、 セットアップしてもらった ubuntu も 32bit 版だった。

「1」 を入力して VPS のコンソールを表示させてみる:

press the number> 1

Ubuntu 10.04 LTS sengoku.xen.prgmr.com hvc0

sengoku.xen.prgmr.com login: root
Password:
Last login: Mon May 31 23:51:42 UTC 2010 on hvc0
Linux sengoku.xen.prgmr.com 2.6.32-22-generic-pae #33-Ubuntu SMP Wed Apr 28 14:57:29 UTC 2010 i686 GNU/Linux
Ubuntu 10.04 LTS

Welcome to Ubuntu!
 * Documentation:  https://help.ubuntu.com/
root@sengoku:~# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/xvda1            11812024   1068124  10143876  10% /
none                    249488       136    249352   1% /dev
none                    253744         0    253744   0% /dev/shm
none                    253744        28    253716   1% /var/run
none                    253744         0    253744   0% /var/lock
none                    253744         0    253744   0% /lib/init/rw
none                  11812024   1068124  10143876  10% /var/lib/ureadahead/debugfs
root@sengoku:~# 

前掲のメールに書いてあった通り、 ユーザ 「root」 パスワード 「password」 でログインできた。 初めてログインしたのに 「Last login: Mon May 31 23:51:42 UTC 2010 on hvc0」 などと出ているが、 この ubuntu イメージを作ったとき試しに root でログインしたのが残ってしまったのだと思われる。 新規ユーザのセットアップのたびに、 5/31 に作ったイメージを使いまわしているのだろう。

ディスク 12GB のプランのはずなのに 1GB ほど足りないのは swap パーティションがあるから?と思い、 fdisk で確認してみる:

root@sengoku:~# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1494    12000523+  83  Linux
root@sengoku:~# 

シリンダの最大値は 1566 なので、 72 (=1566-1494) シリンダが使われずに余っている。 試しにパーティションを割当ててみたら普通に使えた。

自前 distro 10GB, swap 1GB, ubuntu 1GB といった切り分けかたにするため、 レスキュー用のカーネル 「CentOS 5.5 rescue」 をブートしてみる:

    GNU GRUB  version 0.97  (65536K lower / 0K upper memory)

 +-------------------------------------------------------------------------+
 | user bootloader configuration                                           |  
 | CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)                               |
 | CentOS 5.5 installer                                                    |
 | NetBSD 5.0.2 installer                                                  |
 | Ubuntu 10.04 LTS installer                                              |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |
 |                                                                         |  
 +-------------------------------------------------------------------------+
    Use the ^ and v keys to select which entry is highlighted.
    Press enter to boot the selected OS, 'e' to edit the
    commands before booting, or 'c' for a command-line.

二行目の 「CentOS 5.5 rescue (2.6.18-194.3.1.el5xen)」 を選択してブート。 root でログイン (パスワードは無し) して fdisk を使ってパーティションを切り直した。 せっかくセットアップしてもらった Ubuntu も、 3分と使わずにお別れ。

CentOS release 5.5 (Final)
Kernel 2.6.18-194.3.1.el5xen on an x86_64

sengoku.xen.prgmr.com login: root
[root@sengoku ~]# 
        ...
[root@sengoku ~]# fdisk -l /dev/xvda

Disk /dev/xvda: 12.8 GB, 12884901888 bytes
255 heads, 63 sectors/track, 1566 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

    Device Boot      Start         End      Blocks   Id  System
/dev/xvda1               1        1306    10490413+  83  Linux
/dev/xvda2            1307        1438     1060290   82  Linux swap / Solaris
/dev/xvda3            1439        1566     1028160   83  Linux
[root@sengoku ~]# mount /dev/xvda1 /mnt
[root@sengoku ~]# 

私は個人で管理している Linux マシン (自宅と職場と VPS 合わせて十数台, もちろん商用サービスを行なっているサーバではない) のディスクの内容を、 コマンド一発で同一に保てるようにしている (つまりマスタで変更/追加/削除したファイルを各マシンへコピー/各マシンで削除)。

もちろん、 各マシンの性能や 32bit/64bit などの CPU 種別、 ディスクの容量に応じて違う部分もあるわけで、 この同期コマンドは同期先の各マシンに合わせてコピーしないファイルのリストを生成した上で rsync を実行している。 なので、 新しくマシンを増やすときもマスタ上で同期コマンドを実行するだけで、 インストールが完了するはずだが...

More...
Filed under: システム構築・運用 — hiroaki_sengoku @ 09:44
2010年5月26日

サイボウズ オフィス8 のカレンダーを iCalendar 形式に変換するスクリプトを書いてみた hatena_b

私の職場ではグループウェアとしてサイボウズを利用している。 自分のスケジュールを iCalendar 形式で取得してみたくなった (Nexus One を買うとそういう気分になる ^^;) ので、 google で検索してみたところ次の二つのスクリプトが見つかった:

あいにく職場のサイボウズはオフィス8 なので、 オフィス8 に未対応の前者は使えない。 後者はオフィス8 用だが、 「暫定版」 と書いてある通りいろいろバグがある。 さくっと修正してみて一応それっぽく動かすことはできたのだが、 使い続けていくとなると一から書き直したほうがいいような気がしてきた (わたし的にはこの手のものを PHP スクリプトでは書きたくない) ので、 perl で書き直してみた

この perl スクリプト cybozu8_ical を、

% cybozu8_ical --conf /path/to/config.yaml

などと実行すると、 オフィス8 の 「月予定」 (月間スケジュール) ページをアクセスして、 iCalendar 形式に変換する。 向こう一週間以内の予定については 「予定の詳細」 ページもアクセスして、 「メモ」 および 「設備」 も取得する。 「月予定」 の全ての予定について 「予定の詳細」をアクセスすると、 オフィス8 サーバに負荷をかけすぎる懸念があったので、 このような仕様にしてみた。

設定ファイル config.yaml は以下のような感じ:

cybozu_url: http://intra.klab.org/cgi-bin/klcb/ag.cgi
calname: cybozu
userid: 73
password: xxxxxxxx
time_zone: Asia/Tokyo
input_encoding: shiftjis
output_file: /home/sengoku/tmp/cybozu.ics

「cybozu_url」 はオフィス8 の URL を指定する (もちろん intra.klab.org は KLab 社内LAN からしかアクセスできない)。 「userid」 と 「password」 はスケジュールを取得したいユーザの ID とパスワード。 「output_file」 に指定したファイルへ iCalendar 形式のスケジュールを出力する。 「output_file」 を省略すると標準出力へ書き出す。

なお 「--conf /path/to/config.yaml」 オプションを省略すると、 cybozu8_ical と同じディレクトリにある config.yaml が読み込まれる。

cybozu8_ical で生成した iCalendar 形式のファイルは、 とりあえず Google カレンダー および Thunderbird + Lightning で読み込めることは確認したが、 なにぶんまだ RFC 2445 を真面目に読んでいないので、 不具合などあったらご指摘頂けると有難い。

私はオフィス8 の API を知らないので、 「月予定」 「予定の詳細」 のページを取得してきて scrape しているだけ。 「繰り返し予定」 は個々の予定として扱っている。 iCalendar の UID (各予定固有のID) が同じままだと 2個目以降の予定が無視されてしまうので、 UID の末尾に通し番号をつけて互いに区別できるようにしている。

サイボウズのスケジュールは 「場所」 を登録できない。 その代わり 「施設」 として会議室を登録する。 ところが私の職場の場合 「施設」 には会議室だけでなく 「ビデオ会議システム」 などもあったりするので、 「施設」 に登録されているデータをそのまま iCalendar の LOCATION として使うわけにもいかない。 そこで 「施設」 に会議室の名称が登録されている場合のみ、 その会議室名を LOCATION として出力するようにしている。

iCalendar の LOCATION として出力すべき会議室名の一覧を、 cybozu8_ical スクリプトの始めの方で、

my @facility = (
        '20F 大会議室1', '20F 大会議室2', '20F 大会議室3',
        '20F 中会議室1', '20F 中会議室2',
        '20F 小会議室1', '20F 小会議室2',
        '20F 和室', '22F 社長室前MTGスペ-ス'
);

などと定義している。 ここに列挙されていない会議室等は 「施設」 に登録されていても単に無視される。

Filed under: Android,プログラミングと開発環境 — hiroaki_sengoku @ 16:01
« Newer PostsOlder Posts »