blob: 56968433368ecf092c93f34a98f91806c64a7efa [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
353 // We're only interested in dumping debug info for already
354 // instantiated services. There's little value in dumping the
355 // debug info for a service we create on the fly, so we only operate
356 // on the "mServicesTable".
357 sp<IServiceManager> serviceManager;
358 if (mEmitDebugInfo && &table == &mServicesTable) {
359 serviceManager = ::android::hardware::defaultServiceManager();
360 }
361
362 for (const auto &entry : table) {
363 printLine(entry.interfaceName,
364 entry.transport,
365 getArchString(entry.arch),
366 entry.serverPid == NO_PID ? "N/A" : std::to_string(entry.serverPid),
367 entry.serverCmdline,
368 entry.serverObjectAddress == NO_PTR ? "N/A" : toHexString(entry.serverObjectAddress),
369 join(entry.clientPids, " "),
370 join(entry.clientCmdlines, ";"));
371
372 if (serviceManager != nullptr) {
373 auto pair = splitFirst(entry.interfaceName, '/');
374 mLshal.emitDebugInfo(serviceManager, pair.first, pair.second, {}, mOut.buf());
375 }
376 }
377 mOut << std::endl;
378 });
379
380}
381
382void ListCommand::dump() {
383 if (mVintf) {
384 dumpVintf();
385 if (!!mFileOutput) {
386 mFileOutput.buf().close();
387 delete &mFileOutput.buf();
388 mFileOutput = nullptr;
389 }
390 mOut = std::cout;
391 } else {
392 dumpTable();
393 }
394}
395
396void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
397 Table *table = nullptr;
398 switch (source) {
399 case HWSERVICEMANAGER_LIST :
400 table = &mServicesTable; break;
401 case PTSERVICEMANAGER_REG_CLIENT :
402 table = &mPassthroughRefTable; break;
403 case LIST_DLLIB :
404 table = &mImplementationsTable; break;
405 default:
406 mErr << "Error: Unknown source of entry " << source << std::endl;
407 }
408 if (table) {
409 table->entries.push_back(std::forward<TableEntry>(entry));
410 }
411}
412
413Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
414 using namespace ::android::hardware;
415 using namespace ::android::hidl::manager::V1_0;
416 using namespace ::android::hidl::base::V1_0;
417 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
418 std::map<std::string, TableEntry> entries;
419 for (const auto &info : infos) {
420 std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
421 std::string{info.instanceName.c_str()};
422 entries.emplace(interfaceName, TableEntry{
423 .interfaceName = interfaceName,
424 .transport = "passthrough",
425 .serverPid = NO_PID,
426 .serverObjectAddress = NO_PTR,
427 .clientPids = {},
428 .arch = ARCH_UNKNOWN
429 }).first->second.arch |= fromBaseArchitecture(info.arch);
430 }
431 for (auto &&pair : entries) {
432 putEntry(LIST_DLLIB, std::move(pair.second));
433 }
434 });
435 if (!ret.isOk()) {
436 mErr << "Error: Failed to call list on getPassthroughServiceManager(): "
437 << ret.description() << std::endl;
438 return DUMP_ALL_LIBS_ERROR;
439 }
440 return OK;
441}
442
443Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
444 using namespace ::android::hardware;
445 using namespace ::android::hardware::details;
446 using namespace ::android::hidl::manager::V1_0;
447 using namespace ::android::hidl::base::V1_0;
448 auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
449 for (const auto &info : infos) {
450 if (info.clientPids.size() <= 0) {
451 continue;
452 }
453 putEntry(PTSERVICEMANAGER_REG_CLIENT, {
454 .interfaceName =
455 std::string{info.interfaceName.c_str()} + "/" +
456 std::string{info.instanceName.c_str()},
457 .transport = "passthrough",
458 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
459 .serverObjectAddress = NO_PTR,
460 .clientPids = info.clientPids,
461 .arch = fromBaseArchitecture(info.arch)
462 });
463 }
464 });
465 if (!ret.isOk()) {
466 mErr << "Error: Failed to call debugDump on defaultServiceManager(): "
467 << ret.description() << std::endl;
468 return DUMP_PASSTHROUGH_ERROR;
469 }
470 return OK;
471}
472
473Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
474 using namespace ::std;
475 using namespace ::android::hardware;
476 using namespace ::android::hidl::manager::V1_0;
477 using namespace ::android::hidl::base::V1_0;
478 const std::string mode = "hwbinder";
479
480 hidl_vec<hidl_string> fqInstanceNames;
481 // copying out for timeoutIPC
482 auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
483 fqInstanceNames = names;
484 });
485 if (!listRet.isOk()) {
486 mErr << "Error: Failed to list services for " << mode << ": "
487 << listRet.description() << std::endl;
488 return DUMP_BINDERIZED_ERROR;
489 }
490
491 Status status = OK;
492 // server pid, .ptr value of binder object, child pids
493 std::map<std::string, DebugInfo> allDebugInfos;
494 std::map<pid_t, std::map<uint64_t, Pids>> allPids;
495 for (const auto &fqInstanceName : fqInstanceNames) {
496 const auto pair = splitFirst(fqInstanceName, '/');
497 const auto &serviceName = pair.first;
498 const auto &instanceName = pair.second;
499 auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
500 if (!getRet.isOk()) {
501 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
502 << "cannot be fetched from service manager:"
503 << getRet.description() << std::endl;
504 status |= DUMP_BINDERIZED_ERROR;
505 continue;
506 }
507 sp<IBase> service = getRet;
508 if (service == nullptr) {
509 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
510 << "cannot be fetched from service manager (null)"
511 << std::endl;
512 status |= DUMP_BINDERIZED_ERROR;
513 continue;
514 }
515 auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &debugInfo) {
516 allDebugInfos[fqInstanceName] = debugInfo;
517 if (debugInfo.pid >= 0) {
518 allPids[static_cast<pid_t>(debugInfo.pid)].clear();
519 }
520 });
521 if (!debugRet.isOk()) {
522 mErr << "Warning: Skipping \"" << fqInstanceName << "\": "
523 << "debugging information cannot be retrieved:"
524 << debugRet.description() << std::endl;
525 status |= DUMP_BINDERIZED_ERROR;
526 }
527 }
528 for (auto &pair : allPids) {
529 pid_t serverPid = pair.first;
530 if (!getReferencedPids(serverPid, &allPids[serverPid])) {
531 mErr << "Warning: no information for PID " << serverPid
532 << ", are you root?" << std::endl;
533 status |= DUMP_BINDERIZED_ERROR;
534 }
535 }
536 for (const auto &fqInstanceName : fqInstanceNames) {
537 auto it = allDebugInfos.find(fqInstanceName);
538 if (it == allDebugInfos.end()) {
539 putEntry(HWSERVICEMANAGER_LIST, {
540 .interfaceName = fqInstanceName,
541 .transport = mode,
542 .serverPid = NO_PID,
543 .serverObjectAddress = NO_PTR,
544 .clientPids = {},
545 .arch = ARCH_UNKNOWN
546 });
547 continue;
548 }
549 const DebugInfo &info = it->second;
550 putEntry(HWSERVICEMANAGER_LIST, {
551 .interfaceName = fqInstanceName,
552 .transport = mode,
553 .serverPid = info.pid,
554 .serverObjectAddress = info.ptr,
555 .clientPids = info.pid == NO_PID || info.ptr == NO_PTR
556 ? Pids{} : allPids[info.pid][info.ptr],
557 .arch = fromBaseArchitecture(info.arch),
558 });
559 }
560 return status;
561}
562
563Status ListCommand::fetch() {
564 Status status = OK;
565 auto bManager = ::android::hardware::defaultServiceManager();
566 if (bManager == nullptr) {
567 mErr << "Failed to get defaultServiceManager()!" << std::endl;
568 status |= NO_BINDERIZED_MANAGER;
569 } else {
570 status |= fetchBinderized(bManager);
571 // Passthrough PIDs are registered to the binderized manager as well.
572 status |= fetchPassthrough(bManager);
573 }
574
575 auto pManager = ::android::hardware::getPassthroughServiceManager();
576 if (pManager == nullptr) {
577 mErr << "Failed to get getPassthroughServiceManager()!" << std::endl;
578 status |= NO_PASSTHROUGH_MANAGER;
579 } else {
580 status |= fetchAllLibraries(pManager);
581 }
582 return status;
583}
584
585Status ListCommand::parseArgs(const std::string &command, const Arg &arg) {
586 static struct option longOptions[] = {
587 // long options with short alternatives
588 {"help", no_argument, 0, 'h' },
589 {"interface", no_argument, 0, 'i' },
590 {"transport", no_argument, 0, 't' },
591 {"arch", no_argument, 0, 'r' },
592 {"pid", no_argument, 0, 'p' },
593 {"address", no_argument, 0, 'a' },
594 {"clients", no_argument, 0, 'c' },
595 {"cmdline", no_argument, 0, 'm' },
596 {"debug", optional_argument, 0, 'd' },
597
598 // long options without short alternatives
599 {"sort", required_argument, 0, 's' },
600 {"init-vintf",optional_argument, 0, 'v' },
601 { 0, 0, 0, 0 }
602 };
603
604 int optionIndex;
605 int c;
606 // Lshal::parseArgs has set optind to the next option to parse
607 for (;;) {
608 // using getopt_long in case we want to add other options in the future
609 c = getopt_long(arg.argc, arg.argv,
610 "hitrpacmd", longOptions, &optionIndex);
611 if (c == -1) {
612 break;
613 }
614 switch (c) {
615 case 's': {
616 if (strcmp(optarg, "interface") == 0 || strcmp(optarg, "i") == 0) {
617 mSortColumn = TableEntry::sortByInterfaceName;
618 } else if (strcmp(optarg, "pid") == 0 || strcmp(optarg, "p") == 0) {
619 mSortColumn = TableEntry::sortByServerPid;
620 } else {
621 mErr << "Unrecognized sorting column: " << optarg << std::endl;
622 mLshal.usage(command);
623 return USAGE;
624 }
625 break;
626 }
627 case 'v': {
628 if (optarg) {
629 mFileOutput = new std::ofstream{optarg};
630 mOut = mFileOutput;
631 if (!mFileOutput.buf().is_open()) {
632 mErr << "Could not open file '" << optarg << "'." << std::endl;
633 return IO_ERROR;
634 }
635 }
636 mVintf = true;
637 }
638 case 'i': {
639 mSelectedColumns |= ENABLE_INTERFACE_NAME;
640 break;
641 }
642 case 't': {
643 mSelectedColumns |= ENABLE_TRANSPORT;
644 break;
645 }
646 case 'r': {
647 mSelectedColumns |= ENABLE_ARCH;
648 break;
649 }
650 case 'p': {
651 mSelectedColumns |= ENABLE_SERVER_PID;
652 break;
653 }
654 case 'a': {
655 mSelectedColumns |= ENABLE_SERVER_ADDR;
656 break;
657 }
658 case 'c': {
659 mSelectedColumns |= ENABLE_CLIENT_PIDS;
660 break;
661 }
662 case 'm': {
663 mEnableCmdlines = true;
664 break;
665 }
666 case 'd': {
667 mEmitDebugInfo = true;
668
669 if (optarg) {
670 mFileOutput = new std::ofstream{optarg};
671 mOut = mFileOutput;
672 if (!mFileOutput.buf().is_open()) {
673 mErr << "Could not open file '" << optarg << "'." << std::endl;
674 return IO_ERROR;
675 }
676 chown(optarg, AID_SHELL, AID_SHELL);
677 }
678 break;
679 }
680 case 'h': // falls through
681 default: // see unrecognized options
682 mLshal.usage(command);
683 return USAGE;
684 }
685 }
686 if (optind < arg.argc) {
687 // see non option
688 mErr << "Unrecognized option `" << arg.argv[optind] << "`" << std::endl;
689 }
690
691 if (mSelectedColumns == 0) {
692 mSelectedColumns = ENABLE_INTERFACE_NAME | ENABLE_SERVER_PID | ENABLE_CLIENT_PIDS;
693 }
694 return OK;
695}
696
697Status ListCommand::main(const std::string &command, const Arg &arg) {
698 Status status = parseArgs(command, arg);
699 if (status != OK) {
700 return status;
701 }
702 status = fetch();
703 postprocess();
704 dump();
705 return status;
706}
707
708} // namespace lshal
709} // namespace android
710