もぐてっく

人は1つ歳を取る度、1ビットづつ大きくなれると信じてた。

mikutter Advent Calendar 2013 17日目 肉食系プラグイン作成術

はじめに

この記事はmikutter Advent Calendar 2013(http://www.adventar.org/calendars/120)17日目です。

まだまだカレンダーには空きがございます。チャンスです。
mikutterを使ってみての感想とか、短めのエントリも全然OKと思います。
ぜひともmikutterへの思いを繋いで行きましょう。


さてさて、皆さんご存知の通り、mikutterのプラグイン機構はとってもエレガント。
ほんの少しのコーディングで様々な機能をmikutterに追加することが出来ます。

例えば、クリスマスムード漂うあなたのTLを真っ赤に染める位なら、ほんの数行でちょちょいのちょいです。

# -*- coding: utf-8 -*-

Plugin.create :red_background do
  filter_message_background_color do |message, color|
    color = [65535, 0, 0]
    [message, color]
  end
end


でも「mikutterちゃんにこんなこともして欲しい(>_<)」がエスカレートしてくると、用意されたAPIでは実現できないことも出てきますよね。

そこで今回は「肉食系mikutterプラグイン作成術」と題して、少々強引な方法を使って、あなた好みのステキなmikutterをgetする方法を書いてみようと思います。

復習:1分で何となく分かるmikutterプラグイン

mikutterは処理の要所要所で「フィルタ」や「イベント」を通じて、プラグインと会話しようとします。
プラグインは興味のある「フィルタ」、「イベント」をハンドリングする事でmikutterの挙動を変更しする事が出来ます。

上記の真っ赤なTLの例ではfilter_message_background_colorと言うハンドラを定義して、つぶやきが描画される際に呼ばれる:message_backgroundと言うフィルタを捕捉しています。
そしてfilter_message_background_colorの中で色コードを設定することで、つぶやきの背景色を変更しています。

さて、本題

この様に強力なイベント機構によってダントツの拡張性を誇っているmikutterさんですが、残念ながらこのモデルは万能ではありません。


例えば、タブに表示されるアイコンのサイズを変更したいとします。

この場合、タブのアイコンが書き換わるタイミングに発行されるフィルタをハンドリングして、少し小さめのアイコンを返してやれば良いと言う発想になります。

しかしながら、mikutterには残念ながらそのフィルタは用意されていません。
フィルタが発行されない事にはプラグインは介入のしようがありません。困りました。


ここであきらめるのも悔しいので、抜け道が無いかソースコードを読んでみます。
タブのアイコンを表示する処理はgtkプラグインのtab_update_icon()と言うシンプルなメソッドで行われています。

def tab_update_icon(i_tab)
  type_strict i_tab => Plugin::GUI::TabLike
  tab = widgetof(i_tab)
  if tab
    tab.remove(tab.child) if tab.child
    if i_tab.icon.is_a?(String)
      tab.add(::Gtk::WebIcon.new(i_tab.icon, 24, 24).show) # ここの24が書き換えれれば・・・
    else
      tab.add(::Gtk::Label.new(i_tab.name).show) end end
  self end

直接gtk.rbを書き換えてしまえば話は早いのですが、これだけのためにgtkプラグインをフォークするのもアホらしいですし、ユーザにパッチ作業を要求するのも非常にアレです。

どうにかプラグインで実現出来ないか?

黒魔術ですべてを思いのままに


まどか「・・・私なら、何でも願いが叶うって言ったよね?」

QB「ああ、君ならどんな不可能も可能にできるだろう。さぁ、願いを言うんだ。まどか。」


すーっ・・・


まどか「すべてのメソッドを、生まれた後に書き換えたい!全てのインスタンス、クラスとモジュールのすべてを!この手で!!」


QB「そんな祈りが叶うとすれば、それはオーバーライドなんてものじゃない。オブジェクト指向そのものに対する叛逆だ・・・君は本当に神になるつもりなのかい!?」


なれちゃいます、神様。


思い出してください。
mikutterはRubyで作られています。

Rubyと言えば動的プログラミング。
別名「黒魔術」を使えば、任意のタイミングでクラスのメソッドを変える事すら、我々には不可能では無いのです。


早速、黒魔術でプラグインからtab_update_iconを上書きしてしまいましょう。

Plugin.create :tab_icon_size do
  Plugin[:gtk].instance_eval {
    # 独自のタブアイコンメソッドを上書き定義
    def tab_update_icon(i_tab)
      type_strict i_tab => Plugin::GUI::TabLike

      tab = widgetof(i_tab)
      if tab
        tab.remove(tab.child) if tab.child
        if i_tab.icon.is_a?(String)
          tab.add(::Gtk::WebIcon.new(i_tab.icon, 12, 12).show) # 半分の大きさになれっ!
        else
          tab.add(::Gtk::Label.new(i_tab.name).show) end end
      self
    end
  }
end

このコードが実行されて以降、tab_update_icon()が呼ばれると、上書きした方のメソッドが実行される様になります。
すげぇぜRuby


この手法は、巷では「モンキーパッチ」と呼ばれている様です。
元のソースコードを一切触らずパッチが充てられるので、アプリケーションのHotFixなどに有効活用されています。
オブジェクト指向が流行った時に言われてた「差分プログラミング」って奴ですね。

でも、モンキーパッチは便利な反面、どこぞのプラグインがこっそりメソッドの挙動を変更した場合、非常に分かりにくいバグを生む恐れもあります。

mikutterに於いては、複数のプラグインが同じメソッドをモンキーパッチした場合、先にロードされたプラグインが正常に動作しない事になります。
また、モンキーパッチによってmikutterのコアの動作を阻害する可能性もあります。

(後、mikutterの薄い本vol.4でとしぁさんがモンキーパッチをちらっとdisってたのもちょい気になります。)


複数のプラグインが共存できないと言う点で、もはやこれはプラグインではなく「MOD」とか呼んで区別した方がいいかもしれません。


しかしながら有益な技には違いないので、何とか副作用を抑えて活用して行きたいところですね。


例えば、何かしらモンキーパッチを検出する機構を作って競合を発見可能にすれば上記の事故は起こらないかもしれません。


・・・


さて、エントリがやたらと長くなってきたので、今回はこれ位で。
次回はこれまた黒魔術を使って、モンキーパッチ検出機構を構築して見ようと思います。

(そのうち続く)

mikutter Advent Calendar 15日目 Ubuntuユーザに送る、国産twitterクライアント「mikutter」の楽しみ方

この記事はmikutter Advent Calendar 2013(http://www.adventar.org/calendars/120)15日目でございます。

2013年はmikutterがUbuntuの公式リポジトリ(てかDebian sid)に登録され、早速日経Linuxに取り上げられるなど、mikutterメジャーデビューの年となりましたね。

来年は、Debian系を使う色んな人がておくれ・・・ゲフンゲフン。
mikutterを楽しんでもらえる状況になれば良いなと思います。

と言う訳で、今回は来年のmikutterあべのミクすに拍車を掛けるべく、Ubuntu13.10でのmikutterとの付き合い方を纏めてみようと思います。

Ubuntu13.10にmikutterをインストールしてみよう

前述の通り、Ubuntu13.10ではUbuntuソフトウェアセンターからmikutterがインストールが出来る様になりました。

さっそく検索窓に「mikutter」と入力し、インストールを始めましょう。

f:id:moguno:20131215003958p:plain

検索に引っかからない場合は、まずソフトウェアアップデートを行うと良い様です。


では、Unityからmikutterを起動して見ましょう。
mikutterが起動すると、流暢な日本語でTwitterのサイトから暗証番号(PINコード)を取得する様に促されます。

f:id:moguno:20131215004044p:plain

「あれ、mikutterってもしかしてアレ系のソフトなん・・・?」などと一抹の不安を抱きつつ、URLをクリックしてユーザ名とパスワードを入力し、PINコードを入手しましょう。

インスタントメッセンジャーを思わせるウインドウが表示されればセットアップは完了です。

f:id:moguno:20131215004208p:plain

起動直後は検索タブがアクティブなので何も表示されませんが、「t」アイコンのタブをクリックする事でタイムラインが表示されます。

プラグインを導入してみよう

mikutterの大きな特徴として、強力なプラグイン機構が挙げられます。

mikutterのプラグインは非常に簡単に作成出来るため、腕に覚えのあるプログラマ達が日々モリモリとプラグインを量産しています。
12/15現在、公式Wikiに登録されているだけでも78種類のプラグインがあり、その全てが日本語完備と言うちょっと凄い状況になっています。

http://yuzuki.hachune.net/wiki/Plugin

プラグインを使いこなすことで、掛け値無しで自分だけのmikutterを手に入れることが出来ちゃうと言う訳です。

それでは、一般的な配布形態であるGitHubからプラグインを導入する手順を解説して行きます。

(1)GitHubのサイトを開く

例として、ここでは拙作プラグイン「mikutter-hide-titlebar」を導入してみます。
まずは、mikutter Wikiのリンクから、GitHubのページに飛んでください。

http://yuzuki.hachune.net/wiki/Plugin

f:id:moguno:20131215004802p:plain

(2)GitHubで[Download ZIP](Zipでくれ)ボタンを押して、プラグインをダウンロードします。

f:id:moguno:20131215004915p:plain

(3)ダウンロードしたZipファイルをアーカイブマネージャで開き、展開ボタンを押します。

f:id:moguno:20131215005029p:plain

(a)Ctrl + Hを押して隠しファイルを表示させ、.mikutterフォルダに移動します。

f:id:moguno:20131215005141p:plain

(b).mikutterフォルダにpluginフォルダを新規作成します。

f:id:moguno:20131215005352p:plain

(c).mikutter/pluginフォルダにZipを展開します。

これで~/.mikutter/pluginフォルダの下にmikutter-hide-titlebar-masterフォルダが出来たはずです。

(4)展開したmikutter-hide-titlebar-masterフォルダのフォルダ名から-masterを削除します。

f:id:moguno:20131215005545p:plain

プラグインの.rbファイルとフォルダ名を合わせないとプラグインとして認識してくれないので、ここ何気に重要です。


以上でプラグインの導入は完了です。
mikutterを再起動すると、タイトルバーが無くなってすっきりした外観になっているはずです。

f:id:moguno:20131215010323p:plain

ウインドウの操作はステータスバーに増えたボタンで行えるのでご安心を。

もしもプラグインが気に入らなければ、フォルダを削除してmikutterを再起動すればOKです。

「バグ技(察し)」でもっと便利に

実はmikutterには「プラグインフォルダに特定の名前のファイルを置くと、何故だか昔のUIに戻ってしまう」と言う問題があります。

作者もこの問題は認識しており、Twitterのデザインガイドラインを逸脱する恐れがあるとして、ユーザに注意喚起を行っています。

しかしながら、プラグイン機構の仕様の副作用として発生するこの問題は修正が困難で、現在に於いても解決策は見つかっていません。

そんな状況を良い事に、Twitterデザインガイドラインが気に入らない一部のユーザは、このバグ技を利用して直感的な旧UIを使っていると言う状況です(察し)。


ここでは、mikutter初心者の皆さんが誤ってこの問題を再現させないために、具体的な再現条件を書いておきます(察し)。

(1)~/.mikutter/pluginフォルダを開く。

f:id:moguno:20131215011410p:plain

(2)空のドキュメントを作成する。

f:id:moguno:20131215011507p:plain

(3)ドキュメントのファイル名をdisplay_requirements.rbに変更する。

f:id:moguno:20131215011902p:plain

これで再現手順は完了です。

mikutterを再起動すると、懐かしのUIが出現してしまいます (察し) 。
下部の青い鳥がいなくなっていたり、時刻表示が絶対時刻になったりしていますね。

f:id:moguno:20131215011914p:plain

なんか実績「display_requirements」が解除されていますが、きっとバグでしょう(察し)。

mikutterを使う際には、誤って上記の手順を踏まない様に十分注意しましょう(察し)。

mikutter Advent Calendar 7日目 プラグイン書くの楽しいです( º﹃º` )

mikutter Advent Calendar 2013(http://www.adventar.org/calendars/120)7日目でございます。

早いもので、後少し寝れば 年末の納期がぁぁ!あの機能まだ人のアサイン決まって無いprz... 2013年も終わりですね。


思い返せば、2013年も色々ありました。

  • MacBook Air買ってmikutterを動かしたら組織から命を狙われたり。
  • 仕事の合間を縫ってOSCに薄い本を買いに行くも京都駅で遭難して夢潰えたり。
  • mikutterのプラグインを猿の様に書きまくったり。


こんな充実した(?)mikutterライフの結果、僕の愛娘は現在こんな感じになってます。

f:id:moguno:20131207004651p:plain:w250

はい、「シンプル」の意味を完全に取り違えてます。我ながらておいっす。

今回はこんな重病に至る過程で見つけた、ちょっとしたプラグイン作成Tipsをご紹介します。

メッセージをageる。

Messageにはcreatedとmodifiedって言う2つの日時を格納するフィールドがあります。
createdは投稿日時表示に使われる物で、modifiedはメッセージの並びを制御する物になります。

このmodifiedにnowを設定すると、メッセージをTLの一番上に持ってくる事が出来ます。

modifiedには未来の日時を設定する事も出来ます。
この場合は、TL上部にメッセージを一定時間張り付ける事が出来ます。
通知領域みたいに使えますね。

  def pin(msg, pinning)
    if pinning then
      # メッセージを5分間TL上部に張り付ける
      msg[:modified] = Time.new + 5 * 60
      msg[:pinned] = "pinned" # (※1)
    else
      msg[:modified] = Time.new
      msg[:pinned] = nil
    end

    # メッセージ変更をコアに通知する。
    Plugin::call(:message_modified, msg)

    msg
  end

ちなみに、Messageには任意のフィールドを追加する事が出来ます(※1)
後続のフィルタで自プラグインが処理したメッセージの背景色を変えたいときとかの目印に使えて便利です。

  filter_message_background_color { |message, color|
    if message.message[:pinned] then # ※1のフィールドが存在するなら背景色変更
      color = UserConfig[:gosunkugi_background_color]
    end

    [message, color]
  }

ステータスバーにボタンを追加する

バージョンアップの度に季節感のあるメッセージが表示されるステータスバー。
実はここ。ツールバーになっていて、デフォルトのネギレンチの他にもボタンを追加する事が出来ます。

f:id:moguno:20131207093523p:plain


やり方は簡単。
 roleが:windowなCommandを作るだけです。

  command(:open_my_profile,
          name: 'プロフィール',
          condition: lambda{ |opt| true },
          visible: true,
          icon: Service.primary.user_obj[:profile_image_url],
          role: :window) do |opt|
            Plugin.call(:show_profile, Service.primary, Service.primary.user_obj) end

GTKプラグインを操作する。

mikutterは0.2辺りからUIとコア部分の明確な分離が提唱されました。

それにより、UIプラグインの実装はコアから隠匿され、コアとUIは抽象化されたAPIで会話するようになりました。

その結果、標準のGTKプラグイン以外のUIプラグインが生まれてきました。

現在ではmikutter デーモンモードの様なUI不要な使い方や、Luna等の今となっては非力なワークステーションでも動作するCUIなインターフェースなどが登場しています。

一方、別のプラグインがGTKプラグインのオブジェクトを直接操作する事は出来なくなった・・・と思ってたら、ひっそりとインターフェースが存在してました。

:gui_get_gtk_widgetフィルタを使えば、抽象化GUIオブジェクトi_window、i_tab、i_timelineなどを引数にして、対応したGTKオブジェクトが取得出来ます。

  on_window_created do |i_window|
    begin
      # メインウインドウを取得
      window_tmp = Plugin.filtering(:gui_get_gtk_widget,i_window)

      if (window_tmp == nil) || (window_tmp[0] == nil) then
        next
      end

      # windowはGtk::Windowのインスタンス
      window = window_tmp[0]

      # 後はお好きに。

さいごに

ここで紹介したTipsを使った拙作プラグインはGitHubに置いてあります。
活用頂けたら幸いです。

https://github.com/moguno

タイトルバーを隠すmikutterプラグインを作ったよ。

なにこれ?

タイトルバーってなんでタイトルとウインドウいじるボタンしか無いくせに常時画面に居座ってるの?ピクセルの無駄ですよね。

と言う訳で登場です。タイトルバーを消して空間を無駄無く使うmikutterプラグイン。

f:id:moguno:20130910002341p:plain:h400

はい。何か締まりのない画面ですね。

どっちかと言うと、こうやって画面端に常駐してる風に配置する事を想定しています。
タイトルバーの分表示出来る情報が増えて良いです。

f:id:moguno:20130910003727p:plain:h400

使い方

インストールすると即座にタイトルバーが消えて、ステータスバーに三つのボタンが追加されます。
ボタンの機能は左からこんな感じです。

ウインドウ移動

ボタンを押した状態でマウスを動かすとウインドウを移動出来ます。

ウインドウを閉じる

ウインドウを閉じます。ておくれてる人は使う機会は無いでしょう。

ウインドウ最大化

ウインドウを最大化します。

ウインドウ最小化

ウインドウを最小化します。ておくれ(略

インストール

お好きなプラグインディレクトリにて、以下のコマンドでインストール出来ます。

git clone http://github.com/moguno/mikutter-hide-titlebar

Alcohol52%のイメージファイルmdf + mdsをwav + cueに変換する

前回めでたく成功したbin + cue→wav + cue変換。

bin + cueをwav + cueに変換する - もぐてっく

でも、うちのライブラリはbin + cueじゃなくてAlcohol52%ネイティブのフォーマットであるmds + mdfファイルなのよね。

ひとつずつ再変換しても良いんだけど、超面倒だよね。うん。面倒だ。

まぁ、mdfとbinは完全互換なのは調査済みだから、後はmdsファイルを適当にパースしてcueファイル作れば良いんじゃね?

(mdsファイルをテキストエディットで開く)

バババ、バイナリィィィィィィィィィィィィィィ!?


バイナリファイルでした(泣)。


でも、こんなことではめげません。めげずに調べます。


mdsファイルのデータ構造はここにドキュメントがあるのですが、所々計算が間違っててどうも怪しいです。

http://developer.berlios.de/docman/display_doc.php?docid=840&group_id=2545

なので、大変参考にしつつ(ありがとうございます)hexdumpとにらめっこしながらそれっぽい構造体を作ってみました。


これでmdsファイルの中身が読めるようになりました。
でも、それだけではダメでした。ダメダメでした。

mdsのdata_blockに格納されてる値でcueファイルを作ると、全体的に2秒ずれてしまいました。単純に2秒引けば良いんですけど、なにかとてもきもち悪いから調べてみました。


なんでも、CD-DAは「きかくじょう」、トラック1の前に2秒(150フレーム)の無音区間(プリギャップ)が必要なのだそうです。

そして、その2秒の無音区間は無駄なので、mdfファイルからは削られてます。

でもでも、mdsは2秒のプリギャップを削る前のタイムスタンプを保持しているので、件のズレが発生します。

多分ヘッダのpregap_corrに入ってる-150が削ったプリギャップだと思うので、これを75で割って引き去るようにしました。


はい。これで出来ました。

moguno/mdsmdf2cuewav · GitHub

第一引数にmdsファイルを指定すると、同じフォルダにcueファイルとwavファイルが出来ます。


これで本気でWindowsが捨てられる準備が出来ましたv

bin + cueをwav + cueに変換する

約1ヶ月半にMacBook Airを買って、現時点での究極のGUIを堪能している今日この頃ですが、一つだけ不満が・・・。

MacってCD-DAのイメージファイルをマウント出来る仕組みが無いのよね。

Windows時代に物理的耐用年数(20年)を過ぎたCD達をイメージ化してたんだけど、これが死んでしまうのは非常に痛い。

LINDBERGのEXTRA FLIGHTとかFlight Recorderとかが聞けなくなったらと思うと、もうウルウルっす・・・。

ウルウルしながら調べたところ、XLDってフリーソフトを使えばwav + cueファイルを分割してiTunesに登録してくれることを発見。やった。

ああ、でも愛用のAlcohol52%はcueシート付きのイメージファイルが作れるけど、イメージ本体はbinファイルなのでアウト。

号泣しながらさらに調べたところ、binとwavの差はRIFFコンテナへの格納有無だけらしいので簡単に変換出来そう。

よし、bin + cueファイルをwav + cueファイルに変換してみよう!

やることの説明

CD-DAのイメージファイルであるところのbinファイルは、CD-DAの内容を44.1kHz、16bit、2チャンネル(ステレオ)のPCM形式で保存したものです。サウンドレコーダでいう「CDの音質」と言う奴です。
(CDなんだから当たり前ですが。てか最近の子ってサウンドレコーダ知ってんの?)


一方のwavファイルは、RIFFコンテナに格納されたサウンドデータです。
RIFFはチャンク構造を持っており、名前付きの可変長データを複数格納出来るようになっています。

[RIFFヘッダ][チャンクヘッダ1(チャンク名)(データサイズ)][データ1][チャックヘッダ2(チャンク名)(データサイズ)][データ2]・・・

最近の概念だと、キーバリューストアだと思ってもらったら良いです。

さて、RIFFについて何となく理解したところで、wavファイルを形成するためのチャンクを見ていきます。

最低限必要なチャンクは"fmt"と"data"の2つ。

先に書いたサンプリングレートその他の情報はイメージファイルからは推測が不能なため、"fmt"チャンクにフォーマットの情報を格納する必要があります。
そして、肝心のデータは"data"チャンクに格納します。

受け売り文献:wav ファイルフォーマット


と、言う訳でbinファイルをwavファイルに変換するには、以下の情報を納めたファイルを作れば良いことになります。

  1. RIFFヘッダ(ファイルサイズ以外は固定データ)
  2. fmtチャンクヘッダ(固定データ)
  3. フォーマット情報(固定データ)
  4. dataチャンクヘッダ(データサイズ以外は固定データ)
  5. binファイルの内容(可変データ)


割と簡単。

実装してみた

複雑なことはしなくても良いので、シェルスクリプトで変換することにしました。
ついでにcueシート内のイメージファイル名の変換機能も付けました。

#! /bin/sh

encode_int32()
{
	X01000000=`printf %d 0x01000000`
	X00010000=`printf %d 0x00010000`
	X00000100=`printf %d 0x00000100`

	printf '\\x%x' `expr $1 % $X00000100`
	printf '\\x%x' `expr $1 / $X00000100 % $X00000100`
	printf '\\x%x' `expr $1 / $X00010000 % $X00000100`
	printf '\\x%x' `expr $1 / $X01000000 % $X00000100`
}


bin2wav()
{
	WAVE_SIZE=`wc -c "$1" | cut -f2 -d " "`
	SIZE=`expr 48 + $WAVE_SIZE - 8`

	HEAD_1=RIFF
	HEAD_2=`encode_int32 $SIZE`
	HEAD_3='WAVEfmt \x10\x00\x00\x00\x01\x00\x02\x00\x44\xAC\x00\x00\x10\xB1\x02\x00\x04\x00\x10\x00data'
	HEAD_4=`encode_int32 $WAVE_SIZE`

	printf "$HEAD_1$HEAD_2$HEAD_3$HEAD_4" > "$2"
	cat "$1" >> "$2"
}


rewrite_cue()
{
	mv "$1" "$1"_bin2wav
	echo FILE \"$2\" WAVE > tmp
	tail +2 "$1"_bin2wav >> tmp
	mv tmp "$1"
}


cd `dirname "$1"`

CUE_EXT=`echo "$1" | sed -E 's/^.+(\.[^\.]+)$/\1/g'`
BASENAME=`basename "$1" $CUE_EXT`

if [ "$CUE_EXT" == ".cue" ];then
	BIN_EXT=".bin"
	WAV_EXT=".wav"
elif [ "$CUE_EXT" == ".CUE" ];then
	BIN_EXT=".BIN"
	WAV_EXT=".WAV"
else
	echo Invalid cue file > /dev/stderr
	exit 1
fi

bin2wav "$BASENAME$BIN_EXT" "$BASENAME$WAV_EXT"
rewrite_cue "$BASENAME$CUE_EXT" "$BASENAME$WAV_EXT"


第一引数にcueファイルのファイル名を指定してやれば、めでたくXLDで読めるwav + cueが完成します。

Raspberry Pi meets XBMCなRaspbmcを無線アクセスポイント化してみた

Raspberry Piって超軽いから、出張に持って行っても苦にならないよね。

これをホテルに持ち込んでテレビに接続してAirPlayで動画を見れる様にしたら¥1,000/泊のメディア費削減になるんじゃね!

ついでにこれが無線ルータになったら、ホテルのネット回線も使い放題!超実用的!!

AirPlayはRaspbmcと言うRaspberry PiをXBMC箱にするディストリを使えば可能。
これに出っ張らないUSB無線LANをつなげてアクセスポイントにしてやればいいね。

と、言う訳でRaspbmcの無線アクセスポイント化スタートです。

用意するもの

・Raspbmc導入済みのRaspberry Pi(Model B、256MB版)
・PLANEX GW-USNano2

PLANEX ハイパワー無線LAN ゲーム機用 Wi-Fi USBアダプタ GW-USNANO2-G

PLANEX ハイパワー無線LAN ゲーム機用 Wi-Fi USBアダプタ GW-USNANO2-G

USB無線LANRealtekのRTL8192CUチップを指名買い。たまたまじゃんぱらで安かったPLANEXのGW-USNano2にしました。

指名買いの理由は、Linuxでのアクセスポイント利用をサポートしてた(公式ドライバに専用のhostapdが同梱されてる)から。独自改変したhostapdを使うってのはなんかダサいんですが、裏を返せばこのチップ専用にフィッティングされてる訳で、細かいトラブルを避けられると思った次第。

ドライバのダウンロード

まずはブラウザの使えるクライアントでRealtekのサイトにアクセスして、RTL8192CUのドライバを取ってきます。今回はRTL8192xC_USB_linux_v3.4.4_4749.20121105.zipを使います。

RaspbmcにはRTL8192CUのドライバが標準で入っているので、今回使うのはこの中のhostapdだけです。

取りあえずRaspbmcにログイン

Raspbmcが起動したのを確認して、sshでアクセスします。
AvahiやBonjourが生きてる環境なら名前でアクセス出来るから超便利。なんでWindowsはサポートしないんだろ。

なお、パスワードはraspberryです。

$ ssh pi@raspbmc.local


初回ログイン時にロケールとかタイムゾーンとかを訊いてきますが、まあお好みで。
(ここでははen_US.UTF-8とAsia/Tokyoにしました。)

今後sudoが面倒なのでrootになっておきます。
(なんかいい方法があったはずだけど忘れた。)

$ sudo su -
#


後、別の端末を開いてさっきダウンロードしたドライバをRaspbmcに転送しときます。

$ scp -p ~/Downloads/RTL8192xC_USB_linux_v3.4.4_4749.20121105-1.zip pi@raspbmc.local:/tmp/

必要なパッケージをインストール

ここからはRaspbmcにログインした端末での作業です。
hostapdのコンパイルに必要なパッケージとお手軽DHCP&DNSサーバであるところのdnsmasqを導入します。

# apt-get update
# apt-get install unzip build-essential dnsmasq

専用hostapdのインストール

なぜかzipなアーカイブを解凍しつつ、最深部(※)にあるソースコードをコンパイルすればこのステージはクリアです。
(※:パスが100文字超になって笑けます。)
途中に設定ファイルのテンプレートが落ちているので、忘れずにゲットして/etc/にコピーしておきましょう。

# unzip /tmp/RTL8192xC_USB_linux_v3.4.4_4749.20121105-1.zip
# cd RTL8188C_8192C_USB_linux_v3.4.4_4749.20121105/wpa_supplicant_hostapd/
# cp -p rtl_hostapd_2G.conf /etc/hostapd.conf
# unzip wpa_supplicant_hostapd-0.8_rtw_20120803.zip
# cd wpa_supplicant_hostapd-0.8/hostapd/
# make
# make install

/etc/hostapd.confの修正

さっき手に入れた設定ファイルをカスタマイズします。
WPSがONだとWPA設定でもWEPのアクセスポイントになってしまうのでWPSを切ります。

 13 ##### Wi-Fi Protected Setup (WPS) #############################################
 14
 15 eap_server=1
 16
 17 # WPS state
 18 # 0 = WPS disabled (default)
 19 # 1 = WPS enabled, not configured
 20 # 2 = WPS enabled, configured
 21 wps_state=0      # 2→0

にっくきNetworkManagerとの戦い

近頃クライアント系で流行のNetworkManager。
GUIで設定が出来たり、ケーブル接続時に通知メッセージが出たりとかで格好良いんですけど、そいつが古き良きネットワーク設定の仕組みを見事にぶち壊してくれています。
ifconfigでIPアドレス設定してても、LANケーブルを抜いたらそれが吹っ飛ぶとか勘弁して欲しいです。

今回も例によってNetworkManagerがしっかり無線LANを管理しちゃってて、hostapdの動きをことごとく邪魔してくれやがります。シネバイイノニ。

しかしながら安易にNetworkManagerを停止させると今度はRaspbmcのXBMCが文句を言い出すので、今回はなんとか共存の道を探らねばなりません。

爆乳の魔王が相手なら和平交渉も楽しいんでしょうが、相手バイナリファイルだし。面倒いなぁ。

まぁいいや。んでは、色々ファイルを書き換えていきます。

/etc/NetworkManager/NetworkManager.confの編集


「ごめんなさい。そして、ありがとうございました。
 私に、私のwlan0を返してください。それは私が背負うべき物でした。」


などと、NetworkManager様に丁重にお願いします。ちっ。

あらかじめ、ifconfigで無線インターフェースのMACアドレスを控えておきます。

# ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr 00:22:cf:b7:a5:da  ←コレ
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


メモしたMACアドレスをNetworkManagerの管理対象外にします。

  1 [main]
  2 plugins=ifupdown,keyfile
  3
  4 no-auto-default=B8:27:EB:9B:C0:8C,
  5
  6 [ifupdown]
  7 managed=false
  8
  9 # 以下追加。11行目のmac以降は調べたMACアドレス
 10 [keyfile]
 11 unmanaged-devices=mac:00:22:cf:b7:a5:da


これを保存すると即座に対象のインターフェースがダウンするので、その誠実さにちょっと感激したりしました。

/etc/network/if-up.d/secure-rmcの編集

まだいました!お邪魔設定ファイル!

こやつはifupしたタイミングで実行されて、そのインターフェース用の基本的なnetfilterルールを設定しやがります。
(インターフェースのサブネットからのパケットはACCEPT、それ以外はDROP。)

通常の用途だと毒にも薬にもならないルールですが、今回のアクセスポイント化に当たっては、クライアントからのDHCPパケット(0.0.0.0→255.255.255.255)が捨てられてしまうのでダメダメです。

# こいつのせいでifupで立ち上げたらうまくDHCPIPアドレスが取れなくて、ifconfigでなら正常動作と言う謎の動作になって数時間悩んだよ。。。

ルールを追加してやります。

125 logger -t iptables "Configuring ip tables for interface $IFACE"
126 if [ "$IFACE" != "lo" ]; then
127     NETMASK=$(get_subnet $IFACE)
128     iptables -A INPUT -s $NETMASK -i $IFACE -j ACCEPT
129     # DHCPを許可するルールを追加
130     iptables -A INPUT -s 0.0.0.0 -i $IFACE -j ACCEPT
131     iptables -A INPUT -i $IFACE -j DROP
132 fi

/etc/network/interfacesの編集

ここからは前向きな設定作業です。
無線インターフェースのIPアドレス他を設定していきます。
hostapdのinitスクリプトを書くのが面倒なのでpost-up/pre-downで起動・停止を制御することにします。
さらに、IPマスカレードの設定も入れときます。

interfaces超便利!

  1 allow-hotplug wlan0
  2
  3 iface wlan0 inet static
  4     address 192.168.0.1
  5     netmask 255.255.255.0
  6     broadcast 192.168.0.255
  7     network 192.168.0.0
  8     post-up /usr/local/bin/hostapd -B /etc/hostapd.conf
  9     post-up iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j MASQUERADE
  9     pre-down kill `pidof hostapd`
 10     pre-down iptables -t nat -D POSTROUTING -o eth0 -s 192.168.0.0/24 -j MASQUERADE


なぜかauto wlan0ではインターフェースが上がらなかったので、allow-hotplugにしてます。

/etc/dnsmasq.confの編集

無線インターフェースに対するDHCPサーバの設定を行います。

90行目のinterfaceのコメントを外して、wlan0を指定します。
これを忘れると家中のIPネットワークが混乱の渦に巻き込まれるので注意。

 86 # If you want dnsmasq to listen for DHCP and DNS requests only on
 87 # specified interfaces (and the loopback) give the name of the
 88 # interface (eg eth0) here.
 89 # Repeat the line for more than one interface.
 90 interface=wlan0


147行目のdhcp-rangeのコメントを外します。

143 # This is an example of a DHCP range where the netmask is given. This
144 # is needed for networks we reach the dnsmasq DHCP server via a relay
145 # agent. If you don't know what a DHCP relay agent is, you probably
146 # don't need to worry about this.
147 dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h

/etc/sysctl.d/ipv6.confの新規作成

RaspbmcのAirPlayでハマったでござる - もぐてっく

で、書いたAirPlayのためのIPv6無効化を行っておきます。

  1 net.ipv6.conf.all.disable_ipv6 = 1

/opt/xbmc-bcm/xbmc-bin/share/xbmc/addons/script.raspbmc.settings/autostart.pyの編集

Raspbmcはリセットボタンを押さずに電源を引っこ抜くと、次回起動時に

「ちゃんと画面端のパワーボタン押して終了せぇやボケぇ!死ぬかと思たわ!!」

と、怒られます。

その主張はごもっともなんですが、外出先でこのダイアログが出たら消す方法が無いので、当該コードをコメントアウトします。

126 #       if DISTRO == "Raspbmc" and os.path.isfile("/home/pi/.bootstatus") and check_service_running("wd") == "true" and not os.path.isfile("/home/pi/.nowarning"):
127 #             import xbmcgui
128 #             dialog = xbmcgui.Dialog()
129 #             dialog.ok("Raspbmc did not shut down properly", "Raspbmc should always be shut down via the\npower icon in the lower left corner")

リブート!

これでRaspberry Piの無線AP化は完了です。リブートして成果を確認します。

お手持ちの無線クライアントを見ると、rtwapってアクセスポイントが増えています。
パスワード87654321で接続出来れば成功です!