#define MS_CLASS "Settings"
#include "Settings.hpp"
#include "Logger.hpp"
#include "MediaSoupErrors.hpp"
#include "Utils.hpp"
#include <flatbuffers/flatbuffers.h>
#include <cctype>
#include <iterator>
#include <mutex>
#include <sstream>
extern "C"
{
#include <getopt.h>
}
static std::mutex GlobalSyncMutex;
thread_local struct Settings::Configuration Settings::configuration;
const ankerl::unordered_dense::map<std::string, LogLevel> Settings::String2LogLevel =
{
{ "debug", LogLevel::LOG_DEBUG },
{ "warn", LogLevel::LOG_WARN },
{ "error", LogLevel::LOG_ERROR },
{ "none", LogLevel::LOG_NONE }
};
const ankerl::unordered_dense::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[] =
{
{ .name="logLevel", .has_arg=optional_argument, .flag=nullptr, .val='l' },
{ .name="logTags", .has_arg=optional_argument, .flag=nullptr, .val='t' },
{ .name="rtcMinPort", .has_arg=optional_argument, .flag=nullptr, .val='m' },
{ .name="rtcMaxPort", .has_arg=optional_argument, .flag=nullptr, .val='M' },
{ .name="dtlsCertificateFile", .has_arg=optional_argument, .flag=nullptr, .val='c' },
{ .name="dtlsPrivateKeyFile", .has_arg=optional_argument, .flag=nullptr, .val='p' },
{ .name="libwebrtcFieldTrials", .has_arg=optional_argument, .flag=nullptr, .val='W' },
{ .name="disableLiburing", .has_arg=optional_argument, .flag=nullptr, .val='d' },
{ .name=nullptr, .has_arg=0, .flag=nullptr, .val=0 }
};
std::string stringValue;
std::vector<std::string> logTags;
const std::scoped_lock lock(GlobalSyncMutex);
optind = 1; opterr = 0;
while ((c = getopt_long_only(argc, argv, "", options, std::addressof(optionIdx))) != -1)
{
if (!optarg)
{
MS_THROW_TYPE_ERROR("missing value in command line argument in option '%c'", c);
}
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 'W':
{
stringValue = std::string(optarg);
if (stringValue != Settings::configuration.libwebrtcFieldTrials)
{
MS_WARN_TAG(
info,
"overriding default value of libwebrtcFieldTrials may generate crashes in mediasoup-worker");
Settings::configuration.libwebrtcFieldTrials = stringValue;
}
break;
}
case 'd':
{
stringValue = std::string(optarg);
if (stringValue == "true")
{
Settings::configuration.disableLiburing = true;
}
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::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.at(level);
}
void Settings::SetLogTags(const std::vector<std::string>& tags)
{
MS_TRACE();
struct LogTags logTags;
for (const auto& tag : tags)
{
if (tag == "info")
{
logTags.info = true;
}
else if (tag == "ice")
{
logTags.ice = true;
}
else if (tag == "dtls")
{
logTags.dtls = true;
}
else if (tag == "rtp")
{
logTags.rtp = true;
}
else if (tag == "srtp")
{
logTags.srtp = true;
}
else if (tag == "rtcp")
{
logTags.rtcp = true;
}
else if (tag == "rtx")
{
logTags.rtx = true;
}
else if (tag == "bwe")
{
logTags.bwe = true;
}
else if (tag == "score")
{
logTags.score = true;
}
else if (tag == "simulcast")
{
logTags.simulcast = true;
}
else if (tag == "svc")
{
logTags.svc = true;
}
else if (tag == "sctp")
{
logTags.sctp = true;
}
else if (tag == "message")
{
logTags.message = true;
}
}
Settings::configuration.logTags = logTags;
}
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.at(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());
}
if (!Settings::configuration.libwebrtcFieldTrials.empty())
{
MS_DEBUG_TAG(
info, " libwebrtcFieldTrials: %s", Settings::configuration.libwebrtcFieldTrials.c_str());
}
MS_DEBUG_TAG(info, " disableLiburing: %s", Settings::configuration.disableLiburing ? "yes" : "no");
MS_DEBUG_TAG(info, "</configuration>");
}
void Settings::HandleRequest(Channel::ChannelRequest* request)
{
MS_TRACE();
switch (request->method)
{
case Channel::ChannelRequest::Method::WORKER_UPDATE_SETTINGS:
{
const auto* body = request->data->body_as<FBS::Worker::UpdateSettingsRequest>();
if (flatbuffers::IsFieldPresent(body, FBS::Worker::UpdateSettingsRequest::VT_LOGLEVEL))
{
auto logLevel = body->logLevel()->str();
Settings::SetLogLevel(logLevel);
}
if (flatbuffers::IsFieldPresent(body, FBS::Worker::UpdateSettingsRequest::VT_LOGTAGS))
{
std::vector<std::string> logTags;
for (const auto& logTag : *body->logTags())
{
logTags.push_back(logTag->str());
}
Settings::SetLogTags(logTags);
}
Settings::PrintConfiguration();
request->Accept();
break;
}
default:
{
MS_THROW_ERROR("unknown method '%s'", request->methodCStr);
}
}
}
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());
}
}