• 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 <algorithm>
22 #include <fstream>
23 #include <functional>
24 #include <iomanip>
25 #include <iostream>
26 #include <map>
27 #include <regex>
28 #include <sstream>
29 
30 #include <android-base/file.h>
31 #include <android-base/logging.h>
32 #include <android-base/parseint.h>
33 #include <android/hidl/manager/1.0/IServiceManager.h>
34 #include <hidl-hash/Hash.h>
35 #include <hidl-util/FQName.h>
36 #include <private/android_filesystem_config.h>
37 #include <sys/stat.h>
38 #include <vintf/HalManifest.h>
39 #include <vintf/parse_string.h>
40 #include <vintf/parse_xml.h>
41 
42 #include "Lshal.h"
43 #include "PipeRelay.h"
44 #include "Timeout.h"
45 #include "utils.h"
46 
47 using ::android::hardware::hidl_string;
48 using ::android::hardware::hidl_vec;
49 using ::android::hidl::base::V1_0::DebugInfo;
50 using ::android::hidl::base::V1_0::IBase;
51 using ::android::hidl::manager::V1_0::IServiceManager;
52 
53 namespace android {
54 namespace lshal {
55 
toSchemaType(Partition p)56 vintf::SchemaType toSchemaType(Partition p) {
57     return (p == Partition::SYSTEM) ? vintf::SchemaType::FRAMEWORK : vintf::SchemaType::DEVICE;
58 }
59 
toPartition(vintf::SchemaType t)60 Partition toPartition(vintf::SchemaType t) {
61     switch (t) {
62         case vintf::SchemaType::FRAMEWORK: return Partition::SYSTEM;
63         // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM.
64         case vintf::SchemaType::DEVICE: return Partition::VENDOR;
65     }
66     return Partition::UNKNOWN;
67 }
68 
getPackageAndVersion(const std::string & fqInstance)69 std::string getPackageAndVersion(const std::string& fqInstance) {
70     return splitFirst(fqInstance, ':').first;
71 }
72 
out() const73 NullableOStream<std::ostream> ListCommand::out() const {
74     return mLshal.out();
75 }
76 
err() const77 NullableOStream<std::ostream> ListCommand::err() const {
78     return mLshal.err();
79 }
80 
GetName()81 std::string ListCommand::GetName() {
82     return "list";
83 }
getSimpleDescription() const84 std::string ListCommand::getSimpleDescription() const {
85     return "List HALs.";
86 }
87 
parseCmdline(pid_t pid) const88 std::string ListCommand::parseCmdline(pid_t pid) const {
89     return android::procpartition::getCmdline(pid);
90 }
91 
getCmdline(pid_t pid)92 const std::string &ListCommand::getCmdline(pid_t pid) {
93     static const std::string kEmptyString{};
94     if (pid == NO_PID) return kEmptyString;
95     auto pair = mCmdlines.find(pid);
96     if (pair != mCmdlines.end()) {
97         return pair->second;
98     }
99     mCmdlines[pid] = parseCmdline(pid);
100     return mCmdlines[pid];
101 }
102 
removeDeadProcesses(Pids * pids)103 void ListCommand::removeDeadProcesses(Pids *pids) {
104     static const pid_t myPid = getpid();
105     pids->erase(std::remove_if(pids->begin(), pids->end(), [this](auto pid) {
106         return pid == myPid || this->getCmdline(pid).empty();
107     }), pids->end());
108 }
109 
getPartition(pid_t pid)110 Partition ListCommand::getPartition(pid_t pid) {
111     if (pid == NO_PID) return Partition::UNKNOWN;
112     auto it = mPartitions.find(pid);
113     if (it != mPartitions.end()) {
114         return it->second;
115     }
116     Partition partition = android::procpartition::getPartition(pid);
117     mPartitions.emplace(pid, partition);
118     return partition;
119 }
120 
121 // Give sensible defaults when nothing can be inferred from runtime.
122 // process: Partition inferred from executable location or cmdline.
resolvePartition(Partition process,const FqInstance & fqInstance) const123 Partition ListCommand::resolvePartition(Partition process, const FqInstance& fqInstance) const {
124     if (fqInstance.inPackage("vendor") || fqInstance.inPackage("com")) {
125         return Partition::VENDOR;
126     }
127 
128     if (fqInstance.inPackage("android.frameworks") || fqInstance.inPackage("android.system") ||
129         fqInstance.inPackage("android.hidl")) {
130         return Partition::SYSTEM;
131     }
132 
133     // Some android.hardware HALs are served from system. Check the value from executable
134     // location / cmdline first.
135     if (fqInstance.inPackage("android.hardware")) {
136         if (process != Partition::UNKNOWN) {
137             return process;
138         }
139         return Partition::VENDOR;
140     }
141 
142     return process;
143 }
144 
match(const vintf::ManifestInstance & instance,const FqInstance & fqInstance,vintf::TransportArch ta)145 bool match(const vintf::ManifestInstance& instance, const FqInstance& fqInstance,
146            vintf::TransportArch ta) {
147     // For hwbinder libs, allow missing arch in manifest.
148     // For passthrough libs, allow missing interface/instance in table.
149     return (ta.transport == instance.transport()) &&
150             (ta.transport == vintf::Transport::HWBINDER ||
151              vintf::contains(instance.arch(), ta.arch)) &&
152             (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) &&
153             (!fqInstance.hasInstance() || fqInstance.getInstance() == instance.instance());
154 }
155 
match(const vintf::MatrixInstance & instance,const FqInstance & fqInstance,vintf::TransportArch)156 bool match(const vintf::MatrixInstance& instance, const FqInstance& fqInstance,
157            vintf::TransportArch /* ta */) {
158     return (!fqInstance.hasInterface() || fqInstance.getInterface() == instance.interface()) &&
159             (!fqInstance.hasInstance() || instance.matchInstance(fqInstance.getInstance()));
160 }
161 
162 template <typename ObjectType>
getVintfInfo(const std::shared_ptr<const ObjectType> & object,const FqInstance & fqInstance,vintf::TransportArch ta,VintfInfo value)163 VintfInfo getVintfInfo(const std::shared_ptr<const ObjectType>& object,
164                        const FqInstance& fqInstance, vintf::TransportArch ta, VintfInfo value) {
165     bool found = false;
166     (void)object->forEachInstanceOfVersion(fqInstance.getPackage(), fqInstance.getVersion(),
167                                            [&](const auto& instance) {
168                                                found = match(instance, fqInstance, ta);
169                                                return !found; // continue if not found
170                                            });
171     return found ? value : VINTF_INFO_EMPTY;
172 }
173 
getDeviceManifest() const174 std::shared_ptr<const vintf::HalManifest> ListCommand::getDeviceManifest() const {
175     return vintf::VintfObject::GetDeviceHalManifest();
176 }
177 
getDeviceMatrix() const178 std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getDeviceMatrix() const {
179     return vintf::VintfObject::GetDeviceCompatibilityMatrix();
180 }
181 
getFrameworkManifest() const182 std::shared_ptr<const vintf::HalManifest> ListCommand::getFrameworkManifest() const {
183     return vintf::VintfObject::GetFrameworkHalManifest();
184 }
185 
getFrameworkMatrix() const186 std::shared_ptr<const vintf::CompatibilityMatrix> ListCommand::getFrameworkMatrix() const {
187     return vintf::VintfObject::GetFrameworkCompatibilityMatrix();
188 }
189 
getVintfInfo(const std::string & fqInstanceName,vintf::TransportArch ta) const190 VintfInfo ListCommand::getVintfInfo(const std::string& fqInstanceName,
191                                     vintf::TransportArch ta) const {
192     FqInstance fqInstance;
193     if (!fqInstance.setTo(fqInstanceName) &&
194         // Ignore interface / instance for passthrough libs
195         !fqInstance.setTo(getPackageAndVersion(fqInstanceName))) {
196         err() << "Warning: Cannot parse '" << fqInstanceName << "'; no VINTF info." << std::endl;
197         return VINTF_INFO_EMPTY;
198     }
199 
200     return lshal::getVintfInfo(getDeviceManifest(), fqInstance, ta, DEVICE_MANIFEST) |
201             lshal::getVintfInfo(getFrameworkManifest(), fqInstance, ta, FRAMEWORK_MANIFEST) |
202             lshal::getVintfInfo(getDeviceMatrix(), fqInstance, ta, DEVICE_MATRIX) |
203             lshal::getVintfInfo(getFrameworkMatrix(), fqInstance, ta, FRAMEWORK_MATRIX);
204 }
205 
scanBinderContext(pid_t pid,const std::string & contextName,std::function<void (const std::string &)> eachLine)206 static bool scanBinderContext(pid_t pid,
207         const std::string &contextName,
208         std::function<void(const std::string&)> eachLine) {
209     std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
210     if (!ifs.is_open()) {
211         return false;
212     }
213 
214     static const std::regex kContextLine("^context (\\w+)$");
215 
216     bool isDesiredContext = false;
217     std::string line;
218     std::smatch match;
219     while(getline(ifs, line)) {
220         if (std::regex_search(line, match, kContextLine)) {
221             isDesiredContext = match.str(1) == contextName;
222             continue;
223         }
224 
225         if (!isDesiredContext) {
226             continue;
227         }
228 
229         eachLine(line);
230     }
231     return true;
232 }
233 
getPidInfo(pid_t serverPid,PidInfo * pidInfo) const234 bool ListCommand::getPidInfo(
235         pid_t serverPid, PidInfo *pidInfo) const {
236     static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+");
237     static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)");
238 
239     std::smatch match;
240     return scanBinderContext(serverPid, "hwbinder", [&](const std::string& line) {
241         if (std::regex_search(line, match, kReferencePrefix)) {
242             const std::string &ptrString = "0x" + match.str(2); // use number after c
243             uint64_t ptr;
244             if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) {
245                 // Should not reach here, but just be tolerant.
246                 err() << "Could not parse number " << ptrString << std::endl;
247                 return;
248             }
249             const std::string proc = " proc ";
250             auto pos = line.rfind(proc);
251             if (pos != std::string::npos) {
252                 for (const std::string &pidStr : split(line.substr(pos + proc.size()), ' ')) {
253                     int32_t pid;
254                     if (!::android::base::ParseInt(pidStr, &pid)) {
255                         err() << "Could not parse number " << pidStr << std::endl;
256                         return;
257                     }
258                     pidInfo->refPids[ptr].push_back(pid);
259                 }
260             }
261 
262             return;
263         }
264 
265         if (std::regex_search(line, match, kThreadPrefix)) {
266             // "1" is waiting in binder driver
267             // "2" is poll. It's impossible to tell if these are in use.
268             //     and HIDL default code doesn't use it.
269             bool isInUse = match.str(1) != "1";
270             // "0" is a thread that has called into binder
271             // "1" is looper thread
272             // "2" is main looper thread
273             bool isHwbinderThread = match.str(2) != "0";
274 
275             if (!isHwbinderThread) {
276                 return;
277             }
278 
279             if (isInUse) {
280                 pidInfo->threadUsage++;
281             }
282 
283             pidInfo->threadCount++;
284             return;
285         }
286 
287         // not reference or thread line
288         return;
289     });
290 }
291 
getPidInfoCached(pid_t serverPid)292 const PidInfo* ListCommand::getPidInfoCached(pid_t serverPid) {
293     auto pair = mCachedPidInfos.insert({serverPid, PidInfo{}});
294     if (pair.second /* did insertion take place? */) {
295         if (!getPidInfo(serverPid, &pair.first->second)) {
296             return nullptr;
297         }
298     }
299     return &pair.first->second;
300 }
301 
shouldFetchHalType(const HalType & type) const302 bool ListCommand::shouldFetchHalType(const HalType &type) const {
303     return (std::find(mFetchTypes.begin(), mFetchTypes.end(), type) != mFetchTypes.end());
304 }
305 
tableForType(HalType type)306 Table* ListCommand::tableForType(HalType type) {
307     switch (type) {
308         case HalType::BINDERIZED_SERVICES:
309             return &mServicesTable;
310         case HalType::PASSTHROUGH_CLIENTS:
311             return &mPassthroughRefTable;
312         case HalType::PASSTHROUGH_LIBRARIES:
313             return &mImplementationsTable;
314         case HalType::VINTF_MANIFEST:
315             return &mManifestHalsTable;
316         case HalType::LAZY_HALS:
317             return &mLazyHalsTable;
318         default:
319             LOG(FATAL) << "Unknown HAL type " << static_cast<int64_t>(type);
320             return nullptr;
321     }
322 }
tableForType(HalType type) const323 const Table* ListCommand::tableForType(HalType type) const {
324     return const_cast<ListCommand*>(this)->tableForType(type);
325 }
326 
forEachTable(const std::function<void (Table &)> & f)327 void ListCommand::forEachTable(const std::function<void(Table &)> &f) {
328     for (const auto& type : mListTypes) {
329         f(*tableForType(type));
330     }
331 }
forEachTable(const std::function<void (const Table &)> & f) const332 void ListCommand::forEachTable(const std::function<void(const Table &)> &f) const {
333     for (const auto& type : mListTypes) {
334         f(*tableForType(type));
335     }
336 }
337 
postprocess()338 void ListCommand::postprocess() {
339     forEachTable([this](Table &table) {
340         if (mSortColumn) {
341             std::sort(table.begin(), table.end(), mSortColumn);
342         }
343         for (TableEntry &entry : table) {
344             entry.serverCmdline = getCmdline(entry.serverPid);
345             removeDeadProcesses(&entry.clientPids);
346             for (auto pid : entry.clientPids) {
347                 entry.clientCmdlines.push_back(this->getCmdline(pid));
348             }
349         }
350         for (TableEntry& entry : table) {
351             if (entry.partition == Partition::UNKNOWN) {
352                 entry.partition = getPartition(entry.serverPid);
353             }
354             entry.vintfInfo = getVintfInfo(entry.interfaceName, {entry.transport, entry.arch});
355         }
356     });
357     // use a double for loop here because lshal doesn't care about efficiency.
358     for (TableEntry &packageEntry : mImplementationsTable) {
359         std::string packageName = packageEntry.interfaceName;
360         FQName fqPackageName;
361         if (!FQName::parse(packageName.substr(0, packageName.find("::")), &fqPackageName)) {
362             continue;
363         }
364         for (TableEntry &interfaceEntry : mPassthroughRefTable) {
365             if (interfaceEntry.arch != vintf::Arch::ARCH_EMPTY) {
366                 continue;
367             }
368             FQName interfaceName;
369             if (!FQName::parse(splitFirst(interfaceEntry.interfaceName, '/').first, &interfaceName)) {
370                 continue;
371             }
372             if (interfaceName.getPackageAndVersion() == fqPackageName) {
373                 interfaceEntry.arch = packageEntry.arch;
374             }
375         }
376     }
377 
378     mServicesTable.setDescription(
379             "| All binderized services (registered with hwservicemanager)");
380     mPassthroughRefTable.setDescription(
381             "| All interfaces that getService() has ever returned as a passthrough interface;\n"
382             "| PIDs / processes shown below might be inaccurate because the process\n"
383             "| might have relinquished the interface or might have died.\n"
384             "| The Server / Server CMD column can be ignored.\n"
385             "| The Clients / Clients CMD column shows all process that have ever dlopen'ed \n"
386             "| the library and successfully fetched the passthrough implementation.");
387     mImplementationsTable.setDescription(
388             "| All available passthrough implementations (all -impl.so files).\n"
389             "| These may return subclasses through their respective HIDL_FETCH_I* functions.");
390     mManifestHalsTable.setDescription(
391             "| All HALs that are in VINTF manifest.");
392     mLazyHalsTable.setDescription(
393             "| All HALs that are declared in VINTF manifest:\n"
394             "|    - as hwbinder HALs but are not registered to hwservicemanager, and\n"
395             "|    - as hwbinder/passthrough HALs with no implementation.");
396 }
397 
addEntryWithInstance(const TableEntry & entry,vintf::HalManifest * manifest) const398 bool ListCommand::addEntryWithInstance(const TableEntry& entry,
399                                        vintf::HalManifest* manifest) const {
400     FqInstance fqInstance;
401     if (!fqInstance.setTo(entry.interfaceName)) {
402         err() << "Warning: '" << entry.interfaceName << "' is not a valid FqInstance." << std::endl;
403         return false;
404     }
405 
406     if (fqInstance.getPackage() == gIBaseFqName.package()) {
407         return true; // always remove IBase from manifest
408     }
409 
410     Partition partition = resolvePartition(entry.partition, fqInstance);
411 
412     if (partition == Partition::UNKNOWN) {
413         err() << "Warning: Cannot guess the partition of FqInstance " << fqInstance.string()
414               << std::endl;
415         return false;
416     }
417 
418     if (partition != mVintfPartition) {
419         return true; // strip out instances that is in a different partition.
420     }
421 
422     vintf::Arch arch;
423     if (entry.transport == vintf::Transport::HWBINDER) {
424         arch = vintf::Arch::ARCH_EMPTY; // no need to specify arch in manifest
425     } else if (entry.transport == vintf::Transport::PASSTHROUGH) {
426         if (entry.arch == vintf::Arch::ARCH_EMPTY) {
427             err() << "Warning: '" << entry.interfaceName << "' doesn't have bitness info.";
428             return false;
429         }
430         arch = entry.arch;
431     } else {
432         err() << "Warning: '" << entry.transport << "' is not a valid transport." << std::endl;
433         return false;
434     }
435 
436     std::string e;
437     if (!manifest->insertInstance(fqInstance, entry.transport, arch, vintf::HalFormat::HIDL, &e)) {
438         err() << "Warning: Cannot insert '" << fqInstance.string() << ": " << e << std::endl;
439         return false;
440     }
441     return true;
442 }
443 
addEntryWithoutInstance(const TableEntry & entry,const vintf::HalManifest * manifest) const444 bool ListCommand::addEntryWithoutInstance(const TableEntry& entry,
445                                           const vintf::HalManifest* manifest) const {
446     const auto& packageAndVersion = splitFirst(getPackageAndVersion(entry.interfaceName), '@');
447     const auto& package = packageAndVersion.first;
448     vintf::Version version;
449     if (!vintf::parse(packageAndVersion.second, &version)) {
450         err() << "Warning: Cannot parse version '" << packageAndVersion.second << "' for entry '"
451               << entry.interfaceName << "'" << std::endl;
452         return false;
453     }
454 
455     bool found = false;
456     (void)manifest->forEachInstanceOfVersion(package, version, [&found](const auto&) {
457         found = true;
458         return false; // break
459     });
460     return found;
461 }
462 
dumpVintf(const NullableOStream<std::ostream> & out) const463 void ListCommand::dumpVintf(const NullableOStream<std::ostream>& out) const {
464     using vintf::operator|=;
465     using vintf::operator<<;
466     using namespace std::placeholders;
467 
468     vintf::HalManifest manifest;
469     manifest.setType(toSchemaType(mVintfPartition));
470 
471     std::vector<std::string> error;
472     for (const TableEntry& entry : mServicesTable)
473         if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName);
474     for (const TableEntry& entry : mPassthroughRefTable)
475         if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName);
476     for (const TableEntry& entry : mManifestHalsTable)
477         if (!addEntryWithInstance(entry, &manifest)) error.push_back(entry.interfaceName);
478 
479     std::vector<std::string> passthrough;
480     for (const TableEntry& entry : mImplementationsTable)
481         if (!addEntryWithoutInstance(entry, &manifest)) passthrough.push_back(entry.interfaceName);
482 
483     out << "<!-- " << std::endl
484         << "    This is a skeleton " << manifest.type() << " manifest. Notes: " << std::endl
485         << INIT_VINTF_NOTES;
486     if (!error.empty()) {
487         out << std::endl << "    The following HALs are not added; see warnings." << std::endl;
488         for (const auto& e : error) {
489             out << "        " << e << std::endl;
490         }
491     }
492     if (!passthrough.empty()) {
493         out << std::endl
494             << "    The following HALs are passthrough and no interface or instance " << std::endl
495             << "    names can be inferred." << std::endl;
496         for (const auto& e : passthrough) {
497             out << "        " << e << std::endl;
498         }
499     }
500     out << "-->" << std::endl;
501     out << vintf::gHalManifestConverter(manifest, vintf::SerializeFlags::HALS_ONLY);
502 }
503 
504 std::string ListCommand::INIT_VINTF_NOTES{
505     "    1. If a HAL is supported in both hwbinder and passthrough transport,\n"
506     "       only hwbinder is shown.\n"
507     "    2. It is likely that HALs in passthrough transport does not have\n"
508     "       <interface> declared; users will have to write them by hand.\n"
509     "    3. A HAL with lower minor version can be overridden by a HAL with\n"
510     "       higher minor version if they have the same name and major version.\n"
511     "    4. This output is intended for launch devices.\n"
512     "       Upgrading devices should not use this tool to generate device\n"
513     "       manifest and replace the existing manifest directly, but should\n"
514     "       edit the existing manifest manually.\n"
515     "       Specifically, devices which launched at Android O-MR1 or earlier\n"
516     "       should not use the 'fqname' format for required HAL entries and\n"
517     "       should instead use the legacy package, name, instance-name format\n"
518     "       until they are updated.\n"
519 };
520 
fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a)521 static vintf::Arch fromBaseArchitecture(::android::hidl::base::V1_0::DebugInfo::Architecture a) {
522     switch (a) {
523         case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_64BIT:
524             return vintf::Arch::ARCH_64;
525         case ::android::hidl::base::V1_0::DebugInfo::Architecture::IS_32BIT:
526             return vintf::Arch::ARCH_32;
527         case ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN: // fallthrough
528         default:
529             return vintf::Arch::ARCH_EMPTY;
530     }
531 }
532 
dumpTable(const NullableOStream<std::ostream> & out) const533 void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const {
534     if (mNeat) {
535         std::vector<const Table*> tables;
536         forEachTable([&tables](const Table &table) {
537             tables.push_back(&table);
538         });
539         MergedTable(std::move(tables)).createTextTable().dump(out.buf());
540         return;
541     }
542 
543     forEachTable([this, &out](const Table &table) {
544 
545         // We're only interested in dumping debug info for already
546         // instantiated services. There's little value in dumping the
547         // debug info for a service we create on the fly, so we only operate
548         // on the "mServicesTable".
549         std::function<std::string(const std::string&)> emitDebugInfo = nullptr;
550         if (mEmitDebugInfo && &table == &mServicesTable) {
551             emitDebugInfo = [this](const auto& iName) {
552                 std::stringstream ss;
553                 auto pair = splitFirst(iName, '/');
554                 mLshal.emitDebugInfo(pair.first, pair.second, {},
555                                      false /* excludesParentInstances */, ss,
556                                      NullableOStream<std::ostream>(nullptr));
557                 return ss.str();
558             };
559         }
560         table.createTextTable(mNeat, emitDebugInfo).dump(out.buf());
561         out << std::endl;
562     });
563 }
564 
dump()565 Status ListCommand::dump() {
566     auto dump = mVintf ? &ListCommand::dumpVintf : &ListCommand::dumpTable;
567 
568     if (mFileOutputPath.empty()) {
569         (*this.*dump)(out());
570         return OK;
571     }
572 
573     std::ofstream fileOutput(mFileOutputPath);
574     if (!fileOutput.is_open()) {
575         err() << "Could not open file '" << mFileOutputPath << "'." << std::endl;
576         return IO_ERROR;
577     }
578     chown(mFileOutputPath.c_str(), AID_SHELL, AID_SHELL);
579 
580     (*this.*dump)(NullableOStream<std::ostream>(fileOutput));
581 
582     fileOutput.flush();
583     fileOutput.close();
584     return OK;
585 }
586 
putEntry(HalType type,TableEntry && entry)587 void ListCommand::putEntry(HalType type, TableEntry &&entry) {
588     tableForType(type)->add(std::forward<TableEntry>(entry));
589 }
590 
fetchAllLibraries(const sp<IServiceManager> & manager)591 Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) {
592     if (!shouldFetchHalType(HalType::PASSTHROUGH_LIBRARIES)) { return OK; }
593 
594     using namespace ::android::hardware;
595     using namespace ::android::hidl::manager::V1_0;
596     using namespace ::android::hidl::base::V1_0;
597     using std::literals::chrono_literals::operator""s;
598     auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) {
599         std::map<std::string, TableEntry> entries;
600         for (const auto &info : infos) {
601             std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" +
602                     std::string{info.instanceName.c_str()};
603             entries.emplace(interfaceName, TableEntry{
604                 .interfaceName = interfaceName,
605                 .transport = vintf::Transport::PASSTHROUGH,
606                 .clientPids = info.clientPids,
607             }).first->second.arch |= fromBaseArchitecture(info.arch);
608         }
609         for (auto &&pair : entries) {
610             putEntry(HalType::PASSTHROUGH_LIBRARIES, std::move(pair.second));
611         }
612     });
613     if (!ret.isOk()) {
614         err() << "Error: Failed to call list on getPassthroughServiceManager(): "
615              << ret.description() << std::endl;
616         return DUMP_ALL_LIBS_ERROR;
617     }
618     return OK;
619 }
620 
fetchPassthrough(const sp<IServiceManager> & manager)621 Status ListCommand::fetchPassthrough(const sp<IServiceManager> &manager) {
622     if (!shouldFetchHalType(HalType::PASSTHROUGH_CLIENTS)) { return OK; }
623 
624     using namespace ::android::hardware;
625     using namespace ::android::hardware::details;
626     using namespace ::android::hidl::manager::V1_0;
627     using namespace ::android::hidl::base::V1_0;
628     auto ret = timeoutIPC(manager, &IServiceManager::debugDump, [&] (const auto &infos) {
629         for (const auto &info : infos) {
630             if (info.clientPids.size() <= 0) {
631                 continue;
632             }
633             putEntry(HalType::PASSTHROUGH_CLIENTS, {
634                 .interfaceName =
635                         std::string{info.interfaceName.c_str()} + "/" +
636                         std::string{info.instanceName.c_str()},
637                 .transport = vintf::Transport::PASSTHROUGH,
638                 .serverPid = info.clientPids.size() == 1 ? info.clientPids[0] : NO_PID,
639                 .clientPids = info.clientPids,
640                 .arch = fromBaseArchitecture(info.arch)
641             });
642         }
643     });
644     if (!ret.isOk()) {
645         err() << "Error: Failed to call debugDump on defaultServiceManager(): "
646              << ret.description() << std::endl;
647         return DUMP_PASSTHROUGH_ERROR;
648     }
649     return OK;
650 }
651 
fetchBinderized(const sp<IServiceManager> & manager)652 Status ListCommand::fetchBinderized(const sp<IServiceManager> &manager) {
653     using vintf::operator<<;
654 
655     if (!shouldFetchHalType(HalType::BINDERIZED_SERVICES)) { return OK; }
656 
657     const vintf::Transport mode = vintf::Transport::HWBINDER;
658     hidl_vec<hidl_string> fqInstanceNames;
659     // copying out for timeoutIPC
660     auto listRet = timeoutIPC(manager, &IServiceManager::list, [&] (const auto &names) {
661         fqInstanceNames = names;
662     });
663     if (!listRet.isOk()) {
664         err() << "Error: Failed to list services for " << mode << ": "
665              << listRet.description() << std::endl;
666         return DUMP_BINDERIZED_ERROR;
667     }
668 
669     Status status = OK;
670     std::map<std::string, TableEntry> allTableEntries;
671     for (const auto &fqInstanceName : fqInstanceNames) {
672         // create entry and default assign all fields.
673         TableEntry& entry = allTableEntries[fqInstanceName];
674         entry.interfaceName = fqInstanceName;
675         entry.transport = mode;
676         entry.serviceStatus = ServiceStatus::NON_RESPONSIVE;
677 
678         status |= fetchBinderizedEntry(manager, &entry);
679     }
680 
681     for (auto& pair : allTableEntries) {
682         putEntry(HalType::BINDERIZED_SERVICES, std::move(pair.second));
683     }
684     return status;
685 }
686 
fetchBinderizedEntry(const sp<IServiceManager> & manager,TableEntry * entry)687 Status ListCommand::fetchBinderizedEntry(const sp<IServiceManager> &manager,
688                                          TableEntry *entry) {
689     Status status = OK;
690     const auto handleError = [&](Status additionalError, const std::string& msg) {
691         err() << "Warning: Skipping \"" << entry->interfaceName << "\": " << msg << std::endl;
692         status |= DUMP_BINDERIZED_ERROR | additionalError;
693     };
694 
695     const auto pair = splitFirst(entry->interfaceName, '/');
696     const auto &serviceName = pair.first;
697     const auto &instanceName = pair.second;
698     auto getRet = timeoutIPC(manager, &IServiceManager::get, serviceName, instanceName);
699     if (!getRet.isOk()) {
700         handleError(TRANSACTION_ERROR,
701                     "cannot be fetched from service manager:" + getRet.description());
702         return status;
703     }
704     sp<IBase> service = getRet;
705     if (service == nullptr) {
706         handleError(NO_INTERFACE, "cannot be fetched from service manager (null)");
707         return status;
708     }
709 
710     // getDebugInfo
711     do {
712         DebugInfo debugInfo;
713         auto debugRet = timeoutIPC(service, &IBase::getDebugInfo, [&] (const auto &received) {
714             debugInfo = received;
715         });
716         if (!debugRet.isOk()) {
717             handleError(TRANSACTION_ERROR,
718                         "debugging information cannot be retrieved: " + debugRet.description());
719             break; // skip getPidInfo
720         }
721 
722         entry->serverPid = debugInfo.pid;
723         entry->serverObjectAddress = debugInfo.ptr;
724         entry->arch = fromBaseArchitecture(debugInfo.arch);
725 
726         if (debugInfo.pid != NO_PID) {
727             const PidInfo* pidInfo = getPidInfoCached(debugInfo.pid);
728             if (pidInfo == nullptr) {
729                 handleError(IO_ERROR,
730                             "no information for PID " + std::to_string(debugInfo.pid) +
731                             ", are you root?");
732                 break;
733             }
734             if (debugInfo.ptr != NO_PTR) {
735                 auto it = pidInfo->refPids.find(debugInfo.ptr);
736                 if (it != pidInfo->refPids.end()) {
737                     entry->clientPids = it->second;
738                 }
739             }
740             entry->threadUsage = pidInfo->threadUsage;
741             entry->threadCount = pidInfo->threadCount;
742         }
743     } while (0);
744 
745     // hash
746     do {
747         ssize_t hashIndex = -1;
748         auto ifaceChainRet = timeoutIPC(service, &IBase::interfaceChain, [&] (const auto& c) {
749             for (size_t i = 0; i < c.size(); ++i) {
750                 if (serviceName == c[i]) {
751                     hashIndex = static_cast<ssize_t>(i);
752                     break;
753                 }
754             }
755         });
756         if (!ifaceChainRet.isOk()) {
757             handleError(TRANSACTION_ERROR,
758                         "interfaceChain fails: " + ifaceChainRet.description());
759             break; // skip getHashChain
760         }
761         if (hashIndex < 0) {
762             handleError(BAD_IMPL, "Interface name does not exist in interfaceChain.");
763             break; // skip getHashChain
764         }
765         auto hashRet = timeoutIPC(service, &IBase::getHashChain, [&] (const auto& hashChain) {
766             if (static_cast<size_t>(hashIndex) >= hashChain.size()) {
767                 handleError(BAD_IMPL,
768                             "interfaceChain indicates position " + std::to_string(hashIndex) +
769                             " but getHashChain returns " + std::to_string(hashChain.size()) +
770                             " hashes");
771                 return;
772             }
773 
774             auto&& hashArray = hashChain[hashIndex];
775             std::vector<uint8_t> hashVec{hashArray.data(), hashArray.data() + hashArray.size()};
776             entry->hash = Hash::hexString(hashVec);
777         });
778         if (!hashRet.isOk()) {
779             handleError(TRANSACTION_ERROR, "getHashChain failed: " + hashRet.description());
780         }
781     } while (0);
782     if (status == OK) {
783         entry->serviceStatus = ServiceStatus::ALIVE;
784     }
785     return status;
786 }
787 
fetchManifestHals()788 Status ListCommand::fetchManifestHals() {
789     if (!shouldFetchHalType(HalType::VINTF_MANIFEST)) { return OK; }
790     Status status = OK;
791 
792     for (auto manifest : {getDeviceManifest(), getFrameworkManifest()}) {
793         if (manifest == nullptr) {
794             status |= VINTF_ERROR;
795             continue;
796         }
797 
798         std::map<std::string, TableEntry> entries;
799 
800         manifest->forEachInstance([&] (const vintf::ManifestInstance& manifestInstance) {
801             TableEntry entry{
802                 .interfaceName = manifestInstance.getFqInstance().string(),
803                 .transport = manifestInstance.transport(),
804                 .arch = manifestInstance.arch(),
805                 // TODO(b/71555570): Device manifest does not distinguish HALs from vendor or ODM.
806                 .partition = toPartition(manifest->type()),
807                 .serviceStatus = ServiceStatus::DECLARED};
808             std::string key = entry.interfaceName;
809             entries.emplace(std::move(key), std::move(entry));
810             return true;
811         });
812 
813         for (auto&& pair : entries)
814             mManifestHalsTable.add(std::move(pair.second));
815     }
816     return status;
817 }
818 
fetchLazyHals()819 Status ListCommand::fetchLazyHals() {
820     using vintf::operator<<;
821 
822     if (!shouldFetchHalType(HalType::LAZY_HALS)) { return OK; }
823     Status status = OK;
824 
825     for (const TableEntry& manifestEntry : mManifestHalsTable) {
826         if (manifestEntry.transport == vintf::Transport::HWBINDER) {
827             if (!hasHwbinderEntry(manifestEntry)) {
828                 mLazyHalsTable.add(TableEntry(manifestEntry));
829             }
830             continue;
831         }
832         if (manifestEntry.transport == vintf::Transport::PASSTHROUGH) {
833             if (!hasPassthroughEntry(manifestEntry)) {
834                 mLazyHalsTable.add(TableEntry(manifestEntry));
835             }
836             continue;
837         }
838         err() << "Warning: unrecognized transport in VINTF manifest: "
839               << manifestEntry.transport;
840         status |= VINTF_ERROR;
841     }
842     return status;
843 }
844 
hasHwbinderEntry(const TableEntry & entry) const845 bool ListCommand::hasHwbinderEntry(const TableEntry& entry) const {
846     for (const TableEntry& existing : mServicesTable) {
847         if (existing.interfaceName == entry.interfaceName) {
848             return true;
849         }
850     }
851     return false;
852 }
853 
hasPassthroughEntry(const TableEntry & entry) const854 bool ListCommand::hasPassthroughEntry(const TableEntry& entry) const {
855     FqInstance entryFqInstance;
856     if (!entryFqInstance.setTo(entry.interfaceName)) {
857         return false; // cannot parse, so add it anyway.
858     }
859     for (const TableEntry& existing : mImplementationsTable) {
860         FqInstance existingFqInstance;
861         if (!existingFqInstance.setTo(getPackageAndVersion(existing.interfaceName))) {
862             continue;
863         }
864 
865         // For example, manifest may say graphics.mapper@2.1 but passthroughServiceManager
866         // can only list graphics.mapper@2.0.
867         if (entryFqInstance.getPackage() == existingFqInstance.getPackage() &&
868             vintf::Version{entryFqInstance.getVersion()}
869                 .minorAtLeast(vintf::Version{existingFqInstance.getVersion()})) {
870             return true;
871         }
872     }
873     return false;
874 }
875 
fetch()876 Status ListCommand::fetch() {
877     Status status = OK;
878     auto bManager = mLshal.serviceManager();
879     if (bManager == nullptr) {
880         err() << "Failed to get defaultServiceManager()!" << std::endl;
881         status |= NO_BINDERIZED_MANAGER;
882     } else {
883         status |= fetchBinderized(bManager);
884         // Passthrough PIDs are registered to the binderized manager as well.
885         status |= fetchPassthrough(bManager);
886     }
887 
888     auto pManager = mLshal.passthroughManager();
889     if (pManager == nullptr) {
890         err() << "Failed to get getPassthroughServiceManager()!" << std::endl;
891         status |= NO_PASSTHROUGH_MANAGER;
892     } else {
893         status |= fetchAllLibraries(pManager);
894     }
895     status |= fetchManifestHals();
896     status |= fetchLazyHals();
897     return status;
898 }
899 
initFetchTypes()900 void ListCommand::initFetchTypes() {
901     // TODO: refactor to do polymorphism on each table (so that dependency graph is not hardcoded).
902     static const std::map<HalType, std::set<HalType>> kDependencyGraph{
903         {HalType::LAZY_HALS, {HalType::BINDERIZED_SERVICES,
904                               HalType::PASSTHROUGH_LIBRARIES,
905                               HalType::VINTF_MANIFEST}},
906     };
907     mFetchTypes.insert(mListTypes.begin(), mListTypes.end());
908     for (HalType listType : mListTypes) {
909         auto it = kDependencyGraph.find(listType);
910         if (it != kDependencyGraph.end()) {
911             mFetchTypes.insert(it->second.begin(), it->second.end());
912         }
913     }
914 }
915 
registerAllOptions()916 void ListCommand::registerAllOptions() {
917     int v = mOptions.size();
918     // A list of acceptable command line options
919     // key: value returned by getopt_long
920     // long options with short alternatives
921     mOptions.push_back({'h', "help", no_argument, v++, [](ListCommand*, const char*) {
922         return USAGE;
923     }, ""});
924     mOptions.push_back({'i', "interface", no_argument, v++, [](ListCommand* thiz, const char*) {
925         thiz->mSelectedColumns.push_back(TableColumnType::INTERFACE_NAME);
926         return OK;
927     }, "print the instance name column"});
928     mOptions.push_back({'l', "released", no_argument, v++, [](ListCommand* thiz, const char*) {
929         thiz->mSelectedColumns.push_back(TableColumnType::RELEASED);
930         return OK;
931     }, "print the 'is released?' column\n(Y=released, N=unreleased, ?=unknown)"});
932     mOptions.push_back({'t', "transport", no_argument, v++, [](ListCommand* thiz, const char*) {
933         thiz->mSelectedColumns.push_back(TableColumnType::TRANSPORT);
934         return OK;
935     }, "print the transport mode column"});
936     mOptions.push_back({'r', "arch", no_argument, v++, [](ListCommand* thiz, const char*) {
937         thiz->mSelectedColumns.push_back(TableColumnType::ARCH);
938         return OK;
939     }, "print the bitness column"});
940     mOptions.push_back({'s', "hash", no_argument, v++, [](ListCommand* thiz, const char*) {
941         thiz->mSelectedColumns.push_back(TableColumnType::HASH);
942         return OK;
943     }, "print hash of the interface"});
944     mOptions.push_back({'p', "pid", no_argument, v++, [](ListCommand* thiz, const char*) {
945         thiz->mSelectedColumns.push_back(TableColumnType::SERVER_PID);
946         return OK;
947     }, "print the server PID, or server cmdline if -m is set"});
948     mOptions.push_back({'a', "address", no_argument, v++, [](ListCommand* thiz, const char*) {
949         thiz->mSelectedColumns.push_back(TableColumnType::SERVER_ADDR);
950         return OK;
951     }, "print the server object address column"});
952     mOptions.push_back({'c', "clients", no_argument, v++, [](ListCommand* thiz, const char*) {
953         thiz->mSelectedColumns.push_back(TableColumnType::CLIENT_PIDS);
954         return OK;
955     }, "print the client PIDs, or client cmdlines if -m is set"});
956     mOptions.push_back({'e', "threads", no_argument, v++, [](ListCommand* thiz, const char*) {
957         thiz->mSelectedColumns.push_back(TableColumnType::THREADS);
958         return OK;
959     }, "print currently used/available threads\n(note, available threads created lazily)"});
960     mOptions.push_back({'m', "cmdline", no_argument, v++, [](ListCommand* thiz, const char*) {
961         thiz->mEnableCmdlines = true;
962         return OK;
963     }, "print cmdline instead of PIDs"});
964     mOptions.push_back({'d', "debug", optional_argument, v++, [](ListCommand* thiz, const char* arg) {
965         thiz->mEmitDebugInfo = true;
966         if (arg) thiz->mFileOutputPath = arg;
967         return OK;
968     }, "Emit debug info from\nIBase::debug with empty options. Cannot be used with --neat.\n"
969         "Writes to specified file if 'arg' is provided, otherwise stdout."});
970 
971     mOptions.push_back({'V', "vintf", no_argument, v++, [](ListCommand* thiz, const char*) {
972         thiz->mSelectedColumns.push_back(TableColumnType::VINTF);
973         return OK;
974     }, "print VINTF info. This column contains a comma-separated list of:\n"
975        "    - DM: if the HAL is in the device manifest\n"
976        "    - DC: if the HAL is in the device compatibility matrix\n"
977        "    - FM: if the HAL is in the framework manifest\n"
978        "    - FC: if the HAL is in the framework compatibility matrix"});
979     mOptions.push_back({'S', "service-status", no_argument, v++, [](ListCommand* thiz, const char*) {
980         thiz->mSelectedColumns.push_back(TableColumnType::SERVICE_STATUS);
981         return OK;
982     }, "print service status column. Possible values are:\n"
983        "    - alive: alive and running hwbinder service;\n"
984        "    - registered;dead: registered to hwservicemanager but is not responsive;\n"
985        "    - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n"
986        "    - N/A: no information for passthrough HALs."});
987 
988     // long options without short alternatives
989     mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) {
990         thiz->mVintf = true;
991         if (thiz->mVintfPartition == Partition::UNKNOWN)
992             thiz->mVintfPartition = Partition::VENDOR;
993         if (arg) thiz->mFileOutputPath = arg;
994         return OK;
995     }, "form a skeleton HAL manifest to specified file,\nor stdout if no file specified."});
996     mOptions.push_back({'\0', "init-vintf-partition", required_argument, v++, [](ListCommand* thiz, const char* arg) {
997         if (!arg) return USAGE;
998         thiz->mVintfPartition = android::procpartition::parsePartition(arg);
999         if (thiz->mVintfPartition == Partition::UNKNOWN) return USAGE;
1000         return OK;
1001     }, "Specify the partition of the HAL manifest\ngenerated by --init-vintf.\n"
1002        "Valid values are 'system', 'vendor', and 'odm'. Default is 'vendor'."});
1003     mOptions.push_back({'\0', "sort", required_argument, v++, [](ListCommand* thiz, const char* arg) {
1004         if (strcmp(arg, "interface") == 0 || strcmp(arg, "i") == 0) {
1005             thiz->mSortColumn = TableEntry::sortByInterfaceName;
1006         } else if (strcmp(arg, "pid") == 0 || strcmp(arg, "p") == 0) {
1007             thiz->mSortColumn = TableEntry::sortByServerPid;
1008         } else {
1009             thiz->err() << "Unrecognized sorting column: " << arg << std::endl;
1010             return USAGE;
1011         }
1012         return OK;
1013     }, "sort by a column. 'arg' can be (i|interface) or (p|pid)."});
1014     mOptions.push_back({'\0', "neat", no_argument, v++, [](ListCommand* thiz, const char*) {
1015         thiz->mNeat = true;
1016         return OK;
1017     }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."});
1018     mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) {
1019         if (!arg) { return USAGE; }
1020 
1021         static const std::map<std::string, HalType> kHalTypeMap {
1022             {"binderized", HalType::BINDERIZED_SERVICES},
1023             {"b", HalType::BINDERIZED_SERVICES},
1024             {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS},
1025             {"c", HalType::PASSTHROUGH_CLIENTS},
1026             {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES},
1027             {"l", HalType::PASSTHROUGH_LIBRARIES},
1028             {"vintf", HalType::VINTF_MANIFEST},
1029             {"v", HalType::VINTF_MANIFEST},
1030             {"lazy", HalType::LAZY_HALS},
1031             {"z", HalType::LAZY_HALS},
1032         };
1033 
1034         std::vector<std::string> halTypesArgs = split(std::string(arg), ',');
1035         for (const auto& halTypeArg : halTypesArgs) {
1036             if (halTypeArg.empty()) continue;
1037 
1038             const auto& halTypeIter = kHalTypeMap.find(halTypeArg);
1039             if (halTypeIter == kHalTypeMap.end()) {
1040 
1041                 thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl;
1042                 return USAGE;
1043             }
1044 
1045             // Append unique (non-repeated) HAL types to the reporting list
1046             HalType halType = halTypeIter->second;
1047             if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) ==
1048                 thiz->mListTypes.end()) {
1049                 thiz->mListTypes.push_back(halType);
1050             }
1051         }
1052 
1053         if (thiz->mListTypes.empty()) { return USAGE; }
1054         return OK;
1055     }, "comma-separated list of one or more sections.\nThe output is restricted to the selected "
1056        "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|"
1057        "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."});
1058 }
1059 
1060 // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining
1061 // the lifetime of "options" during the usage of the returned array.
getLongOptions(const ListCommand::RegisteredOptions & options,int * longOptFlag)1062 static std::unique_ptr<struct option[]> getLongOptions(
1063         const ListCommand::RegisteredOptions& options,
1064         int* longOptFlag) {
1065     std::unique_ptr<struct option[]> ret{new struct option[options.size() + 1]};
1066     int i = 0;
1067     for (const auto& e : options) {
1068         ret[i].name = e.longOption.c_str();
1069         ret[i].has_arg = e.hasArg;
1070         ret[i].flag = longOptFlag;
1071         ret[i].val = e.val;
1072 
1073         i++;
1074     }
1075     // getopt_long last option has all zeros
1076     ret[i].name = nullptr;
1077     ret[i].has_arg = 0;
1078     ret[i].flag = nullptr;
1079     ret[i].val = 0;
1080 
1081     return ret;
1082 }
1083 
1084 // Create 'optstring' argument to getopt_long.
getShortOptions(const ListCommand::RegisteredOptions & options)1085 static std::string getShortOptions(const ListCommand::RegisteredOptions& options) {
1086     std::stringstream ss;
1087     for (const auto& e : options) {
1088         if (e.shortOption != '\0') {
1089             ss << e.shortOption;
1090         }
1091     }
1092     return ss.str();
1093 }
1094 
parseArgs(const Arg & arg)1095 Status ListCommand::parseArgs(const Arg &arg) {
1096     mListTypes.clear();
1097 
1098     if (mOptions.empty()) {
1099         registerAllOptions();
1100     }
1101     int longOptFlag;
1102     std::unique_ptr<struct option[]> longOptions = getLongOptions(mOptions, &longOptFlag);
1103     std::string shortOptions = getShortOptions(mOptions);
1104 
1105     // suppress output to std::err for unknown options
1106     opterr = 0;
1107 
1108     int optionIndex;
1109     int c;
1110     // Lshal::parseArgs has set optind to the next option to parse
1111     for (;;) {
1112         c = getopt_long(arg.argc, arg.argv,
1113                 shortOptions.c_str(), longOptions.get(), &optionIndex);
1114         if (c == -1) {
1115             break;
1116         }
1117         const RegisteredOption* found = nullptr;
1118         if (c == 0) {
1119             // see long option
1120             for (const auto& e : mOptions) {
1121                 if (longOptFlag == e.val) found = &e;
1122             }
1123         } else {
1124             // see short option
1125             for (const auto& e : mOptions) {
1126                 if (c == e.shortOption) found = &e;
1127             }
1128         }
1129 
1130         if (found == nullptr) {
1131             // see unrecognized options
1132             err() << "unrecognized option `" << arg.argv[optind - 1] << "'" << std::endl;
1133             return USAGE;
1134         }
1135 
1136         Status status = found->op(this, optarg);
1137         if (status != OK) {
1138             return status;
1139         }
1140     }
1141     if (optind < arg.argc) {
1142         // see non option
1143         err() << "unrecognized option `" << arg.argv[optind] << "'" << std::endl;
1144         return USAGE;
1145     }
1146 
1147     if (mNeat && mEmitDebugInfo) {
1148         err() << "Error: --neat should not be used with --debug." << std::endl;
1149         return USAGE;
1150     }
1151 
1152     if (mSelectedColumns.empty()) {
1153         mSelectedColumns = {TableColumnType::VINTF, TableColumnType::RELEASED,
1154                             TableColumnType::INTERFACE_NAME, TableColumnType::THREADS,
1155                             TableColumnType::SERVER_PID, TableColumnType::CLIENT_PIDS};
1156     }
1157 
1158     if (mEnableCmdlines) {
1159         for (size_t i = 0; i < mSelectedColumns.size(); ++i) {
1160             if (mSelectedColumns[i] == TableColumnType::SERVER_PID) {
1161                 mSelectedColumns[i] = TableColumnType::SERVER_CMD;
1162             }
1163             if (mSelectedColumns[i] == TableColumnType::CLIENT_PIDS) {
1164                 mSelectedColumns[i] = TableColumnType::CLIENT_CMDS;
1165             }
1166         }
1167     }
1168 
1169     // By default, list all HAL types
1170     if (mListTypes.empty()) {
1171         mListTypes = {HalType::BINDERIZED_SERVICES, HalType::PASSTHROUGH_CLIENTS,
1172                       HalType::PASSTHROUGH_LIBRARIES};
1173     }
1174     initFetchTypes();
1175 
1176     forEachTable([this] (Table& table) {
1177         table.setSelectedColumns(this->mSelectedColumns);
1178     });
1179 
1180     return OK;
1181 }
1182 
main(const Arg & arg)1183 Status ListCommand::main(const Arg &arg) {
1184     Status status = parseArgs(arg);
1185     if (status != OK) {
1186         return status;
1187     }
1188     status = fetch();
1189     postprocess();
1190     status |= dump();
1191     return status;
1192 }
1193 
getHelpMessageForArgument() const1194 const std::string& ListCommand::RegisteredOption::getHelpMessageForArgument() const {
1195     static const std::string empty{};
1196     static const std::string optional{"[=<arg>]"};
1197     static const std::string required{"=<arg>"};
1198 
1199     if (hasArg == optional_argument) {
1200         return optional;
1201     }
1202     if (hasArg == required_argument) {
1203         return required;
1204     }
1205     return empty;
1206 }
1207 
usage() const1208 void ListCommand::usage() const {
1209 
1210     err() << "list:" << std::endl
1211           << "    lshal" << std::endl
1212           << "    lshal list" << std::endl
1213           << "        List all hals with default ordering and columns (`lshal list -Vliepc`)" << std::endl
1214           << "    lshal list [-h|--help]" << std::endl
1215           << "        -h, --help: Print help message for list (`lshal help list`)" << std::endl
1216           << "    lshal [list] [OPTIONS...]" << std::endl;
1217     for (const auto& e : mOptions) {
1218         if (e.help.empty()) {
1219             continue;
1220         }
1221         err() << "        ";
1222         if (e.shortOption != '\0')
1223             err() << "-" << e.shortOption << e.getHelpMessageForArgument();
1224         if (e.shortOption != '\0' && !e.longOption.empty())
1225             err() << ", ";
1226         if (!e.longOption.empty())
1227             err() << "--" << e.longOption << e.getHelpMessageForArgument();
1228         err() << ": ";
1229         std::vector<std::string> lines = split(e.help, '\n');
1230         for (const auto& line : lines) {
1231             if (&line != &lines.front())
1232                 err() << "            ";
1233             err() << line << std::endl;
1234         }
1235     }
1236 }
1237 
1238 }  // namespace lshal
1239 }  // namespace android
1240 
1241