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