blob: 4ed5e2777294a371c810dfd48cf2ab52f6e07c25 [file] [edit]
/*
* 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 <openthread-br/config.h>
#include <algorithm>
#include <fstream>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
#include <assert.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openthread/logging.h>
#include <openthread/platform/radio.h>
#if __ANDROID__ && OTBR_CONFIG_ANDROID_PROPERTY_ENABLE
#include <cutils/properties.h>
#endif
#include "agent/application.hpp"
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "common/mainloop.hpp"
#include "common/types.hpp"
#include "ncp/ncp_openthread.hpp"
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_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 __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},
{"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;
}
static constexpr char kAutoAttachDisableArg[] = "--auto-attach=0";
static char sAutoAttachDisableArgStorage[sizeof(kAutoAttachDisableArg)];
static std::vector<char *> AppendAutoAttachDisableArg(int argc, char *argv[])
{
std::vector<char *> 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;
}
static void PrintHelp(const char *aProgramName)
{
fprintf(stderr,
"Usage: %s [-I interfaceName] [-B backboneIfName] [-d DEBUG_LEVEL] [-v] [--auto-attach[=0/1]] RADIO_URL "
"[RADIO_URL]\n"
" --auto-attach defaults to 1\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)
{
otbrLogLevel level = OTBR_LOG_INFO;
#if __ANDROID__ && OTBR_CONFIG_ANDROID_PROPERTY_ENABLE
char value[PROPERTY_VALUE_MAX];
property_get("ro.build.type", value, "user");
if (!strcmp(value, "user"))
{
level = OTBR_LOG_WARNING;
}
#endif
return level;
}
static void PrintRadioVersionAndExit(const std::vector<const char *> &aRadioUrls)
{
otbr::Ncp::ControllerOpenThread ncpOpenThread{/* aInterfaceName */ "", aRadioUrls, /* aBackboneInterfaceName */ "",
/* aDryRun */ true, /* aEnableAutoAttach */ false};
const char *radioVersion;
ncpOpenThread.Init();
radioVersion = otPlatRadioGetVersionString(ncpOpenThread.GetInstance());
otbrLogNotice("Radio version: %s", radioVersion);
printf("%s\n", radioVersion);
ncpOpenThread.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 printRadioVersion = false;
bool enableAutoAttach = true;
const char *restListenAddress = "";
int restListenPort = kPortNumber;
std::vector<const char *> radioUrls;
std::vector<const char *> backboneInterfaceNames;
long parseResult;
std::set_new_handler(OnAllocateFailed);
while ((opt = getopt_long(argc, argv, "B:d:hI:Vv", 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<otbrLogLevel>(parseResult);
break;
case OTBR_OPT_INTERFACE_NAME:
interfaceName = optarg;
break;
case OTBR_OPT_VERBOSE:
verbose = 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);
otbrLogNotice("Running %s", OTBR_PACKAGE_VERSION);
otbrLogNotice("Thread version: %s", otbr::Ncp::ControllerOpenThread::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 __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 __ANDROID__
if (setjmp(sResetJump))
{
std::vector<char *> args = AppendAutoAttachDisableArg(argc, argv);
alarm(0);
#if OPENTHREAD_ENABLE_COVERAGE
__gcov_flush();
#endif
execvp(args[0], args.data());
}
#endif
return realmain(argc, argv);
}