system/core
Revision | 5b54942749913341f7a76d61def6ddefcb45ca16 (tree) |
---|---|
Zeit | 2019-03-28 19:36:05 |
Autor | Chih-Wei Huang <cwhuang@linu...> |
Commiter | Chih-Wei Huang |
ueventd: auto load modules on uevents (pie-x86)
This is an almost rewritten patch which implements the auto modules
loading function for pie-x86.
The commits from nougat-x86 and oreo-x86 are squashed:
* ueventd: defer modules loading if explicitly listed
* ueventd: auto load modules on uevents
* init: add modprobe
* ueventd: load modules in the main process only
* modprobe: handle module options correctly
* modprobe: fix return value
@@ -87,6 +87,7 @@ LOCAL_REQUIRED_MODULES := \ | ||
87 | 87 | |
88 | 88 | # Create symlinks. |
89 | 89 | LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \ |
90 | + ln -sf ../init $(TARGET_ROOT_OUT)/sbin/modprobe; \ | |
90 | 91 | ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \ |
91 | 92 | ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd |
92 | 93 |
@@ -33,7 +33,6 @@ | ||
33 | 33 | #include <sys/resource.h> |
34 | 34 | #include <sys/socket.h> |
35 | 35 | #include <sys/stat.h> |
36 | -#include <sys/syscall.h> | |
37 | 36 | #include <sys/system_properties.h> |
38 | 37 | #include <sys/time.h> |
39 | 38 | #include <sys/types.h> |
@@ -50,6 +49,7 @@ | ||
50 | 49 | #include <android-base/unique_fd.h> |
51 | 50 | #include <bootloader_message/bootloader_message.h> |
52 | 51 | #include <cutils/android_reboot.h> |
52 | +#include <cutils/probe_module.h> | |
53 | 53 | #include <ext4_utils/ext4_crypt.h> |
54 | 54 | #include <ext4_utils/ext4_crypt_init_extensions.h> |
55 | 55 | #include <fs_mgr.h> |
@@ -231,10 +231,7 @@ static Result<Success> do_insmod(const BuiltinArguments& args) { | ||
231 | 231 | std::string filename = *it++; |
232 | 232 | std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' '); |
233 | 233 | |
234 | - unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC))); | |
235 | - if (fd == -1) return ErrnoError() << "open(\"" << filename << "\") failed"; | |
236 | - | |
237 | - int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags); | |
234 | + int rc = insmod(filename.c_str(), options.c_str(), flags); | |
238 | 235 | if (rc == -1) return ErrnoError() << "finit_module for \"" << filename << "\" failed"; |
239 | 236 | |
240 | 237 | return Success(); |
@@ -22,6 +22,8 @@ | ||
22 | 22 | #include <unistd.h> |
23 | 23 | |
24 | 24 | #include <memory> |
25 | +#include <set> | |
26 | +#include <thread> | |
25 | 27 | |
26 | 28 | #include <android-base/logging.h> |
27 | 29 | #include <android-base/stringprintf.h> |
@@ -29,10 +31,13 @@ | ||
29 | 31 | #include <private/android_filesystem_config.h> |
30 | 32 | #include <selinux/android.h> |
31 | 33 | #include <selinux/selinux.h> |
34 | +#include <cutils/klog.h> | |
35 | +#include <cutils/probe_module.h> | |
32 | 36 | |
33 | 37 | #include "selinux.h" |
34 | 38 | #include "ueventd.h" |
35 | 39 | #include "util.h" |
40 | +#include "parser.h" | |
36 | 41 | |
37 | 42 | #ifdef _INIT_INIT_H |
38 | 43 | #error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals" |
@@ -419,6 +424,91 @@ void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) { | ||
419 | 424 | HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links); |
420 | 425 | } |
421 | 426 | |
427 | +void DeviceHandler::HandleModuleEvent(const Uevent& uevent, std::vector<std::string>* mod_queue) | |
428 | +{ | |
429 | + if (!uevent.modalias.empty() && uevent.action == "add") { | |
430 | + if (mod_aliases_.empty()) { | |
431 | + ReadModulesDescFiles(); | |
432 | + } | |
433 | + bool deferred = false; | |
434 | + if (mod_queue) { | |
435 | + for (auto& entry : deferred_mod_aliases_) { | |
436 | + if (!fnmatch(entry.first.c_str(), uevent.modalias.c_str(), 0)) { | |
437 | + mod_queue->emplace_back(entry.second); | |
438 | + deferred = true; | |
439 | + } | |
440 | + } | |
441 | + } | |
442 | + if (!deferred) { | |
443 | + LoadModule(uevent); | |
444 | + } | |
445 | + } | |
446 | +} | |
447 | + | |
448 | +bool DeviceHandler::LoadModule(const Uevent& uevent) const | |
449 | +{ | |
450 | + bool ret = false; | |
451 | + for (auto& entry : mod_aliases_) { | |
452 | + if (!fnmatch(entry.first.c_str(), uevent.modalias.c_str(), 0)) { | |
453 | + ret |= LoadModule(entry.second); | |
454 | + } | |
455 | + } | |
456 | + return ret; | |
457 | +} | |
458 | + | |
459 | +bool DeviceHandler::LoadModule(const std::string& mod, const char* options) const | |
460 | +{ | |
461 | + bool ret = !insmod_by_dep(mod.c_str(), options, NULL, 0, NULL); | |
462 | + if (!ret) { | |
463 | + PLOG(WARNING) << "failed to load " << mod; | |
464 | + } | |
465 | + return ret; | |
466 | +} | |
467 | + | |
468 | +void DeviceHandler::ReadModulesDescFiles() | |
469 | +{ | |
470 | + auto line_parser = [] (auto args, std::set<std::string>* set_) -> Result<Success> { | |
471 | + if (args.size() < 2) { | |
472 | + return Error() << "must have 2 entries"; | |
473 | + } | |
474 | + | |
475 | + set_->emplace(args[1]); | |
476 | + return Success(); | |
477 | + }; | |
478 | + using namespace std::placeholders; | |
479 | + std::set<std::string> blacklist, deferred; | |
480 | + | |
481 | + Parser parser; | |
482 | + parser.AddSingleLineParser("blacklist", std::bind(line_parser, _1, &blacklist)); | |
483 | + parser.AddSingleLineParser("deferred", std::bind(line_parser, _1, &deferred)); | |
484 | + | |
485 | + // wait until the file is ready | |
486 | + while (!parser.ParseConfig("/system/etc/modules.blacklist")) { | |
487 | + std::this_thread::sleep_for(100ms); | |
488 | + } | |
489 | + | |
490 | + parser.AddSingleLineParser("alias", [&] (auto args) -> Result<Success> { | |
491 | + if (args.size() < 3) { | |
492 | + return Error() << "must have 3 entries"; | |
493 | + } | |
494 | + if (deferred.find(args[2]) != deferred.end()) { | |
495 | + deferred_mod_aliases_.emplace(args[1], args[2]); | |
496 | + } else if (blacklist.find(args[2]) == blacklist.end()) { | |
497 | + mod_aliases_.emplace(args[1], args[2]); | |
498 | + } | |
499 | + return Success(); | |
500 | + }); | |
501 | + char alias[PATH_MAX]; | |
502 | + strlcat(get_default_mod_path(alias), "modules.alias", PATH_MAX); | |
503 | + parser.ParseConfig(alias); | |
504 | +} | |
505 | + | |
506 | +void DeviceHandler::OnColdBootDone() | |
507 | +{ | |
508 | + mod_aliases_.insert(deferred_mod_aliases_.begin(), deferred_mod_aliases_.end()); | |
509 | + deferred_mod_aliases_.clear(); | |
510 | +} | |
511 | + | |
422 | 512 | DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions, |
423 | 513 | std::vector<SysfsPermissions> sysfs_permissions, |
424 | 514 | std::vector<Subsystem> subsystems, std::set<std::string> boot_devices, |
@@ -434,5 +524,40 @@ DeviceHandler::DeviceHandler() | ||
434 | 524 | : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, |
435 | 525 | std::vector<Subsystem>{}, std::set<std::string>{}, false) {} |
436 | 526 | |
527 | +int modprobe_main(int argc, char **argv) | |
528 | +{ | |
529 | + // We only accept requests from root user (kernel) | |
530 | + if (getuid()) return -EPERM; | |
531 | + | |
532 | + // Kernel will launch a user space program specified by | |
533 | + // /proc/sys/kernel/modprobe to load modules. | |
534 | + // No deferred loading in this case. | |
535 | + while (argc > 1 && (!strcmp(argv[1], "-q") || !strcmp(argv[1], "--"))) { | |
536 | + klog_set_level(KLOG_NOTICE_LEVEL); | |
537 | + argc--, argv++; | |
538 | + } | |
539 | + | |
540 | + if (argc < 2) { | |
541 | + // it is called without enough arguments | |
542 | + return -EINVAL; | |
543 | + } | |
544 | + | |
545 | + std::string options; | |
546 | + if (argc > 2) { | |
547 | + options = argv[2]; | |
548 | + for (int i = 3; i < argc; ++i) { | |
549 | + options += ' '; | |
550 | + options += argv[i]; | |
551 | + } | |
552 | + } | |
553 | + KLOG_NOTICE("modprobe", "%s %s", argv[1], options.c_str()); | |
554 | + | |
555 | + Uevent uevent = { .modalias = argv[1] }; | |
556 | + DeviceHandler dh; | |
557 | + dh.ReadModulesDescFiles(); | |
558 | + dh.OnColdBootDone(); | |
559 | + exit(!dh.LoadModule(uevent) && !dh.LoadModule(uevent.modalias, options.c_str())); | |
560 | +} | |
561 | + | |
437 | 562 | } // namespace init |
438 | 563 | } // namespace android |
@@ -23,6 +23,7 @@ | ||
23 | 23 | #include <algorithm> |
24 | 24 | #include <set> |
25 | 25 | #include <string> |
26 | +#include <map> | |
26 | 27 | #include <vector> |
27 | 28 | |
28 | 29 | #include <android-base/file.h> |
@@ -109,6 +110,11 @@ class DeviceHandler { | ||
109 | 110 | ~DeviceHandler(){}; |
110 | 111 | |
111 | 112 | void HandleDeviceEvent(const Uevent& uevent); |
113 | + void HandleModuleEvent(const Uevent& uevent, std::vector<std::string>* mod_queue = nullptr); | |
114 | + bool LoadModule(const Uevent& uevent) const; | |
115 | + bool LoadModule(const std::string& mod, const char* options = "") const; | |
116 | + void ReadModulesDescFiles(); | |
117 | + void OnColdBootDone(); | |
112 | 118 | |
113 | 119 | std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const; |
114 | 120 | void set_skip_restorecon(bool value) { skip_restorecon_ = value; } |
@@ -127,6 +133,8 @@ class DeviceHandler { | ||
127 | 133 | std::vector<SysfsPermissions> sysfs_permissions_; |
128 | 134 | std::vector<Subsystem> subsystems_; |
129 | 135 | std::set<std::string> boot_devices_; |
136 | + std::multimap<std::string, std::string> mod_aliases_; | |
137 | + std::multimap<std::string, std::string> deferred_mod_aliases_; | |
130 | 138 | bool skip_restorecon_; |
131 | 139 | std::string sysfs_mount_point_; |
132 | 140 | }; |
@@ -549,6 +549,10 @@ static void InstallSigtermHandler() { | ||
549 | 549 | } |
550 | 550 | |
551 | 551 | int main(int argc, char** argv) { |
552 | + if (!strcmp(basename(argv[0]), "modprobe")) { | |
553 | + return modprobe_main(argc, argv); | |
554 | + } | |
555 | + | |
552 | 556 | if (!strcmp(basename(argv[0]), "ueventd")) { |
553 | 557 | return ueventd_main(argc, argv); |
554 | 558 | } |
@@ -29,6 +29,7 @@ struct Uevent { | ||
29 | 29 | std::string firmware; |
30 | 30 | std::string partition_name; |
31 | 31 | std::string device_name; |
32 | + std::string modalias; | |
32 | 33 | int partition_num; |
33 | 34 | int major; |
34 | 35 | int minor; |
@@ -39,6 +39,7 @@ static void ParseEvent(const char* msg, Uevent* uevent) { | ||
39 | 39 | uevent->firmware.clear(); |
40 | 40 | uevent->partition_name.clear(); |
41 | 41 | uevent->device_name.clear(); |
42 | + uevent->modalias.clear(); | |
42 | 43 | // currently ignoring SEQNUM |
43 | 44 | while (*msg) { |
44 | 45 | if (!strncmp(msg, "ACTION=", 7)) { |
@@ -68,6 +69,9 @@ static void ParseEvent(const char* msg, Uevent* uevent) { | ||
68 | 69 | } else if (!strncmp(msg, "DEVNAME=", 8)) { |
69 | 70 | msg += 8; |
70 | 71 | uevent->device_name = msg; |
72 | + } else if (!strncmp(msg, "MODALIAS=", 9)) { | |
73 | + msg += 9; | |
74 | + uevent->modalias = msg; | |
71 | 75 | } |
72 | 76 | |
73 | 77 | // advance to after the next \0 |
@@ -127,6 +127,7 @@ class ColdBoot { | ||
127 | 127 | |
128 | 128 | unsigned int num_handler_subprocesses_; |
129 | 129 | std::vector<Uevent> uevent_queue_; |
130 | + std::vector<std::string> mod_queue_; | |
130 | 131 | |
131 | 132 | std::set<pid_t> subprocess_pids_; |
132 | 133 | }; |
@@ -142,7 +143,7 @@ void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_pr | ||
142 | 143 | void ColdBoot::RegenerateUevents() { |
143 | 144 | uevent_listener_.RegenerateUevents([this](const Uevent& uevent) { |
144 | 145 | HandleFirmwareEvent(uevent); |
145 | - | |
146 | + device_handler_.HandleModuleEvent(uevent, &mod_queue_); | |
146 | 147 | uevent_queue_.emplace_back(std::move(uevent)); |
147 | 148 | return ListenerAction::kContinue; |
148 | 149 | }); |
@@ -212,6 +213,11 @@ void ColdBoot::Run() { | ||
212 | 213 | |
213 | 214 | WaitForSubProcesses(); |
214 | 215 | |
216 | + device_handler_.OnColdBootDone(); | |
217 | + for (auto& mod : mod_queue_) { | |
218 | + device_handler_.LoadModule(mod); | |
219 | + } | |
220 | + | |
215 | 221 | close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); |
216 | 222 | KLOG_INFO("Coldboot", "took %f seconds", cold_boot_timer.duration().count() / 1000.0f); |
217 | 223 | } |
@@ -281,6 +287,7 @@ int ueventd_main(int argc, char** argv) { | ||
281 | 287 | |
282 | 288 | uevent_listener.Poll([&device_handler](const Uevent& uevent) { |
283 | 289 | HandleFirmwareEvent(uevent); |
290 | + device_handler.HandleModuleEvent(uevent); | |
284 | 291 | device_handler.HandleDeviceEvent(uevent); |
285 | 292 | return ListenerAction::kContinue; |
286 | 293 | }); |
@@ -21,6 +21,7 @@ namespace android { | ||
21 | 21 | namespace init { |
22 | 22 | |
23 | 23 | int ueventd_main(int argc, char** argv); |
24 | +int modprobe_main(int argc, char **argv); | |
24 | 25 | |
25 | 26 | } // namespace init |
26 | 27 | } // namespace android |
@@ -105,6 +105,7 @@ cc_library { | ||
105 | 105 | "ashmem-dev.cpp", |
106 | 106 | "klog.cpp", |
107 | 107 | "partition_utils.cpp", |
108 | + "probe_module.c", | |
108 | 109 | "properties.cpp", |
109 | 110 | "qtaguid.cpp", |
110 | 111 | "trace-dev.cpp", |
@@ -0,0 +1,106 @@ | ||
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 | +/* get_default_mod_path() - get the default modules path | |
25 | + * It checks /system/lib/modules/$(uname -r)/ first. If it doesn't exist, | |
26 | + * fall back to /system/lib/modules/. | |
27 | + * | |
28 | + * def_mod_path: The buffer to be filled | |
29 | + * | |
30 | + * return : def_mod_path | |
31 | + */ | |
32 | +extern char *get_default_mod_path(char *def_mod_path); | |
33 | + | |
34 | +/* insmod() - load a kernel module (target) from a file | |
35 | + * | |
36 | + * filename : Filename of the target module. | |
37 | + * | |
38 | + * args : A string of target module's parameters. NOTE: we only | |
39 | + * support parameters of the target module. | |
40 | + * | |
41 | + * flags : MODULE_INIT_* flags defined in module.h | |
42 | + * | |
43 | + * return : 0 for success; -1 indicates an error | |
44 | + */ | |
45 | +extern int insmod(const char *filename, const char *options, int flags); | |
46 | + | |
47 | +/* insmod_by_dep() - load a kernel module (target) with its dependency | |
48 | + * The module's dependency must be described in the provided dependency file. | |
49 | + * other modules in the dependency chain will be loaded prior to the target. | |
50 | + * | |
51 | + * module_name: Name of the target module. e.g. name "MyModule" is for | |
52 | + * module file MyModule.ko. | |
53 | + * | |
54 | + * args : A string of target module's parameters. NOTE: we only | |
55 | + * support parameters of the target module. | |
56 | + * | |
57 | + * dep_name : Name of dependency file. If it is NULL, we will look | |
58 | + * up /system/lib/modules/modules.dep by default. | |
59 | + * | |
60 | + * strip : Non-zero values remove paths of modules in dependency. | |
61 | + * before loading them. The final path of a module will be | |
62 | + * base/MyModule.ko. This is for devices which put every | |
63 | + * modules into a single directory. | |
64 | + * | |
65 | + * Passing 0 to strip keeps module paths in dependency file. | |
66 | + * e.g. "kernel/drivers/.../MyModule.ko" in dep file will | |
67 | + * be loaded as base/kernel/drivers/.../MyModule.ko . | |
68 | + * | |
69 | + * base : Base dir, a prefix to be added to module's path prior to | |
70 | + * loading. The last character prior to base string's terminator | |
71 | + * must be a '/'. If it is NULL, we will take | |
72 | + * /system/lib/modules/modules.dep by default. | |
73 | + * | |
74 | + * return : 0 for success; non-zero for any errors. | |
75 | + * | |
76 | + * Note: | |
77 | + * When loading modules, function will not fail for any modules which are | |
78 | + * already in kernel. The module parameters passed to function will not be | |
79 | + * effective in this case if target module is already loaded into kernel. | |
80 | + */ | |
81 | +extern int insmod_by_dep( | |
82 | + const char *module_name, | |
83 | + const char *args, | |
84 | + const char *dep_name, | |
85 | + int strip, | |
86 | + const char * base); | |
87 | + | |
88 | +/* rmmod_by_dep() - remove a module (target) from kernel with its dependency | |
89 | + * The module's dependency must be described in the provided dependency file. | |
90 | + * This function will try to remove other modules in the dependency chain too | |
91 | + * | |
92 | + * module_name: Name of the target module. e.g. name "MyModule" is for | |
93 | + * module file MyModule.ko. | |
94 | + * | |
95 | + * dep_name : Name of dependency file. If it is NULL, we will look | |
96 | + * up /system/lib/modules/modules.dep by default. | |
97 | + * | |
98 | + * return : 0 for success; non-zero for any errors. | |
99 | + */ | |
100 | +extern int rmmod_by_dep(const char *module_name, const char *dep_name); | |
101 | + | |
102 | +#ifdef __cplusplus | |
103 | +} | |
104 | +#endif | |
105 | + | |
106 | +#endif /*_LIBS_CUTILS_PROBEMODULE_H*/ |
@@ -0,0 +1,386 @@ | ||
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 <limits.h> | |
21 | +#include <errno.h> | |
22 | +#include <fcntl.h> | |
23 | +#include <cutils/misc.h> | |
24 | +#include <sys/syscall.h> | |
25 | +#include <sys/utsname.h> | |
26 | + | |
27 | +#define LOG_TAG "ProbeModule" | |
28 | +#include <cutils/log.h> | |
29 | + | |
30 | +#define LDM_DEFAULT_MOD_PATH "/system/lib/modules/" | |
31 | + | |
32 | +extern int delete_module(const char *, unsigned int); | |
33 | + | |
34 | +/* get_default_mod_path() interface to outside, | |
35 | + * refer to its description in probe_module.h | |
36 | + */ | |
37 | +char *get_default_mod_path(char *def_mod_path) | |
38 | +{ | |
39 | + int len; | |
40 | + struct utsname buf; | |
41 | + uname(&buf); | |
42 | + len = snprintf(def_mod_path, PATH_MAX, "%s", LDM_DEFAULT_MOD_PATH); | |
43 | + strcpy(def_mod_path + len, buf.release); | |
44 | + if (access(def_mod_path, F_OK)) | |
45 | + def_mod_path[len] = '\0'; | |
46 | + else | |
47 | + strcat(def_mod_path, "/"); | |
48 | + return def_mod_path; | |
49 | +} | |
50 | + | |
51 | +int insmod(const char *filename, const char *options, int flags) | |
52 | +{ | |
53 | + int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC); | |
54 | + if (fd == -1) { | |
55 | + ALOGE("insmod: open(\"%s\") failed: %s", filename, strerror(errno)); | |
56 | + return -1; | |
57 | + } | |
58 | + int rc = syscall(__NR_finit_module, fd, options, flags); | |
59 | + if (rc == -1) { | |
60 | + if (errno == EEXIST) { | |
61 | + rc = 0; | |
62 | + } else { | |
63 | + ALOGE("finit_module for \"%s\" failed: %s", filename, strerror(errno)); | |
64 | + } | |
65 | + } | |
66 | + close(fd); | |
67 | + return rc; | |
68 | +} | |
69 | + | |
70 | +static char *strip_path(char *str) | |
71 | +{ | |
72 | + char *ptr = strrchr(str, '/'); | |
73 | + return ptr ? ptr + 1 : str; | |
74 | +} | |
75 | + | |
76 | +static void hyphen_to_underscore(char *str) | |
77 | +{ | |
78 | + while (str && *str != '\0') { | |
79 | + if (*str == '-') | |
80 | + *str = '_'; | |
81 | + str++; | |
82 | + } | |
83 | +} | |
84 | + | |
85 | +/* Compare module names, but don't differentiate '_' and '-'. | |
86 | + * return: 0 when s1 is matched to s2 or size is zero. | |
87 | + * non-zero in any other cases. | |
88 | + */ | |
89 | +static int match_name(const char *s1, const char *s2, const size_t size) | |
90 | +{ | |
91 | + size_t i; | |
92 | + | |
93 | + if (!size) | |
94 | + return 0; | |
95 | + | |
96 | + for (i = 0; i < size; i++, s1++, s2++) { | |
97 | + | |
98 | + if ((*s1 == '_' || *s1 == '-') && (*s2 == '_' || *s2 == '-')) | |
99 | + continue; | |
100 | + | |
101 | + if (*s1 != *s2) | |
102 | + return -1; | |
103 | + | |
104 | + if (*s1 == '\0') | |
105 | + return 0; | |
106 | + } | |
107 | + | |
108 | + return 0; | |
109 | +} | |
110 | + | |
111 | +/* check if a line in dep file is target module's dependency. | |
112 | + * return 1 when it is, otherwise 0 in any other cases. | |
113 | + */ | |
114 | +static int is_target_module(char *line, const char *target) | |
115 | +{ | |
116 | + char *token; | |
117 | + char name[PATH_MAX]; | |
118 | + const char *delimiter = ":"; | |
119 | + int ret = 0; | |
120 | + | |
121 | + /* search token */ | |
122 | + token = strstr(line, delimiter); | |
123 | + | |
124 | + if (!token) { | |
125 | + ALOGE("invalid line: no token"); | |
126 | + return 0; | |
127 | + } | |
128 | + | |
129 | + /* only take stuff before the token */ | |
130 | + *token = '\0'; | |
131 | + | |
132 | + /* use "module.ko" in comparision */ | |
133 | + strcat(strcpy(name, target), ".ko"); | |
134 | + | |
135 | + ret = !match_name(strip_path(line), name, strlen(name)); | |
136 | + | |
137 | + /* restore [single] token, keep line unchanged until we parse it later */ | |
138 | + *token = *delimiter; | |
139 | + | |
140 | + return ret; | |
141 | + | |
142 | +} | |
143 | + | |
144 | +/* turn a single string into an array of dependency. | |
145 | + * | |
146 | + * return: dependency array's address if it succeeded. Caller | |
147 | + * is responsible to free the array's memory. | |
148 | + * NULL when any error happens. | |
149 | + */ | |
150 | +static char **setup_dep(char *line) | |
151 | +{ | |
152 | + char *tmp = line; | |
153 | + char *brk; | |
154 | + int i; | |
155 | + char **dep; | |
156 | + | |
157 | + for (i = 2; (tmp = strchr(tmp, ' ')); i++) | |
158 | + tmp++; | |
159 | + | |
160 | + dep = malloc(sizeof(char *) * i); | |
161 | + if (dep) { | |
162 | + i = 0; | |
163 | + do { | |
164 | + tmp = strtok_r(i ? NULL : line, ": ", &brk); | |
165 | + } while ((dep[i++] = tmp)); | |
166 | + } | |
167 | + | |
168 | + return dep; | |
169 | +} | |
170 | + | |
171 | +/* install all modules in the dependency chain | |
172 | + * deps : A array of module file names, must be terminated by a NULL pointer | |
173 | + * args : The module parameters for target module. | |
174 | + * strip : Non-zero to strip out path info in the file name; | |
175 | + * 0 to keep path info when loading modules. | |
176 | + * base : a prefix to module path, it will NOT be affected by strip flag. | |
177 | + * return : 0 for success or nothing to do; non-zero when any error occurs. | |
178 | + */ | |
179 | +static int insmod_s(char *dep[], const char *args, int strip, const char *base) | |
180 | +{ | |
181 | + char *name; | |
182 | + int cnt; | |
183 | + size_t len; | |
184 | + int ret = 0; | |
185 | + char path_name[PATH_MAX]; | |
186 | + char def_mod_path[PATH_MAX]; | |
187 | + const char *base_dir; | |
188 | + | |
189 | + if (base && strlen(base)) | |
190 | + base_dir = base; | |
191 | + else | |
192 | + base_dir = get_default_mod_path(def_mod_path); | |
193 | + | |
194 | + /* load modules in reversed order */ | |
195 | + for (cnt = 0; dep[cnt]; cnt++) | |
196 | + ; | |
197 | + | |
198 | + len = strlen(strcpy(path_name, base_dir)); | |
199 | + | |
200 | + while (!ret && cnt--) { | |
201 | + | |
202 | + name = strip ? strip_path(dep[cnt]) : dep[cnt]; | |
203 | + | |
204 | + strcpy(path_name + len, name); | |
205 | + | |
206 | + ret = insmod(path_name, cnt ? "" : args, 0); | |
207 | + } | |
208 | + | |
209 | + return ret; | |
210 | +} | |
211 | + | |
212 | +/* remove all modules in a dependency chain | |
213 | + * NOTE: We assume module name in kernel is same as the file name without .ko | |
214 | + */ | |
215 | +static int rmmod_s(char *dep[], int flags) | |
216 | +{ | |
217 | + int i; | |
218 | + int ret = 0; | |
219 | + | |
220 | + for (i = 0; dep[i]; i++) { | |
221 | + char *mod_name = strip_path(dep[i]); | |
222 | + size_t len = strlen(mod_name); | |
223 | + | |
224 | + if (len > 3 && strstr(mod_name, ".ko") == (mod_name + len - 3)) { | |
225 | + mod_name[len - 3] = '\0'; | |
226 | + | |
227 | + hyphen_to_underscore(mod_name); | |
228 | + | |
229 | + ret = delete_module(mod_name, flags); | |
230 | + | |
231 | + if (ret) { | |
232 | + ALOGE("%s: Failed to remove module [%s] error (%s)", | |
233 | + __FUNCTION__, mod_name, strerror(errno)); | |
234 | + break; | |
235 | + | |
236 | + } | |
237 | + } | |
238 | + } | |
239 | + | |
240 | + return ret; | |
241 | +} | |
242 | + | |
243 | +/* look_up_dep() find and setup target module's dependency in modules.dep | |
244 | + * | |
245 | + * dep_file: a pointer to module's dep file loaded in memory, its content | |
246 | + * will be CHANGED during parsing. | |
247 | + * | |
248 | + * return: a pointer to an array which holds the dependency strings and | |
249 | + * terminated by a NULL pointer. Caller is responsible to free the | |
250 | + * array's memory. | |
251 | + * | |
252 | + * non-zero in any other cases. Content of dep array is invalid. | |
253 | + */ | |
254 | +static char **look_up_dep(const char *module_name, void *dep_file) | |
255 | +{ | |
256 | + char *line; | |
257 | + char *saved_pos; | |
258 | + char *start; | |
259 | + char **dep = NULL; | |
260 | + | |
261 | + if (!dep_file || !module_name || *module_name == '\0') | |
262 | + return NULL; | |
263 | + | |
264 | + start = (char *)dep_file; | |
265 | + | |
266 | + /* We expect modules.dep file has a new line char before EOF. */ | |
267 | + while ((line = strtok_r(start, "\n", &saved_pos)) != NULL) { | |
268 | + | |
269 | + start = NULL; | |
270 | + | |
271 | + if (is_target_module(line, module_name)) { | |
272 | + | |
273 | + dep = setup_dep(line); | |
274 | + /* job done */ | |
275 | + break; | |
276 | + } | |
277 | + } | |
278 | + | |
279 | + return dep; | |
280 | +} | |
281 | + | |
282 | +/* load_dep_file() load a dep file (usually it is modules.dep) | |
283 | + * into memory. Caller is responsible to free the memory. | |
284 | + * | |
285 | + * file_name: dep file's name, if it is NULL or an empty string, | |
286 | + * This function will try to load a dep file in the | |
287 | + * default path defined in LDM_DEFAULT_DEP_FILE | |
288 | + * | |
289 | + * return: a pointer to the allocated mem which holds all | |
290 | + * content of the depfile. a zero pointer will be | |
291 | + * returned for any errors. | |
292 | + * */ | |
293 | +static void *load_dep_file(const char *file_name) | |
294 | +{ | |
295 | + unsigned int len; | |
296 | + char def_mod_path[PATH_MAX]; | |
297 | + if (!file_name || *file_name == '\0') { | |
298 | + file_name = get_default_mod_path(def_mod_path); | |
299 | + strcat(def_mod_path, "modules.dep"); | |
300 | + } | |
301 | + | |
302 | + return load_file(file_name, &len); | |
303 | +} | |
304 | + | |
305 | +/* insmod_by_dep() interface to outside, | |
306 | + * refer to its description in probe_module.h | |
307 | + */ | |
308 | +int insmod_by_dep(const char *module_name, | |
309 | + const char *args, | |
310 | + const char *dep_name, | |
311 | + int strip, | |
312 | + const char *base) | |
313 | +{ | |
314 | + void *dep_file; | |
315 | + char **dep = NULL; | |
316 | + int ret = -1; | |
317 | + | |
318 | + if (!module_name || *module_name == '\0') { | |
319 | + ALOGE("need valid module name"); | |
320 | + return ret; | |
321 | + } | |
322 | + | |
323 | + dep_file = load_dep_file(dep_name); | |
324 | + | |
325 | + if (!dep_file) { | |
326 | + ALOGE("cannot load dep file : %s", dep_name); | |
327 | + return ret; | |
328 | + } | |
329 | + | |
330 | + dep = look_up_dep(module_name, dep_file); | |
331 | + | |
332 | + if (!dep) { | |
333 | + ALOGE("%s: cannot load module: [%s]", __FUNCTION__, module_name); | |
334 | + goto free_file; | |
335 | + } | |
336 | + | |
337 | + ret = insmod_s(dep, args, strip, base); | |
338 | + | |
339 | + free(dep); | |
340 | + | |
341 | +free_file: | |
342 | + free(dep_file); | |
343 | + | |
344 | + return ret; | |
345 | + | |
346 | +} | |
347 | + | |
348 | +/* rmmod_by_dep() interface to outside, | |
349 | + * refer to its description in probe_module.h | |
350 | + */ | |
351 | +int rmmod_by_dep(const char *module_name, const char *dep_name) | |
352 | +{ | |
353 | + void *dep_file; | |
354 | + char **dep = NULL; | |
355 | + int ret = -1; | |
356 | + | |
357 | + if (!module_name || *module_name == '\0') { | |
358 | + ALOGE("need valid module name"); | |
359 | + return ret; | |
360 | + } | |
361 | + | |
362 | + dep_file = load_dep_file(dep_name); | |
363 | + | |
364 | + if (!dep_file) { | |
365 | + ALOGE("cannot load dep file : %s", dep_name); | |
366 | + return ret; | |
367 | + } | |
368 | + | |
369 | + dep = look_up_dep(module_name, dep_file); | |
370 | + | |
371 | + if (!dep) { | |
372 | + ALOGE("%s: cannot remove module: [%s]", __FUNCTION__, module_name); | |
373 | + goto free_file; | |
374 | + } | |
375 | + | |
376 | + ret = rmmod_s(dep, O_NONBLOCK); | |
377 | + | |
378 | + free(dep); | |
379 | + | |
380 | +free_file: | |
381 | + free(dep_file); | |
382 | + | |
383 | + return ret; | |
384 | +} | |
385 | + | |
386 | +/* end of file */ |