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