• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file.h>
29 #include <android-base/parseint.h>
30 #include <android/hidl/manager/1.0/IServiceManager.h>
31 #include <hidl-hash/Hash.h>
32 #include <hidl-util/FQName.h>
33 #include <private/android_filesystem_config.h>
34 #include <sys/stat.h>
35 #include <vintf/HalManifest.h>
36 #include <vintf/parse_string.h>
37 #include <vintf/parse_xml.h>
38 
39 #include "Lshal.h"
40 #include "PipeRelay.h"
41 #include "Timeout.h"
42 #include "utils.h"
43 
44 using ::android::hardware::hidl_string;
45 using ::android::hardware::hidl_vec;
46 using ::android::hidl::base::V1_0::DebugInfo;
47 using ::android::hidl::base::V1_0::IBase;
48 using ::android::hidl::manager::V1_0::IServiceManager;
49 
50 namespace android {
51 namespace lshal {
52 
toSchemaType(Partition p)53 vintf::SchemaType toSchemaType(Partition p) {
54     return (p == Partition::SYSTEM) ? vintf::SchemaType::FRAMEWORK : vintf::SchemaType::DEVICE;
55 }
56 
out() const57 NullableOStream<std::ostream> ListCommand::out() const {
58     return mLshal.out();
59 }
60 
err() const61 NullableOStream<std::ostream> ListCommand::err() const {
62     return mLshal.err();
63 }
64 
GetName()65 std::string ListCommand::GetName() {
66     return "list";
67 }
getSimpleDescription() const68 std::string ListCommand::getSimpleDescription() const {
69     return "List HALs.";
70 }
71 
parseCmdline(pid_t pid) const72 std::string ListCommand::parseCmdline(pid_t pid) const {
73     return android::procpartition::getCmdline(pid);
74 }
75 
getCmdline(pid_t pid)76 const std::string &ListCommand::getCmdline(pid_t pid) {
77     auto pair = mCmdlines.find(pid);
78     if (pair != mCmdlines.end()) {
79         return pair->second;
80     }
81     mCmdlines[pid] = parseCmdline(pid);
82     return mCmdlines[pid];
83 }
84 
removeDeadProcesses(Pids * pids)85 void ListCommand::removeDeadProcesses(Pids *pids) {
86     static const pid_t myPid = getpid();
87     pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
88         return pid == myPid || this->getCmdline(pid).empty();
89     }), pids->end());
90 }
91 
getPartition(pid_t pid)92 Partition ListCommand::getPartition(pid_t pid) {
93     auto it = mPartitions.find(pid);
94     if (it != mPartitions.end()) {
95         return it->second;
96     }
97     Partition partition = android::procpartition::getPartition(pid);
98     mPartitions.emplace(pid, partition);
99     return partition;
100 }
101 
102 // Give sensible defaults when nothing can be inferred from runtime.
103 // process: Partition inferred from executable location or cmdline.
resolvePartition(Partition process,const FQName & fqName) const104 Partition ListCommand::resolvePartition(Partition process, const FQName& fqName) const {
105     if (fqName.inPackage("vendor") ||
106         fqName.inPackage("com")) {
107         return Partition::VENDOR;
108     }
109 
110     if (fqName.inPackage("android.frameworks") ||
111         fqName.inPackage("android.system") ||
112         fqName.inPackage("android.hidl")) {
113         return Partition::SYSTEM;
114     }
115 
116     // Some android.hardware HALs are served from system. Check the value from executable
117     // location / cmdline first.
118     if (fqName.inPackage("android.hardware")) {
119         if (process != Partition::UNKNOWN) {
120             return process;
121         }
122         return Partition::VENDOR;
123     }
124 
125     return process;
126 }
127 
scanBinderContext(pid_t pid,const std::string & contextName,std::function<void (const std::string &)> eachLine)128 static bool scanBinderContext(pid_t pid,
129         const std::string &contextName,
130         std::function<void(const std::string&)> eachLine) {
131     std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
132     if (!ifs.is_open()) {
133         return false;
134     }
135 
136     static const std::regex kContextLine("^context (\\w+)$");
137 
138     bool isDesiredContext = false;
139     std::string line;
140     std::smatch match;
141     while(getline(ifs, line)) {
142         if (std::regex_search(line, match, kContextLine)) {
143             isDesiredContext = match.str(1) == contextName;
144             continue;
145         }
146 
147         if (!isDesiredContext) {
148             continue;
149         }
150 
151         eachLine(line);
152     }
153     return true;
154 }
155 
getPidInfo(pid_t serverPid,PidInfo * pidInfo) const156 bool ListCommand::getPidInfo(
157         pid_t serverPid, PidInfo *pidInfo) const {
158     static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
159     static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
160 
161     std::smatch match;
162     return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
163         if (std::regex_search(line, match, kReferencePrefix)) {
164             const std::string &ptrString = "0x" + match.str(2); // use number after c
165             uint64_t ptr;
166             if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
167                 // Should not reach here, but just be tolerant.
168                 err() << "Could not parse number " << ptrString << std::endl;
169                 return;
170             }
171             const std::string proc = " proc ";
172             auto pos = line.rfind(proc);
173             if (pos != std::string::npos) {
174                 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
175                     int32_t pid;
176                     if (!::android::base::ParseInt(pidStr, &pid)) {
177                         err() << "Could not parse number " << pidStr << std::endl;
178                         return;
179                     }
180                     pidInfo->refPids[ptr].push_back(pid);
181                 }
182             }
183 
184             return;
185         }
186 
187         if (std::regex_search(line, match, kThreadPrefix)) {
188             // "1" is waiting in binder driver
189             // "2" is poll. It's impossible to tell if these are in use.
190             //     and HIDL default code doesn't use it.
191             bool isInUse = match.str(1) != "1";
192             // "0" is a thread that has called into binder
193             // "1" is looper thread
194             // "2" is main looper thread
195             bool isHwbinderThread = match.str(2) != "0";
196 
197             if (!isHwbinderThread) {
198                 return;
199             }
200 
201             if (isInUse) {
202                 pidInfo->threadUsage++;
203             }
204 
205             pidInfo->threadCount++;
206             return;
207         }
208 
209         // not reference or thread line
210         return;
211     });
212 }
213 
getPidInfoCached(pid_t serverPid)214 const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
215     auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
216     if (pair.second /* did insertion take place? */) {
217         if (!getPidInfo(serverPid, &pair.first->second)) {
218             return nullptr;
219         }
220     }
221     return &pair.first->second;
222 }
223 
224 // Must process hwbinder services first, then passthrough services.
forEachTable(const std::function<void (Table &)> & f)225 void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
226     f(mServicesTable);
227     f(mPassthroughRefTable);
228     f(mImplementationsTable);
229 }
forEachTable(const std::function<void (const Table &)> & f) const230 void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
231     f(mServicesTable);
232     f(mPassthroughRefTable);
233     f(mImplementationsTable);
234 }
235 
postprocess()236 void ListCommand::postprocess() {
237     forEachTable([this](Table &table) {
238         if (mSortColumn) {
239             std::sort(table.begin(), table.end(), mSortColumn);
240         }
241         for (TableEntry &entry : table) {
242             entry.serverCmdline = getCmdline(entry.serverPid);
243             removeDeadProcesses(&entry.clientPids);
244             for (auto pid : entry.clientPids) {
245                 entry.clientCmdlines.push_back(this->getCmdline(pid));
246             }
247         }
248         for (TableEntry& entry : table) {
249             entry.partition = getPartition(entry.serverPid);
250         }
251     });
252     // use a double for loop here because lshal doesn't care about efficiency.
253     for (TableEntry &packageEntry : mImplementationsTable) {
254         std::string packageName = packageEntry.interfaceName;
255         FQName fqPackageName{packageName.substr(0, packageName.find("::"))};
256         if (!fqPackageName.isValid()) {
257             continue;
258         }
259         for (TableEntry &interfaceEntry : mPassthroughRefTable) {
260             if (interfaceEntry.arch != ARCH_UNKNOWN) {
261                 continue;
262             }
263             FQName interfaceName{splitFirst(interfaceEntry.interfaceName, '/').first};
264             if (!interfaceName.isValid()) {
265                 continue;
266             }
267             if (interfaceName.getPackageAndVersion() == fqPackageName) {
268                 interfaceEntry.arch = packageEntry.arch;
269             }
270         }
271     }
272 
273     mServicesTable.setDescription(
274             "All binderized services (registered services through hwservicemanager)");
275     mPassthroughRefTable.setDescription(
276             "All interfaces that getService() has ever return as a passthrough interface;\n"
277             "PIDs / processes shown below might be inaccurate because the process\n"
278             "might have relinquished the interface or might have died.\n"
279             "The Server / Server CMD column can be ignored.\n"
280             "The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
281             "the library and successfully fetched the passthrough implementation.");
282     mImplementationsTable.setDescription(
283             "All available passthrough implementations (all -impl.so files).\n"
284             "These may return subclasses through their respective HIDL_FETCH_I* functions.");
285 }
286 
findAndBumpVersion(vintf::ManifestHal * hal,const vintf::Version & version)287 static inline bool findAndBumpVersion(vintf::ManifestHal* hal, const vintf::Version& version) {
288     for (vintf::Version& v : hal->versions) {
289         if (v.majorVer == version.majorVer) {
290             v.minorVer = std::max(v.minorVer, version.minorVer);
291             return true;
292         }
293     }
294     return false;
295 }
296 
dumpVintf(const NullableOStream<std::ostream> & out) const297 void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
298     using vintf::operator|=;
299     using vintf::operator<<;
300 
301     vintf::HalManifest manifest;
302     manifest.setType(toSchemaType(mVintfPartition));
303     forEachTable([this, &manifest] (const Table &table) {
304         for (const TableEntry &entry : table) {
305 
306             std::string fqInstanceName = entry.interfaceName;
307 
308             if (&table == &mImplementationsTable) {
309                 // Quick hack to work around *'s
310                 replaceAll(&fqInstanceName, '*', 'D');
311             }
312             auto splittedFqInstanceName = splitFirst(fqInstanceName, '/');
313             FQName fqName(splittedFqInstanceName.first);
314             if (!fqName.isValid()) {
315                 err() << "Warning: '" << splittedFqInstanceName.first
316                      << "' is not a valid FQName." << std::endl;
317                 continue;
318             }
319 
320             if (fqName.package() == gIBaseFqName.package()) {
321                 continue; // always remove IBase from manifest
322             }
323 
324             Partition partition = resolvePartition(entry.partition, fqName);
325 
326             if (partition == Partition::UNKNOWN) {
327                 err() << "Warning: Cannot guess the partition of instance " << fqInstanceName
328                       << ". It is removed from the generated manifest." << std::endl;
329                 continue;
330             }
331 
332             if (partition != mVintfPartition) {
333                 continue; // strip out instances that is in a different partition.
334             }
335 
336             std::string interfaceName =
337                     &table == &mImplementationsTable ? "" : fqName.name();
338             std::string instanceName =
339                     &table == &mImplementationsTable ? "" : splittedFqInstanceName.second;
340 
341             vintf::Version version{fqName.getPackageMajorVersion(),
342                                    fqName.getPackageMinorVersion()};
343             vintf::Transport transport;
344             vintf::Arch arch;
345             if (entry.transport == "hwbinder") {
346                 transport = vintf::Transport::HWBINDER;
347                 arch = vintf::Arch::ARCH_EMPTY;
348             } else if (entry.transport == "passthrough") {
349                 transport = vintf::Transport::PASSTHROUGH;
350                 switch (entry.arch) {
351                     case lshal::ARCH32:
352                         arch = vintf::Arch::ARCH_32;    break;
353                     case lshal::ARCH64:
354                         arch = vintf::Arch::ARCH_64;    break;
355                     case lshal::ARCH_BOTH:
356                         arch = vintf::Arch::ARCH_32_64; break;
357                     case lshal::ARCH_UNKNOWN: // fallthrough
358                     default:
359                         err() << "Warning: '" << fqName.package()
360                              << "' doesn't have bitness info, assuming 32+64." << std::endl;
361                         arch = vintf::Arch::ARCH_32_64;
362                 }
363             } else {
364                 err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
365                 continue;
366             }
367 
368             bool done = false;
369             for (vintf::ManifestHal *hal : manifest.getHals(fqName.package())) {
370                 if (hal->transport() != transport) {
371                     if (transport != vintf::Transport::PASSTHROUGH) {
372                         err() << "Fatal: should not reach here. Generated result may be wrong for '"
373                              << hal->name << "'."
374                              << std::endl;
375                     }
376                     done = true;
377                     break;
378                 }
379                 if (findAndBumpVersion(hal, version)) {
380                     if (&table != &mImplementationsTable) {
381                         hal->insertLegacyInstance(interfaceName, instanceName);
382                     }
383                     hal->transportArch.arch |= arch;
384                     done = true;
385                     break;
386                 }
387             }
388             if (done) {
389                 continue; // to next TableEntry
390             }
391             vintf::ManifestHal manifestHal{
392                     vintf::HalFormat::HIDL,
393                     std::string{fqName.package()},
394                     {version},
395                     {transport, arch},
396                     {}};
397             if (&table != &mImplementationsTable) {
398                 manifestHal.insertLegacyInstance(interfaceName, instanceName);
399             }
400             if (!manifest.add(std::move(manifestHal))) {
401                 err() << "Warning: cannot add hal '" << fqInstanceName << "'" << std::endl;
402             }
403         }
404     });
405     out << "<!-- " << std::endl
406          << "    This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl
407          << INIT_VINTF_NOTES
408          << "-->" << std::endl;
409     out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlag::HALS_NO_FQNAME);
410 }
411 
412 std::string ListCommand::INIT_VINTF_NOTES{
413     "    1. If a HAL is supported in both hwbinder and passthrough transport, \n"
414     "       only hwbinder is shown.\n"
415     "    2. It is likely that HALs in passthrough transport does not have\n"
416     "       <interface> declared; users will have to write them by hand.\n"
417     "    3. A HAL with lower minor version can be overridden by a HAL with\n"
418     "       higher minor version if they have the same name and major version.\n"
419 };
420 
fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a)421 static Architecture fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
422     switch (a) {
423         case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
424             return ARCH64;
425         case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
426             return ARCH32;
427         case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
428         default:
429             return ARCH_UNKNOWN;
430     }
431 }
432 
dumpTable(const NullableOStream<std::ostream> & out) const433 void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
434     if (mNeat) {
435         MergedTable({&mServicesTable, &mPassthroughRefTable, &mImplementationsTable})
436             .createTextTable().dump(out.buf());
437         return;
438     }
439 
440     forEachTable([this, &out](const Table &table) {
441 
442         // We're only interested in dumping debug info for already
443         // instantiated services. There's little value in dumping the
444         // debug info for a service we create on the fly, so we only operate
445         // on the "mServicesTable".
446         std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
447         if (mEmitDebugInfo && &table == &mServicesTable) {
448             emitDebugInfo = [this](const auto& iName) {
449                 std::stringstream ss;
450                 auto pair = splitFirst(iName, '/');
451                 mLshal.emitDebugInfo(pair.first, pair.second, {},
452                                      false /* excludesParentInstances */, ss,
453                                      NullableOStream<std::ostream>(nullptr));
454                 return ss.str();
455             };
456         }
457         table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
458         out << std::endl;
459     });
460 }
461 
dump()462 Status ListCommand::dump() {
463     auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
464 
465     if (mFileOutputPath.empty()) {
466         (*this.*dump)(out());
467         return OK;
468     }
469 
470     std::ofstream fileOutput(mFileOutputPath);
471     if (!fileOutput.is_open()) {
472         err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
473         return IO_ERROR;
474     }
475     chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
476 
477     (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
478 
479     fileOutput.flush();
480     fileOutput.close();
481     return OK;
482 }
483 
putEntry(TableEntrySource source,TableEntry && entry)484 void ListCommand::putEntry(TableEntrySource source, TableEntry &&entry) {
485     Table *table = nullptr;
486     switch (source) {
487         case HWSERVICEMANAGER_LIST :
488             table = &mServicesTable; break;
489         case PTSERVICEMANAGER_REG_CLIENT :
490             table = &mPassthroughRefTable; break;
491         case LIST_DLLIB :
492             table = &mImplementationsTable; break;
493         default:
494             err() << "Error: Unknown source of entry " << source << std::endl;
495     }
496     if (table) {
497         table->add(std::forward<TableEntry>(entry));
498     }
499 }
500 
fetchAllLibraries(const sp<IServiceManager> & manager)501 Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
502     using namespace ::android::hardware;
503     using namespace ::android::hidl::manager::V1_0;
504     using namespace ::android::hidl::base::V1_0;
505     using std::literals::chrono_literals::operator""s;
506     auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
507         std::map<std::string, TableEntry> entries;
508         for (const auto &info : infos) {
509             std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
510                     std::string{info.instanceName.c_str()};
511             entries.emplace(interfaceName, TableEntry{
512                 .interfaceName = interfaceName,
513                 .transport = "passthrough",
514                 .clientPids = info.clientPids,
515             }).first->second.arch |= fromBaseArchitecture(info.arch);
516         }
517         for (auto &&pair : entries) {
518             putEntry(LIST_DLLIB, std::move(pair.second));
519         }
520     });
521     if (!ret.isOk()) {
522         err() << "Error: Failed to call list on getPassthroughServiceManager(): "
523              << ret.description() << std::endl;
524         return DUMP_ALL_LIBS_ERROR;
525     }
526     return OK;
527 }
528 
fetchPassthrough(const sp<IServiceManager> & manager)529 Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
530     using namespace ::android::hardware;
531     using namespace ::android::hardware::details;
532     using namespace ::android::hidl::manager::V1_0;
533     using namespace ::android::hidl::base::V1_0;
534     auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
535         for (const auto &info : infos) {
536             if (info.clientPids.size() <= 0) {
537                 continue;
538             }
539             putEntry(PTSERVICEMANAGER_REG_CLIENT, {
540                 .interfaceName =
541                         std::string{info.interfaceName.c_str()} + "/" +
542                         std::string{info.instanceName.c_str()},
543                 .transport = "passthrough",
544                 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
545                 .clientPids = info.clientPids,
546                 .arch = fromBaseArchitecture(info.arch)
547             });
548         }
549     });
550     if (!ret.isOk()) {
551         err() << "Error: Failed to call debugDump on defaultServiceManager(): "
552              << ret.description() << std::endl;
553         return DUMP_PASSTHROUGH_ERROR;
554     }
555     return OK;
556 }
557 
fetchBinderized(const sp<IServiceManager> & manager)558 Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
559     const std::string mode = "hwbinder";
560 
561     hidl_vec<hidl_string> fqInstanceNames;
562     // copying out for timeoutIPC
563     auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
564         fqInstanceNames = names;
565     });
566     if (!listRet.isOk()) {
567         err() << "Error: Failed to list services for " << mode << ": "
568              << listRet.description() << std::endl;
569         return DUMP_BINDERIZED_ERROR;
570     }
571 
572     Status status = OK;
573     std::map<std::string, TableEntry> allTableEntries;
574     for (const auto &fqInstanceName : fqInstanceNames) {
575         // create entry and default assign all fields.
576         TableEntry& entry = allTableEntries[fqInstanceName];
577         entry.interfaceName = fqInstanceName;
578         entry.transport = mode;
579 
580         status |= fetchBinderizedEntry(manager, &entry);
581     }
582 
583     for (auto& pair : allTableEntries) {
584         putEntry(HWSERVICEMANAGER_LIST, std::move(pair.second));
585     }
586     return status;
587 }
588 
fetchBinderizedEntry(const sp<IServiceManager> & manager,TableEntry * entry)589 Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
590                                          TableEntry *entry) {
591     Status status = OK;
592     const auto handleError = [&](Status additionalError, const std::string& msg) {
593         err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl;
594         status |= DUMP_BINDERIZED_ERROR | additionalError;
595     };
596 
597     const auto pair = splitFirst(entry->interfaceName, '/');
598     const auto &serviceName = pair.first;
599     const auto &instanceName = pair.second;
600     auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
601     if (!getRet.isOk()) {
602         handleError(TRANSACTION_ERROR,
603                     "cannot be fetched from service manager:" + getRet.description());
604         return status;
605     }
606     sp<IBase> service = getRet;
607     if (service == nullptr) {
608         handleError(NO_INTERFACE, "cannot be fetched from service manager (null)");
609         return status;
610     }
611 
612     // getDebugInfo
613     do {
614         DebugInfo debugInfo;
615         auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
616             debugInfo = received;
617         });
618         if (!debugRet.isOk()) {
619             handleError(TRANSACTION_ERROR,
620                         "debugging information cannot be retrieved: " + debugRet.description());
621             break; // skip getPidInfo
622         }
623 
624         entry->serverPid = debugInfo.pid;
625         entry->serverObjectAddress = debugInfo.ptr;
626         entry->arch = fromBaseArchitecture(debugInfo.arch);
627 
628         if (debugInfo.pid != NO_PID) {
629             const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
630             if (pidInfo == nullptr) {
631                 handleError(IO_ERROR,
632                             "no information for PID " + std::to_string(debugInfo.pid) +
633                             ", are you root?");
634                 break;
635             }
636             if (debugInfo.ptr != NO_PTR) {
637                 auto it = pidInfo->refPids.find(debugInfo.ptr);
638                 if (it != pidInfo->refPids.end()) {
639                     entry->clientPids = it->second;
640                 }
641             }
642             entry->threadUsage = pidInfo->threadUsage;
643             entry->threadCount = pidInfo->threadCount;
644         }
645     } while (0);
646 
647     // hash
648     do {
649         ssize_t hashIndex = -1;
650         auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
651             for (size_t i = 0; i < c.size(); ++i) {
652                 if (serviceName == c[i]) {
653                     hashIndex = static_cast<ssize_t>(i);
654                     break;
655                 }
656             }
657         });
658         if (!ifaceChainRet.isOk()) {
659             handleError(TRANSACTION_ERROR,
660                         "interfaceChain fails: " + ifaceChainRet.description());
661             break; // skip getHashChain
662         }
663         if (hashIndex < 0) {
664             handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
665             break; // skip getHashChain
666         }
667         auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
668             if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
669                 handleError(BAD_IMPL,
670                             "interfaceChain indicates position " + std::to_string(hashIndex) +
671                             " but getHashChain returns " + std::to_string(hashChain.size()) +
672                             " hashes");
673                 return;
674             }
675 
676             auto&& hashArray = hashChain[hashIndex];
677             std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
678             entry->hash = Hash::hexString(hashVec);
679         });
680         if (!hashRet.isOk()) {
681             handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
682         }
683     } while (0);
684     return status;
685 }
686 
fetch()687 Status ListCommand::fetch() {
688     Status status = OK;
689     auto bManager = mLshal.serviceManager();
690     if (bManager == nullptr) {
691         err() << "Failed to get defaultServiceManager()!" << std::endl;
692         status |= NO_BINDERIZED_MANAGER;
693     } else {
694         status |= fetchBinderized(bManager);
695         // Passthrough PIDs are registered to the binderized manager as well.
696         status |= fetchPassthrough(bManager);
697     }
698 
699     auto pManager = mLshal.passthroughManager();
700     if (pManager == nullptr) {
701         err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
702         status |= NO_PASSTHROUGH_MANAGER;
703     } else {
704         status |= fetchAllLibraries(pManager);
705     }
706     return status;
707 }
708 
registerAllOptions()709 void ListCommand::registerAllOptions() {
710     int v = mOptions.size();
711     // A list of acceptable command line options
712     // key: value returned by getopt_long
713     // long options with short alternatives
714     mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) {
715         return USAGE;
716     }, ""});
717     mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) {
718         thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
719         return OK;
720     }, "print the instance name column"});
721     mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
722         thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
723         return OK;
724     }, "print the 'is released?' column\n(Y=released, empty=unreleased or unknown)"});
725     mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
726         thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
727         return OK;
728     }, "print the transport mode column"});
729     mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) {
730         thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
731         return OK;
732     }, "print the bitness column"});
733     mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) {
734         thiz->mSelectedColumns.push_back(TableColumnType::HASH);
735         return OK;
736     }, "print hash of the interface"});
737     mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
738         thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
739         return OK;
740     }, "print the server PID, or server cmdline if -m is set"});
741     mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) {
742         thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
743         return OK;
744     }, "print the server object address column"});
745     mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) {
746         thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
747         return OK;
748     }, "print the client PIDs, or client cmdlines if -m is set"});
749     mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) {
750         thiz->mSelectedColumns.push_back(TableColumnType::THREADS);
751         return OK;
752     }, "print currently used/available threads\n(note, available threads created lazily)"});
753     mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) {
754         thiz->mEnableCmdlines = true;
755         return OK;
756     }, "print cmdline instead of PIDs"});
757     mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) {
758         thiz->mEmitDebugInfo = true;
759         if (arg) thiz->mFileOutputPath = arg;
760         return OK;
761     }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
762         "Writes to specified file if 'arg' is provided, otherwise stdout."});
763 
764     // long options without short alternatives
765     mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
766         thiz->mVintf = true;
767         if (thiz->mVintfPartition == Partition::UNKNOWN)
768             thiz->mVintfPartition = Partition::VENDOR;
769         if (arg) thiz->mFileOutputPath = arg;
770         return OK;
771     }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."});
772     mOptions.push_back({'\0', "init-vintf-partition", required_argument, v++, [](ListCommand* thiz, const char* arg) {
773         if (!arg) return USAGE;
774         thiz->mVintfPartition = android::procpartition::parsePartition(arg);
775         if (thiz->mVintfPartition == Partition::UNKNOWN) return USAGE;
776         return OK;
777     }, "Specify the partition of the HAL manifest\ngenerated by --init-vintf.\n"
778        "Valid values are 'system', 'vendor', and 'odm'. Default is 'vendor'."});
779     mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) {
780         if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) {
781             thiz->mSortColumn = TableEntry::sortByInterfaceName;
782         } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) {
783             thiz->mSortColumn = TableEntry::sortByServerPid;
784         } else {
785             thiz->err() << "Unrecognized sorting column: " << arg << std::endl;
786             return USAGE;
787         }
788         return OK;
789     }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."});
790     mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) {
791         thiz->mNeat = true;
792         return OK;
793     }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
794 }
795 
796 // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
797 // the lifetime of "options" during the usage of the returned array.
getLongOptions(const ListCommand::RegisteredOptions & options,int * longOptFlag)798 static std::unique_ptr<struct option[]> getLongOptions(
799         const ListCommand::RegisteredOptions& options,
800         int* longOptFlag) {
801     std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
802     int i = 0;
803     for (const auto& e : options) {
804         ret[i].name = e.longOption.c_str();
805         ret[i].has_arg = e.hasArg;
806         ret[i].flag = longOptFlag;
807         ret[i].val = e.val;
808 
809         i++;
810     }
811     // getopt_long last option has all zeros
812     ret[i].name = NULL;
813     ret[i].has_arg = 0;
814     ret[i].flag = NULL;
815     ret[i].val = 0;
816 
817     return ret;
818 }
819 
820 // Create 'optstring' argument to getopt_long.
getShortOptions(const ListCommand::RegisteredOptions & options)821 static std::string getShortOptions(const ListCommand::RegisteredOptions& options) {
822     std::stringstream ss;
823     for (const auto& e : options) {
824         if (e.shortOption != '\0') {
825             ss << e.shortOption;
826         }
827     }
828     return ss.str();
829 }
830 
parseArgs(const Arg & arg)831 Status ListCommand::parseArgs(const Arg &arg) {
832 
833     if (mOptions.empty()) {
834         registerAllOptions();
835     }
836     int longOptFlag;
837     std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag);
838     std::string shortOptions = getShortOptions(mOptions);
839 
840     // suppress output to std::err for unknown options
841     opterr = 0;
842 
843     int optionIndex;
844     int c;
845     // Lshal::parseArgs has set optind to the next option to parse
846     for (;;) {
847         c = getopt_long(arg.argc, arg.argv,
848                 shortOptions.c_str(), longOptions.get(), &optionIndex);
849         if (c == -1) {
850             break;
851         }
852         const RegisteredOption* found = nullptr;
853         if (c == 0) {
854             // see long option
855             for (const auto& e : mOptions) {
856                 if (longOptFlag == e.val) found = &e;
857             }
858         } else {
859             // see short option
860             for (const auto& e : mOptions) {
861                 if (c == e.shortOption) found = &e;
862             }
863         }
864 
865         if (found == nullptr) {
866             // see unrecognized options
867             err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl;
868             return USAGE;
869         }
870 
871         Status status = found->op(this, optarg);
872         if (status != OK) {
873             return status;
874         }
875     }
876     if (optind < arg.argc) {
877         // see non option
878         err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
879         return USAGE;
880     }
881 
882     if (mNeat && mEmitDebugInfo) {
883         err() << "Error: --neat should not be used with --debug." << std::endl;
884         return USAGE;
885     }
886 
887     if (mSelectedColumns.empty()) {
888         mSelectedColumns = {TableColumnType::RELEASED,
889                             TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
890                             TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
891     }
892 
893     if (mEnableCmdlines) {
894         for (size_t i = 0; i < mSelectedColumns.size(); ++i) {
895             if (mSelectedColumns[i] == TableColumnType::SERVER_PID) {
896                 mSelectedColumns[i] = TableColumnType::SERVER_CMD;
897             }
898             if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) {
899                 mSelectedColumns[i] = TableColumnType::CLIENT_CMDS;
900             }
901         }
902     }
903 
904     forEachTable([this] (Table& table) {
905         table.setSelectedColumns(this->mSelectedColumns);
906     });
907 
908     return OK;
909 }
910 
main(const Arg & arg)911 Status ListCommand::main(const Arg &arg) {
912     Status status = parseArgs(arg);
913     if (status != OK) {
914         return status;
915     }
916     status = fetch();
917     postprocess();
918     status |= dump();
919     return status;
920 }
921 
splitString(const std::string & s,char c)922 static std::vector<std::string> splitString(const std::string &s, char c) {
923     std::vector<std::string> components;
924 
925     size_t startPos = 0;
926     size_t matchPos;
927     while ((matchPos = s.find(c, startPos)) != std::string::npos) {
928         components.push_back(s.substr(startPos, matchPos - startPos));
929         startPos = matchPos + 1;
930     }
931 
932     if (startPos <= s.length()) {
933         components.push_back(s.substr(startPos));
934     }
935     return components;
936 }
937 
getHelpMessageForArgument() const938 const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
939     static const std::string empty{};
940     static const std::string optional{"[=<arg>]"};
941     static const std::string required{"=<arg>"};
942 
943     if (hasArg == optional_argument) {
944         return optional;
945     }
946     if (hasArg == required_argument) {
947         return required;
948     }
949     return empty;
950 }
951 
usage() const952 void ListCommand::usage() const {
953 
954     err() << "list:" << std::endl
955           << "    lshal" << std::endl
956           << "    lshal list" << std::endl
957           << "        List all hals with default ordering and columns (`lshal list -riepc`)" << std::endl
958           << "    lshal list [-h|--help]" << std::endl
959           << "        -h, --help: Print help message for list (`lshal help list`)" << std::endl
960           << "    lshal [list] [OPTIONS...]" << std::endl;
961     for (const auto& e : mOptions) {
962         if (e.help.empty()) {
963             continue;
964         }
965         err() << "        ";
966         if (e.shortOption != '\0')
967             err() << "-" << e.shortOption << e.getHelpMessageForArgument();
968         if (e.shortOption != '\0' && !e.longOption.empty())
969             err() << ", ";
970         if (!e.longOption.empty())
971             err() << "--" << e.longOption << e.getHelpMessageForArgument();
972         err() << ": ";
973         std::vector<std::string> lines = splitString(e.help, '\n');
974         for (const auto& line : lines) {
975             if (&line != &lines.front())
976                 err() << "            ";
977             err() << line << std::endl;
978         }
979     }
980 }
981 
982 }  // namespace lshal
983 }  // namespace android
984 
985