1#include <errno.h> 2#include <ftw.h> 3#include <getopt.h> 4#include <pdx/client.h> 5#include <pdx/service.h> 6#include <sys/stat.h> 7 8#include <algorithm> 9#include <vector> 10 11#include <pdx/default_transport/client_channel_factory.h> 12 13using android::pdx::default_transport::ClientChannelFactory; 14 15namespace { 16 17constexpr long kClientTimeoutMs = 0; // Don't wait for non-existent services. 18constexpr int kDumpBufferSize = 2 * 4096; // Two pages. 19 20class ControlClient : public android::pdx::ClientBase<ControlClient> { 21 public: 22 explicit ControlClient(const std::string& service_path, long timeout_ms); 23 24 void Reload(); 25 std::string Dump(); 26 27 private: 28 friend BASE; 29 30 ControlClient(const ControlClient&) = delete; 31 void operator=(const ControlClient&) = delete; 32}; 33 34bool option_verbose = false; 35 36static struct option long_options[] = { 37 {"reload", required_argument, 0, 0}, 38 {"dump", required_argument, 0, 0}, 39 {"verbose", no_argument, 0, 0}, 40 {0, 0, 0, 0}, 41}; 42 43#define printf_verbose(fmt, ... /*args*/) \ 44 do { \ 45 if (option_verbose) \ 46 printf(fmt, ##__VA_ARGS__); \ 47 } while (0) 48 49void HexDump(const void* pointer, size_t length); 50 51ControlClient::ControlClient(const std::string& service_path, long timeout_ms) 52 : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {} 53 54void ControlClient::Reload() { 55 android::pdx::Transaction trans{*this}; 56 auto status = trans.Send<void>(android::pdx::opcodes::REPORT_SYSPROP_CHANGE, 57 nullptr, 0, nullptr, 0); 58 if (!status) { 59 fprintf(stderr, "Failed to send reload: %s\n", 60 status.GetErrorMessage().c_str()); 61 } 62} 63 64std::string ControlClient::Dump() { 65 android::pdx::Transaction trans{*this}; 66 std::vector<char> buffer(kDumpBufferSize); 67 auto status = trans.Send<int>(android::pdx::opcodes::DUMP_STATE, nullptr, 0, 68 buffer.data(), buffer.size()); 69 70 printf_verbose("ControlClient::Dump: ret=%d\n", ReturnStatusOrError(status)); 71 72 if (!status) { 73 fprintf(stderr, "Failed to send dump request: %s\n", 74 status.GetErrorMessage().c_str()); 75 return ""; 76 } else if (status.get() > static_cast<ssize_t>(buffer.capacity())) { 77 fprintf(stderr, "Service returned a larger size than requested: %d\n", 78 status.get()); 79 return ""; 80 } 81 82 if (option_verbose) 83 HexDump(buffer.data(), status.get()); 84 85 return std::string(buffer.data(), status.get()); 86} 87 88int Usage(const std::string& command_name) { 89 printf("Usage: %s [options]\n", command_name.c_str()); 90 printf("\t--verbose : Use verbose messages.\n"); 91 printf( 92 "\t--reload <all | service path> : Ask service(s) to reload system " 93 "properties.\n"); 94 printf("\t--dump <all | service path> : Dump service(s) state.\n"); 95 return -1; 96} 97 98typedef int (*CallbackType)(const char* path, const struct stat* sb, 99 int type_flag, FTW* ftw_buffer); 100 101int ReloadCommandCallback(const char* path, const struct stat* sb, 102 int type_flag, FTW* ftw_buffer); 103int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag, 104 FTW* ftw_buffer); 105 106void CallOnAllFiles(CallbackType callback, const std::string& base_path) { 107 const int kMaxDepth = 32; 108 nftw(base_path.c_str(), callback, kMaxDepth, FTW_PHYS); 109} 110 111int ReloadCommand(const std::string& service_path) { 112 printf_verbose("ReloadCommand: service_path=%s\n", service_path.c_str()); 113 114 if (service_path == "" || service_path == "all") { 115 CallOnAllFiles(ReloadCommandCallback, 116 ClientChannelFactory::GetRootEndpointPath()); 117 return 0; 118 } else { 119 auto client = ControlClient::Create(service_path, kClientTimeoutMs); 120 if (!client) { 121 fprintf(stderr, "Failed to open service at \"%s\".\n", 122 service_path.c_str()); 123 return -1; 124 } 125 126 client->Reload(); 127 return 0; 128 } 129} 130 131int DumpCommand(const std::string& service_path) { 132 printf_verbose("DumpCommand: service_path=%s\n", service_path.c_str()); 133 134 if (service_path == "" || service_path == "all") { 135 CallOnAllFiles(DumpCommandCallback, 136 ClientChannelFactory::GetRootEndpointPath()); 137 return 0; 138 } else { 139 auto client = ControlClient::Create(service_path, kClientTimeoutMs); 140 if (!client) { 141 fprintf(stderr, "Failed to open service at \"%s\".\n", 142 service_path.c_str()); 143 return -1; 144 } 145 146 std::string response = client->Dump(); 147 if (!response.empty()) { 148 printf( 149 "--------------------------------------------------------------------" 150 "---\n"); 151 printf("%s:\n", service_path.c_str()); 152 printf("%s\n", response.c_str()); 153 } 154 return 0; 155 } 156} 157 158int ReloadCommandCallback(const char* path, const struct stat*, int type_flag, 159 FTW*) { 160 if (type_flag == FTW_F) 161 ReloadCommand(path); 162 return 0; 163} 164 165int DumpCommandCallback(const char* path, const struct stat*, int type_flag, 166 FTW*) { 167 if (type_flag == FTW_F) 168 DumpCommand(path); 169 return 0; 170} 171 172void HexDump(const void* pointer, size_t length) { 173 uintptr_t address = reinterpret_cast<uintptr_t>(pointer); 174 175 for (size_t count = 0; count < length; count += 16, address += 16) { 176 printf("0x%08lx: ", static_cast<unsigned long>(address)); 177 178 for (size_t i = 0; i < 16u; i++) { 179 if (i < std::min(length - count, static_cast<size_t>(16))) { 180 printf("%02x ", *reinterpret_cast<const uint8_t*>(address + i)); 181 } else { 182 printf(" "); 183 } 184 } 185 186 printf("|"); 187 188 for (size_t i = 0; i < 16u; i++) { 189 if (i < std::min(length - count, static_cast<size_t>(16))) { 190 char c = *reinterpret_cast<const char*>(address + i); 191 if (isalnum(c) || c == ' ') { 192 printf("%c", c); 193 } else { 194 printf("."); 195 } 196 } else { 197 printf(" "); 198 } 199 } 200 201 printf("|\n"); 202 } 203} 204 205} // anonymous namespace 206 207int main(int argc, char** argv) { 208 int getopt_code; 209 int option_index; 210 std::string option = ""; 211 std::string command = ""; 212 std::string command_argument = ""; 213 214 // Process command line options. 215 while ((getopt_code = 216 getopt_long(argc, argv, "", long_options, &option_index)) != -1) { 217 option = long_options[option_index].name; 218 printf_verbose("option=%s\n", option.c_str()); 219 switch (getopt_code) { 220 case 0: 221 if (option == "verbose") { 222 option_verbose = true; 223 } else { 224 command = option; 225 if (optarg) 226 command_argument = optarg; 227 } 228 break; 229 } 230 } 231 232 printf_verbose("command=%s command_argument=%s\n", command.c_str(), 233 command_argument.c_str()); 234 235 if (command == "") { 236 return Usage(argv[0]); 237 } else if (command == "reload") { 238 return ReloadCommand(command_argument); 239 } else if (command == "dump") { 240 return DumpCommand(command_argument); 241 } else { 242 return Usage(argv[0]); 243 } 244} 245