Merge "Declare init.*.rc modules in Android.bp" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 3135d9e..526e2ca 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -603,11 +603,10 @@
}
__attribute__((noinline)) void mte_illegal_setjmp_helper(jmp_buf& jump_buf) {
- // Because the detection of illegal setjmp is done relative to the SP in setjmp,
- // we need to make sure this stack frame is bigger than the one of setjmp.
- // TODO(fmayer): fix that bug and remove the workaround.
- volatile char buf[1024];
- buf[0] = '1';
+ // This frame is at least 8 bytes for storing and restoring the LR before the
+ // setjmp below. So this can never get an empty stack frame, even if we omit
+ // the frame pointer. So, the SP of this is always less (numerically) than the
+ // calling function frame.
setjmp(jump_buf);
}
diff --git a/init/Android.bp b/init/Android.bp
index c3abefe..12ca15a 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,6 +162,7 @@
},
static_libs: [
"libavb",
+ "libavf_cc_flags",
"libbootloader_message",
"libc++fs",
"libcgrouprc_format",
@@ -359,6 +360,7 @@
static_libs: [
"libc++fs",
"libfs_avb",
+ "libavf_cc_flags",
"libfs_mgr",
"libfec",
"libfec_rs",
@@ -459,7 +461,10 @@
cc_binary {
name: "init_first_stage.microdroid",
- defaults: ["init_first_stage_defaults"],
+ defaults: [
+ "avf_build_flags_cc",
+ "init_first_stage_defaults",
+ ],
cflags: ["-DMICRODROID=1"],
installable: false,
}
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 05e00ed..a686d05 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -132,11 +132,19 @@
bool BlockDevInitializer::InitDmDevice(const std::string& device) {
const std::string device_name(basename(device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
+ return InitDevice(syspath, device_name);
+}
+
+bool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {
+ return InitDevice("/sys/devices/platform", dev_name);
+}
+
+bool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {
bool found = false;
- auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
+ auto uevent_callback = [&device_name, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
- LOG(VERBOSE) << "Creating device-mapper device : " << device;
+ LOG(VERBOSE) << "Creating device : " << device_name;
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
@@ -146,13 +154,13 @@
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
if (!found) {
- LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
+ LOG(INFO) << "device '" << device_name << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
- LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
+ LOG(INFO) << "wait for device '" << device_name << "' returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
+ LOG(ERROR) << "device '" << device_name << "' not found after polling timeout";
return false;
}
return true;
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index ec39ce0..d5b1f60 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -24,6 +24,7 @@
namespace android {
namespace init {
+// TODO: should this be renamed to FirstStageDevInitialize?
class BlockDevInitializer final {
public:
BlockDevInitializer();
@@ -32,11 +33,13 @@
bool InitDmUser(const std::string& name);
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
+ bool InitPlatformDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
bool InitMiscDevice(const std::string& name);
+ bool InitDevice(const std::string& syspath, const std::string& device);
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c4d0f75..356aaa0 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -37,6 +37,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android/avf_cc_flags.h>
#include <modprobe/modprobe.h>
#include <private/android_filesystem_config.h>
@@ -385,7 +386,12 @@
// /second_stage_resources is used to preserve files from first to second
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
- "mode=0755,uid=0,gid=0"))
+ "mode=0755,uid=0,gid=0"));
+
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ CHECKCALL(mount("tmpfs", "/microdroid_resources", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0750,uid=0,gid=0"));
+ }
#undef CHECKCALL
SetStdioToDevNull(argv);
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index c0b9281..836d536 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -16,6 +16,7 @@
#include "first_stage_mount.h"
+#include <signal.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <unistd.h>
@@ -33,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android/avf_cc_flags.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
@@ -272,6 +274,11 @@
return true;
}
+// TODO: should this be in a library in packages/modules/Virtualization first_stage_init links?
+static bool IsMicrodroidStrictBoot() {
+ return access("/proc/device-tree/chosen/avf,strict-boot", F_OK) == 0;
+}
+
bool FirstStageMountVBootV2::InitDevices() {
std::set<std::string> devices;
GetSuperDeviceName(&devices);
@@ -283,6 +290,14 @@
return false;
}
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (IsMicrodroidStrictBoot()) {
+ if (!block_dev_init_.InitPlatformDevice("open-dice0")) {
+ return false;
+ }
+ }
+ }
+
if (IsDmLinearEnabled()) {
auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
if (!android::base::Realpath(super_symlink, &super_path_)) {
@@ -527,9 +542,48 @@
return true;
}
+static bool MaybeDeriveMicrodroidVendorDiceNode(Fstab* fstab) {
+ std::optional<std::string> microdroid_vendor_block_dev;
+ for (auto entry = fstab->begin(); entry != fstab->end(); entry++) {
+ if (entry->mount_point == "/vendor") {
+ microdroid_vendor_block_dev.emplace(entry->blk_device);
+ break;
+ }
+ }
+ if (!microdroid_vendor_block_dev.has_value()) {
+ LOG(VERBOSE) << "No microdroid vendor partition to mount";
+ return true;
+ }
+ // clang-format off
+ const std::array<const char*, 7> args = {
+ "/system/bin/derive_microdroid_vendor_dice_node",
+ "--dice-driver", "/dev/open-dice0",
+ "--microdroid-vendor-disk-image", microdroid_vendor_block_dev->data(),
+ "--output", "/microdroid_resources/dice_chain.raw",
+ };
+ // clang-format-on
+ // ForkExecveAndWaitForCompletion calls waitpid to wait for the fork-ed process to finish.
+ // The first_stage_console adds SA_NOCLDWAIT flag to the SIGCHLD handler, which means that
+ // waitpid will always return -ECHLD. Here we re-register a default handler, so that waitpid
+ // works.
+ LOG(INFO) << "Deriving dice node for microdroid vendor partition";
+ signal(SIGCHLD, SIG_DFL);
+ if (!ForkExecveAndWaitForCompletion(args[0], (char**)args.data())) {
+ LOG(ERROR) << "Failed to derive microdroid vendor dice node";
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMountVBootV2::MountPartitions() {
if (!TrySwitchSystemAsRoot()) return false;
+ if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+ if (!MaybeDeriveMicrodroidVendorDiceNode(&fstab_)) {
+ return false;
+ }
+ }
+
if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
for (auto current = fstab_.begin(); current != fstab_.end();) {
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 6f8a4de..59e57b9 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <memory>
+#include <unordered_map>
#include <android-base/file.h>
#include <android-base/logging.h>
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 1a26c4d..150f8f4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -1083,7 +1083,8 @@
return;
}
}
- } else if (reboot_target == "quiescent") {
+ } else if (std::find(cmd_params.begin(), cmd_params.end(), "quiescent")
+ != cmd_params.end()) { // Quiescent can be either subreason or details.
bootloader_message boot = {};
if (std::string err; !read_bootloader_message(&boot, &err)) {
LOG(ERROR) << "Failed to read bootloader message: " << err;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 1f211dd..e191b60 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -111,97 +111,6 @@
return true;
}
-// Forks, executes the provided program in the child, and waits for the completion in the parent.
-// Child's stderr is captured and logged using LOG(ERROR).
-bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
- // Create a pipe used for redirecting child process's output.
- // * pipe_fds[0] is the FD the parent will use for reading.
- // * pipe_fds[1] is the FD the child will use for writing.
- int pipe_fds[2];
- if (pipe(pipe_fds) == -1) {
- PLOG(ERROR) << "Failed to create pipe";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid == -1) {
- PLOG(ERROR) << "Failed to fork for " << filename;
- return false;
- }
-
- if (child_pid == 0) {
- // fork succeeded -- this is executing in the child process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[0]);
-
- // Redirect stderr to the pipe FD provided by the parent
- if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
- PLOG(ERROR) << "Failed to redirect stderr of " << filename;
- _exit(127);
- return false;
- }
- close(pipe_fds[1]);
-
- if (execv(filename, argv) == -1) {
- PLOG(ERROR) << "Failed to execve " << filename;
- return false;
- }
- // Unreachable because execve will have succeeded and replaced this code
- // with child process's code.
- _exit(127);
- return false;
- } else {
- // fork succeeded -- this is executing in the original/parent process
-
- // Close the pipe FD not used by this process
- close(pipe_fds[1]);
-
- // Log the redirected output of the child process.
- // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
- // As a result, we're buffering all output and logging it in one go at the end of the
- // invocation, instead of logging it as it comes in.
- const int child_out_fd = pipe_fds[0];
- std::string child_output;
- if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
- PLOG(ERROR) << "Failed to capture full output of " << filename;
- }
- close(child_out_fd);
- if (!child_output.empty()) {
- // Log captured output, line by line, because LOG expects to be invoked for each line
- std::istringstream in(child_output);
- std::string line;
- while (std::getline(in, line)) {
- LOG(ERROR) << filename << ": " << line;
- }
- }
-
- // Wait for child to terminate
- int status;
- if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
- PLOG(ERROR) << "Failed to wait for " << filename;
- return false;
- }
-
- if (WIFEXITED(status)) {
- int status_code = WEXITSTATUS(status);
- if (status_code == 0) {
- return true;
- } else {
- LOG(ERROR) << filename << " exited with status " << status_code;
- }
- } else if (WIFSIGNALED(status)) {
- LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
- } else {
- LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
- }
-
- return false;
- }
-}
-
bool ReadFirstLine(const char* file, std::string* line) {
line->clear();
diff --git a/init/util.cpp b/init/util.cpp
index e760a59..e5efc7d 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -27,6 +27,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
@@ -747,5 +748,96 @@
return filtered_configs;
}
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {
+ // Create a pipe used for redirecting child process's output.
+ // * pipe_fds[0] is the FD the parent will use for reading.
+ // * pipe_fds[1] is the FD the child will use for writing.
+ int pipe_fds[2];
+ if (pipe(pipe_fds) == -1) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t child_pid = fork();
+ if (child_pid == -1) {
+ PLOG(ERROR) << "Failed to fork for " << filename;
+ return false;
+ }
+
+ if (child_pid == 0) {
+ // fork succeeded -- this is executing in the child process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[0]);
+
+ // Redirect stderr to the pipe FD provided by the parent
+ if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {
+ PLOG(ERROR) << "Failed to redirect stderr of " << filename;
+ _exit(127);
+ return false;
+ }
+ close(pipe_fds[1]);
+
+ if (execv(filename, argv) == -1) {
+ PLOG(ERROR) << "Failed to execve " << filename;
+ return false;
+ }
+ // Unreachable because execve will have succeeded and replaced this code
+ // with child process's code.
+ _exit(127);
+ return false;
+ } else {
+ // fork succeeded -- this is executing in the original/parent process
+
+ // Close the pipe FD not used by this process
+ close(pipe_fds[1]);
+
+ // Log the redirected output of the child process.
+ // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.
+ // As a result, we're buffering all output and logging it in one go at the end of the
+ // invocation, instead of logging it as it comes in.
+ const int child_out_fd = pipe_fds[0];
+ std::string child_output;
+ if (!android::base::ReadFdToString(child_out_fd, &child_output)) {
+ PLOG(ERROR) << "Failed to capture full output of " << filename;
+ }
+ close(child_out_fd);
+ if (!child_output.empty()) {
+ // Log captured output, line by line, because LOG expects to be invoked for each line
+ std::istringstream in(child_output);
+ std::string line;
+ while (std::getline(in, line)) {
+ LOG(ERROR) << filename << ": " << line;
+ }
+ }
+
+ // Wait for child to terminate
+ int status;
+ if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {
+ PLOG(ERROR) << "Failed to wait for " << filename;
+ return false;
+ }
+
+ if (WIFEXITED(status)) {
+ int status_code = WEXITSTATUS(status);
+ if (status_code == 0) {
+ return true;
+ } else {
+ LOG(ERROR) << filename << " exited with status " << status_code;
+ }
+ } else if (WIFSIGNALED(status)) {
+ LOG(ERROR) << filename << " killed by signal " << WTERMSIG(status);
+ } else if (WIFSTOPPED(status)) {
+ LOG(ERROR) << filename << " stopped by signal " << WSTOPSIG(status);
+ } else {
+ LOG(ERROR) << "waitpid for " << filename << " returned unexpected status: " << status;
+ }
+
+ return false;
+ }
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2d02182..aa24123 100644
--- a/init/util.h
+++ b/init/util.h
@@ -117,5 +117,10 @@
// (.rc == .0rc for ranking purposes)
std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
int active_sdk);
+
+// Forks, executes the provided program in the child, and waits for the completion in the parent.
+// Child's stderr is captured and logged using LOG(ERROR).
+bool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]);
+
} // namespace init
} // namespace android
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 5d79d6a..d7a90c4 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -41,6 +41,7 @@
std::vector<std::string>* post_dependencies);
void ResetModuleCount() { module_count_ = 0; }
int GetModuleCount() { return module_count_; }
+ bool IsBlocklisted(const std::string& module_name);
private:
std::string MakeCanonical(const std::string& module_path);
@@ -52,7 +53,6 @@
void AddOption(const std::string& module_name, const std::string& option_name,
const std::string& value);
std::string GetKernelCmdline();
- bool IsBlocklisted(const std::string& module_name);
bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);
bool ParseAliasCallback(const std::vector<std::string>& args);
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
index b375a2f..8ae56d4 100644
--- a/libvendorsupport/include_llndk/android/llndk-versioning.h
+++ b/libvendorsupport/include_llndk/android/llndk-versioning.h
@@ -14,10 +14,9 @@
#pragma once
-#include <sys/cdefs.h>
-
-__BEGIN_DECLS
-
+/* As a vendor default header included in all vendor modules, this header MUST NOT include other
+ * header files or any declarations. Only macros are allowed.
+ */
#if defined(__ANDROID_VENDOR__)
// LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
@@ -32,19 +31,24 @@
"call with '#if (__ANDROID_VENDOR_API__ >= " #vendor_api_level ")'."))) \
_Pragma("clang diagnostic pop")
-// For the vendor libraries, __INTRODUCED_IN must be ignored because they are only for NDKs but not
-// for LLNDKs.
-#undef __INTRODUCED_IN
-#define __INTRODUCED_IN(x)
+// Use this macro as an `if` statement to call an API that are available to both NDK and LLNDK.
+// This returns true for the vendor modules if the vendor_api_level is less than or equal to the
+// ro.board.api_level.
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ constexpr(__ANDROID_VENDOR_API__ >= vendor_api_level)
#else // __ANDROID_VENDOR__
-// For non-vendor libraries, __INTRODUCED_IN_LLNDK must be ignored because it must not change
-// symbols of NDK or the system side of the treble boundary. It leaves a no-op annotation for ABI
-// analysis.
+// __INTRODUCED_IN_LLNDK is for LLNDK only but not for NDK. Ignore this for non-vendor modules.
+// It leaves a no-op annotation for ABI analysis.
+#if !defined(__INTRODUCED_IN_LLNDK)
#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
__attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
+#endif
+
+// For non-vendor modules, API_LEVEL_AT_LEAST is replaced with __builtin_available(sdk_api_level) to
+// guard the API for __INTRODUCED_IN.
+#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
+ (__builtin_available(android sdk_api_level, *))
#endif // __ANDROID_VENDOR__
-
-__END_DECLS
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bf34a9e..ec203f9 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -247,6 +247,7 @@
write /dev/blkio/background/blkio.bfq.weight 10
write /dev/blkio/blkio.group_idle 0
write /dev/blkio/background/blkio.group_idle 0
+ write /dev/blkio/blkio.prio.class promote-to-rt
write /dev/blkio/background/blkio.prio.class restrict-to-be
restorecon_recursive /mnt
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 17d4e31..45dd9b8 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -112,6 +112,7 @@
android::base::SetMinimumLogSeverity(android::base::INFO);
std::vector<std::string> modules;
+ std::string modules_load_file;
std::string module_parameters;
std::string mods;
std::vector<std::string> mod_dirs;
@@ -119,7 +120,7 @@
bool blocklist = false;
int rv = EXIT_SUCCESS;
- int opt;
+ int opt, fd;
int option_index = 0;
// NB: We have non-standard short options -l and -D to make it easier for
// OEMs to transition from toybox.
@@ -144,16 +145,19 @@
// is supported here by default, ignore flag if no argument.
check_mode();
if (optarg == NULL) break;
- if (!android::base::ReadFileToString(optarg, &mods)) {
+
+ // Since libmodprobe doesn't fail when the modules load file
+ // doesn't exist, let's check that here so that we don't
+ // silently fail.
+ fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);
+ if (fd == -1) {
PLOG(ERROR) << "Failed to open " << optarg;
- rv = EXIT_FAILURE;
+ return EXIT_FAILURE;
}
- for (auto mod : android::base::Split(stripComments(mods), "\n")) {
- mod = android::base::Trim(mod);
- if (mod == "") continue;
- if (std::find(modules.begin(), modules.end(), mod) != modules.end()) continue;
- modules.emplace_back(mod);
- }
+ close(fd);
+
+ mod_dirs.emplace_back(android::base::Dirname(optarg));
+ modules_load_file = android::base::Basename(optarg);
break;
case 'b':
blocklist = true;
@@ -233,30 +237,39 @@
LOG(DEBUG) << "mode is " << mode;
LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
+ LOG(DEBUG) << "modules load file is: " << modules_load_file;
LOG(DEBUG) << "module parameters is: " << android::base::Join(module_parameters, " ");
if (modules.empty()) {
if (mode == ListModulesMode) {
// emulate toybox modprobe list with no pattern (list all)
modules.emplace_back("*");
- } else {
+ } else if (modules_load_file.empty()) {
LOG(ERROR) << "No modules given.";
print_usage();
return EXIT_FAILURE;
}
}
- if (parameter_count && modules.size() > 1) {
+ if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {
LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
print_usage();
return EXIT_FAILURE;
}
- Modprobe m(mod_dirs, "modules.load", blocklist);
+ Modprobe m(mod_dirs, modules_load_file.empty() ? "modules.load" : modules_load_file, blocklist);
+ if (mode == AddModulesMode && !modules_load_file.empty()) {
+ if (!m.LoadListedModules(false)) {
+ PLOG(ERROR) << "Failed to load all the modules from " << modules_load_file;
+ return EXIT_FAILURE;
+ }
+ /* Fall-through to load modules provided on the command line (if any)*/
+ }
for (const auto& module : modules) {
switch (mode) {
case AddModulesMode:
if (!m.LoadWithAliases(module, true, module_parameters)) {
+ if (m.IsBlocklisted(module)) continue;
PLOG(ERROR) << "Failed to load module " << module;
rv = EXIT_FAILURE;
}