Home | History | Annotate | Download | only in lshal
      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