xref: /aosp_15_r20/system/core/fs_mgr/tools/dmctl.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
1 /*
2  * Copyright (C) 2018 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 <errno.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <linux/dm-ioctl.h>
21 #include <sys/ioctl.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24 
25 #include <android-base/logging.h>
26 #include <android-base/parseint.h>
27 #include <android-base/unique_fd.h>
28 #include <libdm/dm.h>
29 
30 #include <fstream>
31 #include <functional>
32 #include <iomanip>
33 #include <ios>
34 #include <iostream>
35 #include <map>
36 #include <optional>
37 #include <sstream>
38 #include <string>
39 #include <vector>
40 
41 using namespace std::literals::string_literals;
42 using namespace std::chrono_literals;
43 using namespace android::dm;
44 using DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;
45 
Usage(void)46 static int Usage(void) {
47     std::cerr << "usage: dmctl <command> [command options]" << std::endl;
48     std::cerr << "       dmctl -f file" << std::endl;
49     std::cerr << "commands:" << std::endl;
50     std::cerr << "  create <dm-name> [-ro] <targets...>" << std::endl;
51     std::cerr << "  delete <dm-name>" << std::endl;
52     std::cerr << "  list <devices | targets> [-v]" << std::endl;
53     std::cerr << "  message <dm-name> <sector> <message>" << std::endl;
54     std::cerr << "  getpath <dm-name>" << std::endl;
55     std::cerr << "  getuuid <dm-name>" << std::endl;
56     std::cerr << "  ima <dm-name>" << std::endl;
57     std::cerr << "  info <dm-name>" << std::endl;
58     std::cerr << "  replace <dm-name> <targets...>" << std::endl;
59     std::cerr << "  status <dm-name>" << std::endl;
60     std::cerr << "  resume <dm-name>" << std::endl;
61     std::cerr << "  suspend <dm-name>" << std::endl;
62     std::cerr << "  table <dm-name>" << std::endl;
63     std::cerr << "  help" << std::endl;
64     std::cerr << std::endl;
65     std::cerr << "-f file reads command and all parameters from named file" << std::endl;
66     std::cerr << std::endl;
67     std::cerr << "Target syntax:" << std::endl;
68     std::cerr << "  <target_type> <start_sector> <num_sectors> [target_data]" << std::endl;
69     return -EINVAL;
70 }
71 
72 class TargetParser final {
73   public:
TargetParser(int argc,char ** argv)74     TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}
75 
More() const76     bool More() const { return arg_index_ < argc_; }
Next()77     std::unique_ptr<DmTarget> Next() {
78         if (!HasArgs(3)) {
79             std::cerr << "Expected <target_type> <start_sector> <num_sectors>" << std::endl;
80             return nullptr;
81         }
82 
83         std::string target_type = NextArg();
84         uint64_t start_sector, num_sectors;
85         if (!android::base::ParseUint(NextArg(), &start_sector)) {
86             std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
87             return nullptr;
88         }
89         if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {
90             std::cerr << "Expected non-zero sector count, got: " << PreviousArg() << std::endl;
91             return nullptr;
92         }
93 
94         if (target_type == "zero") {
95             return std::make_unique<DmTargetZero>(start_sector, num_sectors);
96         } else if (target_type == "linear") {
97             if (!HasArgs(2)) {
98                 std::cerr << "Expected \"linear\" <block_device> <sector>" << std::endl;
99                 return nullptr;
100             }
101 
102             std::string block_device = NextArg();
103             uint64_t physical_sector;
104             if (!android::base::ParseUint(NextArg(), &physical_sector)) {
105                 std::cerr << "Expected sector, got: \"" << PreviousArg() << "\"" << std::endl;
106                 return nullptr;
107             }
108             return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,
109                                                     physical_sector);
110         } else if (target_type == "android-verity") {
111             if (!HasArgs(2)) {
112                 std::cerr << "Expected \"android-verity\" <public-key-id> <block_device>"
113                           << std::endl;
114                 return nullptr;
115             }
116             std::string keyid = NextArg();
117             std::string block_device = NextArg();
118             return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,
119                                                            block_device);
120         } else if (target_type == "striped") {
121             if (!HasArgs(3)) {
122                 std::cerr << "Expected \"striped\" <block_device0> <block_device1> <chunksize>"
123                           << std::endl;
124                 return nullptr;
125             }
126             std::string block_device0 = NextArg();
127             std::string block_device1 = NextArg();
128             uint64_t chunk_size;
129             if (!android::base::ParseUint(NextArg(), &chunk_size)) {
130                 std::cerr << "Expected start sector, got: " << PreviousArg() << std::endl;
131                 return nullptr;
132             }
133             return std::make_unique<DmTargetStripe>(start_sector, num_sectors, chunk_size,
134                                                     block_device0, block_device1);
135         } else if (target_type == "bow") {
136             if (!HasArgs(1)) {
137                 std::cerr << "Expected \"bow\" <block_device>" << std::endl;
138                 return nullptr;
139             }
140             std::string block_device = NextArg();
141             return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);
142         } else if (target_type == "snapshot-origin") {
143             if (!HasArgs(1)) {
144                 std::cerr << "Expected \"snapshot-origin\" <block_device>" << std::endl;
145                 return nullptr;
146             }
147             std::string block_device = NextArg();
148             return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,
149                                                             block_device);
150         } else if (target_type == "snapshot") {
151             if (!HasArgs(4)) {
152                 std::cerr
153                         << "Expected \"snapshot\" <block_device> <block_device> <mode> <chunk_size>"
154                         << std::endl;
155                 return nullptr;
156             }
157             std::string base_device = NextArg();
158             std::string cow_device = NextArg();
159             std::string mode_str = NextArg();
160             std::string chunk_size_str = NextArg();
161 
162             SnapshotStorageMode mode;
163             if (mode_str == "P") {
164                 mode = SnapshotStorageMode::Persistent;
165             } else if (mode_str == "N") {
166                 mode = SnapshotStorageMode::Transient;
167             } else {
168                 std::cerr << "Unrecognized mode: " << mode_str << "\n";
169                 return nullptr;
170             }
171 
172             uint32_t chunk_size;
173             if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
174                 std::cerr << "Chunk size must be an unsigned integer.\n";
175                 return nullptr;
176             }
177             return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
178                                                       cow_device, mode, chunk_size);
179         } else if (target_type == "snapshot-merge") {
180             if (!HasArgs(3)) {
181                 std::cerr
182                         << "Expected \"snapshot-merge\" <block_device> <block_device> <chunk_size>"
183                         << std::endl;
184                 return nullptr;
185             }
186             std::string base_device = NextArg();
187             std::string cow_device = NextArg();
188             std::string chunk_size_str = NextArg();
189             SnapshotStorageMode mode = SnapshotStorageMode::Merge;
190 
191             uint32_t chunk_size;
192             if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {
193                 std::cerr << "Chunk size must be an unsigned integer.\n";
194                 return nullptr;
195             }
196             return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,
197                                                       cow_device, mode, chunk_size);
198         } else if (target_type == "user") {
199             if (!HasArgs(1)) {
200                 std::cerr << "Expected \"user\" <control_device_name>" << std::endl;
201                 return nullptr;
202             }
203             std::string control_device = NextArg();
204             return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
205         } else if (target_type == "error") {
206             return std::make_unique<DmTargetError>(start_sector, num_sectors);
207         } else if (target_type == "thin-pool") {
208             if (!HasArgs(4)) {
209                 std::cerr << "Expected \"thin-pool\" <metadata dev> <data dev> <data block size> "
210                              "<low water mark> <feature args>"
211                           << std::endl;
212                 return nullptr;
213             }
214 
215             std::string metadata_dev = NextArg();
216             std::string data_dev = NextArg();
217             std::string data_block_size_str = NextArg();
218             std::string low_water_mark_str = NextArg();
219 
220             uint64_t data_block_size;
221             if (!android::base::ParseUint(data_block_size_str, &data_block_size)) {
222                 std::cerr << "Data block size must be an unsigned integer.\n";
223                 return nullptr;
224             }
225             uint64_t low_water_mark;
226             if (!android::base::ParseUint(low_water_mark_str, &low_water_mark)) {
227                 std::cerr << "Low water mark must be an unsigned integer.\n";
228                 return nullptr;
229             }
230             return std::make_unique<DmTargetThinPool>(start_sector, num_sectors, metadata_dev,
231                                                       data_dev, data_block_size, low_water_mark);
232         } else if (target_type == "thin") {
233             if (!HasArgs(2)) {
234                 std::cerr << "Expected \"thin\" <pool dev> <dev id>" << std::endl;
235                 return nullptr;
236             }
237 
238             std::string pool_dev = NextArg();
239             std::string dev_id_str = NextArg();
240 
241             uint64_t dev_id;
242             if (!android::base::ParseUint(dev_id_str, &dev_id)) {
243                 std::cerr << "Dev id must be an unsigned integer.\n";
244                 return nullptr;
245             }
246             return std::make_unique<DmTargetThin>(start_sector, num_sectors, pool_dev, dev_id);
247         } else {
248             std::cerr << "Unrecognized target type: " << target_type << std::endl;
249             return nullptr;
250         }
251     }
252 
253   private:
HasArgs(int count)254     bool HasArgs(int count) { return arg_index_ + count <= argc_; }
NextArg()255     const char* NextArg() {
256         CHECK(arg_index_ < argc_);
257         return argv_[arg_index_++];
258     }
PreviousArg()259     const char* PreviousArg() {
260         CHECK(arg_index_ >= 0);
261         return argv_[arg_index_ - 1];
262     }
263 
264   private:
265     int arg_index_;
266     int argc_;
267     char** argv_;
268 };
269 
270 struct TableArgs {
271     DmTable table;
272     bool suspended = false;
273 };
274 
parse_table_args(int argc,char ** argv)275 static std::optional<TableArgs> parse_table_args(int argc, char** argv) {
276     TableArgs out;
277 
278     // Parse extended options first.
279     int arg_index = 1;
280     while (arg_index < argc && argv[arg_index][0] == '-') {
281         if (strcmp(argv[arg_index], "-ro") == 0) {
282             out.table.set_readonly(true);
283             arg_index++;
284         } else if (strcmp(argv[arg_index], "-suspended") == 0) {
285             out.suspended = true;
286             arg_index++;
287         } else {
288             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
289             return {};
290         }
291     }
292 
293     // Parse everything else as target information.
294     TargetParser parser(argc - arg_index, argv + arg_index);
295     while (parser.More()) {
296         std::unique_ptr<DmTarget> target = parser.Next();
297         if (!target || !out.table.AddTarget(std::move(target))) {
298             return {};
299         }
300     }
301 
302     if (out.table.num_targets() == 0) {
303         std::cerr << "Must define at least one target." << std::endl;
304         return {};
305     }
306     return {std::move(out)};
307 }
308 
DmCreateCmdHandler(int argc,char ** argv)309 static int DmCreateCmdHandler(int argc, char** argv) {
310     if (argc < 1) {
311         std::cerr << "Usage: dmctl create <dm-name> [--suspended] [-ro] <targets...>" << std::endl;
312         return -EINVAL;
313     }
314     std::string name = argv[0];
315 
316     auto table_args = parse_table_args(argc, argv);
317     if (!table_args) {
318         return -EINVAL;
319     }
320 
321     std::string ignore_path;
322     DeviceMapper& dm = DeviceMapper::Instance();
323     if (!dm.CreateEmptyDevice(name)) {
324         std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
325         return -EIO;
326     }
327     if (!dm.LoadTable(name, table_args->table)) {
328         std::cerr << "Failed to load table for dm device: " << name << std::endl;
329         return -EIO;
330     }
331     if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {
332         std::cerr << "Failed to activate table for " << name << std::endl;
333         return -EIO;
334     }
335     return 0;
336 }
337 
DmDeleteCmdHandler(int argc,char ** argv)338 static int DmDeleteCmdHandler(int argc, char** argv) {
339     if (argc < 1) {
340         std::cerr << "Usage: dmctl delete <name>" << std::endl;
341         return -EINVAL;
342     }
343 
344     std::string name = argv[0];
345     DeviceMapper& dm = DeviceMapper::Instance();
346     if (!dm.DeleteDevice(name)) {
347         std::cerr << "Failed to delete [" << name << "]" << std::endl;
348         return -EIO;
349     }
350     return 0;
351 }
352 
DmReplaceCmdHandler(int argc,char ** argv)353 static int DmReplaceCmdHandler(int argc, char** argv) {
354     if (argc < 1) {
355         std::cerr << "Usage: dmctl replace <dm-name> <targets...>" << std::endl;
356         return -EINVAL;
357     }
358     std::string name = argv[0];
359 
360     auto table_args = parse_table_args(argc, argv);
361     if (!table_args) {
362         return -EINVAL;
363     }
364 
365     DeviceMapper& dm = DeviceMapper::Instance();
366     if (!dm.LoadTable(name, table_args->table)) {
367         std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
368         return -EIO;
369     }
370     if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {
371         std::cerr << "Failed to activate table for " << name << std::endl;
372         return -EIO;
373     }
374     return 0;
375 }
376 
DmListTargets(DeviceMapper & dm,int argc,char ** argv)377 static int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,
378                          [[maybe_unused]] char** argv) {
379     std::vector<DmTargetTypeInfo> targets;
380     if (!dm.GetAvailableTargets(&targets)) {
381         std::cerr << "Failed to read available device mapper targets" << std::endl;
382         return -errno;
383     }
384 
385     std::cout << "Available Device Mapper Targets:" << std::endl;
386     if (targets.empty()) {
387         std::cout << "  <empty>" << std::endl;
388         return 0;
389     }
390 
391     for (const auto& target : targets) {
392         std::cout << std::left << std::setw(20) << target.name() << " : " << target.version()
393                   << std::endl;
394     }
395 
396     return 0;
397 }
398 
DmListDevices(DeviceMapper & dm,int argc,char ** argv)399 static int DmListDevices(DeviceMapper& dm, int argc, char** argv) {
400     std::vector<DmBlockDevice> devices;
401     if (!dm.GetAvailableDevices(&devices)) {
402         std::cerr << "Failed to read available device mapper devices" << std::endl;
403         return -errno;
404     }
405     std::cout << "Available Device Mapper Devices:" << std::endl;
406     if (devices.empty()) {
407         std::cout << "  <empty>" << std::endl;
408         return 0;
409     }
410 
411     bool verbose = (argc && (argv[0] == "-v"s));
412     for (const auto& dev : devices) {
413         std::cout << std::left << std::setw(20) << dev.name() << " : " << dev.Major() << ":"
414                   << dev.Minor() << std::endl;
415         if (verbose) {
416             std::vector<DeviceMapper::TargetInfo> table;
417             if (!dm.GetTableInfo(dev.name(), &table)) {
418                 std::cerr << "Could not query table status for device \"" << dev.name() << "\"."
419                           << std::endl;
420                 return -EINVAL;
421             }
422 
423             uint32_t target_num = 1;
424             for (const auto& target : table) {
425                 std::cout << "  target#" << target_num << ": ";
426                 std::cout << target.spec.sector_start << "-"
427                           << (target.spec.sector_start + target.spec.length) << ": "
428                           << target.spec.target_type;
429                 if (!target.data.empty()) {
430                     std::cout << ", " << target.data;
431                 }
432                 std::cout << std::endl;
433                 target_num++;
434             }
435         }
436     }
437 
438     return 0;
439 }
440 
441 static const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {
442         {"targets", DmListTargets},
443         {"devices", DmListDevices},
444 };
445 
DmListCmdHandler(int argc,char ** argv)446 static int DmListCmdHandler(int argc, char** argv) {
447     if (argc < 1) {
448         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
449         return -EINVAL;
450     }
451 
452     DeviceMapper& dm = DeviceMapper::Instance();
453     for (const auto& l : listmap) {
454         if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);
455     }
456 
457     std::cerr << "Invalid argument to \'dmctl list\': " << argv[0] << std::endl;
458     return -EINVAL;
459 }
460 
DmMessageCmdHandler(int argc,char ** argv)461 static int DmMessageCmdHandler(int argc, char** argv) {
462     if (argc != 3) {
463         std::cerr << "Usage: dmctl message <name> <sector> <message>" << std::endl;
464         return -EINVAL;
465     }
466     uint64_t sector;
467     if (!android::base::ParseUint(argv[1], &sector)) {
468         std::cerr << "Invalid argument for sector: " << argv[1] << std::endl;
469         return -EINVAL;
470     }
471     DeviceMapper& dm = DeviceMapper::Instance();
472     if (!dm.SendMessage(argv[0], sector, argv[2])) {
473         std::cerr << "Could not send message to " << argv[0] << std::endl;
474         return -EINVAL;
475     }
476     return 0;
477 }
478 
HelpCmdHandler(int,char **)479 static int HelpCmdHandler(int /* argc */, char** /* argv */) {
480     Usage();
481     return 0;
482 }
483 
GetPathCmdHandler(int argc,char ** argv)484 static int GetPathCmdHandler(int argc, char** argv) {
485     if (argc != 1) {
486         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
487         return -EINVAL;
488     }
489 
490     DeviceMapper& dm = DeviceMapper::Instance();
491     std::string path;
492     if (!dm.GetDmDevicePathByName(argv[0], &path)) {
493         std::cerr << "Could not query path of device \"" << argv[0] << "\"." << std::endl;
494         return -EINVAL;
495     }
496     std::cout << path << std::endl;
497     return 0;
498 }
499 
GetUuidCmdHandler(int argc,char ** argv)500 static int GetUuidCmdHandler(int argc, char** argv) {
501     if (argc != 1) {
502         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
503         return -EINVAL;
504     }
505 
506     DeviceMapper& dm = DeviceMapper::Instance();
507     std::string uuid;
508     if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {
509         std::cerr << "Could not query uuid of device \"" << argv[0] << "\"." << std::endl;
510         return -EINVAL;
511     }
512     std::cout << uuid << std::endl;
513     return 0;
514 }
515 
InfoCmdHandler(int argc,char ** argv)516 static int InfoCmdHandler(int argc, char** argv) {
517     if (argc != 1) {
518         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
519         return -EINVAL;
520     }
521 
522     DeviceMapper& dm = DeviceMapper::Instance();
523     auto info = dm.GetDetailedInfo(argv[0]);
524     if (!info) {
525         std::cerr << "Invalid device \"" << argv[0] << "\"." << std::endl;
526         return -EINVAL;
527     }
528 
529     constexpr int spacing = 14;
530     std::cout << std::left << std::setw(spacing) << "device"
531               << ": " << argv[0] << std::endl;
532     std::cout << std::left << std::setw(spacing) << "active"
533               << ": " << std::boolalpha << !info->IsSuspended() << std::endl;
534     std::cout << std::left << std::setw(spacing) << "access"
535               << ": ";
536     if (info->IsReadOnly()) {
537         std::cout << "ro ";
538     } else {
539         std::cout << "rw ";
540     }
541     std::cout << std::endl;
542     std::cout << std::left << std::setw(spacing) << "activeTable"
543               << ": " << std::boolalpha << info->IsActiveTablePresent() << std::endl;
544     std::cout << std::left << std::setw(spacing) << "inactiveTable"
545               << ": " << std::boolalpha << info->IsInactiveTablePresent() << std::endl;
546     std::cout << std::left << std::setw(spacing) << "bufferFull"
547               << ": " << std::boolalpha << info->IsBufferFull() << std::endl;
548     return 0;
549 }
550 
DumpTable(const std::string & mode,int argc,char ** argv)551 static int DumpTable(const std::string& mode, int argc, char** argv) {
552     if (argc != 1) {
553         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
554         return -EINVAL;
555     }
556 
557     DeviceMapper& dm = DeviceMapper::Instance();
558     std::vector<DeviceMapper::TargetInfo> table;
559     if (mode == "status") {
560         if (!dm.GetTableStatus(argv[0], &table)) {
561             std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
562                       << std::endl;
563             return -EINVAL;
564         }
565     } else if (mode == "table") {
566         if (!dm.GetTableInfo(argv[0], &table)) {
567             std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
568                       << std::endl;
569             return -EINVAL;
570         }
571     } else if (mode == "ima") {
572         if (!dm.GetTableStatusIma(argv[0], &table)) {
573             std::cerr << "Could not query table status of device \"" << argv[0] << "\"."
574                       << std::endl;
575             return -EINVAL;
576         }
577     }
578 
579     std::cout << "Targets in the device-mapper table for " << argv[0] << ":" << std::endl;
580     for (const auto& target : table) {
581         std::cout << target.spec.sector_start << "-"
582                   << (target.spec.sector_start + target.spec.length) << ": "
583                   << target.spec.target_type;
584         if (!target.data.empty()) {
585             std::cout << ", " << target.data;
586         }
587         std::cout << std::endl;
588     }
589     return 0;
590 }
591 
TableCmdHandler(int argc,char ** argv)592 static int TableCmdHandler(int argc, char** argv) {
593     return DumpTable("table", argc, argv);
594 }
595 
StatusCmdHandler(int argc,char ** argv)596 static int StatusCmdHandler(int argc, char** argv) {
597     return DumpTable("status", argc, argv);
598 }
599 
ImaCmdHandler(int argc,char ** argv)600 static int ImaCmdHandler(int argc, char** argv) {
601     return DumpTable("ima", argc, argv);
602 }
603 
ResumeCmdHandler(int argc,char ** argv)604 static int ResumeCmdHandler(int argc, char** argv) {
605     if (argc != 1) {
606         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
607         return -EINVAL;
608     }
609 
610     DeviceMapper& dm = DeviceMapper::Instance();
611     if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {
612         std::cerr << "Could not resume device \"" << argv[0] << "\"." << std::endl;
613         return -EINVAL;
614     }
615     return 0;
616 }
617 
SuspendCmdHandler(int argc,char ** argv)618 static int SuspendCmdHandler(int argc, char** argv) {
619     if (argc != 1) {
620         std::cerr << "Invalid arguments, see \'dmctl help\'" << std::endl;
621         return -EINVAL;
622     }
623 
624     DeviceMapper& dm = DeviceMapper::Instance();
625     if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {
626         std::cerr << "Could not suspend device \"" << argv[0] << "\"." << std::endl;
627         return -EINVAL;
628     }
629     return 0;
630 }
631 
632 static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
633         // clang-format off
634         {"create", DmCreateCmdHandler},
635         {"delete", DmDeleteCmdHandler},
636         {"replace", DmReplaceCmdHandler},
637         {"list", DmListCmdHandler},
638         {"message", DmMessageCmdHandler},
639         {"help", HelpCmdHandler},
640         {"getpath", GetPathCmdHandler},
641         {"getuuid", GetUuidCmdHandler},
642         {"info", InfoCmdHandler},
643         {"table", TableCmdHandler},
644         {"status", StatusCmdHandler},
645         {"ima", ImaCmdHandler},
646         {"resume", ResumeCmdHandler},
647         {"suspend", SuspendCmdHandler},
648         // clang-format on
649 };
650 
ReadFile(const char * filename,std::vector<std::string> * args,std::vector<char * > * arg_ptrs)651 static bool ReadFile(const char* filename, std::vector<std::string>* args,
652                      std::vector<char*>* arg_ptrs) {
653     std::ifstream file(filename);
654     if (!file) return false;
655 
656     std::string arg;
657     while (file >> arg) args->push_back(arg);
658 
659     for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));
660     return true;
661 }
662 
main(int argc,char ** argv)663 int main(int argc, char** argv) {
664     android::base::InitLogging(argv, &android::base::StderrLogger);
665     if (argc < 2) {
666         return Usage();
667     }
668 
669     std::vector<std::string> args;
670     std::vector<char*> arg_ptrs;
671     if (std::string("-f") == argv[1]) {
672         if (argc != 3) {
673             return Usage();
674         }
675 
676         args.push_back(argv[0]);
677         if (!ReadFile(argv[2], &args, &arg_ptrs)) {
678             return Usage();
679         }
680 
681         argc = arg_ptrs.size();
682         argv = &arg_ptrs[0];
683     }
684 
685     for (const auto& cmd : cmdmap) {
686         if (cmd.first == argv[1]) {
687             return cmd.second(argc - 2, argv + 2);
688         }
689     }
690 
691     return Usage();
692 }
693