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
13 using android::pdx::default_transport::ClientChannelFactory;
14
15 namespace {
16
17 constexpr long kClientTimeoutMs = 0; // Don't wait for non-existent services.
18 constexpr int kDumpBufferSize = 2 * 4096; // Two pages.
19
20 class 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
34 bool option_verbose = false;
35
36 static 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
49 void HexDump(const void* pointer, size_t length);
50
ControlClient(const std::string & service_path,long timeout_ms)51 ControlClient::ControlClient(const std::string& service_path, long timeout_ms)
52 : BASE{ClientChannelFactory::Create(service_path), timeout_ms} {}
53
Reload()54 void 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
Dump()64 std::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
Usage(const std::string & command_name)88 int 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
98 typedef int (*CallbackType)(const char* path, const struct stat* sb,
99 int type_flag, FTW* ftw_buffer);
100
101 int ReloadCommandCallback(const char* path, const struct stat* sb,
102 int type_flag, FTW* ftw_buffer);
103 int DumpCommandCallback(const char* path, const struct stat* sb, int type_flag,
104 FTW* ftw_buffer);
105
CallOnAllFiles(CallbackType callback,const std::string & base_path)106 void 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
ReloadCommand(const std::string & service_path)111 int 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
DumpCommand(const std::string & service_path)131 int 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
ReloadCommandCallback(const char * path,const struct stat *,int type_flag,FTW *)158 int 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
DumpCommandCallback(const char * path,const struct stat *,int type_flag,FTW *)165 int 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
HexDump(const void * pointer,size_t length)172 void 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
main(int argc,char ** argv)207 int 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