Commands.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. /*
  2. * Copyright (c) 2020 Project CHIP Authors
  3. * All rights reserved.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include "Commands.h"
  19. #include "Command.h"
  20. #include <algorithm>
  21. #include <iomanip>
  22. #include <sstream>
  23. #include <string>
  24. #include <lib/support/Base64.h>
  25. #include <lib/support/CHIPMem.h>
  26. #include <lib/support/CodeUtils.h>
  27. #include <platform/CHIPDeviceConfig.h>
  28. #include <platform/KeyValueStoreManager.h>
  29. #include "../clusters/JsonParser.h"
  30. namespace {
  31. char kInteractiveModeName[] = "";
  32. constexpr size_t kInteractiveModeArgumentsMaxLength = 32;
  33. constexpr const char * kOptionalArgumentPrefix = "--";
  34. constexpr const char * kJsonClusterKey = "cluster";
  35. constexpr const char * kJsonCommandKey = "command";
  36. constexpr const char * kJsonCommandSpecifierKey = "command_specifier";
  37. constexpr const char * kJsonArgumentsKey = "arguments";
  38. #if !CHIP_DISABLE_PLATFORM_KVS
  39. template <typename T>
  40. struct HasInitWithString
  41. {
  42. template <typename U>
  43. static constexpr auto check(U *) -> typename std::is_same<decltype(std::declval<U>().Init("")), CHIP_ERROR>::type;
  44. template <typename>
  45. static constexpr std::false_type check(...);
  46. typedef decltype(check<std::remove_reference_t<T>>(nullptr)) type;
  47. public:
  48. static constexpr bool value = type::value;
  49. };
  50. // Template so we can do conditional enabling
  51. template <typename T, std::enable_if_t<HasInitWithString<T>::value, int> = 0>
  52. static void UseStorageDirectory(T & storageManagerImpl, const char * storageDirectory)
  53. {
  54. std::string platformKVS = std::string(storageDirectory) + "/chip_tool_kvs";
  55. storageManagerImpl.Init(platformKVS.c_str());
  56. }
  57. template <typename T, std::enable_if_t<!HasInitWithString<T>::value, int> = 0>
  58. static void UseStorageDirectory(T & storageManagerImpl, const char * storageDirectory)
  59. {}
  60. #endif // !CHIP_DISABLE_PLATFORM_KVS
  61. bool GetArgumentsFromJson(Command * command, Json::Value & value, bool optional, std::vector<std::string> & outArgs)
  62. {
  63. auto memberNames = value.getMemberNames();
  64. std::vector<std::string> args;
  65. for (size_t i = 0; i < command->GetArgumentsCount(); i++)
  66. {
  67. auto argName = command->GetArgumentName(i);
  68. auto memberNamesIterator = memberNames.begin();
  69. while (memberNamesIterator != memberNames.end())
  70. {
  71. auto memberName = *memberNamesIterator;
  72. if (strcasecmp(argName, memberName.c_str()) != 0)
  73. {
  74. memberNamesIterator++;
  75. continue;
  76. }
  77. if (command->GetArgumentIsOptional(i) != optional)
  78. {
  79. memberNamesIterator = memberNames.erase(memberNamesIterator);
  80. continue;
  81. }
  82. if (optional)
  83. {
  84. args.push_back(std::string(kOptionalArgumentPrefix) + argName);
  85. }
  86. auto argValue = value[memberName].asString();
  87. args.push_back(std::move(argValue));
  88. memberNamesIterator = memberNames.erase(memberNamesIterator);
  89. break;
  90. }
  91. }
  92. if (memberNames.size())
  93. {
  94. auto memberName = memberNames.front();
  95. ChipLogError(chipTool, "The argument \"\%s\" is not supported.", memberName.c_str());
  96. return false;
  97. }
  98. outArgs = args;
  99. return true;
  100. };
  101. // Check for arguments with a starting '"' but no ending '"': those
  102. // would indicate that people are using double-quoting, not single
  103. // quoting, on arguments with spaces.
  104. static void DetectAndLogMismatchedDoubleQuotes(int argc, char ** argv)
  105. {
  106. for (int curArg = 0; curArg < argc; ++curArg)
  107. {
  108. char * arg = argv[curArg];
  109. if (!arg)
  110. {
  111. continue;
  112. }
  113. auto len = strlen(arg);
  114. if (len == 0)
  115. {
  116. continue;
  117. }
  118. if (arg[0] == '"' && arg[len - 1] != '"')
  119. {
  120. ChipLogError(chipTool,
  121. "Mismatched '\"' detected in argument: '%s'. Use single quotes to delimit arguments with spaces "
  122. "in them: 'x y', not \"x y\".",
  123. arg);
  124. }
  125. }
  126. }
  127. } // namespace
  128. void Commands::Register(const char * commandSetName, commands_list commandsList, const char * helpText, bool isCluster)
  129. {
  130. VerifyOrDieWithMsg(isCluster || helpText != nullptr, chipTool, "Non-cluster command sets must have help text");
  131. mCommandSets[commandSetName].isCluster = isCluster;
  132. mCommandSets[commandSetName].helpText = helpText;
  133. for (auto & command : commandsList)
  134. {
  135. mCommandSets[commandSetName].commands.push_back(std::move(command));
  136. }
  137. }
  138. int Commands::Run(int argc, char ** argv)
  139. {
  140. CHIP_ERROR err = CHIP_NO_ERROR;
  141. err = chip::Platform::MemoryInit();
  142. VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Memory failure: %s", chip::ErrorStr(err)));
  143. #ifdef CONFIG_USE_LOCAL_STORAGE
  144. err = mStorage.Init();
  145. VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Storage failure: %s", chip::ErrorStr(err)));
  146. chip::Logging::SetLogFilter(mStorage.GetLoggingLevel());
  147. #endif // CONFIG_USE_LOCAL_STORAGE
  148. err = RunCommand(argc, argv);
  149. VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(err)));
  150. exit:
  151. return (err == CHIP_NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE;
  152. }
  153. int Commands::RunInteractive(const char * command, const chip::Optional<char *> & storageDirectory)
  154. {
  155. std::vector<std::string> arguments;
  156. VerifyOrReturnValue(DecodeArgumentsFromInteractiveMode(command, arguments), EXIT_FAILURE);
  157. if (arguments.size() > (kInteractiveModeArgumentsMaxLength - 1 /* for interactive mode name */))
  158. {
  159. ChipLogError(chipTool, "Too many arguments. Ignoring.");
  160. arguments.resize(kInteractiveModeArgumentsMaxLength - 1);
  161. }
  162. int argc = 0;
  163. char * argv[kInteractiveModeArgumentsMaxLength] = {};
  164. argv[argc++] = kInteractiveModeName;
  165. std::string commandStr;
  166. for (auto & arg : arguments)
  167. {
  168. argv[argc] = new char[arg.size() + 1];
  169. strcpy(argv[argc++], arg.c_str());
  170. commandStr += arg;
  171. commandStr += " ";
  172. }
  173. ChipLogProgress(chipTool, "Command: %s", commandStr.c_str());
  174. auto err = RunCommand(argc, argv, true, storageDirectory);
  175. // Do not delete arg[0]
  176. for (auto i = 1; i < argc; i++)
  177. {
  178. delete[] argv[i];
  179. }
  180. return (err == CHIP_NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE;
  181. }
  182. CHIP_ERROR Commands::RunCommand(int argc, char ** argv, bool interactive,
  183. const chip::Optional<char *> & interactiveStorageDirectory)
  184. {
  185. Command * command = nullptr;
  186. if (argc <= 1)
  187. {
  188. ChipLogError(chipTool, "Missing cluster or command set name");
  189. ShowCommandSets(argv[0]);
  190. return CHIP_ERROR_INVALID_ARGUMENT;
  191. }
  192. auto commandSetIter = GetCommandSet(argv[1]);
  193. if (commandSetIter == mCommandSets.end())
  194. {
  195. ChipLogError(chipTool, "Unknown cluster or command set: %s", argv[1]);
  196. ShowCommandSets(argv[0]);
  197. return CHIP_ERROR_INVALID_ARGUMENT;
  198. }
  199. auto & commandList = commandSetIter->second.commands;
  200. auto * helpText = commandSetIter->second.helpText;
  201. if (argc <= 2)
  202. {
  203. ChipLogError(chipTool, "Missing command name");
  204. ShowCommandSet(argv[0], argv[1], commandList, helpText);
  205. return CHIP_ERROR_INVALID_ARGUMENT;
  206. }
  207. bool isGlobalCommand = IsGlobalCommand(argv[2]);
  208. if (!isGlobalCommand)
  209. {
  210. command = GetCommand(commandList, argv[2]);
  211. if (command == nullptr)
  212. {
  213. ChipLogError(chipTool, "Unknown command: %s", argv[2]);
  214. ShowCommandSet(argv[0], argv[1], commandList, helpText);
  215. return CHIP_ERROR_INVALID_ARGUMENT;
  216. }
  217. }
  218. else if (IsEventCommand(argv[2]))
  219. {
  220. if (argc <= 3)
  221. {
  222. ChipLogError(chipTool, "Missing event name");
  223. ShowClusterEvents(argv[0], argv[1], argv[2], commandList);
  224. return CHIP_ERROR_INVALID_ARGUMENT;
  225. }
  226. command = GetGlobalCommand(commandList, argv[2], argv[3]);
  227. if (command == nullptr)
  228. {
  229. ChipLogError(chipTool, "Unknown event: %s", argv[3]);
  230. ShowClusterEvents(argv[0], argv[1], argv[2], commandList);
  231. return CHIP_ERROR_INVALID_ARGUMENT;
  232. }
  233. }
  234. else
  235. {
  236. if (argc <= 3)
  237. {
  238. ChipLogError(chipTool, "Missing attribute name");
  239. ShowClusterAttributes(argv[0], argv[1], argv[2], commandList);
  240. return CHIP_ERROR_INVALID_ARGUMENT;
  241. }
  242. command = GetGlobalCommand(commandList, argv[2], argv[3]);
  243. if (command == nullptr)
  244. {
  245. ChipLogError(chipTool, "Unknown attribute: %s", argv[3]);
  246. ShowClusterAttributes(argv[0], argv[1], argv[2], commandList);
  247. return CHIP_ERROR_INVALID_ARGUMENT;
  248. }
  249. }
  250. int argumentsPosition = isGlobalCommand ? 4 : 3;
  251. if (!command->InitArguments(argc - argumentsPosition, &argv[argumentsPosition]))
  252. {
  253. if (interactive)
  254. {
  255. DetectAndLogMismatchedDoubleQuotes(argc - argumentsPosition, &argv[argumentsPosition]);
  256. }
  257. ShowCommand(argv[0], argv[1], command);
  258. return CHIP_ERROR_INVALID_ARGUMENT;
  259. }
  260. if (interactive)
  261. {
  262. return command->RunAsInteractive(interactiveStorageDirectory);
  263. }
  264. // Now that the command is initialized, get our storage from it as needed
  265. // and set up our loging level.
  266. #ifdef CONFIG_USE_LOCAL_STORAGE
  267. CHIP_ERROR err = mStorage.Init(nullptr, command->GetStorageDirectory().ValueOr(nullptr));
  268. if (err != CHIP_NO_ERROR)
  269. {
  270. ChipLogError(Controller, "Init Storage failure: %s", chip::ErrorStr(err));
  271. return err;
  272. }
  273. chip::Logging::SetLogFilter(mStorage.GetLoggingLevel());
  274. #if !CHIP_DISABLE_PLATFORM_KVS
  275. UseStorageDirectory(chip::DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl(), mStorage.GetDirectory());
  276. #endif // !CHIP_DISABLE_PLATFORM_KVS
  277. #endif // CONFIG_USE_LOCAL_STORAGE
  278. return command->Run();
  279. }
  280. Commands::CommandSetMap::iterator Commands::GetCommandSet(std::string commandSetName)
  281. {
  282. for (auto & commandSet : mCommandSets)
  283. {
  284. std::string key(commandSet.first);
  285. std::transform(key.begin(), key.end(), key.begin(), ::tolower);
  286. if (key.compare(commandSetName) == 0)
  287. {
  288. return mCommandSets.find(commandSet.first);
  289. }
  290. }
  291. return mCommandSets.end();
  292. }
  293. Command * Commands::GetCommand(CommandsVector & commands, std::string commandName)
  294. {
  295. for (auto & command : commands)
  296. {
  297. if (commandName.compare(command->GetName()) == 0)
  298. {
  299. return command.get();
  300. }
  301. }
  302. return nullptr;
  303. }
  304. Command * Commands::GetGlobalCommand(CommandsVector & commands, std::string commandName, std::string attributeName)
  305. {
  306. for (auto & command : commands)
  307. {
  308. if (commandName.compare(command->GetName()) == 0 && attributeName.compare(command->GetAttribute()) == 0)
  309. {
  310. return command.get();
  311. }
  312. }
  313. return nullptr;
  314. }
  315. bool Commands::IsAttributeCommand(std::string commandName) const
  316. {
  317. return commandName.compare("read") == 0 || commandName.compare("write") == 0 || commandName.compare("force-write") == 0 ||
  318. commandName.compare("subscribe") == 0;
  319. }
  320. bool Commands::IsEventCommand(std::string commandName) const
  321. {
  322. return commandName.compare("read-event") == 0 || commandName.compare("subscribe-event") == 0;
  323. }
  324. bool Commands::IsGlobalCommand(std::string commandName) const
  325. {
  326. return IsAttributeCommand(commandName) || IsEventCommand(commandName);
  327. }
  328. void Commands::ShowCommandSetOverview(std::string commandSetName, const CommandSet & commandSet)
  329. {
  330. std::transform(commandSetName.begin(), commandSetName.end(), commandSetName.begin(),
  331. [](unsigned char c) { return std::tolower(c); });
  332. fprintf(stderr, " | * %-82s|\n", commandSetName.c_str());
  333. ShowHelpText(commandSet.helpText);
  334. }
  335. void Commands::ShowCommandSets(std::string executable)
  336. {
  337. fprintf(stderr, "Usage:\n");
  338. fprintf(stderr, " %s cluster_name command_name [param1 param2 ...]\n", executable.c_str());
  339. fprintf(stderr, "or:\n");
  340. fprintf(stderr, " %s command_set_name command_name [param1 param2 ...]\n", executable.c_str());
  341. fprintf(stderr, "\n");
  342. // Table of clusters
  343. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  344. fprintf(stderr, " | Clusters: |\n");
  345. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  346. for (auto & commandSet : mCommandSets)
  347. {
  348. if (commandSet.second.isCluster)
  349. {
  350. ShowCommandSetOverview(commandSet.first, commandSet.second);
  351. }
  352. }
  353. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  354. fprintf(stderr, "\n");
  355. // Table of command sets
  356. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  357. fprintf(stderr, " | Command sets: |\n");
  358. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  359. for (auto & commandSet : mCommandSets)
  360. {
  361. if (!commandSet.second.isCluster)
  362. {
  363. ShowCommandSetOverview(commandSet.first, commandSet.second);
  364. }
  365. }
  366. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  367. }
  368. void Commands::ShowCommandSet(std::string executable, std::string commandSetName, CommandsVector & commands, const char * helpText)
  369. {
  370. fprintf(stderr, "Usage:\n");
  371. fprintf(stderr, " %s %s command_name [param1 param2 ...]\n", executable.c_str(), commandSetName.c_str());
  372. if (helpText)
  373. {
  374. fprintf(stderr, "\n%s\n", helpText);
  375. }
  376. fprintf(stderr, "\n");
  377. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  378. fprintf(stderr, " | Commands: |\n");
  379. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  380. bool readCommand = false;
  381. bool writeCommand = false;
  382. bool writeOverrideCommand = false;
  383. bool subscribeCommand = false;
  384. bool readEventCommand = false;
  385. bool subscribeEventCommand = false;
  386. for (auto & command : commands)
  387. {
  388. bool shouldPrint = true;
  389. if (IsGlobalCommand(command->GetName()))
  390. {
  391. if (strcmp(command->GetName(), "read") == 0 && !readCommand)
  392. {
  393. readCommand = true;
  394. }
  395. else if (strcmp(command->GetName(), "write") == 0 && !writeCommand)
  396. {
  397. writeCommand = true;
  398. }
  399. else if (strcmp(command->GetName(), "force-write") == 0 && !writeOverrideCommand)
  400. {
  401. writeOverrideCommand = true;
  402. }
  403. else if (strcmp(command->GetName(), "subscribe") == 0 && !subscribeCommand)
  404. {
  405. subscribeCommand = true;
  406. }
  407. else if (strcmp(command->GetName(), "read-event") == 0 && !readEventCommand)
  408. {
  409. readEventCommand = true;
  410. }
  411. else if (strcmp(command->GetName(), "subscribe-event") == 0 && !subscribeEventCommand)
  412. {
  413. subscribeEventCommand = true;
  414. }
  415. else
  416. {
  417. shouldPrint = false;
  418. }
  419. }
  420. if (shouldPrint)
  421. {
  422. fprintf(stderr, " | * %-82s|\n", command->GetName());
  423. ShowHelpText(command->GetHelpText());
  424. }
  425. }
  426. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  427. }
  428. void Commands::ShowClusterAttributes(std::string executable, std::string clusterName, std::string commandName,
  429. CommandsVector & commands)
  430. {
  431. fprintf(stderr, "Usage:\n");
  432. fprintf(stderr, " %s %s %s attribute-name [param1 param2 ...]\n", executable.c_str(), clusterName.c_str(),
  433. commandName.c_str());
  434. fprintf(stderr, "\n");
  435. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  436. fprintf(stderr, " | Attributes: |\n");
  437. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  438. for (auto & command : commands)
  439. {
  440. if (commandName.compare(command->GetName()) == 0)
  441. {
  442. fprintf(stderr, " | * %-82s|\n", command->GetAttribute());
  443. }
  444. }
  445. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  446. }
  447. void Commands::ShowClusterEvents(std::string executable, std::string clusterName, std::string commandName,
  448. CommandsVector & commands)
  449. {
  450. fprintf(stderr, "Usage:\n");
  451. fprintf(stderr, " %s %s %s event-name [param1 param2 ...]\n", executable.c_str(), clusterName.c_str(), commandName.c_str());
  452. fprintf(stderr, "\n");
  453. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  454. fprintf(stderr, " | Events: |\n");
  455. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  456. for (auto & command : commands)
  457. {
  458. if (commandName.compare(command->GetName()) == 0)
  459. {
  460. fprintf(stderr, " | * %-82s|\n", command->GetEvent());
  461. }
  462. }
  463. fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
  464. }
  465. void Commands::ShowCommand(std::string executable, std::string clusterName, Command * command)
  466. {
  467. fprintf(stderr, "Usage:\n");
  468. std::string arguments;
  469. std::string description;
  470. arguments += command->GetName();
  471. if (command->GetReadOnlyGlobalCommandArgument())
  472. {
  473. arguments += ' ';
  474. arguments += command->GetReadOnlyGlobalCommandArgument();
  475. }
  476. size_t argumentsCount = command->GetArgumentsCount();
  477. for (size_t i = 0; i < argumentsCount; i++)
  478. {
  479. std::string arg;
  480. bool isOptional = command->GetArgumentIsOptional(i);
  481. if (isOptional)
  482. {
  483. arg += "[--";
  484. }
  485. arg += command->GetArgumentName(i);
  486. if (isOptional)
  487. {
  488. arg += "]";
  489. }
  490. arguments += " ";
  491. arguments += arg;
  492. const char * argDescription = command->GetArgumentDescription(i);
  493. if ((argDescription != nullptr) && (strlen(argDescription) > 0))
  494. {
  495. description += "\n";
  496. description += arg;
  497. description += ":\n ";
  498. description += argDescription;
  499. description += "\n";
  500. }
  501. }
  502. fprintf(stderr, " %s %s %s\n", executable.c_str(), clusterName.c_str(), arguments.c_str());
  503. if (command->GetHelpText())
  504. {
  505. fprintf(stderr, "\n%s\n", command->GetHelpText());
  506. }
  507. if (description.size() > 0)
  508. {
  509. fprintf(stderr, "%s\n", description.c_str());
  510. }
  511. }
  512. bool Commands::DecodeArgumentsFromInteractiveMode(const char * command, std::vector<std::string> & args)
  513. {
  514. // Remote clients may not know the ordering of arguments, so instead of a strict ordering arguments can
  515. // be passed in as a json payload encoded in base64 and are reordered on the fly.
  516. return IsJsonString(command) ? DecodeArgumentsFromBase64EncodedJson(command, args)
  517. : DecodeArgumentsFromStringStream(command, args);
  518. }
  519. bool Commands::DecodeArgumentsFromBase64EncodedJson(const char * json, std::vector<std::string> & args)
  520. {
  521. Json::Value jsonValue;
  522. bool parsed = JsonParser::ParseCustomArgument(json, json + kJsonStringPrefixLen, jsonValue);
  523. VerifyOrReturnValue(parsed, false, ChipLogError(chipTool, "Error while parsing json."));
  524. VerifyOrReturnValue(jsonValue.isObject(), false, ChipLogError(chipTool, "Unexpected json type."));
  525. VerifyOrReturnValue(jsonValue.isMember(kJsonClusterKey), false,
  526. ChipLogError(chipTool, "'%s' key not found in json.", kJsonClusterKey));
  527. VerifyOrReturnValue(jsonValue.isMember(kJsonCommandKey), false,
  528. ChipLogError(chipTool, "'%s' key not found in json.", kJsonCommandKey));
  529. VerifyOrReturnValue(jsonValue.isMember(kJsonArgumentsKey), false,
  530. ChipLogError(chipTool, "'%s' key not found in json.", kJsonArgumentsKey));
  531. VerifyOrReturnValue(IsBase64String(jsonValue[kJsonArgumentsKey].asString().c_str()), false,
  532. ChipLogError(chipTool, "'arguments' is not a base64 string."));
  533. auto clusterName = jsonValue[kJsonClusterKey].asString();
  534. auto commandName = jsonValue[kJsonCommandKey].asString();
  535. auto arguments = jsonValue[kJsonArgumentsKey].asString();
  536. auto clusterIter = GetCommandSet(clusterName);
  537. VerifyOrReturnValue(clusterIter != mCommandSets.end(), false,
  538. ChipLogError(chipTool, "Cluster '%s' is not supported.", clusterName.c_str()));
  539. auto & commandList = clusterIter->second.commands;
  540. auto command = GetCommand(commandList, commandName);
  541. if (jsonValue.isMember(kJsonCommandSpecifierKey) && IsGlobalCommand(commandName))
  542. {
  543. auto commandSpecifierName = jsonValue[kJsonCommandSpecifierKey].asString();
  544. command = GetGlobalCommand(commandList, commandName, commandSpecifierName);
  545. }
  546. VerifyOrReturnValue(nullptr != command, false, ChipLogError(chipTool, "Unknown command."));
  547. auto encodedData = arguments.c_str();
  548. encodedData += kBase64StringPrefixLen;
  549. size_t encodedDataSize = strlen(encodedData);
  550. size_t expectedMaxDecodedSize = BASE64_MAX_DECODED_LEN(encodedDataSize);
  551. chip::Platform::ScopedMemoryBuffer<uint8_t> decodedData;
  552. VerifyOrReturnValue(decodedData.Calloc(expectedMaxDecodedSize + 1 /* for null */), false);
  553. size_t decodedDataSize = chip::Base64Decode(encodedData, static_cast<uint16_t>(encodedDataSize), decodedData.Get());
  554. VerifyOrReturnValue(decodedDataSize != 0, false, ChipLogError(chipTool, "Error while decoding base64 data."));
  555. decodedData.Get()[decodedDataSize] = '\0';
  556. Json::Value jsonArguments;
  557. bool parsedArguments = JsonParser::ParseCustomArgument(encodedData, chip::Uint8::to_char(decodedData.Get()), jsonArguments);
  558. VerifyOrReturnValue(parsedArguments, false, ChipLogError(chipTool, "Error while parsing json."));
  559. VerifyOrReturnValue(jsonArguments.isObject(), false, ChipLogError(chipTool, "Unexpected json type, expects and object."));
  560. std::vector<std::string> mandatoryArguments;
  561. std::vector<std::string> optionalArguments;
  562. VerifyOrReturnValue(GetArgumentsFromJson(command, jsonArguments, false /* addOptional */, mandatoryArguments), false);
  563. VerifyOrReturnValue(GetArgumentsFromJson(command, jsonArguments, true /* addOptional */, optionalArguments), false);
  564. args.push_back(std::move(clusterName));
  565. args.push_back(std::move(commandName));
  566. if (jsonValue.isMember(kJsonCommandSpecifierKey))
  567. {
  568. auto commandSpecifierName = jsonValue[kJsonCommandSpecifierKey].asString();
  569. args.push_back(std::move(commandSpecifierName));
  570. }
  571. args.insert(args.end(), mandatoryArguments.begin(), mandatoryArguments.end());
  572. args.insert(args.end(), optionalArguments.begin(), optionalArguments.end());
  573. return true;
  574. }
  575. bool Commands::DecodeArgumentsFromStringStream(const char * command, std::vector<std::string> & args)
  576. {
  577. std::string arg;
  578. std::stringstream ss(command);
  579. while (ss >> std::quoted(arg, '\''))
  580. {
  581. args.push_back(std::move(arg));
  582. }
  583. return true;
  584. }
  585. void Commands::ShowHelpText(const char * helpText)
  586. {
  587. if (helpText == nullptr)
  588. {
  589. return;
  590. }
  591. // We leave 82 chars for command/cluster names. The help text starts
  592. // two chars further to the right, so there are 80 chars left
  593. // for it.
  594. if (strlen(helpText) > 80)
  595. {
  596. // Add "..." at the end to indicate truncation, and only
  597. // show the first 77 chars, since that's what will fit.
  598. fprintf(stderr, " | - %.77s...|\n", helpText);
  599. }
  600. else
  601. {
  602. fprintf(stderr, " | - %-80s|\n", helpText);
  603. }
  604. }