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