#define MS_CLASS "Settings"
#include "Settings.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Utils.hpp"
#include <cctype>
#include <iterator>
#include <mutex>
#include <nlohmann/json.hpp>
#include <sstream>
extern "C"
{
#include <getopt.h>
}
static std::mutex globalSyncMutex;
thread_local struct Settings::Configuration Settings::configuration;
absl::flat_hash_map<std::string, LogLevel> Settings::string2LogLevel =
{
{ "debug", LogLevel::LOG_DEBUG },
{ "warn", LogLevel::LOG_WARN },
{ "error", LogLevel::LOG_ERROR },
{ "none", LogLevel::LOG_NONE }
};
absl::flat_hash_map<LogLevel, std::string> Settings::logLevel2String =
{
{ LogLevel::LOG_DEBUG, "debug" },
{ LogLevel::LOG_WARN, "warn" },
{ LogLevel::LOG_ERROR, "error" },
{ LogLevel::LOG_NONE, "none" }
};
void Settings::SetConfiguration(int argc, char* argv[])
{
MS_TRACE();
int c;
int optionIdx{ 0 };
struct option options[] =
{
{ "logLevel", optional_argument, nullptr, 'l' },
{ "logTags", optional_argument, nullptr, 't' },
{ "rtcMinPort", optional_argument, nullptr, 'm' },
{ "rtcMaxPort", optional_argument, nullptr, 'M' },
{ "dtlsCertificateFile", optional_argument, nullptr, 'c' },
{ "dtlsPrivateKeyFile", optional_argument, nullptr, 'p' },
{ nullptr, 0, nullptr, 0 }
};
std::string stringValue;
std::vector<std::string> logTags;
std::lock_guard<std::mutex> lock(globalSyncMutex);
optind = 1; opterr = 0; while ((c = getopt_long_only(argc, argv, "", options, &optionIdx)) != -1)
{
if (!optarg)
MS_THROW_TYPE_ERROR("unknown configuration parameter: %s", optarg);
switch (c)
{
case 'l':
{
stringValue = std::string(optarg);
SetLogLevel(stringValue);
break;
}
case 't':
{
stringValue = std::string(optarg);
logTags.push_back(stringValue);
break;
}
case 'm':
{
try
{
Settings::configuration.rtcMinPort = static_cast<uint16_t>(std::stoi(optarg));
}
catch (const std::exception& error)
{
MS_THROW_TYPE_ERROR("%s", error.what());
}
break;
}
case 'M':
{
try
{
Settings::configuration.rtcMaxPort = static_cast<uint16_t>(std::stoi(optarg));
}
catch (const std::exception& error)
{
MS_THROW_TYPE_ERROR("%s", error.what());
}
break;
}
case 'c':
{
stringValue = std::string(optarg);
Settings::configuration.dtlsCertificateFile = stringValue;
break;
}
case 'p':
{
stringValue = std::string(optarg);
Settings::configuration.dtlsPrivateKeyFile = stringValue;
break;
}
case '?':
{
if (isprint(optopt) != 0)
MS_THROW_TYPE_ERROR("invalid option '-%c'", (char)optopt);
else
MS_THROW_TYPE_ERROR("unknown long option given as argument");
}
case ':':
{
MS_THROW_TYPE_ERROR("option '%c' requires an argument", (char)optopt);
}
default:
{
MS_THROW_TYPE_ERROR("'default' should never happen");
}
}
}
if (!logTags.empty())
Settings::SetLogTags(logTags);
if (Settings::configuration.rtcMaxPort < Settings::configuration.rtcMinPort)
MS_THROW_TYPE_ERROR("rtcMaxPort cannot be less than rtcMinPort");
Settings::SetDtlsCertificateAndPrivateKeyFiles();
}
void Settings::PrintConfiguration()
{
MS_TRACE();
std::vector<std::string> logTags;
std::ostringstream logTagsStream;
if (Settings::configuration.logTags.info)
logTags.emplace_back("info");
if (Settings::configuration.logTags.ice)
logTags.emplace_back("ice");
if (Settings::configuration.logTags.dtls)
logTags.emplace_back("dtls");
if (Settings::configuration.logTags.rtp)
logTags.emplace_back("rtp");
if (Settings::configuration.logTags.srtp)
logTags.emplace_back("srtp");
if (Settings::configuration.logTags.rtcp)
logTags.emplace_back("rtcp");
if (Settings::configuration.logTags.rtx)
logTags.emplace_back("rtx");
if (Settings::configuration.logTags.bwe)
logTags.emplace_back("bwe");
if (Settings::configuration.logTags.score)
logTags.emplace_back("score");
if (Settings::configuration.logTags.simulcast)
logTags.emplace_back("simulcast");
if (Settings::configuration.logTags.svc)
logTags.emplace_back("svc");
if (Settings::configuration.logTags.sctp)
logTags.emplace_back("sctp");
if (Settings::configuration.logTags.message)
logTags.emplace_back("message");
if (!logTags.empty())
{
std::copy(
logTags.begin(), logTags.end() - 1, std::ostream_iterator<std::string>(logTagsStream, ","));
logTagsStream << logTags.back();
}
MS_DEBUG_TAG(info, "<configuration>");
MS_DEBUG_TAG(
info,
" logLevel : %s",
Settings::logLevel2String[Settings::configuration.logLevel].c_str());
MS_DEBUG_TAG(info, " logTags : %s", logTagsStream.str().c_str());
MS_DEBUG_TAG(info, " rtcMinPort : %" PRIu16, Settings::configuration.rtcMinPort);
MS_DEBUG_TAG(info, " rtcMaxPort : %" PRIu16, Settings::configuration.rtcMaxPort);
if (!Settings::configuration.dtlsCertificateFile.empty())
{
MS_DEBUG_TAG(
info, " dtlsCertificateFile : %s", Settings::configuration.dtlsCertificateFile.c_str());
MS_DEBUG_TAG(
info, " dtlsPrivateKeyFile : %s", Settings::configuration.dtlsPrivateKeyFile.c_str());
}
MS_DEBUG_TAG(info, "</configuration>");
}
void Settings::HandleRequest(Channel::ChannelRequest* request)
{
MS_TRACE();
switch (request->methodId)
{
case Channel::ChannelRequest::MethodId::WORKER_UPDATE_SETTINGS:
{
auto jsonLogLevelIt = request->data.find("logLevel");
auto jsonLogTagsIt = request->data.find("logTags");
if (jsonLogLevelIt != request->data.end() && jsonLogLevelIt->is_string())
{
std::string logLevel = *jsonLogLevelIt;
Settings::SetLogLevel(logLevel);
}
if (jsonLogTagsIt != request->data.end() && jsonLogTagsIt->is_array())
{
std::vector<std::string> logTags;
for (const auto& logTag : *jsonLogTagsIt)
{
if (logTag.is_string())
logTags.push_back(logTag);
}
Settings::SetLogTags(logTags);
}
Settings::PrintConfiguration();
request->Accept();
break;
}
default:
{
MS_THROW_ERROR("unknown method '%s'", request->method.c_str());
}
}
}
void Settings::SetLogLevel(std::string& level)
{
MS_TRACE();
Utils::String::ToLowerCase(level);
if (Settings::string2LogLevel.find(level) == Settings::string2LogLevel.end())
MS_THROW_TYPE_ERROR("invalid value '%s' for logLevel", level.c_str());
Settings::configuration.logLevel = Settings::string2LogLevel[level];
}
void Settings::SetLogTags(const std::vector<std::string>& tags)
{
MS_TRACE();
struct LogTags newLogTags;
for (auto& tag : tags)
{
if (tag == "info")
newLogTags.info = true;
else if (tag == "ice")
newLogTags.ice = true;
else if (tag == "dtls")
newLogTags.dtls = true;
else if (tag == "rtp")
newLogTags.rtp = true;
else if (tag == "srtp")
newLogTags.srtp = true;
else if (tag == "rtcp")
newLogTags.rtcp = true;
else if (tag == "rtx")
newLogTags.rtx = true;
else if (tag == "bwe")
newLogTags.bwe = true;
else if (tag == "score")
newLogTags.score = true;
else if (tag == "simulcast")
newLogTags.simulcast = true;
else if (tag == "svc")
newLogTags.svc = true;
else if (tag == "sctp")
newLogTags.sctp = true;
else if (tag == "message")
newLogTags.message = true;
}
Settings::configuration.logTags = newLogTags;
}
void Settings::SetDtlsCertificateAndPrivateKeyFiles()
{
MS_TRACE();
if (
!Settings::configuration.dtlsCertificateFile.empty() &&
Settings::configuration.dtlsPrivateKeyFile.empty())
{
MS_THROW_TYPE_ERROR("missing dtlsPrivateKeyFile");
}
else if (
Settings::configuration.dtlsCertificateFile.empty() &&
!Settings::configuration.dtlsPrivateKeyFile.empty())
{
MS_THROW_TYPE_ERROR("missing dtlsCertificateFile");
}
else if (
Settings::configuration.dtlsCertificateFile.empty() &&
Settings::configuration.dtlsPrivateKeyFile.empty())
{
return;
}
const std::string& dtlsCertificateFile = Settings::configuration.dtlsCertificateFile;
const std::string& dtlsPrivateKeyFile = Settings::configuration.dtlsPrivateKeyFile;
try
{
Utils::File::CheckFile(dtlsCertificateFile.c_str());
}
catch (const MediaSoupError& error)
{
MS_THROW_TYPE_ERROR("dtlsCertificateFile: %s", error.what());
}
try
{
Utils::File::CheckFile(dtlsPrivateKeyFile.c_str());
}
catch (const MediaSoupError& error)
{
MS_THROW_TYPE_ERROR("dtlsPrivateKeyFile: %s", error.what());
}
}