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