PythonでHardware UUIDを取得する
ノードロックライセンス機能を実装するにあたって,ハードウェアUUIDを取得する必要があったため,少し調べてみました.
■ はじめに
いわゆるノードロックライセンスでは,コンピュータ固有のIDに紐づけてライセンスを発行するため,ハードウェアを識別するための何かが必要となります.そのための「何か」として簡単に思いつくものとしては,MACアドレスやIPアドレス,ホスト名などがあるかと思います.このうちIPアドレス,ホスト名は,簡単に取得することができますが,変更されやすい性質のものなので,今回の用途にはあまり向いていません.Pythonのuuidモジュールには,getnode()というハードウェアアドレス(MACアドレス)を取得する関数があるので,これを使うのが簡単な方法かもしれませんが,MACアドレスも,NICが故障するなどして交換したら変わってしまいます.
CPUID命令を用いて,CPU固有のシリアル番号(PSN)を取得し,これを利用するという手もありますが,調べてみたところ,PSNはPentium IIIにのみ実装されているあだ花的な機能なので,これも却下です.(ちなみに,PyCPUIDというパッケージを使えば,Pythonから簡単にCPUID命令の実行結果を取得することが可能です.)さらにいろいろ調べてみたところ(参考サイト[1]),ハードウェア固有のIDとして,Hardware UUIDが利用できそうなことが分かったので,今回はこれを利用しようと思います.
■ コマンドからのHardware UUID取得
Hardware UUIDは,dmidecodeコマンドを使って調べることが可能です.
# dmidecode | grep UUID
もしくは,hal-get-propertyコマンドを使って以下のようにしても取得できます.
$ hal-get-property --udi /org/freedesktop/Hal/devices/computer --key system.hardware.uuid
ただし,hal-get-propertyの実行には,messagebusとhaldaemonの各サービスが立ち上がっている必要があります.
# service messagebus start # service haldaemon start
■ HALライブラリを用いてのUUID取得
さて,上記のコマンドを,Popenなどで,Pythonから実行して結果を取得するという方法でも良いのですが,できればライブラリ経由などで直接取得したいところです.参考サイト[1]を見ると,PythonからHALライブラリ(libhal)を呼び出すサンプルコードがあったので,今回はこれを利用することにしました.以下がその抜粋です.
import ctypes from ctypes.util import find_library from ctypes import Structure class DBusError(Structure): _fields_ = [("name", ctypes.c_char_p), # ... 省略 ... class HardwareUuid(object): # ... 省略 ... @property def _uuid(self): if not self._uuid_: udi = ctypes.c_char_p("/org/freedesktop/Hal/devices/computer") key = ctypes.c_char_p("system.hardware.uuid") self._hal.libhal_device_get_property_string.restype = \ ctypes.c_char_p self._uuid_ = self._hal.libhal_device_get_property_string( self._ctx, udi, key, self._dbus_error) return self._uuid_
このPythonのコードは,ctypesというPythonの外部関数インターフェイス(FFI)用モジュールを使って,Cで実装されたライブラリ(libhal)の関数を呼び出しています.ただ,残念なことにこのコードをそのまま利用すると,セグメンテーションフォールトとなってしまいました.どうやらバグがあるようです.libhalのドキュメント(参考サイト[2],[3])をもとに上記コードを調べてみたところ,_uuidメソッド定義の最後の方にlibhal_device_get_property_stringを呼び出している部分で,DBusErrorオブジェクトを渡しているところがありますが,そこで必要なctypes.byrefが抜けているのが原因とわかりました.
最終的にはエラー処理のコードなどを追加して,以下のような形にしました.
import ctypes from ctypes.util import find_library from ctypes import Structure class DBusRuntimeError(Exception): pass class DBusError(Structure): _fields_ = [("name", ctypes.c_char_p), ("message", ctypes.c_char_p), ("dummy1", ctypes.c_int), ("dummy2", ctypes.c_int), ("dummy3", ctypes.c_int), ("dummy4", ctypes.c_int), ("dummy5", ctypes.c_int), ("padding1", ctypes.c_void_p),] class HardwareUuid(object): def __init__(self, dbus_error=DBusError): self._hal = ctypes.cdll.LoadLibrary(find_library('hal')) self._dbus_error = dbus_error() self._hal.dbus_error_init(ctypes.byref(self._dbus_error)) self._conn = self._hal.dbus_bus_get(ctypes.c_int(1), ctypes.byref(self._dbus_error)) if self._hal.dbus_error_is_set(ctypes.byref(self._dbus_error)): emesg = "Unable to connect to DBus: %s" % self._dbus_error.message raise DBusRuntimeError(emesg) self._ctx = self._hal.libhal_ctx_new() if not self._hal.libhal_ctx_set_dbus_connection(self._ctx, self._conn): emesg = "Error: %s" % self._dbus_error.message raise DBusRuntimeError(emesg) if not self._hal.libhal_ctx_init(self._ctx, ctypes.byref(self._dbus_error)): emesg = "Hal context initializing failure: %s" % self._dbus_error.message raise DBusRuntimeError(emesg) self._uuid_ = None def __call__(self): return self._uuid @property def _uuid(self): if not self._uuid_: udi = ctypes.c_char_p("/org/freedesktop/Hal/devices/computer") key = ctypes.c_char_p("system.hardware.uuid") self._hal.libhal_device_get_property_string.restype = ctypes.c_char_p self._uuid_ = self._hal.libhal_device_get_property_string( self._ctx, udi, key, ctypes.byref(self._dbus_error)) if self._hal.dbus_error_is_set(ctypes.byref(self._dbus_error)): emesg = "Error getting string property: %s" % self._dbus_error.message raise DBusRuntimeError(emesg) return self._uuid_
実行してみます。
>>> getuuid = HardwareUuid()
>>> getuuid()
'564D5934-8B4C-6465-D4B3-519D08DCC3C9'
めでたしめでたし.
■ 参考サイト
[1] http://stackoverflow.com/questions/2461141/get-a-unique-computer-id-in-python-on-windows-and-linux
[2] http://harmattan-dev.nokia.com/docs/platform-api-reference/showdoc.php?pkn=libhal&wb=daily-docs&url=Li94bWwvZGFpbHktZG9jcy9saWJoYWw%3D
[3] http://madison-project.wdfiles.com/local--files/tutorials/Hal_Tutorial.pdf