blob: b8cca51a9a6aadb2d581981cb344fe0657c52d6d [file] [log] [blame]
/*
* Copyright (c) 2020 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "Commands.h"
#include "Command.h"
#include <algorithm>
#include <string>
#if CONFIG_DEVICE_LAYER
#include <platform/CHIPDeviceLayer.h>
#endif
#include <support/CHIPMem.h>
#include <support/CodeUtils.h>
void Commands::Register(const char * clusterName, commands_list commandsList)
{
for (auto & command : commandsList)
{
mClusters[clusterName].push_back(std::move(command));
}
}
int Commands::Run(NodeId localId, NodeId remoteId, int argc, char ** argv)
{
CHIP_ERROR err = CHIP_NO_ERROR;
chip::Controller::CommissionerInitParams initParams;
Command * command = nullptr;
err = chip::Platform::MemoryInit();
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Memory failure: %s", chip::ErrorStr(err)));
#if CHIP_DEVICE_LAYER_TARGET_LINUX && CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
// By default, Linux device is configured as a BLE peripheral while the controller needs a BLE central.
SuccessOrExit(err = chip::DeviceLayer::Internal::BLEMgrImpl().ConfigureBle(/* BLE adapter ID */ 0, /* BLE central */ true));
#endif
err = mStorage.Init();
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init Storage failure: %s", chip::ErrorStr(err)));
chip::Logging::SetLogFilter(mStorage.GetLoggingLevel());
initParams.storageDelegate = &mStorage;
err = mOpCredsIssuer.Initialize(mStorage);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Operational Cred Issuer: %s", chip::ErrorStr(err)));
initParams.operationalCredentialsDelegate = &mOpCredsIssuer;
err = mController.SetUdpListenPort(mStorage.GetListenPort());
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Commissioner: %s", chip::ErrorStr(err)));
err = mController.Init(localId, initParams);
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Commissioner: %s", chip::ErrorStr(err)));
#if CONFIG_USE_SEPARATE_EVENTLOOP
// ServiceEvents() calls StartEventLoopTask(), which is paired with the
// StopEventLoopTask() below.
err = mController.ServiceEvents();
VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Init failure! Run Loop: %s", chip::ErrorStr(err)));
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
err = RunCommand(localId, remoteId, argc, argv, &command);
SuccessOrExit(err);
#if !CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().RunEventLoop();
#endif // !CONFIG_USE_SEPARATE_EVENTLOOP
if (command)
{
err = command->GetCommandExitStatus();
}
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(err));
}
#if CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
if (command)
{
command->Shutdown();
}
//
// We can call DeviceController::Shutdown() safely without grabbing the stack lock
// since the CHIP thread and event queue have been stopped, preventing any thread
// races.
//
mController.Shutdown();
return (err == CHIP_NO_ERROR) ? EXIT_SUCCESS : EXIT_FAILURE;
}
CHIP_ERROR Commands::RunCommand(NodeId localId, NodeId remoteId, int argc, char ** argv, Command ** ranCommand)
{
CHIP_ERROR err = CHIP_NO_ERROR;
std::map<std::string, CommandsVector>::iterator cluster;
Command * command = nullptr;
if (argc <= 1)
{
ChipLogError(chipTool, "Missing cluster name");
ShowClusters(argv[0]);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
cluster = GetCluster(argv[1]);
if (cluster == mClusters.end())
{
ChipLogError(chipTool, "Unknown cluster: %s", argv[1]);
ShowClusters(argv[0]);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
if (argc <= 2)
{
ChipLogError(chipTool, "Missing command name");
ShowCluster(argv[0], argv[1], cluster->second);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
if (!IsGlobalCommand(argv[2]))
{
command = GetCommand(cluster->second, argv[2]);
if (command == nullptr)
{
ChipLogError(chipTool, "Unknown command: %s", argv[2]);
ShowCluster(argv[0], argv[1], cluster->second);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
}
else
{
if (argc <= 3)
{
ChipLogError(chipTool, "Missing attribute name");
ShowClusterAttributes(argv[0], argv[1], argv[2], cluster->second);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
command = GetGlobalCommand(cluster->second, argv[2], argv[3]);
if (command == nullptr)
{
ChipLogError(chipTool, "Unknown attribute: %s", argv[3]);
ShowClusterAttributes(argv[0], argv[1], argv[2], cluster->second);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
}
if (!command->InitArguments(argc - 3, &argv[3]))
{
ShowCommand(argv[0], argv[1], command);
ExitNow(err = CHIP_ERROR_INVALID_ARGUMENT);
}
{
Command::ExecutionContext execContext;
execContext.commissioner = &mController;
execContext.opCredsIssuer = &mOpCredsIssuer;
execContext.storage = &mStorage;
execContext.localId = localId;
execContext.remoteId = remoteId;
command->SetExecutionContext(execContext);
*ranCommand = command;
//
// Set this to true first BEFORE we send commands to ensure we don't end
// up in a situation where the response comes back faster than we can
// set the variable to true, which will cause it to block indefinitely.
//
command->UpdateWaitForResponse(true);
#if CONFIG_USE_SEPARATE_EVENTLOOP
chip::DeviceLayer::PlatformMgr().ScheduleWork(RunQueuedCommand, reinterpret_cast<intptr_t>(command));
command->WaitForResponse(command->GetWaitDurationInSeconds());
#else // CONFIG_USE_SEPARATE_EVENTLOOP
err = command->Run();
SuccessOrExit(err);
command->ScheduleWaitForResponse(command->GetWaitDurationInSeconds());
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
}
exit:
return err;
}
void Commands::RunQueuedCommand(intptr_t commandArg)
{
auto * command = reinterpret_cast<Command *>(commandArg);
CHIP_ERROR err = command->Run();
if (err != CHIP_NO_ERROR)
{
command->SetCommandExitStatus(err);
}
}
std::map<std::string, Commands::CommandsVector>::iterator Commands::GetCluster(std::string clusterName)
{
for (auto & cluster : mClusters)
{
std::string key(cluster.first);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
if (key.compare(clusterName) == 0)
{
return mClusters.find(cluster.first);
}
}
return mClusters.end();
}
Command * Commands::GetCommand(CommandsVector & commands, std::string commandName)
{
for (auto & command : commands)
{
if (commandName.compare(command->GetName()) == 0)
{
return command.get();
}
}
return nullptr;
}
Command * Commands::GetGlobalCommand(CommandsVector & commands, std::string commandName, std::string attributeName)
{
for (auto & command : commands)
{
if (commandName.compare(command->GetName()) == 0 && attributeName.compare(command->GetAttribute()) == 0)
{
return command.get();
}
}
return nullptr;
}
bool Commands::IsGlobalCommand(std::string commandName) const
{
return commandName.compare("read") == 0 || commandName.compare("write") == 0 || commandName.compare("report") == 0;
}
void Commands::ShowClusters(std::string executable)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s cluster_name command_name [param1 param2 ...]\n", executable.c_str());
fprintf(stderr, "\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
fprintf(stderr, " | Clusters: |\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
for (auto & cluster : mClusters)
{
std::string clusterName(cluster.first);
std::transform(clusterName.begin(), clusterName.end(), clusterName.begin(),
[](unsigned char c) { return std::tolower(c); });
fprintf(stderr, " | * %-82s|\n", clusterName.c_str());
}
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
}
void Commands::ShowCluster(std::string executable, std::string clusterName, CommandsVector & commands)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s %s command_name [param1 param2 ...]\n", executable.c_str(), clusterName.c_str());
fprintf(stderr, "\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
fprintf(stderr, " | Commands: |\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
bool readCommand = false;
bool writeCommand = false;
bool reportCommand = false;
for (auto & command : commands)
{
bool shouldPrint = true;
if (IsGlobalCommand(command->GetName()))
{
if (strcmp(command->GetName(), "read") == 0 && readCommand == false)
{
readCommand = true;
}
else if (strcmp(command->GetName(), "write") == 0 && writeCommand == false)
{
writeCommand = true;
}
else if (strcmp(command->GetName(), "report") == 0 && reportCommand == false)
{
reportCommand = true;
}
else
{
shouldPrint = false;
}
}
if (shouldPrint)
{
fprintf(stderr, " | * %-82s|\n", command->GetName());
}
}
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
}
void Commands::ShowClusterAttributes(std::string executable, std::string clusterName, std::string commandName,
CommandsVector & commands)
{
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s %s %s attribute-name [param1 param2 ...]\n", executable.c_str(), clusterName.c_str(),
commandName.c_str());
fprintf(stderr, "\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
fprintf(stderr, " | Attributes: |\n");
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
for (auto & command : commands)
{
if (commandName.compare(command->GetName()) == 0)
{
fprintf(stderr, " | * %-82s|\n", command->GetAttribute());
}
}
fprintf(stderr, " +-------------------------------------------------------------------------------------+\n");
}
void Commands::ShowCommand(std::string executable, std::string clusterName, Command * command)
{
fprintf(stderr, "Usage:\n");
std::string arguments = "";
arguments += command->GetName();
size_t argumentsCount = command->GetArgumentsCount();
for (size_t i = 0; i < argumentsCount; i++)
{
arguments += " ";
arguments += command->GetArgumentName(i);
}
fprintf(stderr, " %s %s %s\n", executable.c_str(), clusterName.c_str(), arguments.c_str());
}