system/corennnnn
修訂 | f77c1188341bf0d89221c65be3c42db47c1beaa2 (tree) |
---|---|
時間 | 2016-07-27 03:49:13 |
作者 | Jianxun Zhang <jianxun.zhang@inte...> |
Commiter | Jaap Jan Meijer |
add modprobe-like capability and automatic ueventd loading
Author: Jianxun Zhang <jianxun.zhang@intel.com>
Author: Daniel Leung <daniel.leung@intel.com>
Author: Andrew Boie <andrew.p.boie@intel.com>
- insmod_by_dep() added to libcutils; loads a module into kernel.
Modules the target module depends on will be loaded first. Loading
will be stopped when an error occurs.
- rmmod_by_dep() added to libcutils; removes a module from kernel.
It also tries to remove other modules the target module depends
on until an error occurs.
- Implement wildcard matching for ueventd rules.
The PCI and USB addresses for devices can change from devices
from devices for a particular class of peripheral, for example,
bluetooth. The ueventd rules created with these addresses are
then device-specific.
This changes the way ueventd rules with wildcard are handled.
Instead of matching just the prefix with a trailing wildcard,
now these rules can have wildcard anywhere in the rule.
The wildcard matching is implemented using fnmatch(), where
its matching is simliar to shell pathname expansion. It suits
this particular usage model well.
For example, instead of creating a rule to match:
, this would suffice:
- Let ueventd auto-load kernel modules. Implements the functionality
for ueventd to auto-load kernel modules when uevents are triggered.
Since /system may not be mounted when uevents are fired,
a deferred loading mechanism is implemented. Once mapping of
module and alias is available, these modules are then loaded.
Modules can also be blacklisted so they will not be loaded
automatically. One example would be the Wifi driver, as
Android's has to control its loading and unloading.
- add 'probemod' builtin command. This command accepts the name of a
kernel module plus a set of command line arguments. The module will
be loaded, along with all its dependencies, using the libcutils
insmod_by_dep() API.
- Drivers in kernel can request modules by launching a program in
user space, the program's path by default is "/sbin/modprobe".
Because Android system has no modprobe and ueventd is the only
program handling the module aliases so far, This patch provides a
cheap approach to handle kernel's requests in ueventd executable.
- Add new builtin init command "coldboot". The main purpose is to
provide an approach in init.*.rc files to fire uevents for devices
under the path which is passed as the the argument. This should be
called after /system is mounted so any queued events that need to
load a module can be fired.
@@ -0,0 +1,83 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2012 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#ifndef _LIBS_CUTILS_PROBEMODULE_H | |
18 | +#define _LIBS_CUTILS_PROBEMODULE_H | |
19 | + | |
20 | +#ifdef __cplusplus | |
21 | +extern "C" { | |
22 | +#endif | |
23 | + | |
24 | +/* insmod_by_dep() - load a kernel module (target) with its dependency | |
25 | + * The module's dependency must be described in the provided dependency file. | |
26 | + * other modules in the dependency chain will be loaded prior to the target. | |
27 | + * | |
28 | + * module_name: Name of the target module. e.g. name "MyModule" is for | |
29 | + * module file MyModule.ko. | |
30 | + * | |
31 | + * args : A string of target module's parameters. NOTE: we only | |
32 | + * support parameters of the target module. | |
33 | + * | |
34 | + * dep_name : Name of dependency file. If it is NULL, we will look | |
35 | + * up /system/lib/modules/modules.dep by default. | |
36 | + * | |
37 | + * strip : Non-zero values remove paths of modules in dependency. | |
38 | + * before loading them. The final path of a module will be | |
39 | + * base/MyModule.ko. This is for devices which put every | |
40 | + * modules into a single directory. | |
41 | + * | |
42 | + * Passing 0 to strip keeps module paths in dependency file. | |
43 | + * e.g. "kernel/drivers/.../MyModule.ko" in dep file will | |
44 | + * be loaded as base/kernel/drivers/.../MyModule.ko . | |
45 | + * | |
46 | + * base : Base dir, a prefix to be added to module's path prior to | |
47 | + * loading. The last character prior to base string's terminator | |
48 | + * must be a '/'. If it is NULL, we will take | |
49 | + * /system/lib/modules/modules.dep by default. | |
50 | + * | |
51 | + * return : 0 for success; non-zero for any errors. | |
52 | + * | |
53 | + * Note: | |
54 | + * When loading modules, function will not fail for any modules which are | |
55 | + * already in kernel. The module parameters passed to function will not be | |
56 | + * effective in this case if target module is already loaded into kernel. | |
57 | + */ | |
58 | +extern int insmod_by_dep( | |
59 | + const char *module_name, | |
60 | + const char *args, | |
61 | + const char *dep_name, | |
62 | + int strip, | |
63 | + const char * base); | |
64 | + | |
65 | +/* rmmod_by_dep() - remove a module (target) from kernel with its dependency | |
66 | + * The module's dependency must be described in the provided dependency file. | |
67 | + * This function will try to remove other modules in the dependency chain too | |
68 | + * | |
69 | + * module_name: Name of the target module. e.g. name "MyModule" is for | |
70 | + * module file MyModule.ko. | |
71 | + * | |
72 | + * dep_name : Name of dependency file. If it is NULL, we will look | |
73 | + * up /system/lib/modules/modules.dep by default. | |
74 | + * | |
75 | + * return : 0 for success; non-zero for any errors. | |
76 | + */ | |
77 | +extern int rmmod_by_dep(const char *module_name, const char *dep_name); | |
78 | + | |
79 | +#ifdef __cplusplus | |
80 | +} | |
81 | +#endif | |
82 | + | |
83 | +#endif /*_LIBS_CUTILS_PROBEMODULE_H*/ |
@@ -37,10 +37,13 @@ | ||
37 | 37 | #include <selinux/label.h> |
38 | 38 | |
39 | 39 | #include <fs_mgr.h> |
40 | +#include <dirent.h> | |
41 | +#include <fnmatch.h> | |
40 | 42 | #include <base/stringprintf.h> |
41 | 43 | #include <cutils/partition_utils.h> |
42 | 44 | #include <cutils/android_reboot.h> |
43 | 45 | #include <logwrap/logwrap.h> |
46 | +#include <cutils/probe_module.h> | |
44 | 47 | #include <private/android_filesystem_config.h> |
45 | 48 | |
46 | 49 | #include "init.h" |
@@ -286,6 +289,40 @@ int do_insmod(int nargs, char **args) | ||
286 | 289 | return do_insmod_inner(nargs, args, size); |
287 | 290 | } |
288 | 291 | |
292 | +static int do_probemod_inner(int nargs, char **args, int opt_len) | |
293 | +{ | |
294 | + char options[opt_len + 1]; | |
295 | + int i; | |
296 | + int ret; | |
297 | + | |
298 | + options[0] = '\0'; | |
299 | + if (nargs > 2) { | |
300 | + strcpy(options, args[2]); | |
301 | + for (i = 3; i < nargs; ++i) { | |
302 | + strcat(options, " "); | |
303 | + strcat(options, args[i]); | |
304 | + } | |
305 | + } | |
306 | + | |
307 | + ret = insmod_by_dep(args[1], options, NULL, 1, NULL); | |
308 | + if (ret) | |
309 | + ERROR("Couldn't probe module '%s'\n", args[1]); | |
310 | + return ret; | |
311 | +} | |
312 | + | |
313 | +int do_probemod(int nargs, char **args) | |
314 | +{ | |
315 | + int i; | |
316 | + int size = 0; | |
317 | + | |
318 | + if (nargs > 2) { | |
319 | + for (i = 2; i < nargs; ++i) | |
320 | + size += strlen(args[i]) + 1; | |
321 | + } | |
322 | + | |
323 | + return do_probemod_inner(nargs, args, size); | |
324 | +} | |
325 | + | |
289 | 326 | int do_mkdir(int nargs, char **args) |
290 | 327 | { |
291 | 328 | mode_t mode = 0755; |
@@ -582,6 +619,16 @@ int do_swapon_all(int nargs, char **args) | ||
582 | 619 | return ret; |
583 | 620 | } |
584 | 621 | |
622 | +int do_builtin_coldboot(int nargs, char **args) | |
623 | +{ | |
624 | + if (nargs != 2 || !args[1] || *args[1] == '\0') | |
625 | + return -1; | |
626 | + | |
627 | + coldboot(args[1]); | |
628 | + | |
629 | + return 0; | |
630 | +} | |
631 | + | |
585 | 632 | int do_setprop(int nargs, char **args) |
586 | 633 | { |
587 | 634 | const char *name = args[1]; |
@@ -41,6 +41,7 @@ | ||
41 | 41 | #include <sys/wait.h> |
42 | 42 | |
43 | 43 | #include <cutils/list.h> |
44 | +#include <cutils/probe_module.h> | |
44 | 45 | #include <cutils/uevent.h> |
45 | 46 | |
46 | 47 | #include "devices.h" |
@@ -49,12 +50,18 @@ | ||
49 | 50 | #include "log.h" |
50 | 51 | #include "property_service.h" |
51 | 52 | #include <zlib.h> |
53 | +#include "parser.h" | |
52 | 54 | |
53 | 55 | #define SYSFS_PREFIX "/sys" |
54 | 56 | static const char *firmware_dirs[] = { "/etc/firmware", |
55 | 57 | "/vendor/firmware", |
56 | 58 | "/firmware/image" }; |
57 | 59 | |
60 | +#define MODULES_ALIAS "/system/lib/modules/modules.alias" | |
61 | +#define MODULES_BLKLST "/system/etc/modules.blacklist" | |
62 | +#define READ_MODULES_ALIAS 1 | |
63 | +#define READ_MODULES_BLKLST 2 | |
64 | + | |
58 | 65 | extern struct selabel_handle *sehandle; |
59 | 66 | |
60 | 67 | extern char boot_device[PROP_VALUE_MAX]; |
@@ -68,6 +75,7 @@ struct uevent { | ||
68 | 75 | const char *firmware; |
69 | 76 | const char *partition_name; |
70 | 77 | const char *device_name; |
78 | + const char *modalias; | |
71 | 79 | int partition_num; |
72 | 80 | int major; |
73 | 81 | int minor; |
@@ -95,9 +103,26 @@ struct platform_node { | ||
95 | 103 | struct listnode list; |
96 | 104 | }; |
97 | 105 | |
106 | +struct module_alias_node { | |
107 | + char *name; | |
108 | + char *pattern; | |
109 | + struct listnode list; | |
110 | +}; | |
111 | + | |
112 | +struct module_blacklist_node { | |
113 | + char *name; | |
114 | + struct listnode list; | |
115 | +}; | |
116 | + | |
98 | 117 | static list_declare(sys_perms); |
99 | 118 | static list_declare(dev_perms); |
100 | 119 | static list_declare(platform_names); |
120 | +static list_declare(modules_aliases_map); | |
121 | +static list_declare(modules_blacklist); | |
122 | +static list_declare(deferred_module_loading_list); | |
123 | + | |
124 | +static int read_modules_aliases(); | |
125 | +static int read_modules_blacklist(); | |
101 | 126 | |
102 | 127 | int add_dev_perms(const char *name, const char *attr, |
103 | 128 | mode_t perm, unsigned int uid, unsigned int gid, |
@@ -398,6 +423,7 @@ static void parse_event(const char *msg, struct uevent *uevent) | ||
398 | 423 | uevent->partition_name = NULL; |
399 | 424 | uevent->partition_num = -1; |
400 | 425 | uevent->device_name = NULL; |
426 | + uevent->modalias = NULL; | |
401 | 427 | |
402 | 428 | /* currently ignoring SEQNUM */ |
403 | 429 | while(*msg) { |
@@ -428,6 +454,9 @@ static void parse_event(const char *msg, struct uevent *uevent) | ||
428 | 454 | } else if(!strncmp(msg, "DEVNAME=", 8)) { |
429 | 455 | msg += 8; |
430 | 456 | uevent->device_name = msg; |
457 | + } else if(!strncmp(msg, "MODALIAS=", 9)) { | |
458 | + msg += 9; | |
459 | + uevent->modalias = msg; | |
431 | 460 | } |
432 | 461 | |
433 | 462 | /* advance to after the next \0 */ |
@@ -819,8 +848,148 @@ static void handle_generic_device_event(struct uevent *uevent) | ||
819 | 848 | uevent->major, uevent->minor, links); |
820 | 849 | } |
821 | 850 | |
851 | +static int is_module_blacklisted(const char *name) | |
852 | +{ | |
853 | + struct listnode *blklst_node; | |
854 | + struct module_blacklist_node *blacklist; | |
855 | + int ret = 0; | |
856 | + | |
857 | + if (!name) goto out; | |
858 | + | |
859 | + /* See if module is blacklisted, skip if it is */ | |
860 | + list_for_each(blklst_node, &modules_blacklist) { | |
861 | + blacklist = node_to_item(blklst_node, | |
862 | + struct module_blacklist_node, | |
863 | + list); | |
864 | + if (!strcmp(name, blacklist->name)) { | |
865 | + INFO("modules %s is blacklisted\n", name); | |
866 | + ret = 1; | |
867 | + goto out; | |
868 | + } | |
869 | + } | |
870 | + | |
871 | +out: | |
872 | + return ret; | |
873 | +} | |
874 | + | |
875 | +static int load_module_by_device_modalias(const char *id) | |
876 | +{ | |
877 | + struct listnode *alias_node; | |
878 | + struct module_alias_node *alias; | |
879 | + int ret = -1; | |
880 | + | |
881 | + if (!id) goto out; | |
882 | + | |
883 | + list_for_each(alias_node, &modules_aliases_map) { | |
884 | + alias = node_to_item(alias_node, struct module_alias_node, list); | |
885 | + | |
886 | + if (alias && alias->name && alias->pattern) { | |
887 | + if (fnmatch(alias->pattern, id, 0) == 0) { | |
888 | + INFO("trying to load module %s due to uevents\n", alias->name); | |
889 | + | |
890 | + if (!is_module_blacklisted(alias->name)) { | |
891 | + if (insmod_by_dep(alias->name, "", NULL, 1, NULL)) { | |
892 | + /* cannot load module. try another one since | |
893 | + * there may be another match. | |
894 | + */ | |
895 | + INFO("cannot load module %s due to uevents\n", | |
896 | + alias->name); | |
897 | + } else { | |
898 | + /* loading was successful */ | |
899 | + INFO("loaded module %s due to uevents\n", alias->name); | |
900 | + ret = 0; | |
901 | + goto out; | |
902 | + } | |
903 | + } | |
904 | + } | |
905 | + } | |
906 | + } | |
907 | + | |
908 | +out: | |
909 | + return ret; | |
910 | +} | |
911 | + | |
912 | +static void handle_deferred_module_loading() | |
913 | +{ | |
914 | + struct listnode *node = NULL; | |
915 | + struct listnode *next = NULL; | |
916 | + struct module_alias_node *alias = NULL; | |
917 | + | |
918 | + /* try to read the module alias mapping if map is empty | |
919 | + * if succeed, loading all the modules in the queue | |
920 | + */ | |
921 | + if (!list_empty(&modules_aliases_map)) { | |
922 | + list_for_each_safe(node, next, &deferred_module_loading_list) { | |
923 | + alias = node_to_item(node, struct module_alias_node, list); | |
924 | + | |
925 | + if (alias && alias->pattern) { | |
926 | + INFO("deferred loading of module for %s\n", alias->pattern); | |
927 | + load_module_by_device_modalias(alias->pattern); | |
928 | + free(alias->pattern); | |
929 | + list_remove(node); | |
930 | + free(alias); | |
931 | + } | |
932 | + } | |
933 | + } | |
934 | +} | |
935 | + | |
936 | +int module_probe(const char *modalias) | |
937 | +{ | |
938 | + if (list_empty(&modules_aliases_map)) { | |
939 | + if (read_modules_aliases() == 0) | |
940 | + read_modules_blacklist(); | |
941 | + else | |
942 | + return -1; | |
943 | + } | |
944 | + | |
945 | + return load_module_by_device_modalias(modalias); | |
946 | +} | |
947 | + | |
948 | +static void handle_module_loading(const char *modalias) | |
949 | +{ | |
950 | + struct module_alias_node *node; | |
951 | + | |
952 | + /* once modules.alias can be read, | |
953 | + * we load all the deferred ones | |
954 | + */ | |
955 | + if (list_empty(&modules_aliases_map)) { | |
956 | + if (read_modules_aliases() == 0) { | |
957 | + read_modules_blacklist(); | |
958 | + handle_deferred_module_loading(); | |
959 | + } | |
960 | + } | |
961 | + | |
962 | + if (!modalias) return; | |
963 | + | |
964 | + if (list_empty(&modules_aliases_map)) { | |
965 | + /* if module alias mapping is empty, | |
966 | + * queue it for loading later | |
967 | + */ | |
968 | + node = (module_alias_node *) calloc(1, sizeof(*node)); | |
969 | + if (node) { | |
970 | + node->pattern = strdup(modalias); | |
971 | + if (!node->pattern) { | |
972 | + free(node); | |
973 | + } else { | |
974 | + list_add_tail(&deferred_module_loading_list, &node->list); | |
975 | + INFO("add to queue for deferred module loading: %s", | |
976 | + node->pattern); | |
977 | + } | |
978 | + } else { | |
979 | + ERROR("failed to allocate memory to store device id for deferred module loading.\n"); | |
980 | + } | |
981 | + } else { | |
982 | + load_module_by_device_modalias(modalias); | |
983 | + } | |
984 | + | |
985 | +} | |
986 | + | |
822 | 987 | static void handle_device_event(struct uevent *uevent) |
823 | 988 | { |
989 | + if (!strcmp(uevent->action,"add")) { | |
990 | + handle_module_loading(uevent->modalias); | |
991 | + } | |
992 | + | |
824 | 993 | if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online")) |
825 | 994 | fixup_sys_perms(uevent->path); |
826 | 995 |
@@ -998,6 +1167,148 @@ static void handle_firmware_event(struct uevent *uevent) | ||
998 | 1167 | } |
999 | 1168 | } |
1000 | 1169 | |
1170 | +static void parse_line_module_alias(struct parse_state *state, int nargs, char **args) | |
1171 | +{ | |
1172 | + struct module_alias_node *node; | |
1173 | + | |
1174 | + if (!args || | |
1175 | + (nargs != 3) || | |
1176 | + !args[0] || !args[1] || !args[2]) { | |
1177 | + /* empty line or not enough arguments */ | |
1178 | + return; | |
1179 | + } | |
1180 | + | |
1181 | + node = (module_alias_node *) calloc(1, sizeof(*node)); | |
1182 | + if (!node) return; | |
1183 | + | |
1184 | + node->name = strdup(args[2]); | |
1185 | + if (!node->name) { | |
1186 | + free(node); | |
1187 | + return; | |
1188 | + } | |
1189 | + | |
1190 | + node->pattern = strdup(args[1]); | |
1191 | + if (!node->pattern) { | |
1192 | + free(node->name); | |
1193 | + free(node); | |
1194 | + return; | |
1195 | + } | |
1196 | + | |
1197 | + list_add_tail(&modules_aliases_map, &node->list); | |
1198 | +} | |
1199 | + | |
1200 | +static void parse_line_module_blacklist(struct parse_state *state, int nargs, char **args) | |
1201 | +{ | |
1202 | + struct module_blacklist_node *node; | |
1203 | + | |
1204 | + if (!args || | |
1205 | + (nargs != 2) || | |
1206 | + !args[0] || !args[1]) { | |
1207 | + /* empty line or not enough arguments */ | |
1208 | + return; | |
1209 | + } | |
1210 | + | |
1211 | + /* this line does not being with "blacklist" */ | |
1212 | + if (strncmp(args[0], "blacklist", 9)) return; | |
1213 | + | |
1214 | + node = (module_blacklist_node *) calloc(1, sizeof(*node)); | |
1215 | + if (!node) return; | |
1216 | + | |
1217 | + node->name = strdup(args[1]); | |
1218 | + if (!node->name) { | |
1219 | + free(node); | |
1220 | + return; | |
1221 | + } | |
1222 | + | |
1223 | + list_add_tail(&modules_blacklist, &node->list); | |
1224 | +} | |
1225 | + | |
1226 | +static int __read_modules_desc_file(int mode) | |
1227 | +{ | |
1228 | + struct parse_state state; | |
1229 | + char *args[3]; | |
1230 | + int nargs; | |
1231 | + char *fn; | |
1232 | + int fd = -1; | |
1233 | + int ret = -1; | |
1234 | + int args_to_read = 0; | |
1235 | + std::string data; | |
1236 | + | |
1237 | + if (mode == READ_MODULES_ALIAS) { | |
1238 | + /* read modules.alias */ | |
1239 | + if (asprintf(&fn, "%s", MODULES_ALIAS) <= 0) { | |
1240 | + goto out; | |
1241 | + } | |
1242 | + } else if (mode == READ_MODULES_BLKLST) { | |
1243 | + /* read modules.blacklist */ | |
1244 | + if (asprintf(&fn, "%s", MODULES_BLKLST) <= 0) { | |
1245 | + goto out; | |
1246 | + } | |
1247 | + } else { | |
1248 | + /* unknown mode */ | |
1249 | + goto out; | |
1250 | + } | |
1251 | + | |
1252 | + fd = open(fn, O_RDONLY); | |
1253 | + if (fd == -1) { | |
1254 | + goto out; | |
1255 | + } | |
1256 | + | |
1257 | + /* read the whole file */ | |
1258 | + if (!read_file(fn, &data)) { | |
1259 | + goto out; | |
1260 | + } | |
1261 | + | |
1262 | + /* invoke tokenizer */ | |
1263 | + nargs = 0; | |
1264 | + state.filename = fn; | |
1265 | + state.line = 1; | |
1266 | + state.ptr = const_cast<char *>(data.c_str()); | |
1267 | + state.nexttoken = 0; | |
1268 | + if (mode == READ_MODULES_ALIAS) { | |
1269 | + state.parse_line = parse_line_module_alias; | |
1270 | + args_to_read = 3; | |
1271 | + } else if (mode == READ_MODULES_BLKLST) { | |
1272 | + state.parse_line = parse_line_module_blacklist; | |
1273 | + args_to_read = 2; | |
1274 | + } | |
1275 | + for (;;) { | |
1276 | + int token = next_token(&state); | |
1277 | + switch (token) { | |
1278 | + case T_EOF: | |
1279 | + state.parse_line(&state, 0, 0); | |
1280 | + ret = 0; | |
1281 | + goto out; | |
1282 | + case T_NEWLINE: | |
1283 | + if (nargs) { | |
1284 | + state.parse_line(&state, nargs, args); | |
1285 | + nargs = 0; | |
1286 | + } | |
1287 | + break; | |
1288 | + case T_TEXT: | |
1289 | + if (nargs < args_to_read) { | |
1290 | + args[nargs++] = state.text; | |
1291 | + } | |
1292 | + break; | |
1293 | + } | |
1294 | + } | |
1295 | + ret = 0; | |
1296 | + | |
1297 | +out: | |
1298 | + if (fd != -1) { | |
1299 | + close(fd); | |
1300 | + } | |
1301 | + return ret; | |
1302 | +} | |
1303 | + | |
1304 | +static int read_modules_aliases() { | |
1305 | + return __read_modules_desc_file(READ_MODULES_ALIAS); | |
1306 | +} | |
1307 | + | |
1308 | +static int read_modules_blacklist() { | |
1309 | + return __read_modules_desc_file(READ_MODULES_BLKLST); | |
1310 | +} | |
1311 | + | |
1001 | 1312 | #define UEVENT_MSG_LEN 2048 |
1002 | 1313 | void handle_device_fd() |
1003 | 1314 | { |
@@ -1070,7 +1381,7 @@ static void do_coldboot(DIR *d) | ||
1070 | 1381 | } |
1071 | 1382 | } |
1072 | 1383 | |
1073 | -static void coldboot(const char *path) | |
1384 | +void coldboot(const char *path) | |
1074 | 1385 | { |
1075 | 1386 | DIR *d = opendir(path); |
1076 | 1387 | if(d) { |
@@ -21,10 +21,12 @@ | ||
21 | 21 | |
22 | 22 | extern void handle_device_fd(); |
23 | 23 | extern void device_init(void); |
24 | +extern int module_probe(const char *alias); | |
24 | 25 | extern int add_dev_perms(const char *name, const char *attr, |
25 | 26 | mode_t perm, unsigned int uid, |
26 | 27 | unsigned int gid, unsigned short prefix, |
27 | 28 | unsigned short wildcard); |
28 | 29 | int get_device_fd(); |
29 | 30 | |
31 | +void coldboot(const char *path); | |
30 | 32 | #endif /* _INIT_DEVICES_H */ |
@@ -129,6 +129,7 @@ static int lookup_keyword(const char *s) | ||
129 | 129 | if (!strcmp(s, "onsole")) return K_console; |
130 | 130 | if (!strcmp(s, "hown")) return K_chown; |
131 | 131 | if (!strcmp(s, "hmod")) return K_chmod; |
132 | + if (!strcmp(s, "oldboot")) return K_coldboot; | |
132 | 133 | if (!strcmp(s, "ritical")) return K_critical; |
133 | 134 | break; |
134 | 135 | case 'd': |
@@ -173,6 +174,7 @@ static int lookup_keyword(const char *s) | ||
173 | 174 | break; |
174 | 175 | case 'p': |
175 | 176 | if (!strcmp(s, "owerctl")) return K_powerctl; |
177 | + if (!strcmp(s, "robemod")) return K_probemod; | |
176 | 178 | break; |
177 | 179 | case 'r': |
178 | 180 | if (!strcmp(s, "estart")) return K_restart; |
@@ -1,5 +1,6 @@ | ||
1 | 1 | #ifndef KEYWORD |
2 | 2 | int do_bootchart_init(int nargs, char **args); |
3 | +int do_builtin_coldboot(int nargs, char **args); | |
3 | 4 | int do_class_start(int nargs, char **args); |
4 | 5 | int do_class_stop(int nargs, char **args); |
5 | 6 | int do_class_reset(int nargs, char **args); |
@@ -15,6 +16,7 @@ int do_mkdir(int nargs, char **args); | ||
15 | 16 | int do_mount_all(int nargs, char **args); |
16 | 17 | int do_mount(int nargs, char **args); |
17 | 18 | int do_powerctl(int nargs, char **args); |
19 | +int do_probemod(int nargs, char **args); | |
18 | 20 | int do_restart(int nargs, char **args); |
19 | 21 | int do_restorecon(int nargs, char **args); |
20 | 22 | int do_restorecon_recursive(int nargs, char **args); |
@@ -51,6 +53,7 @@ enum { | ||
51 | 53 | KEYWORD(class_reset, COMMAND, 1, do_class_reset) |
52 | 54 | KEYWORD(class_start, COMMAND, 1, do_class_start) |
53 | 55 | KEYWORD(class_stop, COMMAND, 1, do_class_stop) |
56 | + KEYWORD(coldboot, COMMAND, 1, do_builtin_coldboot) | |
54 | 57 | KEYWORD(console, OPTION, 0, 0) |
55 | 58 | KEYWORD(copy, COMMAND, 2, do_copy) |
56 | 59 | KEYWORD(critical, OPTION, 0, 0) |
@@ -77,6 +80,7 @@ enum { | ||
77 | 80 | KEYWORD(onrestart, OPTION, 0, 0) |
78 | 81 | KEYWORD(on, SECTION, 0, 0) |
79 | 82 | KEYWORD(powerctl, COMMAND, 1, do_powerctl) |
83 | + KEYWORD(probemod, COMMAND, 1, do_probemod) | |
80 | 84 | KEYWORD(restart, COMMAND, 1, do_restart) |
81 | 85 | KEYWORD(restorecon, COMMAND, 1, do_restorecon) |
82 | 86 | KEYWORD(restorecon_recursive, COMMAND, 1, do_restorecon_recursive) |
@@ -31,6 +31,7 @@ commonSources := \ | ||
31 | 31 | threads.c \ |
32 | 32 | sched_policy.c \ |
33 | 33 | iosched_policy.c \ |
34 | + probe_module.c \ | |
34 | 35 | str_parms.c \ |
35 | 36 | fs_config.c |
36 | 37 |
@@ -0,0 +1,456 @@ | ||
1 | +/* | |
2 | + * Copyright (C) 2012 The Android Open Source Project | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +#include <stdio.h> | |
18 | +#include <stdlib.h> | |
19 | +#include <string.h> | |
20 | +#include <errno.h> | |
21 | +#include <fcntl.h> | |
22 | +#include <cutils/misc.h> | |
23 | + | |
24 | +#define LOG_TAG "ProbeModule" | |
25 | +#include <cutils/log.h> | |
26 | + | |
27 | + | |
28 | +#define LDM_DEFAULT_DEP_FILE "/system/lib/modules/modules.dep" | |
29 | +#define LDM_DEFAULT_MOD_PATH "/system/lib/modules/" | |
30 | +#define LDM_INIT_DEP_NUM 10 | |
31 | + | |
32 | +extern int init_module(void *, unsigned long, const char *); | |
33 | +extern int delete_module(const char *, unsigned int); | |
34 | + | |
35 | +static void dump_dep(char **dep) | |
36 | +{ | |
37 | + int d; | |
38 | + | |
39 | + for (d = 0; dep[d]; d++) | |
40 | + ALOGD("DUMP DEP: %s\n", dep[d]); | |
41 | +} | |
42 | + | |
43 | +static char * strip_path(const char * const str) | |
44 | +{ | |
45 | + char *ptr; | |
46 | + int i; | |
47 | + | |
48 | + /* initialize pos to terminator */ | |
49 | + for (i = strlen(str); i > 0; i--) | |
50 | + if (str[i - 1] == '/') | |
51 | + break; | |
52 | + | |
53 | + return (char *)&str[i]; | |
54 | +} | |
55 | + | |
56 | +static void hyphen_to_underscore(char *str) | |
57 | +{ | |
58 | + while (str && *str != '\0') { | |
59 | + if (*str == '-') | |
60 | + *str = '_'; | |
61 | + str++; | |
62 | + } | |
63 | +} | |
64 | + | |
65 | +/* Compare module names, but don't differentiate '_' and '-'. | |
66 | + * return: 0 when s1 is matched to s2 or size is zero. | |
67 | + * non-zero in any other cases. | |
68 | + */ | |
69 | +static int match_name(const char *s1, const char *s2, const size_t size) | |
70 | +{ | |
71 | + size_t i; | |
72 | + | |
73 | + if (!size) | |
74 | + return 0; | |
75 | + | |
76 | + for (i = 0; i < size; i++, s1++, s2++) { | |
77 | + | |
78 | + if ((*s1 == '_' || *s1 == '-') && (*s2 == '_' || *s2 == '-')) | |
79 | + continue; | |
80 | + | |
81 | + if (*s1 != *s2) | |
82 | + return -1; | |
83 | + | |
84 | + if (*s1 == '\0') | |
85 | + return 0; | |
86 | + } | |
87 | + | |
88 | + return 0; | |
89 | +} | |
90 | + | |
91 | +/* check if a line in dep file is target module's dependency. | |
92 | + * return 1 when it is, otherwise 0 in any other cases. | |
93 | + */ | |
94 | +static int is_target_module(char *line, const char *target) | |
95 | +{ | |
96 | + char *token; | |
97 | + char *name; | |
98 | + size_t name_len; | |
99 | + const char *suffix = ".ko"; | |
100 | + const char *delimiter = ":"; | |
101 | + int ret = 0; | |
102 | + | |
103 | + /* search token */ | |
104 | + token = strstr(line, delimiter); | |
105 | + | |
106 | + if (!token) { | |
107 | + ALOGE("invalid line: no token\n"); | |
108 | + return 0; | |
109 | + } | |
110 | + | |
111 | + /* only take stuff before the token */ | |
112 | + *token = '\0'; | |
113 | + | |
114 | + /* use "module.ko" in comparision */ | |
115 | + name_len = strlen(suffix) + strlen(target) + 1; | |
116 | + | |
117 | + name = malloc(sizeof(char) * name_len); | |
118 | + | |
119 | + if (!name) { | |
120 | + ALOGE("cannot alloc ram for comparision\n"); | |
121 | + return 0; | |
122 | + } | |
123 | + | |
124 | + snprintf(name, name_len, "%s%s", target, suffix); | |
125 | + | |
126 | + ret = !match_name(strip_path(line), name, name_len); | |
127 | + | |
128 | + /* restore [single] token, keep line unchanged until we parse it later */ | |
129 | + *token = *delimiter; | |
130 | + | |
131 | + free(name); | |
132 | + | |
133 | + return ret; | |
134 | + | |
135 | +} | |
136 | + | |
137 | +/* turn a single string into an array of dependency. | |
138 | + * | |
139 | + * return: dependency array's address if it succeeded. Caller | |
140 | + * is responsible to free the array's memory. | |
141 | + * NULL when any error happens. | |
142 | + */ | |
143 | +static char** setup_dep(char *line) | |
144 | +{ | |
145 | + char *tmp; | |
146 | + char *brk; | |
147 | + int dep_num = LDM_INIT_DEP_NUM; | |
148 | + char **new; | |
149 | + int i; | |
150 | + char **dep = NULL; | |
151 | + | |
152 | + dep = malloc(sizeof(char *) * dep_num); | |
153 | + | |
154 | + if (!dep) { | |
155 | + ALOGE("cannot alloc dep array\n"); | |
156 | + return dep; | |
157 | + } | |
158 | + | |
159 | + for (i = 0, tmp = strtok_r(line, ": ", &brk); | |
160 | + tmp; | |
161 | + tmp = strtok_r(NULL, ": ", &brk), i++) { | |
162 | + | |
163 | + /* check if we need enlarge dep array */ | |
164 | + if (!(i < dep_num - 1)) { | |
165 | + | |
166 | + dep_num += LDM_INIT_DEP_NUM; | |
167 | + | |
168 | + new = realloc(dep, dep_num); | |
169 | + | |
170 | + if (!new) { | |
171 | + ALOGE("failed to enlarge dep buffer\n"); | |
172 | + free(dep); | |
173 | + return NULL; | |
174 | + } | |
175 | + else | |
176 | + dep = new; | |
177 | + } | |
178 | + | |
179 | + dep[i] = tmp; | |
180 | + | |
181 | + } | |
182 | + /* terminate array with a null pointer */ | |
183 | + dep[i] = NULL; | |
184 | + | |
185 | + return dep; | |
186 | +} | |
187 | + | |
188 | +static int insmod(const char *path_name, const char *args) | |
189 | +{ | |
190 | + void *data; | |
191 | + unsigned int len; | |
192 | + int ret; | |
193 | + | |
194 | + data = load_file(path_name, &len); | |
195 | + | |
196 | + if (!data) { | |
197 | + ALOGE("%s: Failed to load module file [%s]\n", __FUNCTION__, path_name); | |
198 | + return -1; | |
199 | + } | |
200 | + | |
201 | + ret = init_module(data, len, args); | |
202 | + | |
203 | + if (ret != 0 && errno != EEXIST) { | |
204 | + ALOGE("%s: Failed to insmod [%s] with args [%s] error: %s ret: %d\n", | |
205 | + __FUNCTION__, path_name, args, strerror(errno), ret); | |
206 | + ret = -1; | |
207 | + } | |
208 | + else | |
209 | + ret = 0; /* if module is already in kernel, return success. */ | |
210 | + | |
211 | + free(data); | |
212 | + | |
213 | + return ret; | |
214 | +} | |
215 | + | |
216 | +/* install all modules in the dependency chain | |
217 | + * deps : A array of module file names, must be terminated by a NULL pointer | |
218 | + * args : The module parameters for target module. | |
219 | + * strip : Non-zero to strip out path info in the file name; | |
220 | + * 0 to keep path info when loading modules. | |
221 | + * base : a prefix to module path, it will NOT be affected by strip flag. | |
222 | + * return : 0 for success or nothing to do; non-zero when any error occurs. | |
223 | + */ | |
224 | +static int insmod_s(char *dep[], const char *args, int strip, const char *base) | |
225 | +{ | |
226 | + char *name; | |
227 | + char *path_name; | |
228 | + int cnt; | |
229 | + size_t len; | |
230 | + int ret = 0; | |
231 | + const char * base_dir = LDM_DEFAULT_MOD_PATH; | |
232 | + | |
233 | + if (base && strlen(base)) | |
234 | + base_dir = base; | |
235 | + | |
236 | + /* load modules in reversed order */ | |
237 | + for (cnt = 0; dep[cnt]; cnt++) | |
238 | + ; | |
239 | + | |
240 | + while (cnt--) { | |
241 | + | |
242 | + name = strip ? strip_path(dep[cnt]) : dep[cnt]; | |
243 | + | |
244 | + len = strlen(base_dir) + strlen(name) + 1; | |
245 | + | |
246 | + path_name = malloc(sizeof(char) * len); | |
247 | + | |
248 | + if (!path_name) { | |
249 | + ALOGE("alloc module [%s] path failed\n", path_name); | |
250 | + return -1; | |
251 | + } | |
252 | + | |
253 | + snprintf(path_name, len, "%s%s", base_dir, name); | |
254 | + | |
255 | + if (cnt) | |
256 | + ret = insmod(path_name, ""); | |
257 | + else | |
258 | + ret = insmod(path_name, args); | |
259 | + | |
260 | + free(path_name); | |
261 | + | |
262 | + if (ret) | |
263 | + break; | |
264 | + } | |
265 | + | |
266 | + return ret; | |
267 | +} | |
268 | + | |
269 | +static int rmmod(const char *mod_name, unsigned int flags) | |
270 | +{ | |
271 | + return delete_module(mod_name, flags); | |
272 | +} | |
273 | + | |
274 | +/* remove all modules in a dependency chain | |
275 | + * NOTE: We assume module name in kernel is same as the file name without .ko | |
276 | + */ | |
277 | +static int rmmod_s(char *dep[], unsigned int flags) | |
278 | +{ | |
279 | + int i; | |
280 | + int ret = 0; | |
281 | + char * mod_name; | |
282 | + | |
283 | + for (i = 0; dep[i]; i++) { | |
284 | + size_t len; | |
285 | + mod_name = strip_path(dep[i]); | |
286 | + len = strlen(mod_name); | |
287 | + | |
288 | + if (len > strlen(".ko") | |
289 | + && mod_name[len - 1] == 'o' | |
290 | + && mod_name[len - 2] == 'k' | |
291 | + && mod_name[len - 3] == '.') { | |
292 | + | |
293 | + mod_name[len - 3] = '\0'; | |
294 | + | |
295 | + hyphen_to_underscore(mod_name); | |
296 | + | |
297 | + ret = rmmod(mod_name, flags); | |
298 | + | |
299 | + if (ret) { | |
300 | + ALOGE("%s: Failed to remove module [%s] error (%s)\n", | |
301 | + __FUNCTION__, mod_name, strerror(errno)); | |
302 | + break; | |
303 | + | |
304 | + } | |
305 | + } | |
306 | + } | |
307 | + | |
308 | + return ret; | |
309 | +} | |
310 | + | |
311 | +/* look_up_dep() find and setup target module's dependency in modules.dep | |
312 | + * | |
313 | + * dep_file: a pointer to module's dep file loaded in memory, its content | |
314 | + * will be CHANGED during parsing. | |
315 | + * | |
316 | + * return: a pointer to an array which holds the dependency strings and | |
317 | + * terminated by a NULL pointer. Caller is responsible to free the | |
318 | + * array's memory. | |
319 | + * | |
320 | + * non-zero in any other cases. Content of dep array is invalid. | |
321 | + */ | |
322 | +static char ** look_up_dep(const char *module_name, void *dep_file) | |
323 | +{ | |
324 | + | |
325 | + char *line; | |
326 | + char *saved_pos; | |
327 | + char *start; | |
328 | + int ret = -1; | |
329 | + char **dep = NULL; | |
330 | + | |
331 | + if (!dep_file || !module_name || *module_name == '\0') | |
332 | + return NULL; | |
333 | + | |
334 | + start = (char *)dep_file; | |
335 | + | |
336 | + /* We expect modules.dep file has a new line char before EOF. */ | |
337 | + while ((line = strtok_r(start, "\n", &saved_pos)) != NULL) { | |
338 | + | |
339 | + start = NULL; | |
340 | + | |
341 | + if (is_target_module(line, module_name)) { | |
342 | + | |
343 | + dep = setup_dep(line); | |
344 | + /* job done */ | |
345 | + break; | |
346 | + } | |
347 | + } | |
348 | + | |
349 | + return dep; | |
350 | +} | |
351 | + | |
352 | +/* load_dep_file() load a dep file (usually it is modules.dep) | |
353 | + * into memory. Caller is responsible to free the memory. | |
354 | + * | |
355 | + * file_name: dep file's name, if it is NULL or an empty string, | |
356 | + * This function will try to load a dep file in the | |
357 | + * default path defined in LDM_DEFAULT_DEP_FILE | |
358 | + * | |
359 | + * return: a pointer to the allocated mem which holds all | |
360 | + * content of the depfile. a zero pointer will be | |
361 | + * returned for any errors. | |
362 | + * */ | |
363 | +static void *load_dep_file(const char *file_name) | |
364 | +{ | |
365 | + const char *dep_file_name = LDM_DEFAULT_DEP_FILE; | |
366 | + unsigned int len; | |
367 | + | |
368 | + if (file_name && *file_name != '\0') | |
369 | + dep_file_name = file_name; | |
370 | + | |
371 | + return load_file(dep_file_name, &len); | |
372 | +} | |
373 | + | |
374 | +/* insmod_by_dep() interface to outside, | |
375 | + * refer to its description in probe_module.h | |
376 | + */ | |
377 | +int insmod_by_dep(const char *module_name, | |
378 | + const char *args, | |
379 | + const char *dep_name, | |
380 | + int strip, | |
381 | + const char *base) | |
382 | +{ | |
383 | + void *dep_file; | |
384 | + char **dep = NULL; | |
385 | + int ret = -1; | |
386 | + | |
387 | + if (!module_name || *module_name == '\0') { | |
388 | + ALOGE("need valid module name\n"); | |
389 | + return ret; | |
390 | + } | |
391 | + | |
392 | + dep_file = load_dep_file(dep_name); | |
393 | + | |
394 | + if (!dep_file) { | |
395 | + ALOGE("cannot load dep file : %s\n", dep_name); | |
396 | + return ret; | |
397 | + } | |
398 | + | |
399 | + dep = look_up_dep(module_name, dep_file); | |
400 | + | |
401 | + if (!dep) { | |
402 | + ALOGE("%s: cannot load module: [%s]\n", __FUNCTION__, module_name); | |
403 | + goto free_file; | |
404 | + } | |
405 | + | |
406 | + ret = insmod_s(dep, args, strip, base); | |
407 | + | |
408 | + free(dep); | |
409 | + | |
410 | +free_file: | |
411 | + free(dep_file); | |
412 | + | |
413 | + return ret; | |
414 | + | |
415 | +} | |
416 | + | |
417 | +/* rmmod_by_dep() interface to outside, | |
418 | + * refer to its description in probe_module.h | |
419 | + */ | |
420 | +int rmmod_by_dep(const char *module_name, | |
421 | + const char *dep_name) | |
422 | +{ | |
423 | + void *dep_file; | |
424 | + char **dep = NULL; | |
425 | + int ret = -1; | |
426 | + | |
427 | + if (!module_name || *module_name == '\0') { | |
428 | + ALOGE("need valid module name\n"); | |
429 | + return ret; | |
430 | + } | |
431 | + | |
432 | + dep_file = load_dep_file(dep_name); | |
433 | + | |
434 | + if (!dep_file) { | |
435 | + ALOGE("cannot load dep file : %s\n", dep_name); | |
436 | + return ret; | |
437 | + } | |
438 | + | |
439 | + dep = look_up_dep(module_name, dep_file); | |
440 | + | |
441 | + if (!dep) { | |
442 | + ALOGE("%s: cannot remove module: [%s]\n", __FUNCTION__, module_name); | |
443 | + goto free_file; | |
444 | + } | |
445 | + | |
446 | + ret = rmmod_s(dep, O_NONBLOCK); | |
447 | + | |
448 | + free(dep); | |
449 | + | |
450 | +free_file: | |
451 | + free(dep_file); | |
452 | + | |
453 | + return ret; | |
454 | +} | |
455 | + | |
456 | +/* end of file */ |