Python with statement
Python の2.6からwith statement が標準機能となった.2.5でもfuture 機能として取り込まれていたので目新しくはないが,なかなか便利で,もう2.4には戻れない.with statementはtry, except, finally で実現できる構造をもう少し簡単に書けるようにしてくれる.以前のエントリで書いたthreading ライブラリのConditionなどに使うととても簡単に書ける.
このようなwithで使うオブジェクトをContext Managerと呼ぶ.Context Managerを書くのは割に簡単なので,ちょっと書いてみた.こちらの記事によくまとまっている.
実効ユーザIDのセット
Unixのプロセスにはreal user id(実ユーザID)と effective user id(実効ユーザID)の二つのidが割り当てられている.普通のプロセスでは両者は一致しているのだが,セキュリティの関係でことなる値を使いたい場合がある.例えば,任意のユーザのプロセスを操作する必要があるので,root権限が必要なのだけど,普段からroot権限で動くのはいかにも剣呑なので,普段はnobodyで動いているデーモンも多い.実効ユーザIDの戻し忘れはセキュリティホールに直結するので,注意が必要だ.
この実効ユーザIDを操作するContext Manager を書いてみよう.
with文に入るところで __enter__ が呼ばれ,出るところで__exit__が呼ばれるので,それぞれos.seteuidを呼び出しているだけ.__exit__にはちょっと注意が必要で,with文のなかで生じた例外のための引数が余分に存在する.withブロック内部で発生した例外を外に伝搬させるためには,__exit__文の戻り値をFalseにしなければならない.
import os class Euid(): def __init__(self, id): self.id = id def __enter__(self): self.orgeuid = os.geteuid() os.seteuid(self.id) def __exit__(self, exc_type, exc_value, traceback): os.seteuid(self.orgeuid) if exc_type: return False return True
使い方はこんな感じ.
print os.geteuid() with Euid(501): print os.geteuid() print os.geteuid()
これをroot権限で実行してみるとこうなる.
$ python setuid.py 0 501 0
withブロックの中でだけ実効ユーザIDが変更されていることが分かる.ただし,マルチスレッドで使う場合には注意が必要.実効ユーザIDはプロセス単位で定義されるので,別のスレッドから見ると突然実効ユーザIDがパカパカ切り替わることになる.実際あんまりいいサンプルじゃなかったかもしれない.