diff --git a/CMakeLists.txt b/CMakeLists.txt index 497870ad0..2a489282c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ endif() set(CMAKE_CXX_EXTENSIONS OFF) execute_process( - COMMAND git describe --dirty --always 2>/dev/null + COMMAND git describe HEAD --always WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE OT_COMM_GIT_REVISION OUTPUT_STRIP_TRAILING_WHITESPACE ) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 5cc4272ab..4d52ff7ae 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -37,6 +37,9 @@ add_library(commissioner-app file_util.hpp json.cpp json.hpp + logger_util.hpp + sys_logger.cpp + sys_logger.hpp ) target_link_libraries(commissioner-app diff --git a/src/app/cli/interpreter.cpp b/src/app/cli/interpreter.cpp index 1dfe02dd9..02cc85eb9 100644 --- a/src/app/cli/interpreter.cpp +++ b/src/app/cli/interpreter.cpp @@ -37,6 +37,7 @@ #include "app/file_util.hpp" #include "app/json.hpp" +#include "app/sys_logger.hpp" #include "common/error_macros.hpp" #include "common/utils.hpp" @@ -45,29 +46,22 @@ namespace ot { namespace commissioner { const std::map &Interpreter::mEvaluatorMap = *new std::map{ - {"start", &Interpreter::ProcessStart}, - {"stop", &Interpreter::ProcessStop}, - {"active", &Interpreter::ProcessActive}, - {"token", &Interpreter::ProcessToken}, - {"network", &Interpreter::ProcessNetwork}, - {"sessionid", &Interpreter::ProcessSessionId}, - {"borderagent", &Interpreter::ProcessBorderAgent}, - {"joiner", &Interpreter::ProcessJoiner}, - {"commdataset", &Interpreter::ProcessCommDataset}, - {"opdataset", &Interpreter::ProcessOpDataset}, - {"bbrdataset", &Interpreter::ProcessBbrDataset}, - {"reenroll", &Interpreter::ProcessReenroll}, - {"domainreset", &Interpreter::ProcessDomainReset}, - {"migrate", &Interpreter::ProcessMigrate}, - {"mlr", &Interpreter::ProcessMlr}, - {"announce", &Interpreter::ProcessAnnounce}, - {"panid", &Interpreter::ProcessPanId}, - {"energy", &Interpreter::ProcessEnergy}, - {"exit", &Interpreter::ProcessExit}, + {"config", &Interpreter::ProcessConfig}, {"start", &Interpreter::ProcessStart}, + {"stop", &Interpreter::ProcessStop}, {"active", &Interpreter::ProcessActive}, + {"token", &Interpreter::ProcessToken}, {"network", &Interpreter::ProcessNetwork}, + {"sessionid", &Interpreter::ProcessSessionId}, {"borderagent", &Interpreter::ProcessBorderAgent}, + {"joiner", &Interpreter::ProcessJoiner}, {"commdataset", &Interpreter::ProcessCommDataset}, + {"opdataset", &Interpreter::ProcessOpDataset}, {"bbrdataset", &Interpreter::ProcessBbrDataset}, + {"reenroll", &Interpreter::ProcessReenroll}, {"domainreset", &Interpreter::ProcessDomainReset}, + {"migrate", &Interpreter::ProcessMigrate}, {"mlr", &Interpreter::ProcessMlr}, + {"announce", &Interpreter::ProcessAnnounce}, {"panid", &Interpreter::ProcessPanId}, + {"energy", &Interpreter::ProcessEnergy}, {"exit", &Interpreter::ProcessExit}, {"help", &Interpreter::ProcessHelp}, }; const std::map &Interpreter::mUsageMap = *new std::map{ + {"config", "config get pskc\n" + "config set pskc "}, {"start", "start "}, {"stop", "stop"}, {"active", "active"}, @@ -171,11 +165,20 @@ Error Interpreter::Init(const std::string &aConfigFile) Error error; std::string configJson; - Config config; - SuccessOrExit(error = ReadFile(configJson, aConfigFile)); - SuccessOrExit(error = ConfigFromJson(config, configJson)); - SuccessOrExit(error = CommissionerApp::Create(mCommissioner, config)); + if (!aConfigFile.empty()) + { + SuccessOrExit(error = ReadFile(configJson, aConfigFile)); + SuccessOrExit(error = ConfigFromJson(mConfig, configJson)); + } + else + { + // Default to Non-CCM mode if no configuration file is provided. + mConfig.mEnableCcm = false; + mConfig.mPSKc.assign(kMaxPSKcLength, 0xff); + mConfig.mLogger = SysLogger::Create(LogLevel::kDebug); + } + SuccessOrExit(error = CommissionerApp::Create(mCommissioner, mConfig)); exit: return error; @@ -305,6 +308,48 @@ Interpreter::Expression Interpreter::ParseExpression(const std::string &aLiteral return expr; } +Interpreter::Value Interpreter::ProcessConfig(const Expression &aExpr) +{ + Value value; + + VerifyOrExit(aExpr.size() >= 3, value = ERROR_INVALID_ARGS("two few arguments")); + VerifyOrExit(aExpr[2] == "pskc", value = ERROR_INVALID_ARGS("{} is not a valid property", aExpr[2])); + if (aExpr[1] == "get") + { + value = utils::Hex(mConfig.mPSKc); + } + else if (aExpr[1] == "set") + { + ByteArray pskc; + + VerifyOrExit(aExpr.size() >= 4, value = ERROR_INVALID_ARGS("two few arguments")); + SuccessOrExit(value = utils::Hex(pskc, aExpr[3])); + SuccessOrExit(value = UpdateConfig(pskc)); + } + else + { + ExitNow(value = ERROR_INVALID_COMMAND("{} is not a valid sub-command", aExpr[1])); + } + +exit: + return value; +} + +Error Interpreter::UpdateConfig(const ByteArray &aPSKc) +{ + Error error; + + VerifyOrExit(aPSKc.size() <= kMaxPSKcLength, error = ERROR_INVALID_ARGS("invalid PSKc length")); + VerifyOrExit(!mCommissioner->IsActive(), + error = ERROR_INVALID_STATE("cannot set PSKc when the commissioner is active")); + + mConfig.mPSKc = aPSKc; + CommissionerApp::Create(mCommissioner, mConfig).IgnoreError(); + +exit: + return error; +} + Interpreter::Value Interpreter::ProcessStart(const Expression &aExpr) { Error error; diff --git a/src/app/cli/interpreter.hpp b/src/app/cli/interpreter.hpp index 381910a63..ed6daa5cc 100644 --- a/src/app/cli/interpreter.hpp +++ b/src/app/cli/interpreter.hpp @@ -101,6 +101,7 @@ class Interpreter Expression ParseExpression(const std::string &aLiteral); + Value ProcessConfig(const Expression &aExpr); Value ProcessStart(const Expression &aExpr); Value ProcessStop(const Expression &aExpr); Value ProcessActive(const Expression &aExpr); @@ -122,6 +123,8 @@ class Interpreter Value ProcessExit(const Expression &aExpr); Value ProcessHelp(const Expression &aExpr); + Error UpdateConfig(const ByteArray &aPSKc); + static void BorderAgentHandler(const BorderAgent *aBorderAgent, const Error &aError); static const std::string Usage(Expression aExpr); diff --git a/src/app/cli/main.cpp b/src/app/cli/main.cpp index 8effcf619..af7c5d68d 100644 --- a/src/app/cli/main.cpp +++ b/src/app/cli/main.cpp @@ -63,7 +63,7 @@ static void PrintUsage(const std::string &aProgram) { static const std::string usage = "usage: \n" " " + - aProgram + " "; + aProgram + " []"; Console::Write(usage, Console::Color::kWhite); } @@ -89,18 +89,25 @@ static void HandleSignalInterrupt() int main(int argc, const char *argv[]) { - Error error; - Config config; + Error error; + std::string configFile; - if (argc < 2 || ToLower(argv[1]) == "-h" || ToLower(argv[1]) == "--help") + if (argc >= 2) { - PrintUsage(argv[0]); - ExitNow(); - } - else if (ToLower(argv[1]) == "-v" || ToLower(argv[1]) == "--version") - { - PrintVersion(); - ExitNow(); + if (ToLower(argv[1]) == "-h" || ToLower(argv[1]) == "--help") + { + PrintUsage(argv[0]); + ExitNow(); + } + else if (ToLower(argv[1]) == "-v" || ToLower(argv[1]) == "--version") + { + PrintVersion(); + ExitNow(); + } + else + { + configFile = argv[1]; + } } // Block signals in this thread and subsequently spawned threads. @@ -112,7 +119,7 @@ int main(int argc, const char *argv[]) Console::Write(kLogo, Console::Color::kBlue); - SuccessOrExit(error = gInterpreter.Init(argv[1])); + SuccessOrExit(error = gInterpreter.Init(configFile)); gInterpreter.Run(); diff --git a/src/app/file_logger.cpp b/src/app/file_logger.cpp index cfb0d9287..5f00053a6 100644 --- a/src/app/file_logger.cpp +++ b/src/app/file_logger.cpp @@ -28,7 +28,7 @@ /** * @file - * This file defines file logger. + * This file implements file logger. * */ @@ -38,6 +38,7 @@ #include +#include "app/logger_util.hpp" #include "common/error_macros.hpp" #include "common/time.hpp" #include "common/utils.hpp" @@ -46,38 +47,6 @@ namespace ot { namespace commissioner { -static std::string ToString(LogLevel aLevel) -{ - std::string ret; - - switch (aLevel) - { - case LogLevel::kOff: - ret = "off"; - break; - case LogLevel::kCritical: - ret = "critical"; - break; - case LogLevel::kError: - ret = "error"; - break; - case LogLevel::kWarn: - ret = "warn"; - break; - case LogLevel::kInfo: - ret = "info"; - break; - case LogLevel::kDebug: - ret = "debug"; - break; - default: - VerifyOrDie(false); - break; - } - - return ret; -} - Error FileLogger::Create(std::shared_ptr &aFileLogger, const std::string &aFilename, LogLevel aLogLevel) { Error error; @@ -131,7 +100,7 @@ void FileLogger::Log(LogLevel aLevel, const std::string &aRegion, const std::str VerifyOrExit(aLevel <= mLogLevel); VerifyOrExit(mLogFile != nullptr); - logStream << "[ " << TimePointToString(Clock::now()) << " ] [ " << ToString(aLevel) << " ] [ " << aRegion << " ] " + logStream << "[ " << TimePointToString(Clock::now()) << " ] " << ToString(aLevel) << " [ " << aRegion << " ] " << aMsg << std::endl; fputs(logStream.str().c_str(), mLogFile); diff --git a/src/app/logger_util.hpp b/src/app/logger_util.hpp new file mode 100644 index 000000000..b33ffcdd1 --- /dev/null +++ b/src/app/logger_util.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, The OpenThread Commissioner Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file defines utility functions for loggers. + * + */ + +#ifndef OT_COMM_APP_LOGGER_UTIL_HPP_ +#define OT_COMM_APP_LOGGER_UTIL_HPP_ + +#include + +#include + +#include "common/utils.hpp" + +namespace ot { + +namespace commissioner { + +static std::string ToString(LogLevel aLevel) +{ + std::string ret; + + switch (aLevel) + { + case LogLevel::kOff: + ret = "[ off ]"; + break; + case LogLevel::kCritical: + ret = "[ crit ]"; + break; + case LogLevel::kError: + ret = "[ error ]"; + break; + case LogLevel::kWarn: + ret = "[ warn ]"; + break; + case LogLevel::kInfo: + ret = "[ info ]"; + break; + case LogLevel::kDebug: + ret = "[ debug ]"; + break; + default: + VerifyOrDie(false); + break; + } + + return ret; +} + +} // namespace commissioner + +} // namespace ot + +#endif // OT_COMM_APP_LOGGER_UTIL_HPP_ diff --git a/src/app/sys_logger.cpp b/src/app/sys_logger.cpp new file mode 100644 index 000000000..41db918d5 --- /dev/null +++ b/src/app/sys_logger.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2021, The OpenThread Commissioner Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements file logger. + * + */ + +#include "app/sys_logger.hpp" + +#include + +#include +#include + +#include "app/logger_util.hpp" +#include "common/error_macros.hpp" +#include "common/time.hpp" +#include "common/utils.hpp" + +namespace ot { + +namespace commissioner { + +static int ToSysLogLevel(LogLevel aLevel) +{ + int ret; + + switch (aLevel) + { + case LogLevel::kOff: + ret = LOG_EMERG; + break; + case LogLevel::kCritical: + ret = LOG_CRIT; + break; + case LogLevel::kError: + ret = LOG_ERR; + break; + case LogLevel::kWarn: + ret = LOG_WARNING; + break; + case LogLevel::kInfo: + ret = LOG_INFO; + break; + case LogLevel::kDebug: + ret = LOG_DEBUG; + break; + default: + VerifyOrDie(false); + break; + } + + return ret; +} + +std::shared_ptr SysLogger::Create(LogLevel aLogLevel) +{ + auto logger = std::shared_ptr(new SysLogger); + + logger->Init(aLogLevel); + return logger; +} + +SysLogger::~SysLogger() +{ + closelog(); +} + +void SysLogger::Init(LogLevel aLogLevel) +{ + openlog("ot-commissioner", LOG_CONS | LOG_PID, LOG_USER); + mLogLevel = aLogLevel; +} + +void SysLogger::Log(LogLevel aLevel, const std::string &aRegion, const std::string &aMsg) +{ + if (aLevel <= mLogLevel) + { + syslog(ToSysLogLevel(aLevel), "%s [ %s ] %s", ToString(aLevel).c_str(), aRegion.c_str(), aMsg.c_str()); + } +} + +} // namespace commissioner + +} // namespace ot diff --git a/src/app/sys_logger.hpp b/src/app/sys_logger.hpp new file mode 100644 index 000000000..9385e0519 --- /dev/null +++ b/src/app/sys_logger.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, The OpenThread Commissioner Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file defines sys logger. + * + */ + +#ifndef OT_COMM_APP_SYS_LOGGER_HPP_ +#define OT_COMM_APP_SYS_LOGGER_HPP_ + +#include + +#include + +namespace ot { + +namespace commissioner { + +/** + * @brief An implementation of the Logger interface that write log to syslog. + * + */ +class SysLogger : public Logger +{ +public: + ~SysLogger() override; + + /** + * This function returns a sys logger with given minimum log level. + * + * @param[in] aLogLevel The minimum log level. Log messages with a lower + * log level than this will be dropped silently. + * + * @retval An instance of SysLogger. + * + */ + static std::shared_ptr Create(LogLevel aLogLevel); + + void Log(LogLevel aLevel, const std::string &aRegion, const std::string &aMsg) override; + +private: + SysLogger() = default; + void Init(LogLevel aLogLevel); + + LogLevel mLogLevel; +}; + +} // namespace commissioner + +} // namespace ot + +#endif // OT_COMM_APP_SYS_LOGGER_HPP_ diff --git a/tests/integration/test_joining.sh b/tests/integration/test_joining.sh index 1db79138d..3d4380862 100755 --- a/tests/integration/test_joining.sh +++ b/tests/integration/test_joining.sh @@ -47,6 +47,26 @@ test_joining() { stop_daemon } +test_joining_default_config() { + start_daemon + form_network "${PSKC}" + + # Start the commissioner with default config. + start_commissioner "" + send_command_to_commissioner "config set pskc ${PSKC}" + petition_commissioner + send_command_to_commissioner "active" + + ## enable all MeshCoP joiners + send_command_to_commissioner "joiner enable meshcop ${JOINER_EUI64} ${JOINER_CREDENTIAL}" + + start_joiner "meshcop" + + stop_commissioner + + stop_daemon +} + test_joining_fail() { start_daemon form_network "${PSKC}" diff --git a/tools/commissioner_thci/commissioner_ctl.py b/tools/commissioner_thci/commissioner_ctl.py index 4bca6a273..f7e7b038a 100755 --- a/tools/commissioner_thci/commissioner_ctl.py +++ b/tools/commissioner_thci/commissioner_ctl.py @@ -55,10 +55,11 @@ def main(): args = parser.parse_args() if args.action == 'init': + config = os.path.realpath(args.config) if args.config else "" message = { 'type': 'control', 'command': 'init', - 'config': os.path.realpath(args.config), + 'config': config, } elif args.action == 'exit': message = {