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