1. 程式人生 > >Android init.rc啟動服務

Android init.rc啟動服務

轉載 http://m.it610.com/article/5082334.htm 在“上一篇android  init.rc檔案語法詳解”,但是到了android5.0之後,按照上面的方法做,可能我們要啟動的服務就起不來了。這是因為採用了新的安全機制了——SEAndroid/SElinux的安全機制。 
    下面就介紹下,在SEAndroid/SElinux如何配置才能啟動init.rc裡面定義的服務。     下面我們在rc檔案裡面定義一個服務     service test1 /system/bin/test user root group root  disabled oneshot      然後再on boot得時候啟動這個     on boot      start test1      如果在android5.0之前的系統,這樣就可以在on boot啟動這個服務了。但是到了android5.0之後,這個服務可能啟動失敗了。     檢視串列埠資訊(注意不是logcat的資訊,是串列埠資訊),我們會看到“init: Warning!  Service test1 needs a SELinux domain defined; please fix!”這樣警告。這是因為我們沒有為service test1定義SELinux的許可權規則。     對於沒有定義SELinux的許可權規則的service,系統只是給出一條警告,還是會繼續啟動這個程序。如果我們的服務沒有觸及到未允許的許可權操作,那麼這個服務一樣會正常啟動的,我們可以直接無視這個警告。但是如果觸及到未允許的許可權操作,那麼這個服務可能就不能正常啟動。這就需要我們定義一個SELinux domain,新增需要的許可權。下面就介紹如何操作。     第一步,在devices/platform/platform-sub/sepolicy/目錄下新增一個test1.te的檔案(注意不同的平臺可能路徑不完全一樣,反正找到sepolicy目錄就好了,如果在上面介紹的路徑下沒有sepolicy目錄,我們也可以直接新增到external/sepolicy/下)。     然後在sepolicy目錄下的Android.mk檔案中新增下面的內容     BOARD_SEPOLICY_UNION := \     ... \     test1.te     第二步,在我們新建的test1.te檔案中新增如下內容     type test1, domain;     type test1, exec_type, file_type;     init_daemon_domain(test1)     然後在file_contexts(如果在sepolicy目錄下沒有,也可以新增到external/sepolicy/file_contexts)檔案中新增如下內容     /system/bin/test u:object_r:test1_exec:s0     然後編譯,燒錄到目標板,重新啟動。這時再看串列埠資訊,“init: Warning!  Service test1 needs a SELinux domain defined; please fix!”這個警告沒有了。但是我們的服務還是沒有正常啟動起來。這是因為我們還沒有新增許可權。     第三步,我們檢視logcat的除錯資訊。我們可以通過by Log Message="avc"來過濾。     在logcat裡面,我們會看到類似下面的警告     avc: denied { execute_no_trans } for path="/system/bin/toolbox" dev="mmcblk0p7" ino=306 scontext=u:r:test1:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=0     說明test1沒有execute_no_trans的許可權。這就需要我們新增許可權,我們可以在test1.te檔案中按下面的格式新增許可權     allow scontext tcontext:tclass {denied ...};     例如對於上面的警告,我們可以加上如下的語句     allow test1 system_file:file {execute_no_trans};     也可以是這樣 allow test1 system_file:file execute_no_trans;     如果最後面的denied 是多項 就必須用{}括起來。如果是一項是可以不要括號的。     如果我們想賦予這個class下的所有許可權,可以用萬用字元*號來代替。例如     allow test1 system_file:file *;     下面舉幾個例子     avc: denied { execute } for name="pppd" dev="mmcblk0p7" ino=254 scontext=u:r:pppd_gprs:s0 tcontext=u:object_r:ppp_exec:s0 tclass=file permissive=0     對於上面的警告,則需在pppd_gprs.te(如果沒有這個檔案需要參考前面的方法新增這個檔案)新增下面的語句     allow pppd_gprs ppp_exec:file execute ;     denied { write } for name="/" dev="mmcblk0p2" ino=1 scontext=u:r:init:s0 tcontext=u:object_r:vfat:s0 tclass=dir permissive=0     在init.te新增下面的語句     allow init vfat:dir { write   };     另外添加了許可權,有可能編譯出錯。     例如我們添加了     allow test1 system_file:file entrypoint;     可能會出現下面的編譯錯誤     libsepol.check_assertion_helper: neverallow on line 239 of external/sepolicy/domain.te (or line 5194 of policy.conf) violated by allow test1 system_file:file { entrypoint };     我們看看domain.te的239行,有下面的語句     neverallow domain { file_type -exec_type }:file entrypoint;     neverallow和我們定義的allow衝突了,我們需要設定test1為例外     所以要修改上面那一句為     neverallow { domain -test1 } { file_type -exec_type }:file entrypoint;     再編譯,通過了。把image下載到目標板上再執行,服務正常跑起來了。     如果還是不能正常執行,再看logcat的除錯資訊,看看還有沒有avc的警告,如果有,繼續重複前面的步驟,直到沒有警告為止,服務就可以正常運行了。     通過上面的方法,在init.rc裡面啟動服務已經沒有問題了,但是我們有很多服務不是在init.rc裡面啟動的,而是在其他的服務或者程序中根據某些條件,通過property_set("ctl.start", "xxx")、property_set("ctl.stop", "xxx")來啟動或者結束服務。例如我們常用的3G、4G的驅動,往往就是在rild中初始化模組OK後啟動pppd_gprs這個服務的。     但是在5.0下,我們往往會發現rild正常工作了,但是pppd_gprs沒有啟動。logcat看也沒有avc相關的警告。下面就以這個為例子,看看怎麼解決這個問題。     我們接上除錯串列埠,可以看到 init: sys_prop: Unable to start service ctl [pppd_gprs] uid:0 gid:1001 pid:148 init: sys_prop: Unable to stop service ctl [pppd_gprs] uid:0 gid:1001 pid:148 這是因為android在SELINUX的基礎上增加了對property的許可權的限制。 這個串列埠資訊是在 /system/core/init/property_service.c的handle_property_set_fd函式中列印的。通過這個除錯資訊可以看出,是PID=148的程序,沒有設定ctl.start的許可權,造成的。再用ps檢視當前程序,可以看到PID為148的程序是rild。那麼我們給rild賦予許可權就好了。下面介紹如何新增     property對應的上下文都定義在external/sepolicy/property_contexts檔案中,開啟這個檔案我們可以看到裡面有“ctl.                    u:object_r:ctl_default_prop:s0”的定義,說明ctl.start和ctl.stop對應的上下文是ctl_default_prop。在這裡,我們要給rild賦予設定環境變數的許可權。     我們就需要在rild.te的檔案中增加allow rild ctl_default_prop:property_service set;就可以了。如果沒有這個檔案,我們可以參考前面的介紹自己新增一個檔案。然後再編譯,執行,之前的提示沒有了,再看pppd_gprs也已經啟動起來了。     在pppd_gprs的腳本里面,也許有設定property的地方,我們會發現設定property沒有生效,看串列埠資訊,有init:sys_prop: permission denied uid:169  name:net.ppp0.local-ip的提示。這個也是在handle_property_set_fd中列印的,這個原因是一樣的,也是pppd_gprs服務沒有許可權。同樣的參考原來的方法增加許可權就好了。如果不知道是哪個服務設定的property,我們可以把source_ctx也打印出來,就知道是哪個服務了。     最後還有一個問題,如果我們設定的property在property_contexts沒有定義怎麼辦呢?     例如前面舉例的net.ppp0.local-ip沒有定義,那怎麼處理呢?方法如下     1 在property_contexts中新增 net.ppp0.local-ip u:object_r:net_radio_ppp0_prop:s0(net_radio_ppp0_prop可以取任何有名字,也可以是這個檔案裡面已經定義的名字)     2 在property.te檔案中增加type net_radio_ppp0_prop, property_type;(如果1中定義的是已經有的名字,2就不需要了)     3 參考前面的方法新增許可權。就可以了