OSDN Git Service

dumpsys: switch to using a pipe to work around selinux denial.
[android-x86/frameworks-native.git] / cmds / dumpsys / dumpsys.cpp
1 /*
2  * Command that dumps interesting system state to the log.
3  *
4  */
5
6 #define LOG_TAG "dumpsys"
7
8 #include <algorithm>
9 #include <chrono>
10 #include <thread>
11
12 #include <android-base/file.h>
13 #include <android-base/unique_fd.h>
14 #include <binder/IServiceManager.h>
15 #include <binder/Parcel.h>
16 #include <binder/ProcessState.h>
17 #include <binder/TextOutput.h>
18 #include <utils/Log.h>
19 #include <utils/Vector.h>
20
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/poll.h>
27 #include <sys/socket.h>
28 #include <sys/time.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 using namespace android;
33 using android::base::unique_fd;
34 using android::base::WriteFully;
35
36 static int sort_func(const String16* lhs, const String16* rhs)
37 {
38     return lhs->compare(*rhs);
39 }
40
41 static void usage() {
42     fprintf(stderr,
43         "usage: dumpsys\n"
44             "         To dump all services.\n"
45             "or:\n"
46             "       dumpsys [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n"
47             "         --help: shows this help\n"
48             "         -l: only list services, do not dump them\n"
49             "         --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
50             "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
51 }
52
53 bool IsSkipped(const Vector<String16>& skipped, const String16& service) {
54     for (const auto& candidate : skipped) {
55         if (candidate == service) {
56             return true;
57         }
58     }
59     return false;
60 }
61
62 int main(int argc, char* const argv[])
63 {
64     signal(SIGPIPE, SIG_IGN);
65     sp<IServiceManager> sm = defaultServiceManager();
66     fflush(stdout);
67     if (sm == NULL) {
68         ALOGE("Unable to get default service manager!");
69         aerr << "dumpsys: Unable to get default service manager!" << endl;
70         return 20;
71     }
72
73     Vector<String16> services;
74     Vector<String16> args;
75     Vector<String16> skippedServices;
76     bool showListOnly = false;
77     if (argc == 2) {
78         // 1 argument: check for special cases (-l or --help)
79         if (strcmp(argv[1], "--help") == 0) {
80             usage();
81             return 0;
82         }
83         if (strcmp(argv[1], "-l") == 0) {
84             showListOnly = true;
85         }
86     }
87     if (argc == 3) {
88         // 2 arguments: check for special cases (--skip SERVICES)
89         if (strcmp(argv[1], "--skip") == 0) {
90             char* token = strtok(argv[2], ",");
91             while (token != NULL) {
92                 skippedServices.add(String16(token));
93                 token = strtok(NULL, ",");
94             }
95         }
96     }
97     bool dumpAll = argc == 1;
98     if (dumpAll || !skippedServices.empty() || showListOnly) {
99         // gets all services
100         services = sm->listServices();
101         services.sort(sort_func);
102         args.add(String16("-a"));
103     } else {
104         // gets a specific service:
105         // first check if its name is not a special argument...
106         if (strcmp(argv[1], "--skip") == 0 || strcmp(argv[1], "-l") == 0) {
107             usage();
108             return -1;
109         }
110         // ...then gets its arguments
111         services.add(String16(argv[1]));
112         for (int i=2; i<argc; i++) {
113             args.add(String16(argv[i]));
114         }
115     }
116
117     const size_t N = services.size();
118
119     if (N > 1) {
120         // first print a list of the current services
121         aout << "Currently running services:" << endl;
122
123         for (size_t i=0; i<N; i++) {
124             sp<IBinder> service = sm->checkService(services[i]);
125             if (service != NULL) {
126                 bool skipped = IsSkipped(skippedServices, services[i]);
127                 aout << "  " << services[i] << (skipped ? " (skipped)" : "") << endl;
128             }
129         }
130     }
131
132     if (showListOnly) {
133         return 0;
134     }
135
136     for (size_t i = 0; i < N; i++) {
137         String16 service_name = std::move(services[i]);
138         if (IsSkipped(skippedServices, service_name)) continue;
139
140         sp<IBinder> service = sm->checkService(service_name);
141         if (service != NULL) {
142             int sfd[2];
143
144             if (pipe(sfd) != 0) {
145                 aerr << "Failed to create pipe to dump service info for " << service_name
146                      << ": " << strerror(errno) << endl;
147                 continue;
148             }
149
150             unique_fd local_end(sfd[0]);
151             unique_fd remote_end(sfd[1]);
152             sfd[0] = sfd[1] = -1;
153
154             if (N > 1) {
155                 aout << "------------------------------------------------------------"
156                         "-------------------" << endl;
157                 aout << "DUMP OF SERVICE " << service_name << ":" << endl;
158             }
159
160             // dump blocks until completion, so spawn a thread..
161             std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
162                 int err = service->dump(remote_end.get(), args);
163
164                 // It'd be nice to be able to close the remote end of the socketpair before the dump
165                 // call returns, to terminate our reads if the other end closes their copy of the
166                 // file descriptor, but then hangs for some reason. There doesn't seem to be a good
167                 // way to do this, though.
168                 remote_end.clear();
169
170                 if (err != 0) {
171                     aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
172                          << endl;
173                 }
174             });
175
176             // TODO: Make this configurable at runtime.
177             constexpr auto timeout = std::chrono::seconds(10);
178             auto end = std::chrono::steady_clock::now() + timeout;
179
180             struct pollfd pfd = {
181                 .fd = local_end.get(),
182                 .events = POLLIN
183             };
184
185             bool timed_out = false;
186             bool error = false;
187             while (true) {
188                 // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout.
189                 auto time_left_ms = [end]() {
190                     auto now = std::chrono::steady_clock::now();
191                     auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now);
192                     return std::max(diff.count(), 0ll);
193                 };
194
195                 int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
196                 if (rc < 0) {
197                     aerr << "Error in poll while dumping service " << service_name << " : "
198                          << strerror(errno) << endl;
199                     error = true;
200                     break;
201                 } else if (rc == 0) {
202                     timed_out = true;
203                     break;
204                 }
205
206                 char buf[4096];
207                 rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf)));
208                 if (rc < 0) {
209                     aerr << "Failed to read while dumping service " << service_name << ": "
210                          << strerror(errno) << endl;
211                     error = true;
212                     break;
213                 } else if (rc == 0) {
214                     // EOF.
215                     break;
216                 }
217
218                 if (!WriteFully(STDOUT_FILENO, buf, rc)) {
219                     aerr << "Failed to write while dumping service " << service_name << ": "
220                          << strerror(errno) << endl;
221                     error = true;
222                     break;
223                 }
224             }
225
226             if (timed_out) {
227                 aout << endl << "*** SERVICE DUMP TIMEOUT EXPIRED ***" << endl << endl;
228             }
229
230             if (timed_out || error) {
231                 dump_thread.detach();
232             } else {
233                 dump_thread.join();
234             }
235         } else {
236             aerr << "Can't find service: " << service_name << endl;
237         }
238     }
239
240     return 0;
241 }