Android init.rc启动服务

来源:互联网 发布:拜占庭算法 编辑:程序博客网 时间:2024/06/06 13:57
转载 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 参考前面的方法添加权限。就可以了
原创粉丝点击