Support for reloading config from disk, writing config to disk after 60 seconds of no changes, reloading config from disk on API call request

This commit is contained in:
Thulinma 2023-06-03 03:16:32 +02:00
parent cac86fff57
commit 132e59db51
8 changed files with 339 additions and 222 deletions

View file

@ -3,21 +3,6 @@
#include "shared_memory.h"
#include "util.h"
#define COMM_STATUS_SOURCE 0x80
#define COMM_STATUS_DONOTTRACK 0x40
#define COMM_STATUS_DISCONNECT 0x20
#define COMM_STATUS_REQDISCONNECT 0x10
#define COMM_STATUS_ACTIVE 0x1
#define COMM_STATUS_INVALID 0x0
#define SESS_BUNDLE_DEFAULT_VIEWER 14
#define SESS_BUNDLE_DEFAULT_OTHER 15
#define SESS_DEFAULT_STREAM_INFO_MODE 1
#define SESS_HTTP_AS_VIEWER 1
#define SESS_HTTP_AS_OUTPUT 2
#define SESS_HTTP_DISABLED 3
#define SESS_HTTP_AS_UNSPECIFIED 4
#define SESS_TKN_DEFAULT_MODE 15
#define COMM_LOOP(comm, onActive, onDisconnect) \
{\

View file

@ -285,3 +285,20 @@ static inline void show_stackframe(){}
#define NEW_TRACK_ID 0x80000000
#define QUICK_NEGOTIATE 0xC0000000
// Session and Comm library related constants
#define COMM_STATUS_SOURCE 0x80
#define COMM_STATUS_DONOTTRACK 0x40
#define COMM_STATUS_DISCONNECT 0x20
#define COMM_STATUS_REQDISCONNECT 0x10
#define COMM_STATUS_ACTIVE 0x1
#define COMM_STATUS_INVALID 0x0
#define SESS_BUNDLE_DEFAULT_VIEWER 14
#define SESS_BUNDLE_DEFAULT_OTHER 15
#define SESS_DEFAULT_STREAM_INFO_MODE 1
#define SESS_HTTP_AS_VIEWER 1
#define SESS_HTTP_AS_OUTPUT 2
#define SESS_HTTP_DISABLED 3
#define SESS_HTTP_AS_UNSPECIFIED 4
#define SESS_TKN_DEFAULT_MODE 15

View file

@ -5,6 +5,7 @@
#include <cstdio>
#include <cstring>
#include <sys/time.h> //for gettimeofday
#include <sys/stat.h>
#include <time.h> //for time and nanosleep
#include <sstream>
#include <stdlib.h>
@ -166,3 +167,13 @@ std::string Util::getDateString(uint64_t epoch){
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S %z", timeinfo);
return std::string(buffer);
}
/// Gets unix time of last file modification, or 0 if this information is not available for any reason
uint64_t Util::getFileUnixTime(const std::string & filename){
struct stat fInfo;
if (stat(filename.c_str(), &fInfo) == 0){
return fInfo.st_mtime;
}
return 0;
}

View file

@ -21,4 +21,5 @@ namespace Util{
uint64_t getMSFromUTCString(std::string UTCString);
uint64_t getUTCTimeDiff(std::string UTCString, uint64_t epochMillis);
std::string getDateString(uint64_t epoch = 0);
uint64_t getFileUnixTime(const std::string & filename);
}// namespace Util

View file

@ -41,6 +41,8 @@
#define COMPILED_PASSWORD ""
#endif
uint64_t lastConfRead = 0;
/// the following function is a simple check if the user wants to proceed to fix (y), ignore (n) or
/// abort on (a) a question
static inline char yna(std::string &user_input){
@ -72,12 +74,49 @@ void createAccount(std::string account){
}
/// Status monitoring thread.
/// Will check outputs, inputs and converters every three seconds
/// Checks status of "protocols" (listening outputs)
/// Updates config from disk when changed
/// Writes config to disk after some time of no changes
void statusMonitor(void *np){
Controller::loadActiveConnectors();
while (Controller::conf.is_active){
// this scope prevents the configMutex from being locked constantly
// Check configuration file last changed time
uint64_t confTime = Util::getFileUnixTime(Controller::conf.getString("configFile"));
if (Controller::Storage.isMember("config_split")){
jsonForEach(Controller::Storage["config_split"], cs){
if (cs->isString()){
uint64_t subTime = Util::getFileUnixTime(cs->asStringRef());
if (subTime && subTime > confTime){confTime = subTime;}
}
}
}
// If we recently wrote, assume we know the contents since that time, too.
if (lastConfRead < Controller::lastConfigWrite){lastConfRead = Controller::lastConfigWrite;}
// If the config has changed, update Controller::lastConfigChange
{
JSON::Value currConfig;
Controller::getConfigAsWritten(currConfig);
if (Controller::lastConfigSeen != currConfig){
Controller::lastConfigChange = Util::epoch();
Controller::lastConfigSeen = currConfig;
}
}
// Read from disk if they are newer than our last read
if (confTime && confTime > lastConfRead){
INFO_MSG("Configuration files changed - reloading configuration from disk");
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
Controller::readConfigFromDisk();
lastConfRead = Controller::lastConfigChange;
}
// Write to disk if we have made no changes in the last 60 seconds and the files are older than the last change
if (Controller::lastConfigChange > Controller::lastConfigWrite && Controller::lastConfigChange < Util::epoch() - 60){
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
Controller::writeConfigToDisk();
if (lastConfRead < Controller::lastConfigWrite){lastConfRead = Controller::lastConfigWrite;}
}
{ // this scope prevents the configMutex from being locked constantly
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
// checks online protocols, reports changes to status
if (Controller::CheckProtocols(Controller::Storage["config"]["protocols"], Controller::capabilities)){
@ -86,7 +125,8 @@ void statusMonitor(void *np){
// checks stream statuses, reports changes to status
Controller::CheckAllStreams(Controller::Storage["streams"]);
}
Util::sleep(3000); // wait at least 3 seconds
Util::sleep(3000); // wait at most 3 seconds
}
if (Util::Config::is_restarting){
Controller::prepareActiveConnectorsForReload();
@ -137,6 +177,75 @@ void handleUSR1Parent(int signum, siginfo_t *sigInfo, void *ignore){
Util::Config::is_restarting = true;
}
bool interactiveFirstTimeSetup(){
// check for username
if (!Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
std::string in_string = "";
while (yna(in_string) == 'x' && Controller::conf.is_active){
std::cout << "Account not set, do you want to create an account? (y)es, (n)o, (a)bort: ";
std::cout.flush();
std::getline(std::cin, in_string);
switch (yna(in_string)){
case 'y':{
// create account
std::string usr_string = "";
while (!(Controller::Storage.isMember("account") && Controller::Storage["account"].size() > 0) &&
Controller::conf.is_active){
std::cout << "Please type in the username, a colon and a password in the following "
"format; username:password"
<< std::endl
<< ": ";
std::cout.flush();
std::getline(std::cin, usr_string);
createAccount(usr_string);
}
}break;
case 'a': return false; // abort bootup
case 't':{
createAccount("test:test");
if ((Controller::capabilities["connectors"].size()) &&
(!Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") ||
Controller::Storage["config"]["protocols"].size() < 1)){
// create protocols
jsonForEach(Controller::capabilities["connectors"], it){
if (!it->isMember("required")){
JSON::Value newProtocol;
newProtocol["connector"] = it.key();
Controller::Storage["config"]["protocols"].append(newProtocol);
}
}
}
}break;
}
}
}
// check for protocols
if ((Controller::capabilities["connectors"].size()) &&
(!Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") ||
Controller::Storage["config"]["protocols"].size() < 1)){
std::string in_string = "";
while (yna(in_string) == 'x' && Controller::conf.is_active){
std::cout << "Protocols not set, do you want to enable default protocols? (y)es, (n)o, (a)bort: ";
std::cout.flush();
std::getline(std::cin, in_string);
if (yna(in_string) == 'y'){
// create protocols
jsonForEach(Controller::capabilities["connectors"], it){
if (!it->isMember("required")){
JSON::Value newProtocol;
newProtocol["connector"] = it.key();
Controller::Storage["config"]["protocols"].append(newProtocol);
}
}
}else if (yna(in_string) == 'a'){
// abort controller startup
return false;
}
}
}
return true;
}
///\brief The main loop for the controller.
int main_loop(int argc, char **argv){
{
@ -167,11 +276,6 @@ int main_loop(int argc, char **argv){
Controller::conf.addOption("port", stored_port);
Controller::conf.addOption("interface", stored_interface);
Controller::conf.addOption("username", stored_user);
Controller::conf.addOption(
"maxconnsperip",
JSON::fromString("{\"long\":\"maxconnsperip\", \"short\":\"M\", \"arg\":\"integer\" "
"\"default\":0, \"help\":\"Max simultaneous sessions per unique IP address. "
"Only enforced if the USER_NEW trigger is in use.\"}"));
Controller::conf.addOption(
"account", JSON::fromString("{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" "
"\"default\":\"\", \"help\":\"A username:password string to "
@ -234,8 +338,6 @@ int main_loop(int argc, char **argv){
<< "!----" APPNAME " Started at " << buffer << " ----!" << std::endl;
}
}
// reload config from config file
Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile"));
{// spawn thread that reads stderr of process
std::string logPipe = Util::getTmpFolder() + "MstLog";
@ -278,81 +380,8 @@ int main_loop(int argc, char **argv){
}
setenv("MIST_CONTROL", "1", 0); // Signal in the environment that the controller handles all children
}
if (Controller::Storage.isMember("config_split")){
jsonForEach(Controller::Storage["config_split"], cs){
if (cs->isString()){
JSON::Value tmpConf = JSON::fromFile(cs->asStringRef());
if (tmpConf.isMember(cs.key())){
INFO_MSG("Loading '%s' section of config from file %s", cs.key().c_str(), cs->asStringRef().c_str());
Controller::Storage[cs.key()] = tmpConf[cs.key()];
}else{
WARN_MSG("There is no '%s' section in file %s; skipping load", cs.key().c_str(), cs->asStringRef().c_str());
}
}
}
}
// Set default delay before retry
if (!Controller::Storage.isMember("push_settings")){
Controller::Storage["push_settings"]["wait"] = 3;
Controller::Storage["push_settings"]["maxspeed"] = 0;
}
if (Controller::conf.getOption("debug", true).size() > 1){
Controller::Storage["config"]["debug"] = Controller::conf.getInteger("debug");
}
if (Controller::Storage.isMember("config") && Controller::Storage["config"].isMember("debug") &&
Controller::Storage["config"]["debug"].isInt()){
Util::printDebugLevel = Controller::Storage["config"]["debug"].asInt();
}
// check for port, interface and username in arguments
// if they are not there, take them from config file, if there
if (Controller::Storage["config"]["controller"]["port"]){
Controller::conf.getOption("port", true)[0u] =
Controller::Storage["config"]["controller"]["port"];
}
if (Controller::Storage["config"]["controller"]["interface"]){
Controller::conf.getOption("interface", true)[0u] = Controller::Storage["config"]["controller"]["interface"];
}
if (Controller::Storage["config"]["controller"]["username"]){
Controller::conf.getOption("username", true)[0u] = Controller::Storage["config"]["controller"]["username"];
}
if (Controller::Storage["config"]["controller"].isMember("prometheus")){
if (Controller::Storage["config"]["controller"]["prometheus"]){
Controller::Storage["config"]["prometheus"] =
Controller::Storage["config"]["controller"]["prometheus"];
}
Controller::Storage["config"]["controller"].removeMember("prometheus");
}
if (Controller::Storage["config"]["prometheus"]){
Controller::conf.getOption("prometheus", true)[0u] =
Controller::Storage["config"]["prometheus"];
}
if (Controller::Storage["config"].isMember("accesslog")){
Controller::conf.getOption("accesslog", true)[0u] = Controller::Storage["config"]["accesslog"];
}
Controller::Storage["config"]["prometheus"] = Controller::conf.getString("prometheus");
Controller::Storage["config"]["accesslog"] = Controller::conf.getString("accesslog");
Controller::normalizeTrustedProxies(Controller::Storage["config"]["trustedproxy"]);
if (!Controller::Storage["config"]["sessionViewerMode"]){
Controller::Storage["config"]["sessionViewerMode"] = SESS_BUNDLE_DEFAULT_VIEWER;
}
if (!Controller::Storage["config"]["sessionInputMode"]){
Controller::Storage["config"]["sessionInputMode"] = SESS_BUNDLE_DEFAULT_OTHER;
}
if (!Controller::Storage["config"]["sessionOutputMode"]){
Controller::Storage["config"]["sessionOutputMode"] = SESS_BUNDLE_DEFAULT_OTHER;
}
if (!Controller::Storage["config"]["sessionUnspecifiedMode"]){
Controller::Storage["config"]["sessionUnspecifiedMode"] = 0;
}
if (!Controller::Storage["config"]["sessionStreamInfoMode"]){
Controller::Storage["config"]["sessionStreamInfoMode"] = SESS_DEFAULT_STREAM_INFO_MODE;
}
if (!Controller::Storage["config"].isMember("tknMode")){
Controller::Storage["config"]["tknMode"] = SESS_TKN_DEFAULT_MODE;
}
Controller::prometheus = Controller::Storage["config"]["prometheus"].asStringRef();
Controller::accesslog = Controller::Storage["config"]["accesslog"].asStringRef();
Controller::readConfigFromDisk();
Controller::writeConfig();
if (!Controller::conf.is_active){return 0;}
Controller::checkAvailProtocols();
@ -435,74 +464,9 @@ int main_loop(int argc, char **argv){
}
#endif
// if a terminal is connected and we're not logging to file
// if a terminal is connected, check for first time setup
if (Controller::isTerminal){
// check for username
if (!Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
std::string in_string = "";
while (yna(in_string) == 'x' && Controller::conf.is_active){
std::cout << "Account not set, do you want to create an account? (y)es, (n)o, (a)bort: ";
std::cout.flush();
std::getline(std::cin, in_string);
switch (yna(in_string)){
case 'y':{
// create account
std::string usr_string = "";
while (!(Controller::Storage.isMember("account") && Controller::Storage["account"].size() > 0) &&
Controller::conf.is_active){
std::cout << "Please type in the username, a colon and a password in the following "
"format; username:password"
<< std::endl
<< ": ";
std::cout.flush();
std::getline(std::cin, usr_string);
createAccount(usr_string);
}
}break;
case 'a': return 0; // abort bootup
case 't':{
createAccount("test:test");
if ((Controller::capabilities["connectors"].size()) &&
(!Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") ||
Controller::Storage["config"]["protocols"].size() < 1)){
// create protocols
jsonForEach(Controller::capabilities["connectors"], it){
if (!it->isMember("required")){
JSON::Value newProtocol;
newProtocol["connector"] = it.key();
Controller::Storage["config"]["protocols"].append(newProtocol);
}
}
}
}break;
}
}
}
// check for protocols
if ((Controller::capabilities["connectors"].size()) &&
(!Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") ||
Controller::Storage["config"]["protocols"].size() < 1)){
std::string in_string = "";
while (yna(in_string) == 'x' && Controller::conf.is_active){
std::cout << "Protocols not set, do you want to enable default protocols? (y)es, (n)o, "
"(a)bort: ";
std::cout.flush();
std::getline(std::cin, in_string);
if (yna(in_string) == 'y'){
// create protocols
jsonForEach(Controller::capabilities["connectors"], it){
if (!it->isMember("required")){
JSON::Value newProtocol;
newProtocol["connector"] = it.key();
Controller::Storage["config"]["protocols"].append(newProtocol);
}
}
}else if (yna(in_string) == 'a'){
// abort controller startup
return 0;
}
}
}
if (!interactiveFirstTimeSetup()){return 0;}
}
// Check if we have a usable server, if not, print messages with helpful hints
@ -530,39 +494,6 @@ int main_loop(int argc, char **argv){
}
}
// Upgrade old configurations
{
bool foundCMAF = false;
bool edit = false;
JSON::Value newVal;
jsonForEach(Controller::Storage["config"]["protocols"], it){
if ((*it)["connector"].asStringRef() == "HSS"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "DASH"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "SRT"){
edit = true;
JSON::Value newSubRip = *it;
newSubRip["connector"] = "SubRip";
newVal.append(newSubRip);
continue;
}
if ((*it)["connector"].asStringRef() == "CMAF"){foundCMAF = true;}
newVal.append(*it);
}
if (edit && !foundCMAF){newVal.append(JSON::fromString("{\"connector\":\"CMAF\"}"));}
if (edit){
Controller::Storage["config"]["protocols"] = newVal;
Controller::Log("CONF", "Translated protocols to new versions");
}
}
// Generate instanceId once per boot.
if (Controller::instanceId == ""){
srand(mix(clock(), time(0), getpid()));
@ -648,7 +579,7 @@ int main_loop(int argc, char **argv){
/*LTS-END*/
// write config
tthread::lock_guard<tthread::mutex> guard(Controller::logMutex);
Controller::writeConfigToDisk();
Controller::writeConfigToDisk(true);
// stop all child processes
Util::Procs::StopAll();
// give everything some time to print messages
@ -719,3 +650,4 @@ int main(int argc, char **argv){
}
return 0;
}

View file

@ -552,6 +552,11 @@ void Controller::handleAPICommands(JSON::Value &Request, JSON::Value &Response){
Response["config_backup"].assignFrom(Controller::Storage, skip);
}
if (Request.isMember("config_reload")){
INFO_MSG("Reloading configuration from disk on request");
Controller::readConfigFromDisk();
}
if (Request.isMember("config_restore")){
std::set<std::string> skip;
skip.insert("log");
@ -1222,11 +1227,10 @@ void Controller::handleAPICommands(JSON::Value &Request, JSON::Value &Response){
}
Controller::writeConfig();
Controller::configChanged = false;
if (Request.isMember("save")){
Controller::Log("CONF", "Writing config to file on request through API");
Controller::writeConfigToDisk();
Controller::writeConfigToDisk(true);
}
}

View file

@ -22,7 +22,8 @@ namespace Controller{
tthread::mutex configMutex;
tthread::mutex logMutex;
uint64_t logCounter = 0;
bool configChanged = false;
uint64_t lastConfigChange = 0;
uint64_t lastConfigWrite = 0;
bool isTerminal = false;
bool isColorized = false;
uint32_t maxLogsRecs = 0;
@ -36,6 +37,10 @@ namespace Controller{
Util::RelAccX *rlxStrm = 0;
uint64_t systemBoot = Util::unixMS() - Util::bootMS();
JSON::Value lastConfigWriteAttempt;
JSON::Value lastConfigSeen;
std::map<std::string, JSON::Value> lastConfigWritten;
Util::RelAccX *logAccessor(){return rlxLogs;}
Util::RelAccX *accesslogAccessor(){return rlxAccs;}
@ -238,37 +243,193 @@ namespace Controller{
Util::logParser((long long)err, fileno(stdout), Controller::isColorized, &Log);
}
/// Writes the current config to the location set in the configFile setting.
/// On error, prints an error-level message and the config to stdout.
void writeConfigToDisk(){
JSON::Value tmp;
void getConfigAsWritten(JSON::Value & conf){
std::set<std::string> skip;
skip.insert("log");
skip.insert("online");
skip.insert("error");
tmp.assignFrom(Controller::Storage, skip);
conf.assignFrom(Controller::Storage, skip);
}
/// Writes the current config to the location set in the configFile setting.
/// On error, prints an error-level message and the config to stdout.
void writeConfigToDisk(bool forceWrite){
bool success = true;
JSON::Value tmp;
getConfigAsWritten(tmp);
// We keep an extra copy temporarily, since we want to keep the "full" config around for comparisons
JSON::Value mainConfig = tmp;
if (Controller::Storage.isMember("config_split")){
jsonForEach(Controller::Storage["config_split"], cs){
if (cs->isString() && tmp.isMember(cs.key())){
// Only (attempt to) write if there was a change since last write success
if (!forceWrite && lastConfigWritten[cs.key()] == tmp[cs.key()]){
if (cs.key() != "config_split"){mainConfig.removeMember(cs.key());}
continue;
}
JSON::Value tmpConf = JSON::fromFile(cs->asStringRef());
tmpConf[cs.key()] = tmp[cs.key()];
// Attempt to write this section to the given file
if (!Controller::WriteFile(cs->asStringRef(), tmpConf.toString())){
ERROR_MSG("Error writing config.%s to %s", cs.key().c_str(), cs->asStringRef().c_str());
std::cout << "**config." << cs.key() <<"**" << std::endl;
std::cout << tmp[cs.key()].toString() << std::endl;
std::cout << "**End config." << cs.key() << "**" << std::endl;
success = false;
// Only print the error + config data if this is new data since the last write attempt
if (tmp[cs.key()] != lastConfigWriteAttempt[cs.key()]){
ERROR_MSG("Error writing config.%s to %s", cs.key().c_str(), cs->asStringRef().c_str());
std::cout << "**config." << cs.key() <<"**" << std::endl;
std::cout << tmp[cs.key()].toString() << std::endl;
std::cout << "**End config." << cs.key() << "**" << std::endl;
}
}else{
// Log the successfully written data
lastConfigWritten[cs.key()] = tmp[cs.key()];
}
if (cs.key() != "config_split"){tmp.removeMember(cs.key());}
// Remove this section from the to-be-written main config
if (cs.key() != "config_split"){mainConfig.removeMember(cs.key());}
}
}
}
if (!Controller::WriteFile(Controller::conf.getString("configFile"), tmp.toString())){
ERROR_MSG("Error writing config to %s", Controller::conf.getString("configFile").c_str());
std::cout << "**Config**" << std::endl;
std::cout << tmp.toString() << std::endl;
std::cout << "**End config**" << std::endl;
// Only (attempt to) write if there was a change since last write success
if (forceWrite || lastConfigWritten[""] != mainConfig){
// Attempt to write this section to the given file
if (!Controller::WriteFile(Controller::conf.getString("configFile"), tmp.toString())){
success = false;
// Only print the error + config data if this is new data since the last write attempt
if (tmp != lastConfigWriteAttempt){
ERROR_MSG("Error writing config to %s", Controller::conf.getString("configFile").c_str());
std::cout << "**Config**" << std::endl;
std::cout << mainConfig.toString() << std::endl;
std::cout << "**End config**" << std::endl;
}
}else{
lastConfigWritten[""] = mainConfig;
}
}
if (success){
INFO_MSG("Wrote updated configuration to disk");
lastConfigWrite = Util::epoch();
}
lastConfigWriteAttempt = tmp;
}
void readConfigFromDisk(){
// reload config from config file
Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile"));
if (Controller::Storage.isMember("config_split")){
jsonForEach(Controller::Storage["config_split"], cs){
if (cs->isString()){
JSON::Value tmpConf = JSON::fromFile(cs->asStringRef());
if (tmpConf.isMember(cs.key())){
INFO_MSG("Loading '%s' section of config from file %s", cs.key().c_str(), cs->asStringRef().c_str());
Controller::Storage[cs.key()] = tmpConf[cs.key()];
}else{
WARN_MSG("There is no '%s' section in file %s; skipping load", cs.key().c_str(), cs->asStringRef().c_str());
}
}
}
}
// Set default delay before retry
if (!Controller::Storage.isMember("push_settings")){
Controller::Storage["push_settings"]["wait"] = 3;
Controller::Storage["push_settings"]["maxspeed"] = 0;
}
if (Controller::conf.getOption("debug", true).size() > 1){
Controller::Storage["config"]["debug"] = Controller::conf.getInteger("debug");
}
if (Controller::Storage.isMember("config") && Controller::Storage["config"].isMember("debug") &&
Controller::Storage["config"]["debug"].isInt()){
Util::printDebugLevel = Controller::Storage["config"]["debug"].asInt();
}
// check for port, interface and username in arguments
// if they are not there, take them from config file, if there
if (Controller::Storage["config"]["controller"]["port"]){
Controller::conf.getOption("port", true)[0u] =
Controller::Storage["config"]["controller"]["port"];
}
if (Controller::Storage["config"]["controller"]["interface"]){
Controller::conf.getOption("interface", true)[0u] = Controller::Storage["config"]["controller"]["interface"];
}
if (Controller::Storage["config"]["controller"]["username"]){
Controller::conf.getOption("username", true)[0u] = Controller::Storage["config"]["controller"]["username"];
}
if (Controller::Storage["config"]["controller"].isMember("prometheus")){
if (Controller::Storage["config"]["controller"]["prometheus"]){
Controller::Storage["config"]["prometheus"] =
Controller::Storage["config"]["controller"]["prometheus"];
}
Controller::Storage["config"]["controller"].removeMember("prometheus");
}
if (Controller::Storage["config"]["prometheus"]){
Controller::conf.getOption("prometheus", true)[0u] =
Controller::Storage["config"]["prometheus"];
}
if (Controller::Storage["config"].isMember("accesslog")){
Controller::conf.getOption("accesslog", true)[0u] = Controller::Storage["config"]["accesslog"];
}
Controller::Storage["config"]["prometheus"] = Controller::conf.getString("prometheus");
Controller::Storage["config"]["accesslog"] = Controller::conf.getString("accesslog");
Controller::normalizeTrustedProxies(Controller::Storage["config"]["trustedproxy"]);
if (!Controller::Storage["config"]["sessionViewerMode"]){
Controller::Storage["config"]["sessionViewerMode"] = SESS_BUNDLE_DEFAULT_VIEWER;
}
if (!Controller::Storage["config"]["sessionInputMode"]){
Controller::Storage["config"]["sessionInputMode"] = SESS_BUNDLE_DEFAULT_OTHER;
}
if (!Controller::Storage["config"]["sessionOutputMode"]){
Controller::Storage["config"]["sessionOutputMode"] = SESS_BUNDLE_DEFAULT_OTHER;
}
if (!Controller::Storage["config"]["sessionUnspecifiedMode"]){
Controller::Storage["config"]["sessionUnspecifiedMode"] = 0;
}
if (!Controller::Storage["config"]["sessionStreamInfoMode"]){
Controller::Storage["config"]["sessionStreamInfoMode"] = SESS_DEFAULT_STREAM_INFO_MODE;
}
if (!Controller::Storage["config"].isMember("tknMode")){
Controller::Storage["config"]["tknMode"] = SESS_TKN_DEFAULT_MODE;
}
Controller::prometheus = Controller::Storage["config"]["prometheus"].asStringRef();
Controller::accesslog = Controller::Storage["config"]["accesslog"].asStringRef();
// Upgrade old configurations
{
bool foundCMAF = false;
bool edit = false;
JSON::Value newVal;
jsonForEach(Controller::Storage["config"]["protocols"], it){
if ((*it)["connector"].asStringRef() == "HSS"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "DASH"){
edit = true;
continue;
}
if ((*it)["connector"].asStringRef() == "SRT"){
edit = true;
JSON::Value newSubRip = *it;
newSubRip["connector"] = "SubRip";
newVal.append(newSubRip);
continue;
}
if ((*it)["connector"].asStringRef() == "CMAF"){foundCMAF = true;}
newVal.append(*it);
}
if (edit && !foundCMAF){newVal.append(JSON::fromString("{\"connector\":\"CMAF\"}"));}
if (edit){
Controller::Storage["config"]["protocols"] = newVal;
Controller::Log("CONF", "Translated protocols to new versions");
}
}
Controller::lastConfigChange = Controller::lastConfigWrite = Util::epoch();
Controller::lastConfigWriteAttempt.null();
getConfigAsWritten(Controller::lastConfigWriteAttempt);
lastConfigSeen = lastConfigWriteAttempt;
}
void writeCapabilities(){

View file

@ -5,7 +5,7 @@
#include <string>
namespace Controller{
extern std::string instanceId; ///< global storage of instanceId (previously uniqID) is set in controller.cpp
extern std::string instanceId; ///< global storage of instanceId (previously uniqID) is set in controller.cpp
extern std::string prometheus; ///< Prometheus access string
extern std::string accesslog; ///< Where to write the access log
extern std::string udpApiBindAddr; ///< Bound address where the UDP API listens
@ -13,11 +13,14 @@ namespace Controller{
extern JSON::Value Storage; ///< Global storage of data.
extern tthread::mutex logMutex; ///< Mutex for log thread.
extern tthread::mutex configMutex; ///< Mutex for server config access.
extern bool configChanged; ///< Bool that indicates config must be written to SHM.
extern bool isTerminal; ///< True if connected to a terminal and not a log file.
extern bool isColorized; ///< True if we colorize the output
extern uint64_t logCounter; ///< Count of logged messages since boot
extern uint64_t systemBoot; ///< Unix time in milliseconds of system boot
extern uint64_t lastConfigChange; ///< Unix time in seconds of last configuration change
extern uint64_t lastConfigWrite; ///< Unix time in seconds of last time configuration was written to disk
extern JSON::Value lastConfigWriteAttempt; ///< Contents of last attempted config write
extern JSON::Value lastConfigSeen; ///< Contents of config last time we looked at it. Used to check for changes.
Util::RelAccX *logAccessor();
Util::RelAccX *accesslogAccessor();
@ -34,7 +37,10 @@ namespace Controller{
/// Write contents to Filename.
bool WriteFile(std::string Filename, std::string contents);
void writeConfigToDisk();
void getConfigAsWritten(JSON::Value & conf);
void writeConfigToDisk(bool forceWrite = false);
void readConfigFromDisk();
void handleMsg(void *err);
void initState();