Mac OS X: 開機時自動執行背景服務 (Startup Items)

Tip 如果要設定使用者登入時要執行的項目,從 System Preferences > Users & Groups > Login Items 設定即可。

SystemStarter 開機時會掃過 /System/Library/StartupItems (系統用) 與 /Library/StartupItems (可自訂) 底下所有的 startup item。已知 MySQL 跟 VirtualBox 都是這麼做的。

Startup item 用一個資料夾來表示,例如 /Library/StartupItems/MyItem,底下要有兩支檔案:

  • 跟資料夾同名的 start script,其實就是個 shell script (executable)。
  • StartupParameters.plist

/Library/StartupItems/MyItem/MyItem (executable)

#!/bin/sh
. /etc/rc.common 1

StartService() {
  # echo MyItem start >> /tmp/MyItemService.log 2
  ...
}

StopService() {
  # echo MyItem stop >> /tmp/MyItemService.log
  ...
}

RestartService() {
  StopService
  StartService
}

RunService "$1"
1 主要是取得 RunService 的定義,但也間接載入了 /etc/hostconfig 裡的開關,可供 startup script 參考。
2 可以用這個簡單的指令來做測試。

RunService 其實是定義在 /etc/rc.common 裡:

##
# Generic action handler
##
RunService ()
{
    case $1 in
      start  ) StartService   ;;
      stop   ) StopService    ;;
      restart) RestartService ;;
      *      ) echo "$0: unknown argument: $1";;
    esac
}

所以之後可以透過下面的指令來測試 service:

/Library/StartupItems/MyItem/MyItem start|stop|restart

/Library/StartupItems/MyItem/StartupParameters.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
    <key>Description</key>
    <string>MyItem</string>
    <key>Provides</key>
    <array>
        <string>MyItem</string>
    </array>
    <key>Requires</key>
    <array>
        <string>Network</string>
    </array>
    <key>OrderPreference</key>
    <string>Late</string>
</dict>
</plist>

重開機,理當要看到 /tmp/MyItemService.log 有一行 “MyItem start"。

Tip 所有的 startup items 預設都是以 root 的身份在執行,如果有以其他使用者留份執行的需求,用 runas 會有效果,雖然網路上找不太到類似的資料。
Note

參考資料

Selenium: 完整網頁截圖

Selenium RC API 裡的 captureEntirePageScreenshot 可以對整個網頁做截圖 (.png),但只對 Firefox 有完整的支援。

以下用一個乾淨的 profile 來做測試:

$ java -jar selenium-server-standalone-<version>.jar -firefoxProfileTemplate <path-to-clean-firefox-profile>
>>> from selenium import selenium
>>> sel = selenium('localhost', '4444', '*firefox', 'http://en.wikipedia.org')
>>> sel.start()
>>> sel.open('wiki/Hello_world_program')
>>>
>>> sel.capture_entire_page_screenshot('/tmp/helloworld.png', '') 1
>>> sel.window_maximize() 2
>>> sel.capture_entire_page_screenshot('/tmp/helloworld_maximize.png', '')
>>>
>>> sel.capture_entire_page_screenshot('/tmp/folder-not-existing/filename')
1 第二個參數 kwargs 一定要給,只好傳入空字串充數。
2 安插了一個 “將視窗放到最大" 的動作,這一個步驟有其必要性,因為截圖的大小會受瀏覽器視窗的寬度影響。下面是將視窗刻意縮小之後的截圖,可以看到文字被嚴重擠壓,但超出視窗大小的部份不致於被截掉。

SeleniumCapturePageScreenshot/helloworld_narrow.png

雖然官方文件說 “The Firefox implementation is mostly borrowed from the Screengrab! Firefox extension.",不過執行期在 Tools > Add-ons > Extensions 並沒有發現 Screengrab 被安裝起來,或許被包進 Selenium RC Runner 了吧?但可以確定的是,不需要額外安裝 extension,就可以使用 Selenium 的截圖功能。

SeleniumCapturePageScreenshot/extensions.png

這個 API 不會自動建立資料夾,也不會在檔名後面補上 .png,使用上要注意:

>>> sel.capture_entire_page_screenshot('/tmp/folder-not-existing/filename', '')
Traceback (most recent call last):
...
Exception: ERROR: Command execution failure. Please search the user group at https://groups.google.com/forum/#!forum/selenium-users for error details from the log window.  The error message is: Component returned failure code: 0x80520012 (NS_ERROR_FILE_NOT_FOUND) [nsIFileOutputStream.init]

事實上,官方文件也提到了這個功能在 IE 上的部份支援 – “… IE non­HTA using the EXPERIMENTAL “Snapsie" utility",以下是在 IE8 上的測試結果:

>>> sel = selenium('localhost', '4444', '*iexplore', 'http://en.wikipedia.org') 1
>>> ...
>>> sel.capture_entire_page_screenshot(r'd:\tmp\hellworld.png', '')
...
Exception: ERROR: captureEntirePageScreenshot is only implemented for Firefox ("
firefox" or "chrome", NOT "firefoxproxy") and IE non-HTA ("iexploreproxy", NOT "
iexplore" or "iehta"). The current browser isn't one of them!

>>> sel = selenium('localhost', '4444', 'iexploreproxy', 'http://en.wikipedia.org') 2
>>> ...
>>> sel.capture_entire_page_screenshot(r'c:\tmp\hellworld.png', '')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\selenium-2.6.0-py2.7.egg\selenium\selenium
.py", line 1907, in capture_entire_page_screenshot
    self.do_command("captureEntirePageScreenshot", [filename,kwargs,])
  File "C:\Python27\lib\site-packages\selenium-2.6.0-py2.7.egg\selenium\selenium
.py", line 217, in do_command
    raise Exception, data
Exception: ERROR: Snapsie failed: Is it installed? Does it have permission to ru
n as an add-on? See http://snapsie.sourceforge.net/
1 先用一般的 mode 做測試,被提示只支援 IE non-HTA (“iexploreproxy", NOT “iexplore" or “iehta")。
2 改用 iexploreproxy mode 後,從提示看起來 SnapsIE 應該是要額外安裝才行。

但不巧的是 SnapsIE 官網寫著 “snapsIE is no longer being actively developed here"。有時間再來試試看安裝 SnapsIE 的結果…


參考資料

Python Programming: 判斷作業系統別

要識別作業系統是 Windows、Linux 還是 Mac,可能的方式有:

  • os.name – 傳回 ntposixdarwin (Mac OS X)、java (Jython) 等字串。
  • sys.platform – 帶有版號,例如 win32linux2darwinjava1.6.0_20 等,判斷時要用 startswith()
  • platform.system() – 傳回 WindowsLinuxDarwinJava 等字串。

比較下來,最直覺的方式就是透過 platform.system()

C:\>python
Python 2.7.2 (default, Jun 12 2011, 14:24:46) [MSC v.1500 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.uname()
('Windows', 'my-host-name', '7', '6.1.7600', 'AMD64', 'Intel64 Family 6 Model 23 Stepping 10, GenuineIntel')
>>> platform.system()
'Windows'
>>>

$ python
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.uname()
('Linux', 'my-host-name', '2.6.35-32-generic', '#67-Ubuntu SMP Mon Mar 5 19:39:49 UTC 2012', 'x86_64', '')
>>> platform.system()
'Linux'
>>>

$ python
Python 2.7.3 (v2.7.3:70274d53c1dd, Apr  9 2012, 20:52:43)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import platform
>>> platform.uname()
('Darwin', 'WFBSSs-Mac-mini.local', '11.3.0', 'Darwin Kernel Version 11.3.0: Thu Jan 12 18:47:41 PST 2012; root:xnu-1699.24.23~1/RELEASE_X86_64', 'x86_64', 'i386')
>>> platform.system()
'Darwin'
>>>

例如:

import platform

if platform.system() == 'Windows':
    import _winreg

參考資料