blob: e953dedea23a6b4661e818f323a60a9edf95ba7c [file] [log] [blame]
Yifan Hongb0dde932017-02-10 17:49:58 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "Lshal.h"
18
19#include <getopt.h>
20
21#include <fstream>
22#include <iomanip>
23#include <iostream>
24#include <map>
25#include <sstream>
26#include <regex>
27
28#include <android-base/parseint.h>
29#include <android/hidl/manager/1.0/IServiceManager.h>
30#include <hidl/ServiceManagement.h>
Yifan Hong4b865492017-02-28 19:38:24 -080031#include <hidl-util/FQName.h>
32#include <vintf/HalManifest.h>
33#include <vintf/parse_xml.h>
Yifan Hongb0dde932017-02-10 17:49:58 -080034
Yifan Honge2dadf02017-02-14 15:43:31 -080035#include "Timeout.h"
36
Yifan Hongb0dde932017-02-10 17:49:58 -080037using ::android::hardware::hidl_string;
38using ::android::hidl::manager::V1_0::IServiceManager;
39
40namespace android {
41namespace lshal {
42
Yifan Hongb0dde932017-02-10 17:49:58 -080043template <typename A>
44std::string join(const A &components, const std::string &separator) {
45 std::stringstream out;
46 bool first = true;
47 for (const auto &component : components) {
48 if (!first) {
49 out << separator;
50 }
51 out << component;
52
53 first = false;
54 }
55 return out.str();
56}
57
58static std::string toHexString(uint64_t t) {
59 std::ostringstream os;
60 os << std::hex << std::setfill('0') << std::setw(16) << t;
61 return os.str();
62}
63
Yifan Hong4b865492017-02-28 19:38:24 -080064template<typename String>
65static std::pair<String, String> splitFirst(const String &s, char c) {
Yifan Hongb0dde932017-02-10 17:49:58 -080066 const char *pos = strchr(s.c_str(), c);
67 if (pos == nullptr) {
68 return {s, {}};
69 }
Yifan Hong4b865492017-02-28 19:38:24 -080070 return {String(s.c_str(), pos - s.c_str()), String(pos + 1)};
Yifan Hongb0dde932017-02-10 17:49:58 -080071}
72
73static std::vector<std::string> split(const std::string &s, char c) {
74 std::vector<std::string> components{};
75 size_t startPos = 0;
76 size_t matchPos;
77 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
78 components.push_back(s.substr(startPos, matchPos - startPos));
79 startPos = matchPos + 1;
80 }
81
82 if (startPos <= s.length()) {
83 components.push_back(s.substr(startPos));
84 }
85 return components;
86}
87
Yifan Hong4b865492017-02-28 19:38:24 -080088static void replaceAll(std::string *s, char from, char to) {
89 for (size_t i = 0; i < s->size(); ++i) {
90 if (s->at(i) == from) {
91 s->at(i) = to;
92 }
93 }
94}
95
Yifan Hongae09a3d2017-02-14 17:33:50 -080096std::string getCmdline(pid_t pid) {
97 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
98 std::string cmdline;
99 if (!ifs.is_open()) {
100 return "";
101 }
102 ifs >> cmdline;
103 return cmdline;
104}
105
106const std::string &Lshal::getCmdline(pid_t pid) {
107 auto pair = mCmdlines.find(pid);
108 if (pair != mCmdlines.end()) {
109 return pair->second;
110 }
111 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
112 return mCmdlines[pid];
113}
114
115void Lshal::removeDeadProcesses(Pids *pids) {
116 static const pid_t myPid = getpid();
117 std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
118 return pid == myPid || this->getCmdline(pid).empty();
119 });
120}
121
Yifan Hongb0dde932017-02-10 17:49:58 -0800122bool Lshal::getReferencedPids(
123 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
124
125 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
126 if (!ifs.is_open()) {
127 return false;
128 }
129
130 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
131
132 std::string line;
133 std::smatch match;
134 while(getline(ifs, line)) {
135 if (!std::regex_search(line, match, prefix)) {
136 // the line doesn't start with the correct prefix
137 continue;
138 }
139 std::string ptrString = "0x" + match.str(2); // use number after c
140 uint64_t ptr;
141 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
142 // Should not reach here, but just be tolerant.
143 mErr << "Could not parse number " << ptrString << std::endl;
144 continue;
145 }
146 const std::string proc = " proc ";
147 auto pos = line.rfind(proc);
148 if (pos != std::string::npos) {
149 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
150 int32_t pid;
151 if (!::android::base::ParseInt(pidStr, &pid)) {
152 mErr << "Could not parse number " << pidStr << std::endl;
153 continue;
154 }
155 (*objects)[ptr].push_back(pid);
156 }
157 }
158 }
159 return true;
160}
161
Yifan Hong38d53e02017-02-13 17:51:59 -0800162void Lshal::postprocess() {
163 if (mSortColumn) {
164 std::sort(mTable.begin(), mTable.end(), mSortColumn);
165 }
Yifan Hongae09a3d2017-02-14 17:33:50 -0800166 for (TableEntry &entry : mTable) {
167 entry.serverCmdline = getCmdline(entry.serverPid);
168 removeDeadProcesses(&entry.clientPids);
169 for (auto pid : entry.clientPids) {
170 entry.clientCmdlines.push_back(this->getCmdline(pid));
171 }
172 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800173}
174
175void Lshal::printLine(
176 const std::string &interfaceName,
177 const std::string &transport, const std::string &server,
Yifan Hongae09a3d2017-02-14 17:33:50 -0800178 const std::string &serverCmdline,
179 const std::string &address, const std::string &clients,
180 const std::string &clientCmdlines) const {
Yifan Hong38d53e02017-02-13 17:51:59 -0800181 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
182 mOut << std::setw(80) << interfaceName << "\t";
183 if (mSelectedColumns & ENABLE_TRANSPORT)
184 mOut << std::setw(10) << transport << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800185 if (mSelectedColumns & ENABLE_SERVER_PID) {
186 if (mEnableCmdlines) {
187 mOut << std::setw(15) << serverCmdline << "\t";
188 } else {
189 mOut << std::setw(5) << server << "\t";
190 }
191 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800192 if (mSelectedColumns & ENABLE_SERVER_ADDR)
193 mOut << std::setw(16) << address << "\t";
Yifan Hongae09a3d2017-02-14 17:33:50 -0800194 if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
195 if (mEnableCmdlines) {
196 mOut << std::setw(0) << clientCmdlines;
197 } else {
198 mOut << std::setw(0) << clients;
199 }
200 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800201 mOut << std::endl;
202}
203
Yifan Hong4b865492017-02-28 19:38:24 -0800204void Lshal::dumpVintf() const {
205 vintf::HalManifest manifest;
206 for (const TableEntry &entry : mTable) {
207
208 std::string fqInstanceName = entry.interfaceName;
209
210 if (entry.source == LIST_DLLIB) {
211 // Quick hack to work around *'s
212 replaceAll(&fqInstanceName, '*', 'D');
213 }
214 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
215 FQName fqName(splittedFqInstanceName.first);
216 if (!fqName.isValid()) {
217 mErr << "Warning: '" << splittedFqInstanceName.first
218 << "' is not a valid FQName." << std::endl;
219 continue;
220 }
221 // Strip out system libs.
222 // TODO(b/34772739): might want to add other framework HAL packages
223 if (fqName.inPackage("android.hidl")) {
224 continue;
225 }
226 std::string interfaceName =
227 entry.source == LIST_DLLIB ? "" : fqName.name();
228 std::string instanceName =
229 entry.source == LIST_DLLIB ? "" : splittedFqInstanceName.second;
230
231 vintf::Transport transport;
232 if (entry.transport == "hwbinder") {
233 transport = vintf::Transport::HWBINDER;
234 } else if (entry.transport == "passthrough") {
235 transport = vintf::Transport::PASSTHROUGH;
236 } else {
237 mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
238 continue;
239 }
240
241 vintf::ManifestHal *hal = manifest.getHal(fqName.package());
242 if (hal == nullptr) {
243 if (!manifest.add(vintf::ManifestHal{
244 .format = vintf::HalFormat::HIDL,
245 .name = fqName.package(),
246 .impl = {.implLevel = vintf::ImplLevel::GENERIC, .impl = ""},
247 .transport = transport
248 })) {
249 mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
250 continue;
251 }
252 hal = manifest.getHal(fqName.package());
253 }
254 if (hal == nullptr) {
255 mErr << "Warning: cannot get hal '" << fqInstanceName
256 << "' after adding it" << std::endl;
257 continue;
258 }
259 vintf::Version version{fqName.getPackageMajorVersion(), fqName.getPackageMinorVersion()};
260 if (std::find(hal->versions.begin(), hal->versions.end(), version) == hal->versions.end()) {
261 hal->versions.push_back(version);
262 }
263 if (entry.source != LIST_DLLIB) {
264 auto it = hal->interfaces.find(interfaceName);
265 if (it == hal->interfaces.end()) {
266 hal->interfaces.insert({interfaceName, {interfaceName, {{instanceName}}}});
267 } else {
268 it->second.instances.insert(instanceName);
269 }
270 }
271 }
272 mOut << vintf::gHalManifestConverter(manifest);
273}
274
275void Lshal::dumpTable() const {
Yifan Hongb0dde932017-02-10 17:49:58 -0800276 mOut << "All services:" << std::endl;
Yifan Hong38d53e02017-02-13 17:51:59 -0800277 mOut << std::left;
Yifan Hongae09a3d2017-02-14 17:33:50 -0800278 printLine("Interface", "Transport", "Server", "Server CMD", "PTR", "Clients", "Clients CMD");
Yifan Hongb0dde932017-02-10 17:49:58 -0800279 for (const auto &entry : mTable) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800280 printLine(entry.interfaceName,
Yifan Hongb0dde932017-02-10 17:49:58 -0800281 entry.transport,
282 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
Yifan Hongae09a3d2017-02-14 17:33:50 -0800283 entry.serverCmdline,
Yifan Hongb0dde932017-02-10 17:49:58 -0800284 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
Yifan Hongae09a3d2017-02-14 17:33:50 -0800285 join(entry.clientPids, " "),
286 join(entry.clientCmdlines, ";"));
Yifan Hongb0dde932017-02-10 17:49:58 -0800287 }
288}
289
Yifan Hong4b865492017-02-28 19:38:24 -0800290void Lshal::dump() {
291 if (mVintf) {
292 dumpVintf();
293 if (!!mFileOutput) {
294 mFileOutput.buf().close();
295 delete &mFileOutput.buf();
296 mFileOutput = nullptr;
297 }
298 mOut = std::cout;
299 } else {
300 dumpTable();
301 }
302}
303
Yifan Hongb0dde932017-02-10 17:49:58 -0800304void Lshal::putEntry(TableEntry &&entry) {
305 mTable.push_back(std::forward<TableEntry>(entry));
306}
307
308Status Lshal::fetchAllLibraries(const sp<IServiceManager> &manager) {
309 using namespace ::android::hardware;
310 using namespace ::android::hidl::manager::V1_0;
311 using namespace ::android::hidl::base::V1_0;
Yifan Honge2dadf02017-02-14 15:43:31 -0800312 auto ret = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &fqInstanceNames) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800313 for (const auto &fqInstanceName : fqInstanceNames) {
314 putEntry({
315 .interfaceName = fqInstanceName,
316 .transport = "passthrough",
317 .serverPid = NO_PID,
318 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800319 .clientPids = {},
320 .source = LIST_DLLIB
Yifan Hongb0dde932017-02-10 17:49:58 -0800321 });
322 }
323 });
324 if (!ret.isOk()) {
325 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
326 << ret.description() << std::endl;
327 return DUMP_ALL_LIBS_ERROR;
328 }
329 return OK;
330}
331
332Status Lshal::fetchPassthrough(const sp<IServiceManager> &manager) {
333 using namespace ::android::hardware;
334 using namespace ::android::hidl::manager::V1_0;
335 using namespace ::android::hidl::base::V1_0;
Yifan Honge2dadf02017-02-14 15:43:31 -0800336 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
Yifan Hongb0dde932017-02-10 17:49:58 -0800337 for (const auto &info : infos) {
338 putEntry({
339 .interfaceName =
340 std::string{info.interfaceName.c_str()} + "/" +
341 std::string{info.instanceName.c_str()},
342 .transport = "passthrough",
343 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
344 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800345 .clientPids = info.clientPids,
346 .source = PTSERVICEMANAGER_REG_CLIENT
Yifan Hongb0dde932017-02-10 17:49:58 -0800347 });
348 }
349 });
350 if (!ret.isOk()) {
351 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
352 << ret.description() << std::endl;
353 return DUMP_PASSTHROUGH_ERROR;
354 }
355 return OK;
356}
357
358Status Lshal::fetchBinderized(const sp<IServiceManager> &manager) {
359 using namespace ::std;
360 using namespace ::android::hardware;
361 using namespace ::android::hidl::manager::V1_0;
362 using namespace ::android::hidl::base::V1_0;
363 const std::string mode = "hwbinder";
Yifan Hongb0dde932017-02-10 17:49:58 -0800364
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800365 hidl_vec<hidl_string> fqInstanceNames;
366 // copying out for timeoutIPC
367 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
368 fqInstanceNames = names;
Yifan Hongb0dde932017-02-10 17:49:58 -0800369 });
370 if (!listRet.isOk()) {
371 mErr << "Error: Failed to list services for " << mode << ": "
372 << listRet.description() << std::endl;
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800373 return DUMP_BINDERIZED_ERROR;
374 }
375
376 Status status = OK;
377 // server pid, .ptr value of binder object, child pids
378 std::map<std::string, DebugInfo> allDebugInfos;
379 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
380 for (const auto &fqInstanceName : fqInstanceNames) {
Yifan Hong4b865492017-02-28 19:38:24 -0800381 const auto pair = splitFirst(fqInstanceName, '/');
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800382 const auto &serviceName = pair.first;
383 const auto &instanceName = pair.second;
384 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
385 if (!getRet.isOk()) {
386 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
387 << "cannot be fetched from service manager:"
388 << getRet.description() << std::endl;
389 status |= DUMP_BINDERIZED_ERROR;
390 continue;
391 }
392 sp<IBase> service = getRet;
393 if (service == nullptr) {
394 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
395 << "cannot be fetched from service manager (null)";
396 status |= DUMP_BINDERIZED_ERROR;
397 continue;
398 }
399 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
400 allDebugInfos[fqInstanceName] = debugInfo;
401 if (debugInfo.pid >= 0) {
402 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
403 }
404 });
405 if (!debugRet.isOk()) {
406 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
407 << "debugging information cannot be retrieved:"
408 << debugRet.description() << std::endl;
409 status |= DUMP_BINDERIZED_ERROR;
410 }
411 }
412 for (auto &pair : allPids) {
413 pid_t serverPid = pair.first;
414 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
415 mErr << "Warning: no information for PID " << serverPid
416 << ", are you root?" << std::endl;
417 status |= DUMP_BINDERIZED_ERROR;
418 }
419 }
420 for (const auto &fqInstanceName : fqInstanceNames) {
421 auto it = allDebugInfos.find(fqInstanceName);
422 if (it == allDebugInfos.end()) {
423 putEntry({
424 .interfaceName = fqInstanceName,
425 .transport = mode,
426 .serverPid = NO_PID,
427 .serverObjectAddress = NO_PTR,
Yifan Hong4b865492017-02-28 19:38:24 -0800428 .clientPids = {},
429 .source = HWSERVICEMANAGER_LIST
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800430 });
431 continue;
432 }
433 const DebugInfo &info = it->second;
434 putEntry({
435 .interfaceName = fqInstanceName,
436 .transport = mode,
437 .serverPid = info.pid,
438 .serverObjectAddress = info.ptr,
439 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
Yifan Hong4b865492017-02-28 19:38:24 -0800440 ? Pids{} : allPids[info.pid][info.ptr],
441 .source = HWSERVICEMANAGER_LIST
Steven Moreland9b5c15d2017-02-28 17:52:58 -0800442 });
Yifan Hongb0dde932017-02-10 17:49:58 -0800443 }
444 return status;
445}
446
447Status Lshal::fetch() {
448 Status status = OK;
449 auto bManager = ::android::hardware::defaultServiceManager();
450 if (bManager == nullptr) {
451 mErr << "Failed to get defaultServiceManager()!" << std::endl;
452 status |= NO_BINDERIZED_MANAGER;
453 } else {
454 status |= fetchBinderized(bManager);
455 // Passthrough PIDs are registered to the binderized manager as well.
456 status |= fetchPassthrough(bManager);
457 }
458
459 auto pManager = ::android::hardware::getPassthroughServiceManager();
460 if (pManager == nullptr) {
461 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
462 status |= NO_PASSTHROUGH_MANAGER;
463 } else {
464 status |= fetchAllLibraries(pManager);
465 }
466 return status;
467}
468
469void Lshal::usage() const {
470 mErr
471 << "usage: lshal" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800472 << " Dump all hals with default ordering and columns [-itpc]." << std::endl
473 << " lshal [--interface|-i] [--transport|-t]" << std::endl
474 << " [--pid|-p] [--address|-a] [--clients|-c] [--cmdline|-m]" << std::endl
Yifan Hong4b865492017-02-28 19:38:24 -0800475 << " [--sort={interface|i|pid|p}] [--init-vintf[=path]]" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800476 << " -i, --interface: print the interface name column" << std::endl
477 << " -n, --instance: print the instance name column" << std::endl
478 << " -t, --transport: print the transport mode column" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800479 << " -p, --pid: print the server PID, or server cmdline if -m is set" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800480 << " -a, --address: print the server object address column" << std::endl
Yifan Hongae09a3d2017-02-14 17:33:50 -0800481 << " -c, --clients: print the client PIDs, or client cmdlines if -m is set"
482 << std::endl
483 << " -m, --cmdline: print cmdline instead of PIDs" << std::endl
Yifan Hong38d53e02017-02-13 17:51:59 -0800484 << " --sort=i, --sort=interface: sort by interface name" << std::endl
485 << " --sort=p, --sort=pid: sort by server pid" << std::endl
Yifan Hong4b865492017-02-28 19:38:24 -0800486 << " --init-vintf=path: form a skeleton HAL manifest to specified file " << std::endl
487 << " (stdout if no file specified)" << std::endl
Yifan Hongb0dde932017-02-10 17:49:58 -0800488 << " lshal [-h|--help]" << std::endl
489 << " -h, --help: show this help information." << std::endl;
490}
491
492Status Lshal::parseArgs(int argc, char **argv) {
493 static struct option longOptions[] = {
Yifan Hong38d53e02017-02-13 17:51:59 -0800494 // long options with short alternatives
495 {"help", no_argument, 0, 'h' },
496 {"interface", no_argument, 0, 'i' },
497 {"transport", no_argument, 0, 't' },
498 {"pid", no_argument, 0, 'p' },
499 {"address", no_argument, 0, 'a' },
500 {"clients", no_argument, 0, 'c' },
Yifan Hongae09a3d2017-02-14 17:33:50 -0800501 {"cmdline", no_argument, 0, 'm' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800502
503 // long options without short alternatives
504 {"sort", required_argument, 0, 's' },
Yifan Hong4b865492017-02-28 19:38:24 -0800505 {"init-vintf",optional_argument, 0, 'v' },
Yifan Hong38d53e02017-02-13 17:51:59 -0800506 { 0, 0, 0, 0 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800507 };
508
509 int optionIndex;
510 int c;
511 optind = 1;
512 for (;;) {
513 // using getopt_long in case we want to add other options in the future
Yifan Hongae09a3d2017-02-14 17:33:50 -0800514 c = getopt_long(argc, argv, "hitpacm", longOptions, &optionIndex);
Yifan Hongb0dde932017-02-10 17:49:58 -0800515 if (c == -1) {
516 break;
517 }
518 switch (c) {
Yifan Hong38d53e02017-02-13 17:51:59 -0800519 case 's': {
520 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
521 mSortColumn = TableEntry::sortByInterfaceName;
522 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
523 mSortColumn = TableEntry::sortByServerPid;
524 } else {
525 mErr << "Unrecognized sorting column: " << optarg << std::endl;
526 usage();
527 return USAGE;
528 }
529 break;
530 }
Yifan Hong4b865492017-02-28 19:38:24 -0800531 case 'v': {
532 if (optarg) {
533 mFileOutput = new std::ofstream{optarg};
534 mOut = mFileOutput;
535 if (!mFileOutput.buf().is_open()) {
536 mErr << "Could not open file '" << optarg << "'." << std::endl;
537 return IO_ERROR;
538 }
539 }
540 mVintf = true;
541 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800542 case 'i': {
543 mSelectedColumns |= ENABLE_INTERFACE_NAME;
544 break;
545 }
546 case 't': {
547 mSelectedColumns |= ENABLE_TRANSPORT;
548 break;
549 }
550 case 'p': {
551 mSelectedColumns |= ENABLE_SERVER_PID;
552 break;
553 }
554 case 'a': {
555 mSelectedColumns |= ENABLE_SERVER_ADDR;
556 break;
557 }
558 case 'c': {
559 mSelectedColumns |= ENABLE_CLIENT_PIDS;
560 break;
561 }
Yifan Hongae09a3d2017-02-14 17:33:50 -0800562 case 'm': {
563 mEnableCmdlines = true;
564 break;
565 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800566 case 'h': // falls through
567 default: // see unrecognized options
568 usage();
569 return USAGE;
570 }
571 }
Yifan Hong38d53e02017-02-13 17:51:59 -0800572
573 if (mSelectedColumns == 0) {
574 mSelectedColumns = ENABLE_INTERFACE_NAME
575 | ENABLE_TRANSPORT | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
576 }
Yifan Hongb0dde932017-02-10 17:49:58 -0800577 return OK;
578}
579
580int Lshal::main(int argc, char **argv) {
581 Status status = parseArgs(argc, argv);
582 if (status != OK) {
583 return status;
584 }
585 status = fetch();
Yifan Hong38d53e02017-02-13 17:51:59 -0800586 postprocess();
Yifan Hongb0dde932017-02-10 17:49:58 -0800587 dump();
588 return status;
589}
590
Yifan Honga57dffb2017-02-21 14:59:00 -0800591void signalHandler(int sig) {
592 if (sig == SIGINT) {
593 int retVal;
594 pthread_exit(&retVal);
595 }
596}
597
Yifan Hongb0dde932017-02-10 17:49:58 -0800598} // namespace lshal
599} // namespace android
600
601int main(int argc, char **argv) {
Yifan Honga57dffb2017-02-21 14:59:00 -0800602 signal(SIGINT, ::android::lshal::signalHandler);
Yifan Hongb0dde932017-02-10 17:49:58 -0800603 return ::android::lshal::Lshal{}.main(argc, argv);
604}