blob: a0ed7a346f2fbb0b42bd799e5ca5c7ab0a398af9 [file] [log] [blame]
Yifan Hong443df792017-05-09 18:49:45 -07001/*
2 * Copyright (C) 2017 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 "ListCommand.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>
31#include <hidl-util/FQName.h>
32#include <private/android_filesystem_config.h>
33#include <sys/stat.h>
34#include <vintf/HalManifest.h>
35#include <vintf/parse_xml.h>
36
37#include "Lshal.h"
38#include "PipeRelay.h"
39#include "Timeout.h"
40#include "utils.h"
41
42using ::android::hardware::hidl_string;
43using ::android::hidl::manager::V1_0::IServiceManager;
44
45namespace android {
46namespace lshal {
47
48ListCommand::ListCommand(Lshal &lshal) : mLshal(lshal), mErr(lshal.err()), mOut(lshal.out()) {
49}
50
51std::string getCmdline(pid_t pid) {
52 std::ifstream ifs("/proc/" + std::to_string(pid) + "/cmdline");
53 std::string cmdline;
54 if (!ifs.is_open()) {
55 return "";
56 }
57 ifs >> cmdline;
58 return cmdline;
59}
60
61const std::string &ListCommand::getCmdline(pid_t pid) {
62 auto pair = mCmdlines.find(pid);
63 if (pair != mCmdlines.end()) {
64 return pair->second;
65 }
66 mCmdlines[pid] = ::android::lshal::getCmdline(pid);
67 return mCmdlines[pid];
68}
69
70void ListCommand::removeDeadProcesses(Pids *pids) {
71 static const pid_t myPid = getpid();
72 std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
73 return pid == myPid || this->getCmdline(pid).empty();
74 });
75}
76
77bool ListCommand::getReferencedPids(
78 pid_t serverPid, std::map<uint64_t, Pids> *objects) const {
79
80 std::ifstream ifs("/d/binder/proc/" + std::to_string(serverPid));
81 if (!ifs.is_open()) {
82 return false;
83 }
84
85 static const std::regex prefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
86
87 std::string line;
88 std::smatch match;
89 while(getline(ifs, line)) {
90 if (!std::regex_search(line, match, prefix)) {
91 // the line doesn't start with the correct prefix
92 continue;
93 }
94 std::string ptrString = "0x" + match.str(2); // use number after c
95 uint64_t ptr;
96 if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
97 // Should not reach here, but just be tolerant.
98 mErr << "Could not parse number " << ptrString << std::endl;
99 continue;
100 }
101 const std::string proc = " proc ";
102 auto pos = line.rfind(proc);
103 if (pos != std::string::npos) {
104 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
105 int32_t pid;
106 if (!::android::base::ParseInt(pidStr, &pid)) {
107 mErr << "Could not parse number " << pidStr << std::endl;
108 continue;
109 }
110 (*objects)[ptr].push_back(pid);
111 }
112 }
113 }
114 return true;
115}
116
117// Must process hwbinder services first, then passthrough services.
118void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
119 f(mServicesTable);
120 f(mPassthroughRefTable);
121 f(mImplementationsTable);
122}
123void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
124 f(mServicesTable);
125 f(mPassthroughRefTable);
126 f(mImplementationsTable);
127}
128
129void ListCommand::postprocess() {
130 forEachTable([this](Table &table) {
131 if (mSortColumn) {
132 std::sort(table.begin(), table.end(), mSortColumn);
133 }
134 for (TableEntry &entry : table) {
135 entry.serverCmdline = getCmdline(entry.serverPid);
136 removeDeadProcesses(&entry.clientPids);
137 for (auto pid : entry.clientPids) {
138 entry.clientCmdlines.push_back(this->getCmdline(pid));
139 }
140 }
141 });
142 // use a double for loop here because lshal doesn't care about efficiency.
143 for (TableEntry &packageEntry : mImplementationsTable) {
144 std::string packageName = packageEntry.interfaceName;
145 FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
146 if (!fqPackageName.isValid()) {
147 continue;
148 }
149 for (TableEntry &interfaceEntry : mPassthroughRefTable) {
150 if (interfaceEntry.arch != ARCH_UNKNOWN) {
151 continue;
152 }
153 FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
154 if (!interfaceName.isValid()) {
155 continue;
156 }
157 if (interfaceName.getPackageAndVersion() == fqPackageName) {
158 interfaceEntry.arch = packageEntry.arch;
159 }
160 }
161 }
162}
163
164void ListCommand::printLine(
165 const std::string &interfaceName,
166 const std::string &transport,
167 const std::string &arch,
168 const std::string &server,
169 const std::string &serverCmdline,
170 const std::string &address, const std::string &clients,
171 const std::string &clientCmdlines) const {
172 if (mSelectedColumns & ENABLE_INTERFACE_NAME)
173 mOut << std::setw(80) << interfaceName << "\t";
174 if (mSelectedColumns & ENABLE_TRANSPORT)
175 mOut << std::setw(10) << transport << "\t";
176 if (mSelectedColumns & ENABLE_ARCH)
177 mOut << std::setw(5) << arch << "\t";
178 if (mSelectedColumns & ENABLE_SERVER_PID) {
179 if (mEnableCmdlines) {
180 mOut << std::setw(15) << serverCmdline << "\t";
181 } else {
182 mOut << std::setw(5) << server << "\t";
183 }
184 }
185 if (mSelectedColumns & ENABLE_SERVER_ADDR)
186 mOut << std::setw(16) << address << "\t";
187 if (mSelectedColumns & ENABLE_CLIENT_PIDS) {
188 if (mEnableCmdlines) {
189 mOut << std::setw(0) << clientCmdlines;
190 } else {
191 mOut << std::setw(0) << clients;
192 }
193 }
194 mOut << std::endl;
195}
196
197void ListCommand::dumpVintf() const {
198 mOut << "<!-- " << std::endl
199 << " This is a skeleton device manifest. Notes: " << std::endl
200 << " 1. android.hidl.*, android.frameworks.*, android.system.* are not included." << std::endl
201 << " 2. If a HAL is supported in both hwbinder and passthrough transport, " << std::endl
202 << " only hwbinder is shown." << std::endl
203 << " 3. It is likely that HALs in passthrough transport does not have" << std::endl
204 << " <interface> declared; users will have to write them by hand." << std::endl
205 << " 4. sepolicy version is set to 0.0. It is recommended that the entry" << std::endl
206 << " is removed from the manifest file and written by assemble_vintf" << std::endl
207 << " at build time." << std::endl
208 << "-->" << std::endl;
209
210 vintf::HalManifest manifest;
211 forEachTable([this, &manifest] (const Table &table) {
212 for (const TableEntry &entry : table) {
213
214 std::string fqInstanceName = entry.interfaceName;
215
216 if (&table == &mImplementationsTable) {
217 // Quick hack to work around *'s
218 replaceAll(&fqInstanceName, '*', 'D');
219 }
220 auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
221 FQName fqName(splittedFqInstanceName.first);
222 if (!fqName.isValid()) {
223 mErr << "Warning: '" << splittedFqInstanceName.first
224 << "' is not a valid FQName." << std::endl;
225 continue;
226 }
227 // Strip out system libs.
228 if (fqName.inPackage("android.hidl") ||
229 fqName.inPackage("android.frameworks") ||
230 fqName.inPackage("android.system")) {
231 continue;
232 }
233 std::string interfaceName =
234 &table == &mImplementationsTable ? "" : fqName.name();
235 std::string instanceName =
236 &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
237
238 vintf::Version version{fqName.getPackageMajorVersion(),
239 fqName.getPackageMinorVersion()};
240 vintf::Transport transport;
241 vintf::Arch arch;
242 if (entry.transport == "hwbinder") {
243 transport = vintf::Transport::HWBINDER;
244 arch = vintf::Arch::ARCH_EMPTY;
245 } else if (entry.transport == "passthrough") {
246 transport = vintf::Transport::PASSTHROUGH;
247 switch (entry.arch) {
248 case lshal::ARCH32:
249 arch = vintf::Arch::ARCH_32; break;
250 case lshal::ARCH64:
251 arch = vintf::Arch::ARCH_64; break;
252 case lshal::ARCH_BOTH:
253 arch = vintf::Arch::ARCH_32_64; break;
254 case lshal::ARCH_UNKNOWN: // fallthrough
255 default:
256 mErr << "Warning: '" << fqName.package()
257 << "' doesn't have bitness info, assuming 32+64." << std::endl;
258 arch = vintf::Arch::ARCH_32_64;
259 }
260 } else {
261 mErr << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
262 continue;
263 }
264
265 bool done = false;
266 for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
267 if (hal->transport() != transport) {
268 if (transport != vintf::Transport::PASSTHROUGH) {
269 mErr << "Fatal: should not reach here. Generated result may be wrong."
270 << std::endl;
271 }
272 done = true;
273 break;
274 }
275 if (hal->hasVersion(version)) {
276 if (&table != &mImplementationsTable) {
277 hal->interfaces[interfaceName].name = interfaceName;
278 hal->interfaces[interfaceName].instances.insert(instanceName);
279 }
280 done = true;
281 break;
282 }
283 }
284 if (done) {
285 continue; // to next TableEntry
286 }
287 decltype(vintf::ManifestHal::interfaces) interfaces;
288 if (&table != &mImplementationsTable) {
289 interfaces[interfaceName].name = interfaceName;
290 interfaces[interfaceName].instances.insert(instanceName);
291 }
292 if (!manifest.add(vintf::ManifestHal{
293 .format = vintf::HalFormat::HIDL,
294 .name = fqName.package(),
295 .versions = {version},
296 .transportArch = {transport, arch},
297 .interfaces = interfaces})) {
298 mErr << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
299 }
300 }
301 });
302 mOut << vintf::gHalManifestConverter(manifest);
303}
304
305static const std::string &getArchString(Architecture arch) {
306 static const std::string sStr64 = "64";
307 static const std::string sStr32 = "32";
308 static const std::string sStrBoth = "32+64";
309 static const std::string sStrUnknown = "";
310 switch (arch) {
311 case ARCH64:
312 return sStr64;
313 case ARCH32:
314 return sStr32;
315 case ARCH_BOTH:
316 return sStrBoth;
317 case ARCH_UNKNOWN: // fall through
318 default:
319 return sStrUnknown;
320 }
321}
322
323static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
324 switch (a) {
325 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
326 return ARCH64;
327 case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
328 return ARCH32;
329 case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
330 default:
331 return ARCH_UNKNOWN;
332 }
333}
334
335void ListCommand::dumpTable() {
336 mServicesTable.description =
337 "All binderized services (registered services through hwservicemanager)";
338 mPassthroughRefTable.description =
339 "All interfaces that getService() has ever return as a passthrough interface;\n"
340 "PIDs / processes shown below might be inaccurate because the process\n"
341 "might have relinquished the interface or might have died.\n"
342 "The Server / Server CMD column can be ignored.\n"
343 "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
344 "the library and successfully fetched the passthrough implementation.";
345 mImplementationsTable.description =
346 "All available passthrough implementations (all -impl.so files)";
347 forEachTable([this] (const Table &table) {
348 mOut << table.description << std::endl;
349 mOut << std::left;
350 printLine("Interface", "Transport", "Arch", "Server", "Server CMD",
351 "PTR", "Clients", "Clients CMD");
352
Yifan Hong443df792017-05-09 18:49:45 -0700353 for (const auto &entry : table) {
354 printLine(entry.interfaceName,
355 entry.transport,
356 getArchString(entry.arch),
357 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
358 entry.serverCmdline,
359 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
360 join(entry.clientPids, " "),
361 join(entry.clientCmdlines, ";"));
362
Yifan Hong48dc9f82017-05-09 19:33:08 -0700363 // We're only interested in dumping debug info for already
364 // instantiated services. There's little value in dumping the
365 // debug info for a service we create on the fly, so we only operate
366 // on the "mServicesTable".
367 if (mEmitDebugInfo && &table == &mServicesTable) {
Yifan Hong443df792017-05-09 18:49:45 -0700368 auto pair = splitFirst(entry.interfaceName, '/');
Yifan Hong48dc9f82017-05-09 19:33:08 -0700369 mLshal.emitDebugInfo(pair.first, pair.second, {}, mOut.buf(),
370 NullableOStream<std::ostream>(nullptr));
Yifan Hong443df792017-05-09 18:49:45 -0700371 }
372 }
373 mOut << std::endl;
374 });
375
376}
377
378void ListCommand::dump() {
379 if (mVintf) {
380 dumpVintf();
381 if (!!mFileOutput) {
382 mFileOutput.buf().close();
383 delete &mFileOutput.buf();
384 mFileOutput = nullptr;
385 }
386 mOut = std::cout;
387 } else {
388 dumpTable();
389 }
390}
391
392void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
393 Table *table = nullptr;
394 switch (source) {
395 case HWSERVICEMANAGER_LIST :
396 table = &mServicesTable; break;
397 case PTSERVICEMANAGER_REG_CLIENT :
398 table = &mPassthroughRefTable; break;
399 case LIST_DLLIB :
400 table = &mImplementationsTable; break;
401 default:
402 mErr << "Error: Unknown source of entry " << source << std::endl;
403 }
404 if (table) {
405 table->entries.push_back(std::forward<TableEntry>(entry));
406 }
407}
408
409Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
410 using namespace ::android::hardware;
411 using namespace ::android::hidl::manager::V1_0;
412 using namespace ::android::hidl::base::V1_0;
413 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
414 std::map<std::string, TableEntry> entries;
415 for (const auto &info : infos) {
416 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
417 std::string{info.instanceName.c_str()};
418 entries.emplace(interfaceName, TableEntry{
419 .interfaceName = interfaceName,
420 .transport = "passthrough",
421 .serverPid = NO_PID,
422 .serverObjectAddress = NO_PTR,
423 .clientPids = {},
424 .arch = ARCH_UNKNOWN
425 }).first->second.arch |= fromBaseArchitecture(info.arch);
426 }
427 for (auto &&pair : entries) {
428 putEntry(LIST_DLLIB, std::move(pair.second));
429 }
430 });
431 if (!ret.isOk()) {
432 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
433 << ret.description() << std::endl;
434 return DUMP_ALL_LIBS_ERROR;
435 }
436 return OK;
437}
438
439Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
440 using namespace ::android::hardware;
441 using namespace ::android::hardware::details;
442 using namespace ::android::hidl::manager::V1_0;
443 using namespace ::android::hidl::base::V1_0;
444 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
445 for (const auto &info : infos) {
446 if (info.clientPids.size() <= 0) {
447 continue;
448 }
449 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
450 .interfaceName =
451 std::string{info.interfaceName.c_str()} + "/" +
452 std::string{info.instanceName.c_str()},
453 .transport = "passthrough",
454 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
455 .serverObjectAddress = NO_PTR,
456 .clientPids = info.clientPids,
457 .arch = fromBaseArchitecture(info.arch)
458 });
459 }
460 });
461 if (!ret.isOk()) {
462 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
463 << ret.description() << std::endl;
464 return DUMP_PASSTHROUGH_ERROR;
465 }
466 return OK;
467}
468
469Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
470 using namespace ::std;
471 using namespace ::android::hardware;
472 using namespace ::android::hidl::manager::V1_0;
473 using namespace ::android::hidl::base::V1_0;
474 const std::string mode = "hwbinder";
475
476 hidl_vec<hidl_string> fqInstanceNames;
477 // copying out for timeoutIPC
478 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
479 fqInstanceNames = names;
480 });
481 if (!listRet.isOk()) {
482 mErr << "Error: Failed to list services for " << mode << ": "
483 << listRet.description() << std::endl;
484 return DUMP_BINDERIZED_ERROR;
485 }
486
487 Status status = OK;
488 // server pid, .ptr value of binder object, child pids
489 std::map<std::string, DebugInfo> allDebugInfos;
490 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
491 for (const auto &fqInstanceName : fqInstanceNames) {
492 const auto pair = splitFirst(fqInstanceName, '/');
493 const auto &serviceName = pair.first;
494 const auto &instanceName = pair.second;
495 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
496 if (!getRet.isOk()) {
497 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
498 << "cannot be fetched from service manager:"
499 << getRet.description() << std::endl;
500 status |= DUMP_BINDERIZED_ERROR;
501 continue;
502 }
503 sp<IBase> service = getRet;
504 if (service == nullptr) {
505 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
506 << "cannot be fetched from service manager (null)"
507 << std::endl;
508 status |= DUMP_BINDERIZED_ERROR;
509 continue;
510 }
511 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
512 allDebugInfos[fqInstanceName] = debugInfo;
513 if (debugInfo.pid >= 0) {
514 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
515 }
516 });
517 if (!debugRet.isOk()) {
518 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
519 << "debugging information cannot be retrieved:"
520 << debugRet.description() << std::endl;
521 status |= DUMP_BINDERIZED_ERROR;
522 }
523 }
524 for (auto &pair : allPids) {
525 pid_t serverPid = pair.first;
526 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
527 mErr << "Warning: no information for PID " << serverPid
528 << ", are you root?" << std::endl;
529 status |= DUMP_BINDERIZED_ERROR;
530 }
531 }
532 for (const auto &fqInstanceName : fqInstanceNames) {
533 auto it = allDebugInfos.find(fqInstanceName);
534 if (it == allDebugInfos.end()) {
535 putEntry(HWSERVICEMANAGER_LIST, {
536 .interfaceName = fqInstanceName,
537 .transport = mode,
538 .serverPid = NO_PID,
539 .serverObjectAddress = NO_PTR,
540 .clientPids = {},
541 .arch = ARCH_UNKNOWN
542 });
543 continue;
544 }
545 const DebugInfo &info = it->second;
546 putEntry(HWSERVICEMANAGER_LIST, {
547 .interfaceName = fqInstanceName,
548 .transport = mode,
549 .serverPid = info.pid,
550 .serverObjectAddress = info.ptr,
551 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
552 ? Pids{} : allPids[info.pid][info.ptr],
553 .arch = fromBaseArchitecture(info.arch),
554 });
555 }
556 return status;
557}
558
559Status ListCommand::fetch() {
560 Status status = OK;
561 auto bManager = ::android::hardware::defaultServiceManager();
562 if (bManager == nullptr) {
563 mErr << "Failed to get defaultServiceManager()!" << std::endl;
564 status |= NO_BINDERIZED_MANAGER;
565 } else {
566 status |= fetchBinderized(bManager);
567 // Passthrough PIDs are registered to the binderized manager as well.
568 status |= fetchPassthrough(bManager);
569 }
570
571 auto pManager = ::android::hardware::getPassthroughServiceManager();
572 if (pManager == nullptr) {
573 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
574 status |= NO_PASSTHROUGH_MANAGER;
575 } else {
576 status |= fetchAllLibraries(pManager);
577 }
578 return status;
579}
580
581Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
582 static struct option longOptions[] = {
583 // long options with short alternatives
584 {"help", no_argument, 0, 'h' },
585 {"interface", no_argument, 0, 'i' },
586 {"transport", no_argument, 0, 't' },
587 {"arch", no_argument, 0, 'r' },
588 {"pid", no_argument, 0, 'p' },
589 {"address", no_argument, 0, 'a' },
590 {"clients", no_argument, 0, 'c' },
591 {"cmdline", no_argument, 0, 'm' },
592 {"debug", optional_argument, 0, 'd' },
593
594 // long options without short alternatives
595 {"sort", required_argument, 0, 's' },
596 {"init-vintf",optional_argument, 0, 'v' },
597 { 0, 0, 0, 0 }
598 };
599
600 int optionIndex;
601 int c;
602 // Lshal::parseArgs has set optind to the next option to parse
603 for (;;) {
604 // using getopt_long in case we want to add other options in the future
605 c = getopt_long(arg.argc, arg.argv,
606 "hitrpacmd", longOptions, &optionIndex);
607 if (c == -1) {
608 break;
609 }
610 switch (c) {
611 case 's': {
612 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
613 mSortColumn = TableEntry::sortByInterfaceName;
614 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
615 mSortColumn = TableEntry::sortByServerPid;
616 } else {
617 mErr << "Unrecognized sorting column: " << optarg << std::endl;
618 mLshal.usage(command);
619 return USAGE;
620 }
621 break;
622 }
623 case 'v': {
624 if (optarg) {
625 mFileOutput = new std::ofstream{optarg};
626 mOut = mFileOutput;
627 if (!mFileOutput.buf().is_open()) {
628 mErr << "Could not open file '" << optarg << "'." << std::endl;
629 return IO_ERROR;
630 }
631 }
632 mVintf = true;
633 }
634 case 'i': {
635 mSelectedColumns |= ENABLE_INTERFACE_NAME;
636 break;
637 }
638 case 't': {
639 mSelectedColumns |= ENABLE_TRANSPORT;
640 break;
641 }
642 case 'r': {
643 mSelectedColumns |= ENABLE_ARCH;
644 break;
645 }
646 case 'p': {
647 mSelectedColumns |= ENABLE_SERVER_PID;
648 break;
649 }
650 case 'a': {
651 mSelectedColumns |= ENABLE_SERVER_ADDR;
652 break;
653 }
654 case 'c': {
655 mSelectedColumns |= ENABLE_CLIENT_PIDS;
656 break;
657 }
658 case 'm': {
659 mEnableCmdlines = true;
660 break;
661 }
662 case 'd': {
663 mEmitDebugInfo = true;
664
665 if (optarg) {
666 mFileOutput = new std::ofstream{optarg};
667 mOut = mFileOutput;
668 if (!mFileOutput.buf().is_open()) {
669 mErr << "Could not open file '" << optarg << "'." << std::endl;
670 return IO_ERROR;
671 }
672 chown(optarg, AID_SHELL, AID_SHELL);
673 }
674 break;
675 }
676 case 'h': // falls through
677 default: // see unrecognized options
678 mLshal.usage(command);
679 return USAGE;
680 }
681 }
682 if (optind < arg.argc) {
683 // see non option
684 mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
685 }
686
687 if (mSelectedColumns == 0) {
688 mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
689 }
690 return OK;
691}
692
693Status ListCommand::main(const std::string &command, const Arg &arg) {
694 Status status = parseArgs(command, arg);
695 if (status != OK) {
696 return status;
697 }
698 status = fetch();
699 postprocess();
700 dump();
701 return status;
702}
703
704} // namespace lshal
705} // namespace android
706