fastbootd: Implement getvar all.
This implements getvar all by invoking each callback and writing an INFO
status for each result. For commands that take arguments, the variable
handler can specify a function that returns all possible arguments.
Currently this only applies to partition variables.
Bug: 78793464
Test: fastboot getvar all works
Change-Id: I1cf84e06bf67614b6f56171c0ee6ca5d7ac383c9
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index eb18141..b1c2958 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -42,26 +42,66 @@
using ::android::hardware::boot::V1_0::Slot;
using namespace android::fs_mgr;
+struct VariableHandlers {
+ // Callback to retrieve the value of a single variable.
+ std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;
+ // Callback to retrieve all possible argument combinations, for getvar all.
+ std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;
+};
+
+static void GetAllVars(FastbootDevice* device, const std::string& name,
+ const VariableHandlers& handlers) {
+ if (!handlers.get_all_args) {
+ std::string message;
+ if (!handlers.get(device, std::vector<std::string>(), &message)) {
+ return;
+ }
+ device->WriteInfo(android::base::StringPrintf("%s:%s", name.c_str(), message.c_str()));
+ return;
+ }
+
+ auto all_args = handlers.get_all_args(device);
+ for (const auto& args : all_args) {
+ std::string message;
+ if (!handlers.get(device, args, &message)) {
+ continue;
+ }
+ std::string arg_string = android::base::Join(args, ":");
+ device->WriteInfo(android::base::StringPrintf("%s:%s:%s", name.c_str(), arg_string.c_str(),
+ message.c_str()));
+ }
+}
+
bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
- using VariableHandler =
- std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)>;
- const std::unordered_map<std::string, VariableHandler> kVariableMap = {
- {FB_VAR_VERSION, GetVersion},
- {FB_VAR_VERSION_BOOTLOADER, GetBootloaderVersion},
- {FB_VAR_VERSION_BASEBAND, GetBasebandVersion},
- {FB_VAR_PRODUCT, GetProduct},
- {FB_VAR_SERIALNO, GetSerial},
- {FB_VAR_SECURE, GetSecure},
- {FB_VAR_UNLOCKED, GetUnlocked},
- {FB_VAR_MAX_DOWNLOAD_SIZE, GetMaxDownloadSize},
- {FB_VAR_CURRENT_SLOT, ::GetCurrentSlot},
- {FB_VAR_SLOT_COUNT, GetSlotCount},
- {FB_VAR_HAS_SLOT, GetHasSlot},
- {FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful},
- {FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable},
- {FB_VAR_PARTITION_SIZE, GetPartitionSize},
- {FB_VAR_IS_LOGICAL, GetPartitionIsLogical},
- {FB_VAR_IS_USERSPACE, GetIsUserspace}};
+ const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+ {FB_VAR_VERSION, {GetVersion, nullptr}},
+ {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+ {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+ {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+ {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+ {FB_VAR_SECURE, {GetSecure, nullptr}},
+ {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+ {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+ {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+ {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+ {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+ {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+ {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+ {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+ {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}}};
+
+ if (args.size() < 2) {
+ return device->WriteFail("Missing argument");
+ }
+
+ // Special case: return all variables that we can.
+ if (args[1] == "all") {
+ for (const auto& [name, handlers] : kVariableMap) {
+ GetAllVars(device, name, handlers);
+ }
+ return device->WriteOkay("");
+ }
// args[0] is command name, args[1] is variable.
auto found_variable = kVariableMap.find(args[1]);
@@ -71,7 +111,7 @@
std::string message;
std::vector<std::string> getvar_args(args.begin() + 2, args.end());
- if (!found_variable->second(device, getvar_args, &message)) {
+ if (!found_variable->second.get(device, getvar_args, &message)) {
return device->WriteFail(message);
}
return device->WriteOkay(message);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6ed6d32..55aca9c 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -137,3 +137,7 @@
bool FastbootDevice::WriteFail(const std::string& message) {
return WriteStatus(FastbootResult::FAIL, message);
}
+
+bool FastbootDevice::WriteInfo(const std::string& message) {
+ return WriteStatus(FastbootResult::INFO, message);
+}
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index addc2ef..171e7ae 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -39,9 +39,10 @@
bool HandleData(bool read, std::vector<char>* data);
std::string GetCurrentSlot();
- // Shortcuts for writing OKAY and FAIL status results.
+ // Shortcuts for writing status results.
bool WriteOkay(const std::string& message);
bool WriteFail(const std::string& message);
+ bool WriteInfo(const std::string& message);
std::vector<char>& download_data() { return download_data_; }
Transport* get_transport() { return transport_.get(); }
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index ec84576..0157e7f 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -16,6 +16,11 @@
#include "utility.h"
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
#include <android-base/logging.h>
#include <fs_mgr_dm_linear.h>
#include <liblp/liblp.h>
@@ -123,3 +128,33 @@
*number = slot[0] - 'a';
return true;
}
+
+std::vector<std::string> ListPartitions(FastbootDevice* device) {
+ std::vector<std::string> partitions;
+
+ // First get physical partitions.
+ struct dirent* de;
+ std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir("/dev/block/by-name"), closedir);
+ while ((de = readdir(by_name.get())) != nullptr) {
+ if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
+ continue;
+ }
+ struct stat s;
+ std::string path = "/dev/block/by-name/" + std::string(de->d_name);
+ if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {
+ partitions.emplace_back(de->d_name);
+ }
+ }
+
+ // Next get logical partitions.
+ if (auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME)) {
+ uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
+ if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
+ for (const auto& partition : metadata->partitions) {
+ std::string partition_name = GetPartitionName(partition);
+ partitions.emplace_back(partition_name);
+ }
+ }
+ }
+ return partitions;
+}
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 0931fc3..4f0d079 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -56,5 +56,5 @@
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool* is_zero_length = nullptr);
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
-
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+std::vector<std::string> ListPartitions(FastbootDevice* device);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 8eeda98..9f3fa75 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -222,3 +222,37 @@
*message = "yes";
return true;
}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {
+ std::vector<std::vector<std::string>> args;
+ auto partitions = ListPartitions(device);
+ for (const auto& partition : partitions) {
+ args.emplace_back(std::initializer_list<std::string>{partition});
+ }
+ return args;
+}
+
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device) {
+ auto partitions = ListPartitions(device);
+
+ std::string slot_suffix = device->GetCurrentSlot();
+ if (!slot_suffix.empty()) {
+ auto names = std::move(partitions);
+ for (const auto& name : names) {
+ std::string slotless_name = name;
+ if (android::base::EndsWith(name, "_a") || android::base::EndsWith(name, "_b")) {
+ slotless_name = name.substr(0, name.rfind("_"));
+ }
+ if (std::find(partitions.begin(), partitions.end(), slotless_name) ==
+ partitions.end()) {
+ partitions.emplace_back(slotless_name);
+ }
+ }
+ }
+
+ std::vector<std::vector<std::string>> args;
+ for (const auto& partition : partitions) {
+ args.emplace_back(std::initializer_list<std::string>{partition});
+ }
+ return args;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index b643bbd..c3a64cf 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -48,3 +48,7 @@
std::string* message);
bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,
std::string* message);
+
+// Helpers for getvar all.
+std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
+std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);