/* * Copyright (c) 2017, The OpenThread 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. */ #define OTBR_LOG_TAG "AGENT" #include #include #include #include #include #include #include #include #include #include #include #if OTBR_ENABLE_PLATFORM_ANDROID #include #endif #include "agent/application.hpp" #include "common/code_utils.hpp" #include "common/logging.hpp" #include "common/mainloop.hpp" #include "common/types.hpp" #include "ncp/thread_host.hpp" #ifdef OTBR_ENABLE_PLATFORM_ANDROID #include #ifndef __ANDROID__ #error "OTBR_ENABLE_PLATFORM_ANDROID can be enabled for only Android devices" #endif #endif static const char kDefaultInterfaceName[] = "wpan0"; // Port number used by Rest server. static const uint32_t kPortNumber = 8081; enum { OTBR_OPT_BACKBONE_INTERFACE_NAME = 'B', OTBR_OPT_DEBUG_LEVEL = 'd', OTBR_OPT_HELP = 'h', OTBR_OPT_INTERFACE_NAME = 'I', OTBR_OPT_VERBOSE = 'v', OTBR_OPT_SYSLOG_DISABLE = 's', OTBR_OPT_VERSION = 'V', OTBR_OPT_SHORTMAX = 128, OTBR_OPT_RADIO_VERSION, OTBR_OPT_AUTO_ATTACH, OTBR_OPT_REST_LISTEN_ADDR, OTBR_OPT_REST_LISTEN_PORT, }; #ifndef OTBR_ENABLE_PLATFORM_ANDROID static jmp_buf sResetJump; #endif static otbr::Application *gApp = nullptr; void __gcov_flush(); static const struct option kOptions[] = { {"backbone-ifname", required_argument, nullptr, OTBR_OPT_BACKBONE_INTERFACE_NAME}, {"debug-level", required_argument, nullptr, OTBR_OPT_DEBUG_LEVEL}, {"help", no_argument, nullptr, OTBR_OPT_HELP}, {"thread-ifname", required_argument, nullptr, OTBR_OPT_INTERFACE_NAME}, {"verbose", no_argument, nullptr, OTBR_OPT_VERBOSE}, {"syslog-disable", no_argument, nullptr, OTBR_OPT_SYSLOG_DISABLE}, {"version", no_argument, nullptr, OTBR_OPT_VERSION}, {"radio-version", no_argument, nullptr, OTBR_OPT_RADIO_VERSION}, {"auto-attach", optional_argument, nullptr, OTBR_OPT_AUTO_ATTACH}, {"rest-listen-address", required_argument, nullptr, OTBR_OPT_REST_LISTEN_ADDR}, {"rest-listen-port", required_argument, nullptr, OTBR_OPT_REST_LISTEN_PORT}, {0, 0, 0, 0}}; static bool ParseInteger(const char *aStr, long &aOutResult) { bool successful = true; char *strEnd; long result; VerifyOrExit(aStr != nullptr, successful = false); errno = 0; result = strtol(aStr, &strEnd, 0); VerifyOrExit(errno != ERANGE, successful = false); VerifyOrExit(aStr != strEnd, successful = false); aOutResult = result; exit: return successful; } #ifndef OTBR_ENABLE_PLATFORM_ANDROID static constexpr char kAutoAttachDisableArg[] = "--auto-attach=0"; static char sAutoAttachDisableArgStorage[sizeof(kAutoAttachDisableArg)]; static std::vector AppendAutoAttachDisableArg(int argc, char *argv[]) { std::vector args(argv, argv + argc); args.erase(std::remove_if( args.begin(), args.end(), [](const char *arg) { return arg != nullptr && std::string(arg).rfind("--auto-attach", 0) == 0; }), args.end()); strcpy(sAutoAttachDisableArgStorage, kAutoAttachDisableArg); args.push_back(sAutoAttachDisableArgStorage); args.push_back(nullptr); return args; } #endif static void PrintHelp(const char *aProgramName) { fprintf(stderr, "Usage: %s [-I interfaceName] [-B backboneIfName] [-d DEBUG_LEVEL] [-v] [-s] [--auto-attach[=0/1]] " "RADIO_URL [RADIO_URL]\n" " --auto-attach defaults to 1\n" " -s disables syslog and prints to standard out\n", aProgramName); fprintf(stderr, "%s", otSysGetRadioUrlHelpString()); } static void PrintVersion(void) { printf("%s\n", OTBR_PACKAGE_VERSION); } static void OnAllocateFailed(void) { otbrLogCrit("Allocate failure, exiting..."); exit(1); } static otbrLogLevel GetDefaultLogLevel(void) { #if OTBR_ENABLE_PLATFORM_ANDROID // The log level is set to DEBUG by default, the final output log will be filtered by Android log system. otbrLogLevel level = OTBR_LOG_DEBUG; char value[PROPERTY_VALUE_MAX]; // Set the Android log level to INFO by default. __android_log_set_minimum_priority(ANDROID_LOG_INFO); property_get("ro.build.type", value, "user"); if (!strcmp(value, "user")) { level = OTBR_LOG_WARNING; } #else otbrLogLevel level = OTBR_LOG_INFO; #endif return level; } static void PrintRadioVersionAndExit(const std::vector &aRadioUrls) { auto host = std::unique_ptr( otbr::Ncp::ThreadHost::Create(/* aInterfaceName */ "", aRadioUrls, /* aBackboneInterfaceName */ "", /* aDryRun */ true, /* aEnableAutoAttach */ false)); const char *coprocessorVersion; host->Init(); coprocessorVersion = host->GetCoprocessorVersion(); printf("%s\n", coprocessorVersion); host->Deinit(); exit(EXIT_SUCCESS); } static int realmain(int argc, char *argv[]) { otbrLogLevel logLevel = GetDefaultLogLevel(); int opt; int ret = EXIT_SUCCESS; const char *interfaceName = kDefaultInterfaceName; bool verbose = false; bool syslogDisable = false; bool printRadioVersion = false; bool enableAutoAttach = true; const char *restListenAddress = ""; int restListenPort = kPortNumber; std::vector radioUrls; std::vector backboneInterfaceNames; long parseResult; std::set_new_handler(OnAllocateFailed); while ((opt = getopt_long(argc, argv, "B:d:hI:Vvs", kOptions, nullptr)) != -1) { switch (opt) { case OTBR_OPT_BACKBONE_INTERFACE_NAME: backboneInterfaceNames.push_back(optarg); otbrLogNotice("Backbone interface: %s", optarg); break; case OTBR_OPT_DEBUG_LEVEL: VerifyOrExit(ParseInteger(optarg, parseResult), ret = EXIT_FAILURE); VerifyOrExit(OTBR_LOG_EMERG <= parseResult && parseResult <= OTBR_LOG_DEBUG, ret = EXIT_FAILURE); logLevel = static_cast(parseResult); break; case OTBR_OPT_INTERFACE_NAME: interfaceName = optarg; break; case OTBR_OPT_VERBOSE: verbose = true; break; case OTBR_OPT_SYSLOG_DISABLE: syslogDisable = true; break; case OTBR_OPT_VERSION: PrintVersion(); ExitNow(); break; case OTBR_OPT_HELP: PrintHelp(argv[0]); ExitNow(ret = EXIT_SUCCESS); break; case OTBR_OPT_RADIO_VERSION: printRadioVersion = true; break; case OTBR_OPT_AUTO_ATTACH: if (optarg == nullptr) { enableAutoAttach = true; } else { VerifyOrExit(ParseInteger(optarg, parseResult), ret = EXIT_FAILURE); enableAutoAttach = parseResult; } break; case OTBR_OPT_REST_LISTEN_ADDR: restListenAddress = optarg; break; case OTBR_OPT_REST_LISTEN_PORT: VerifyOrExit(ParseInteger(optarg, parseResult), ret = EXIT_FAILURE); restListenPort = parseResult; break; default: PrintHelp(argv[0]); ExitNow(ret = EXIT_FAILURE); break; } } otbrLogInit(argv[0], logLevel, verbose, syslogDisable); otbrLogNotice("Running %s", OTBR_PACKAGE_VERSION); otbrLogNotice("Thread version: %s", otbr::Ncp::RcpHost::GetThreadVersion()); otbrLogNotice("Thread interface: %s", interfaceName); if (backboneInterfaceNames.empty()) { otbrLogNotice("Backbone interface is not specified"); } for (int i = optind; i < argc; i++) { otbrLogNotice("Radio URL: %s", argv[i]); radioUrls.push_back(argv[i]); } if (printRadioVersion) { PrintRadioVersionAndExit(radioUrls); assert(false); } { otbr::Application app(interfaceName, backboneInterfaceNames, radioUrls, enableAutoAttach, restListenAddress, restListenPort); gApp = &app; app.Init(); ret = app.Run(); app.Deinit(); } otbrLogDeinit(); exit: return ret; } void otPlatReset(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE; VerifyOrDie(gApp != nullptr, "gApp is null"); gApp->Deinit(); gApp = nullptr; #ifndef OTBR_ENABLE_PLATFORM_ANDROID longjmp(sResetJump, 1); assert(false); #else // Exits immediately on Android. The Android system_server will receive the // signal and decide whether (and how) to restart the ot-daemon exit(0); #endif } int main(int argc, char *argv[]) { #ifndef OTBR_ENABLE_PLATFORM_ANDROID if (setjmp(sResetJump)) { std::vector args = AppendAutoAttachDisableArg(argc, argv); alarm(0); #if OPENTHREAD_ENABLE_COVERAGE __gcov_flush(); #endif execvp(args[0], args.data()); } #endif return realmain(argc, argv); }