Synaptics Clickpad で右クリックを有効にする on Ubuntu Precise (12.04)
2本指タップで右クリックになるし、それに慣れちゃったけど、一応
オンラインで変更するには
$ xinput set-prop "SynPS/2 Synaptics TouchPad" "Synaptics Soft Button Areas" 3872 0 3984 0 0 0 0 0
数字はよそから拾ってきた適当なものだけど。
これで一応右下をクリックすると右クリック扱いとなる。
Skype4Py で bot を作る
コンタクトリストのユーザーがオンラインになったら「おかえり」というストーキング bot。
ちなみに Ubuntu 12.04 (Precise) で Skype4Py (パッケージ名 python-skype
) をインストールするにはレポジトリに ppa:skype-wrapper/ppa
を追加する必要がある (パッケージ skype-wrapper.deb
自体はインストールする必要はない)。
# -*- coding: utf-8 -*- import Skype4Py import Queue import logging # Skype4Py のログを出力する (DEBUG だと大量なので注意; 便利だけど) logging.basicConfig(level=logging.DEBUG) # ALTER CHAT の expected response が変わったので修正 def my_Chat__Alter(self, AlterName, Args=None): return self._Owner._Alter('CHAT', self.Name, AlterName, Args, 'ALTER CHAT %s' % AlterName) Skype4Py.chat.Chat._Alter = my_Chat__Alter q = Queue.Queue() def on_online_status(user, status): print '%s (%s): %s' % (user.Handle, user.FullName, status) if status == 'ONLINE': q.put(user.Handle) def main(): skype = Skype4Py.Skype() skype.OnOnlineStatus = on_online_status skype.Attach() while True: try: item = q.get(True, 86400) # 0 にすると KeyboardInterrupt が効かない chat = skype.CreateChatWith(item) chat.SendMessage('おかえり') chat.Leave() except Queue.Empty: pass main()
skype
をグローバル変数にぶっこんで,on_online_status()
から CreateChatWith()
してもいいんだけど,やり取り等は別スレッドで動いているらしく,ちょいと気持ち悪かったので Queue
でスレッド間通信することにした。おかげで while True: time.sleep(1)
みたいなことをしなくてもよくなった (実質 Queue.get()
してるので同じだけど)。
上のサンプルでは Queue
にユーザーハンドルだけつっこんでるけど,将来的にはそれなりのコマンドオブジェクト等をつっこむようにしたほうがいいですね。
uWSGI でファイルが更新された時にリロードする
最初は inotifyx 使って自力で書こうとしてたんだけど,ブロックしてうまくいかなかったりして,thread でも立てなきゃいけないのかなと思って uwsgidecorators を読んでたら,そもそも uwsgidecorators (というかそもそも uwsgi-core) にファイル更新検知機能がついてた。
メインとなる wsgi ファイルが main.wsgi
というファイル名だったとして,監視するモジュール((別モジュールだてにしなくてもいい (main.wsgi
に入れ込む) のかもしれないけど追ってない。追記: やってみたら別モジュールに独立させなくてもうまくいった。小さい捨て WSGI スクリプトであればリロードのロジックを入れ込むのが楽。(もちろん,WSGI アプリケーションとして独立させるなら入れ込まないほうがいいけど) )) watcher.py
は:
# watcher.py import uwsgi from uwsgidecorators import filemon @filemon('main.wsgi') def reloaded(num): uwsgi.reload()
対応する uWSGI のパラメータは,たとえば:
; uwsgi.ini [uwsgi] master = true plugins = python,http http = :8000 wsgi-file = main.wsgi import = watcher
ファイルがいっぱいあったらどうすんねーんと思うかもしれないけど,@filemon
デコレータは,ディレクトリの監視もできます。
ちなみに内的には inotify 機構を使っているので無駄はないはず。IN_ALL_EVENTS
レベルの監視をしてるんで,touch
とかしてもちゃんとリロードされます。
あと今回の話と関係ないけど,たとえば 1 分間リクエストがなかった場合にワーカーを自動的に kill するには
idle = 60
のようにしておけばよい。uWSGI 使うようなシチュエーションでそんなにメモリをケチるシーンはないかもしんないけど。
追記: idle
(や lazy
) と @filemon
の相性はよくないかもしんない。ワーカーがいない状態で編集してリクエスト投げたら編集前の内容だったことがあった。気のせいかもしれないけど。
もういっこわりと便利なオプションは harakiri
で
harakiri = 60
のようにしておくと,処理に 60 秒以上かかるワーカーは強制的に落とされます。
NLog で動的にログの出力状況を変更する
設定のオンオフで,ログを出力するかどうかを変えたり,出力先 (ファイルや TextBox など) を変更したい場合。
たとえば NLog.config
が以下のようなときに
<?xml version="1.0" encoding="utf-8"?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="file" xsi:type="File" fileName="${basedir}/app.log"/> </targets> </nlog>
target
は定義されているけど rule
は定義されていないので,このままではログはどこにも出力されていない。
この状況で上記の file
target にログを出力するように変更するには,
using NLog; using NLog.Config; NLog.Targets.Target target = LogManager.Configuration.FindTargetByName("file"); LoggingRule rule = new LoggingRule("*", LogLevel.Debug, target); LogManager.Configuration.LoggingRules.Clear(); LogManager.Configuration.LoggingRules.Add(rule); LogManager.ReconfigExistingLoggers();
のようにする。
LogManager.Configuration.LoggingRules
は List<LoggingRule>
なので,List ジェネリックスのメソッドを使って操作すればよい。ただし操作しただけだとすでに生成されてしまっているロガーには (その設定変更が) 伝わらないので LogManager.ReconfigExistingLoggers()
する必要がある。
Rack::Auth::Digest::MD5 のつかいかた
Rack::Auth::Digest::MD5
は opaque
を渡さないといけない((つうかそもそも opaque
は optional なはずなのに Rack::Auth::Digest::MD5
では必須パラメータってのも変なんだけど。))ので素直に書けないと思いがちだけど,現在の Rack::Auth::Digest::MD5
は第2引数に opaque
をとるので,シンプルに use
を使って書ける。
require 'rack/auth/digest/md5' use Rack::Auth::Digest::MD5, 'my realm', '', do |username| 'password' end run my_app
Padrino の場合はこんなふうに。
Padrino.before_load do require 'rack/auth/digest/md5' Padrino.use Rack::Auth::Digest::MD5, 'my realm', '', do |username| 'password' end end
アプリケーションのステートに応じて opaque
を返したい場合((これが本来の opaque
の使い方。とはいえ,いろいろ代替手段があるので (nonce
に入れ込んじゃうとか Cookie 使うとか) opaque
が真面目に使われるケースはないんじゃないかな。))は,結局 Rack::Auth::Digest::MD5
のインスタンスを生成して rack mount していくしかない気がする。そもそもそんなシチュエーションでは rack middleware じゃ単純には無理かな?
ともかく。
実は名前付きパラメータでも引数を渡せるので下記のようにも書ける。
require 'rack/auth/digest/md5' use Rack::Auth::Digest::MD5, { :realm => 'the realm', :opaque => '', }, do |username| 'password' end run my_app
ところで。
せっかく HTTP Digest 認証なのに,生パスワードを書かないといけないなんてダッサいと思いませんか。
:passwords_hashed
パラメータを true
にすると,ハッシュ化したパスワード等 (いわゆる A1
) を渡せばよくなるのでシステムに生パスワードを保存しておく必要がなくなる((実はハッシュ化された A1
(と username
の組) を盗まれると認証できてしまう。なのでハッシュ化して保存したとしてもユーザーの (他のサービスと共用しているかもしれない) 生パスワードが盗まれないという意義しかない。))。
require 'rack/auth/digest/md5' REALM = 'the realm' require 'digest/md5' PWHASH = Digest::MD5.new.update('%s:%s:%s' % ['dayflower', REALM, 'password']) use Rack::Auth::Digest::MD5, { :realm => REALM, :opaque => '', :passwords_hashed => true, }, do |username| PWHASH end run my_app
PWHASH
の算出のところに生パスワード書いてあるけどこれはあくまでサンプルだからであって,あらかじめ計算しておくなり,htdigest
コマンドで生成した値を利用するなり,データベースに保存しておくなり,しておけば生パスワードを保存しておく必要はなくなる。
Rack::Auth::Digest::MD5 での nonce のとりあつかい (とバグ)
一般に,HTTP Digest 認証でリプレイ攻撃を「厳密に」防ぐには
nonce
をサーバサイドで生成しサーバに保持しておく- クライアントから返された
nonce
とnc
の組がすでに認証済ならハネる (新しいnonce
を生成し,stale
を立ててレスポンスするだけでいい) - クライアントから返された
nonce
がサーバサイドに保持したものと違えばハネる (上記と同様stale
token とする)
などする必要がある。
Rack::Auth::Digest::MD5
では nonce
はタイムスタンプになっている。のでサーバサイドに nonce
を保持しておく必要がなくなり実装が楽である。
「一定時間」を超えた nonce
を破棄していけば,その一定時間を超えた段階でのリプレイ攻撃が成立しなくなるのでカジュアルには,悪い選択肢ではない*1。
Rack::Auth::Digest::MD5
ではデフォルトではこの「一定時間」が設定されていない。このことは nonce
が破棄されないことをしめしている。なのでリプレイ攻撃やり放題である (サーバサイドに何も保持していないので nc
のチェックもしてないし)。
「一定時間」を設定するには Rack::Auth::Digest::Nonce::time_limit
に値を設定する。
require 'rack/auth/digest/nonce' Rack::Auth::Digest::Nonce::time_limit = 10
単位は秒なので,この例でいくとサーバが nonce
を発行してから10秒経つと無効な nonce
となる。時間切れになった場合は,stale
属性の立った WWW-Authenticate
をサーバが返すので,ユーザーエージェント側でパスワード入力ダイアログが再び出ることはない。エージェントが新しい nonce
をもとに自動的に認証ダイジェストを計算して再送信することになる((なので time_limit
をかなり小さくしてもまぁ大丈夫なのだが,サーバとクライアント間の通信遅延が大きい場合やクライアントの処理能力が非常に低い場合などはそれなりに大きなものにしておく必要があるだろう。))。
と,これでいいはずなんだけど,現在のところ Rack::Auth::Digest::Nonce
にバグがあるので,このようにすると常に stale
となり延々と認証リクエストレスポンスネゴシエーションが走ってしまう。このバグに対処するには下記のようにすればよい。
require 'rack/auth/digest/nonce' class Rack::Auth::Digest::Nonce def stale? !self.class.time_limit.nil? && (Time.now.to_i - @timestamp) > self.class.time_limit end end
誰も使っていないフィーチャーなのだろう。そもそも安全でない経路で Digest 認証をやることがメジャーではないのかもしれない。
暇ができたら issue 立てて pull request するつもりだけど,テストまで込みで考えるとめんどいなぁ。pull request だしといた。master にマージされた。
Ubuntu 12.04 (Precise) のインストールではまったこと
普通にインストールするぶんにははまらないけど個人的事情ではまった部分です。
つまり備忘録。
LVM を有効にしたインストール
基本路線は Ubuntu 8.04 を LVM 有効にしてインストールする - daily dayflower のとおり。ただし,dm-mod
はインストールする必要ない (だいぶ前からだけど)。
一点はまったのは,「インストールされた環境に lvm2 をインストールする」部分。
ネットワークブートな Live 環境からインストールしようとしたからかもしれないけど,chroot
したあとにうまくネットにつながらなかった。
procfs をマウントしたり,Live 環境の /etc/resolve.conf
をコピーする必要があった。
ubuntu@ubuntu:~$ sudo mount /dev/sda1 /target ubuntu@ubuntu:~$ sudo mount -o bind /proc /target/proc ubuntu@ubuntu:~$ sudo cp /etc/resolve.conf /target/etc/
ネットブートからのインストールだと,ここで /target/etc/network/interface
をいじっといたほうがいいかも (eth0
な部分を削除するか dhcp
に指定する)。
あとは元の手順通り。
ubuntu@ubuntu:~$ sudo chroot /target root@ubuntu:/# apt-get update root@ubuntu:/# apt-get install lvm2 root@ubuntu:/# exit
終了時は umount
もよしなに。
ubuntu@ubuntu:~$ sudo umount /target/proc ubuntu@ubuntu:~$ sudo umount /target
winbind 認証ログイン
基本的に winbind による Active Directory 認証 on Ubuntu 11.04 - daily dayflower のとおりなんだけど,Samba が 3.6.3 になってて設定ディレクティブ等がかわってたのではまった。
[global] workgroup = HOGE realm = HOGE.EXAMPLE.COM security = ADS obey pam restrictions = Yes algorithmic rid base = 10000 template homedir = /home/%U template shell = /bin/bash # winbind separator = ! winbind cache time = 60 winbind use default domain = Yes winbind nss info = sfu:HOGE winbind refresh tickets = Yes winbind normalize names = Yes winbind enum users = Yes winbind enum groups = Yes idmap config * : backend = tdb idmap config * : range = 1000000-1999999 idmap config DOMAIN : backend = rid idmap config DOMAIN : range = 10000-19999
idmap
のデフォルトの backend
等は idmap config * :
のように,ドメインごとの設定と統一感のある記述になった (古い書き方でも deprecated っていわれるだけだけど)。
あと,じっさいに使うドメイン向けじゃなくてデフォルトのを tdb
idmapper 等の永続性のある backend
に指定しておく必要があるみたい (BUILTIN sid とかのために((idmap_rid(8)
のマニュアルのうけうり。)) )。この部分に一番はまった。
winbind ユーザも lightdm からログインする
ディスプレイマネージャが lightdm に変更されていて,こいつはデフォルトだと登録されたユーザしかログオンできない (ユーザ名は選択する)。このままだと,winbind や ldap なユーザはログインできない。
ユーザ名も自分で入力できるようにするためには lightdm の設定をいじる必要がある。
具体的には,/etc/lightdm/lightdm.conf
の [SeatDefaults]
セクションに下記のような設定を付け足す。
[SeatDefaults] greeter-show-manual-login=true
DataMapper でカスタムリレーション
DataMapper - Associations の「Customizing Associations」に書いてある。
User と Message というモデルがあり,Message には宛先が(複数)指定できる,とする。できるだけ規約ベースで書くと以下のようになる。
class User include DataMapper::Resource property :id, Serial has n, :message_receipients has n, :messages, :through => :message_receipients end class Message include DataMapper::Resource property :id, Serial has n, :message_receipients has n, :users, :through => :message_receipients end class MessageReceipient include DataMapper::Resource property :id, Serial belongs_to :user belongs_to :message end u = User.create m = Message.new m.users << u m.save p m.users p u.messages
でもこれだと,「宛先」ぽさとか User の「受信したメッセージ」ぽさの意図がなくなってわかりにくい。
class User has n, :message_receipients, :child_key => [ :receipient_id ] has n, :received_messages, :model => 'Message', :through => :message_receipients, :via => :message end class Message has n, :message_receipients has n, :receipients, :model => 'User', :through => :message_receipients end class MessageReceipient belongs_to :receipient, :model => 'User' belongs_to :message end u = User.create m = Message.new m.receipients << u m.save p m.receipients p u.received_messages
要するに :child_key
とか (今回は使ってないけど) :parent_key
とか :via
などのオプションを使えば,リレーション時にどのような外部キーを使うかなどをカスタマイズできる。
新規開発だけでなくレガシースキーマを相手にする場合にも。
まぁ ORM でリレーションまでやってしまうかどうかという問題もあるけど。