監聽 NetworkManager 的設備異動訊號,執行指定行為(設定 eth0~1)
我的 ThinkPad 安裝 Ubuntu 桌面系統,配合使用需求,我的有線網路設備除了啟用 DHCP 之外,還額外設定了虛擬介面連接內部網路。概略如下列所示:
eth0 inet addr:(DHCP) eth0:1 inet addr:10.1.2.3
但是,因為我的網路設備是由 NetworkManager 所管理,基於 NetworkManager 以及筆記型電腦的操作特性,每當我切換桌面使用者亦或是筆電進入睡眠狀態,都會導致網路設備重置,使得 eth0:1 的設置消失。為此,我寫了一個小程式,透過 NetworkManager 提供的 D-Bus 服務,監聽 eth0 主設備的狀態,每當 eth0 被啟用亦或重新啟用,都能立即執行 ifconfig 設定 eth0:1 。
附帶一提,希望有人可以教我如何在 NetworkManager 中設定 eth0:1 ,感激不盡。
考慮到系統的普及性,這個小程式 (eth0.1.up) 是用 Python 寫的。我個人認為,就語法易學度與軟體普及度來看,在 Linux 桌面環境上,python 足以取代 perl 做為撰寫 script 程式的首選了。在 Debian/Ubuntu 桌面環境上,執行這個小程式的相關套件,都是預設安裝項目。通常不必再額外安裝套件便可使用。
#!/usr/bin/python
### BEGIN INIT INFO
# Provides: eth0.1.up
# Required-Start: network-manager
# Required-Stop:
# Default-Start:
# Default-Stop:
# Short-Description: Raise eth0:1 network interface.
### END INIT INFO
# usage:
# -foreground (or --foreground): run in foreground
LAN_SETTINGS = {
'eth0': {
'address': "10.1.2.3",
'netmask': "255.255.255.0"
}
}
ETH_V1_UP="/sbin/ifconfig %s:1 inet %s netmask %s up"
import os,sys
import dbus, gobject
from dbus.mainloop.glib import DBusGMainLoop
if len(sys.argv) > 1 and sys.argv[1].endswith("-foreground"):
print "Foreground mode running..."
else:
#run this program in background.
try:
pid = os.fork()
except OSError:
sys.exit(1)
if pid > 0:
sys.exit(0)
NetworkManagerServiceName = "org.freedesktop.NetworkManager"
NetworkManagerObjectPath = "/org/freedesktop/NetworkManager"
NetworkManagerInterface = "org.freedesktop.NetworkManager"
NetworkManagerDeviceInterface = "org.freedesktop.NetworkManager.Device"
DBusPropertiesInterface="org.freedesktop.DBus.Properties"
NM_DEVICE_STATE_ACTIVATED=8
class ActivatedHandler(object):
def __init__(self, device_instance):
self.device_instance = device_instance
props = device_instance.GetAll(NetworkManagerDeviceInterface,
dbus_interface=DBusPropertiesInterface)
self.name = props['Interface']
self.device_instance.connect_to_signal("StateChanged",
self.handler,
dbus_interface=NetworkManagerDeviceInterface)
def handler(self, new_state, old_state, reason):
if new_state == NM_DEVICE_STATE_ACTIVATED:
#print "%s activated" % self.name
os.system(ETH_V1_UP % (self.name,
LAN_SETTINGS[self.name]['address'],
LAN_SETTINGS[self.name]['netmask']))
DBusGMainLoop(set_as_default=True)
loop = gobject.MainLoop()
bus = dbus.SystemBus()
try:
nm_instance = bus.get_object(NetworkManagerServiceName, NetworkManagerObjectPath)
except dbus.DBusException:
print "connect to NetworkManager error."
sys.exit(1)
handlers = {}
devices = nm_instance.GetDevices(dbus_interface=NetworkManagerInterface)
for device in devices:
device_instance = bus.get_object(NetworkManagerServiceName, device)
props = device_instance.GetAll(NetworkManagerDeviceInterface,
dbus_interface=DBusPropertiesInterface)
device_name = props['Interface'] # eg. "eth0"
if device_name not in LAN_SETTINGS:
continue
if props['State'] == NM_DEVICE_STATE_ACTIVATED:
#print "%s activated" % device_name
os.system(ETH_V1_UP % (device_name,
LAN_SETTINGS[device_name]['address'],
LAN_SETTINGS[device_name]['netmask']))
handlers[device_name] = ActivatedHandler(device_instance)
devices = False
#print "Working..."
loop.run()
NetworkManager 的 D-Bus 服務,稱得上是目前 D-Bus 服務提供者的經典設計。它可以為每個連接設備實體化一個專責的 D-Bus 服務個體,因此我們首先需要透過主服務介面的 GetDevices() 方法查詢目前活動中的 D-Bus 服務個體的路徑(object path)。再透過此路徑,去接收 eth0 設備的狀態異動訊號(StateChanged signal)。每當狀態改變為 NM_DEVICE_STATE_ACTIVATED (設備已啟用)之後,便調用外部程式 ifconfig 設定 eth0:1 。
儲存上列的 Python 程式碼。以本文為例,命令為 eth0.1.up ,並賦予它可執行權限。接著找個目錄放置,例如 /etc/init.d 。
在此提供兩種方法,令它可在系統啟動後自動執行。
set in rc.local
最簡單的方式,就是手動編譯 /etc/rc.local ,把它的執行指令加到 /etc/rc.local 內。例如:
# rc.local
# Enable eth0:1 virtual network interface.
/etc/init.d/eth0.1.up
exit 0
記得把指令加到 rc.local 最後一行的 exit 0
之前。
set by update-rc.d
第二種作法,就是使用指令 update-rc.d ,將 eth0.1.up 加到 rc?.d 的啟動清單中。通常自定的指令稿都給予第99號啟動順序。如下例:
$ sudo update-rc.d eth0.1.up defaults 99
Adding system startup for /etc/init.d/eth0.1.up ...
/etc/rc0.d/K99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc1.d/K99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc6.d/K99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc2.d/S99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc3.d/S99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc4.d/S99eth0.1.up -> ../init.d/eth0.1.up
/etc/rc5.d/S99eth0.1.up -> ../init.d/eth0.1.up
注意,使用 update-rc.d 時,指令稿必須儲放於 /etc/init.d 目錄內 ,否則 update-rc.d 找不到。
樂多舊回應