Prettified controller + fixed Ctrl+C during startup questions + made 't' answer add protocols + No longer ask to add protocols when none are installed.
This commit is contained in:
parent
499444ba52
commit
b0404075f5
1 changed files with 319 additions and 252 deletions
|
@ -1,61 +1,33 @@
|
||||||
/// \page api API calls
|
|
||||||
/// \brief Listing of all controller API calls.
|
|
||||||
/// The controller listens for commands through a JSON-based API. This page describes the API in full.
|
|
||||||
///
|
|
||||||
/// A default interface implementing this API as a single HTML page is included in the controller itself. This default interface will be send for invalid API requests, and is thus triggered by default when a browser attempts to access the API port directly.
|
|
||||||
/// The default API port is 4242 - but this can be changed through both the API and commandline parameters.
|
|
||||||
///
|
|
||||||
/// To send an API request, simply send a HTTP request to this port for any file, and include either a GET or POST parameter called `"command"`, containing a JSON object as payload. Nearly all members of the request object are optional, and described below.
|
|
||||||
/// A simple example request logging in to the system would look like this:
|
|
||||||
///
|
|
||||||
/// GET /api?command={"authorize":{"username":"test","password":"941d7b88b2312d4373aff526cf7b6114"}} HTTP/1.0
|
|
||||||
///
|
|
||||||
/// Or, when properly URL encoded:
|
|
||||||
///
|
|
||||||
/// GET /api?command=%7B%22authorize%22%3A%7B%22username%22%3A%22test%22%2C%22password%22%3A%22941d7b88b2312d4373aff526cf7b6114%22%7D%7D HTTP/1.0
|
|
||||||
///
|
|
||||||
/// The server is quite lenient about not URL encoding your strings, but it's a good idea to always do it, anyway.
|
|
||||||
/// See the `"authorize"` section below for more information about security and logging in.
|
|
||||||
///
|
|
||||||
/// As mentioned above, sending an invalid request will trigger a response containing the default interface. As you may not want to receive a big HTML page as response to an invalid request, requesting the file `"/api"` (as done in the example above) will force a JSON response, even when the request is invalid.
|
|
||||||
///
|
|
||||||
/// You may also include a `"callback"` or `"jsonp"` HTTP variable, to trigger JSONP compatibility mode. JSONP is useful for getting around the cross-domain scripting protection in most modern browsers. Developers creating non-JavaScript applications will most likely not want to use JSONP mode, though nothing is stopping you if you really want to.
|
|
||||||
///
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// \file controller.cpp
|
/// \file controller.cpp
|
||||||
/// Contains all code for the controller executable.
|
/// Contains all code for the controller executable.
|
||||||
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <ctime>
|
|
||||||
#include <vector>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <mist/config.h>
|
|
||||||
#include <mist/socket.h>
|
|
||||||
#include <mist/http_parser.h>
|
|
||||||
#include <mist/procs.h>
|
|
||||||
#include <mist/auth.h>
|
|
||||||
#include <mist/timing.h>
|
|
||||||
#include <mist/stream.h>
|
|
||||||
#include <mist/defines.h>
|
|
||||||
#include <mist/tinythread.h>
|
|
||||||
#include <mist/shared_memory.h>
|
|
||||||
#include "controller_storage.h"
|
|
||||||
#include "controller_streams.h"
|
|
||||||
#include "controller_capabilities.h"
|
#include "controller_capabilities.h"
|
||||||
#include "controller_connectors.h"
|
#include "controller_connectors.h"
|
||||||
#include "controller_statistics.h"
|
#include "controller_statistics.h"
|
||||||
|
#include "controller_storage.h"
|
||||||
|
#include "controller_streams.h"
|
||||||
|
#include <ctime>
|
||||||
|
#include <iostream>
|
||||||
|
#include <mist/auth.h>
|
||||||
|
#include <mist/config.h>
|
||||||
|
#include <mist/defines.h>
|
||||||
|
#include <mist/http_parser.h>
|
||||||
|
#include <mist/procs.h>
|
||||||
|
#include <mist/shared_memory.h>
|
||||||
|
#include <mist/socket.h>
|
||||||
|
#include <mist/stream.h>
|
||||||
|
#include <mist/timing.h>
|
||||||
|
#include <mist/tinythread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <vector>
|
||||||
/*LTS-START*/
|
/*LTS-START*/
|
||||||
#include <mist/triggers.h>
|
|
||||||
#include "controller_updater.h"
|
|
||||||
#include "controller_limits.h"
|
|
||||||
#include "controller_uplink.h"
|
|
||||||
#include "controller_license.h"
|
#include "controller_license.h"
|
||||||
|
#include "controller_limits.h"
|
||||||
|
#include "controller_updater.h"
|
||||||
|
#include "controller_uplink.h"
|
||||||
|
#include <mist/triggers.h>
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
#include "controller_api.h"
|
#include "controller_api.h"
|
||||||
#include "controller_push.h"
|
#include "controller_push.h"
|
||||||
|
@ -65,30 +37,25 @@
|
||||||
#define COMPILED_PASSWORD ""
|
#define COMPILED_PASSWORD ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// the following function is a simple check if the user wants to proceed to fix (y), ignore (n) or abort on (a) a question
|
/// the following function is a simple check if the user wants to proceed to fix (y), ignore (n) or
|
||||||
static inline char yna(std::string & user_input){
|
/// abort on (a) a question
|
||||||
|
static inline char yna(std::string &user_input){
|
||||||
switch (user_input[0]){
|
switch (user_input[0]){
|
||||||
case 'y': case 'Y':
|
case 'y':
|
||||||
return 'y';
|
case 'Y': return 'y'; break;
|
||||||
break;
|
case 'n':
|
||||||
case 'n': case 'N':
|
case 'N': return 'n'; break;
|
||||||
return 'n';
|
case 'a':
|
||||||
break;
|
case 'A': return 'a'; break;
|
||||||
case 'a': case 'A':
|
case 't':
|
||||||
return 'a';
|
case 'T': return 't'; break;
|
||||||
break;
|
default: return 'x'; break;
|
||||||
case 't': case 'T':
|
|
||||||
return 't';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 'x';
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// createAccount accepts a string in the form of username:account
|
/// createAccount accepts a string in the form of username:account
|
||||||
/// and creates an account.
|
/// and creates an account.
|
||||||
void createAccount (std::string account){
|
void createAccount(std::string account){
|
||||||
if (account.size() > 0){
|
if (account.size() > 0){
|
||||||
size_t colon = account.find(':');
|
size_t colon = account.find(':');
|
||||||
if (colon != std::string::npos && colon != 0 && colon != account.size()){
|
if (colon != std::string::npos && colon != 0 && colon != account.size()){
|
||||||
|
@ -100,25 +67,26 @@ void createAccount (std::string account){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Status monitoring thread.
|
/// Status monitoring thread.
|
||||||
/// Will check outputs, inputs and converters every five seconds
|
/// Will check outputs, inputs and converters every five seconds
|
||||||
void statusMonitor(void * np){
|
void statusMonitor(void *np){
|
||||||
IPC::semaphore configLock(SEM_CONF, O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
IPC::semaphore configLock(SEM_CONF, O_CREAT | O_RDWR, ACCESSPERMS, 1);
|
||||||
Controller::loadActiveConnectors();
|
Controller::loadActiveConnectors();
|
||||||
while (Controller::conf.is_active){
|
while (Controller::conf.is_active){
|
||||||
//this scope prevents the configMutex from being locked constantly
|
// this scope prevents the configMutex from being locked constantly
|
||||||
{
|
{
|
||||||
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
|
tthread::lock_guard<tthread::mutex> guard(Controller::configMutex);
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
//checks online protocols, reports changes to status
|
// checks online protocols, reports changes to status
|
||||||
changed |= Controller::CheckProtocols(Controller::Storage["config"]["protocols"], Controller::capabilities);
|
changed |= Controller::CheckProtocols(Controller::Storage["config"]["protocols"],
|
||||||
//checks stream statuses, reports changes to status
|
Controller::capabilities);
|
||||||
|
// checks stream statuses, reports changes to status
|
||||||
changed |= Controller::CheckAllStreams(Controller::Storage["streams"]);
|
changed |= Controller::CheckAllStreams(Controller::Storage["streams"]);
|
||||||
|
|
||||||
//check if the config semaphore is stuck, by trying to lock it for 5 attempts of 1 second...
|
// check if the config semaphore is stuck, by trying to lock it for 5 attempts of 1 second...
|
||||||
if (!configLock.tryWaitOneSecond() && !configLock.tryWaitOneSecond() && !configLock.tryWaitOneSecond() && !configLock.tryWaitOneSecond()){
|
if (!configLock.tryWaitOneSecond() && !configLock.tryWaitOneSecond() &&
|
||||||
//that failed. We now unlock it, no matter what - and print a warning that it was stuck.
|
!configLock.tryWaitOneSecond() && !configLock.tryWaitOneSecond()){
|
||||||
|
// that failed. We now unlock it, no matter what - and print a warning that it was stuck.
|
||||||
WARN_MSG("Configuration semaphore was stuck. Force-unlocking it and re-writing config.");
|
WARN_MSG("Configuration semaphore was stuck. Force-unlocking it and re-writing config.");
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
@ -128,7 +96,7 @@ void statusMonitor(void * np){
|
||||||
Controller::configChanged = false;
|
Controller::configChanged = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Util::sleep(5000);//wait at least 5 seconds
|
Util::sleep(5000); // wait at least 5 seconds
|
||||||
}
|
}
|
||||||
if (Controller::restarting){
|
if (Controller::restarting){
|
||||||
Controller::prepareActiveConnectorsForReload();
|
Controller::prepareActiveConnectorsForReload();
|
||||||
|
@ -138,106 +106,182 @@ void statusMonitor(void * np){
|
||||||
configLock.unlink();
|
configLock.unlink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned long mix(unsigned long a, unsigned long b, unsigned long c){
|
||||||
|
a = a - b;
|
||||||
|
a = a - c;
|
||||||
|
a = a ^ (c >> 13);
|
||||||
|
b = b - c;
|
||||||
|
b = b - a;
|
||||||
|
b = b ^ (a << 8);
|
||||||
|
c = c - a;
|
||||||
|
c = c - b;
|
||||||
|
c = c ^ (b >> 13);
|
||||||
|
a = a - b;
|
||||||
|
a = a - c;
|
||||||
|
a = a ^ (c >> 12);
|
||||||
|
b = b - c;
|
||||||
|
b = b - a;
|
||||||
|
b = b ^ (a << 16);
|
||||||
|
c = c - a;
|
||||||
|
c = c - b;
|
||||||
|
c = c ^ (b >> 5);
|
||||||
|
a = a - b;
|
||||||
|
a = a - c;
|
||||||
|
a = a ^ (c >> 3);
|
||||||
|
b = b - c;
|
||||||
|
b = b - a;
|
||||||
|
b = b ^ (a << 10);
|
||||||
|
c = c - a;
|
||||||
|
c = c - b;
|
||||||
|
c = c ^ (b >> 15);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
///\brief The main loop for the controller.
|
///\brief The main loop for the controller.
|
||||||
///
|
///
|
||||||
/// \triggers
|
/// \triggers
|
||||||
/// The `"SYSTEM_STOP"` trigger is global, and is ran when the controller shuts down. If cancelled, the controller does not shut down and will attempt to re-open the API socket. Its payload is:
|
/// The `"SYSTEM_STOP"` trigger is global, and is ran when the controller shuts down. If cancelled,
|
||||||
|
/// the controller does not shut down and will attempt to re-open the API socket. Its payload is:
|
||||||
/// ~~~~~~~~~~~~~~~
|
/// ~~~~~~~~~~~~~~~
|
||||||
/// shutdown reason
|
/// shutdown reason
|
||||||
/// ~~~~~~~~~~~~~~~
|
/// ~~~~~~~~~~~~~~~
|
||||||
int main_loop(int argc, char ** argv){
|
int main_loop(int argc, char **argv){
|
||||||
Controller::isTerminal = Controller::isColorized = isatty(fileno(stdin));
|
Controller::isTerminal = Controller::isColorized = isatty(fileno(stdin));
|
||||||
Controller::Storage = JSON::fromFile("config.json");
|
Controller::Storage = JSON::fromFile("config.json");
|
||||||
JSON::Value stored_port = JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP port to listen on.\"}");
|
JSON::Value stored_port =
|
||||||
|
JSON::fromString("{\"long\":\"port\", \"short\":\"p\", \"arg\":\"integer\", \"help\":\"TCP "
|
||||||
|
"port to listen on.\"}");
|
||||||
stored_port["default"] = Controller::Storage["config"]["controller"]["port"];
|
stored_port["default"] = Controller::Storage["config"]["controller"]["port"];
|
||||||
if ( !stored_port["default"]){
|
if (!stored_port["default"]){stored_port["default"] = 4242;}
|
||||||
stored_port["default"] = 4242;
|
JSON::Value stored_interface = JSON::fromString(
|
||||||
}
|
"{\"long\":\"interface\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address "
|
||||||
JSON::Value stored_interface = JSON::fromString("{\"long\":\"interface\", \"short\":\"i\", \"arg\":\"string\", \"help\":\"Interface address to listen on, or 0.0.0.0 for all available interfaces.\"}");
|
"to listen on, or 0.0.0.0 for all available interfaces.\"}");
|
||||||
stored_interface["default"] = Controller::Storage["config"]["controller"]["interface"];
|
stored_interface["default"] = Controller::Storage["config"]["controller"]["interface"];
|
||||||
if ( !stored_interface["default"]){
|
if (!stored_interface["default"]){stored_interface["default"] = "0.0.0.0";}
|
||||||
stored_interface["default"] = "0.0.0.0";
|
JSON::Value stored_user =
|
||||||
}
|
JSON::fromString("{\"long\":\"username\", \"short\":\"u\", \"arg\":\"string\", "
|
||||||
JSON::Value stored_user = JSON::fromString("{\"long\":\"username\", \"short\":\"u\", \"arg\":\"string\", \"help\":\"Username to transfer privileges to, default is root.\"}");
|
"\"help\":\"Username to transfer privileges to, default is root.\"}");
|
||||||
stored_user["default"] = Controller::Storage["config"]["controller"]["username"];
|
stored_user["default"] = Controller::Storage["config"]["controller"]["username"];
|
||||||
if ( !stored_user["default"]){
|
if (!stored_user["default"]){stored_user["default"] = "root";}
|
||||||
stored_user["default"] = "root";
|
|
||||||
}
|
|
||||||
Controller::conf.addOption("port", stored_port);
|
Controller::conf.addOption("port", stored_port);
|
||||||
Controller::conf.addOption("interface", stored_interface);
|
Controller::conf.addOption("interface", stored_interface);
|
||||||
Controller::conf.addOption("username", stored_user);
|
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(
|
||||||
Controller::conf.addOption("account", JSON::fromString("{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" \"default\":\"\", \"help\":\"A username:password string to create a new account with.\"}"));
|
"maxconnsperip",
|
||||||
Controller::conf.addOption("logfile", JSON::fromString("{\"long\":\"logfile\", \"short\":\"L\", \"arg\":\"string\" \"default\":\"\",\"help\":\"Redirect all standard output to a log file, provided with an argument\"}"));
|
JSON::fromString("{\"long\":\"maxconnsperip\", \"short\":\"M\", \"arg\":\"integer\" "
|
||||||
Controller::conf.addOption("accesslog", JSON::fromString("{\"long\":\"accesslog\", \"short\":\"A\", \"arg\":\"string\" \"default\":\"LOG\",\"help\":\"Where to write the access log. If set to 'LOG' (the default), writes to wherever the log is written to. If empty, access logging is turned off. Otherwise, writes to the given filename.\"}"));
|
"\"default\":0, \"help\":\"Max simultaneous sessions per unique IP address. "
|
||||||
Controller::conf.addOption("configFile", JSON::fromString("{\"long\":\"config\", \"short\":\"c\", \"arg\":\"string\" \"default\":\"config.json\", \"help\":\"Specify a config file other than default.\"}"));
|
"Only enforced if the USER_NEW trigger is in use.\"}"));
|
||||||
#ifdef UPDATER
|
Controller::conf.addOption(
|
||||||
Controller::conf.addOption("update", JSON::fromString("{\"default\":0, \"help\":\"Check for and install updates before starting.\", \"short\":\"D\", \"long\":\"update\"}")); /*LTS*/
|
"account", JSON::fromString("{\"long\":\"account\", \"short\":\"a\", \"arg\":\"string\" "
|
||||||
#endif
|
"\"default\":\"\", \"help\":\"A username:password string to "
|
||||||
Controller::conf.addOption("uplink", JSON::fromString("{\"default\":\"\", \"arg\":\"string\", \"help\":\"MistSteward uplink host and port.\", \"short\":\"U\", \"long\":\"uplink\"}")); /*LTS*/
|
"create a new account with.\"}"));
|
||||||
Controller::conf.addOption("uplink-name", JSON::fromString("{\"default\":\"" COMPILED_USERNAME "\", \"arg\":\"string\", \"help\":\"MistSteward uplink username.\", \"short\":\"N\", \"long\":\"uplink-name\"}")); /*LTS*/
|
Controller::conf.addOption(
|
||||||
Controller::conf.addOption("uplink-pass", JSON::fromString("{\"default\":\"" COMPILED_PASSWORD "\", \"arg\":\"string\", \"help\":\"MistSteward uplink password.\", \"short\":\"P\", \"long\":\"uplink-pass\"}")); /*LTS*/
|
"logfile", JSON::fromString("{\"long\":\"logfile\", \"short\":\"L\", \"arg\":\"string\" "
|
||||||
Controller::conf.addOption("prometheus", JSON::fromString("{\"long\":\"prometheus\", \"short\":\"S\", \"arg\":\"string\" \"default\":\"\", \"help\":\"If set, allows collecting of Prometheus-style stats on the given path over the API port.\"}"));
|
"\"default\":\"\",\"help\":\"Redirect all standard output to a "
|
||||||
|
"log file, provided with an argument\"}"));
|
||||||
|
Controller::conf.addOption(
|
||||||
|
"accesslog", JSON::fromString("{\"long\":\"accesslog\", \"short\":\"A\", \"arg\":\"string\" "
|
||||||
|
"\"default\":\"LOG\",\"help\":\"Where to write the access log. "
|
||||||
|
"If set to 'LOG' (the default), writes to wherever the log is "
|
||||||
|
"written to. If empty, access logging is turned off. "
|
||||||
|
"Otherwise, writes to the given filename.\"}"));
|
||||||
|
Controller::conf.addOption(
|
||||||
|
"configFile", JSON::fromString("{\"long\":\"config\", \"short\":\"c\", \"arg\":\"string\" "
|
||||||
|
"\"default\":\"config.json\", \"help\":\"Specify a config "
|
||||||
|
"file other than default.\"}"));
|
||||||
|
#ifdef UPDATER
|
||||||
|
Controller::conf.addOption(
|
||||||
|
"update", JSON::fromString("{\"default\":0, \"help\":\"Check for and install updates before "
|
||||||
|
"starting.\", \"short\":\"D\", \"long\":\"update\"}")); /*LTS*/
|
||||||
|
#endif
|
||||||
|
Controller::conf.addOption(
|
||||||
|
"uplink",
|
||||||
|
JSON::fromString("{\"default\":\"\", \"arg\":\"string\", \"help\":\"MistSteward uplink host "
|
||||||
|
"and port.\", \"short\":\"U\", \"long\":\"uplink\"}")); /*LTS*/
|
||||||
|
Controller::conf.addOption("uplink-name",
|
||||||
|
JSON::fromString("{\"default\":\"" COMPILED_USERNAME
|
||||||
|
"\", \"arg\":\"string\", \"help\":\"MistSteward "
|
||||||
|
"uplink username.\", \"short\":\"N\", "
|
||||||
|
"\"long\":\"uplink-name\"}")); /*LTS*/
|
||||||
|
Controller::conf.addOption("uplink-pass",
|
||||||
|
JSON::fromString("{\"default\":\"" COMPILED_PASSWORD
|
||||||
|
"\", \"arg\":\"string\", \"help\":\"MistSteward "
|
||||||
|
"uplink password.\", \"short\":\"P\", "
|
||||||
|
"\"long\":\"uplink-pass\"}")); /*LTS*/
|
||||||
|
Controller::conf.addOption(
|
||||||
|
"prometheus",
|
||||||
|
JSON::fromString("{\"long\":\"prometheus\", \"short\":\"S\", \"arg\":\"string\" "
|
||||||
|
"\"default\":\"\", \"help\":\"If set, allows collecting of Prometheus-style "
|
||||||
|
"stats on the given path over the API port.\"}"));
|
||||||
Controller::conf.parseArgs(argc, argv);
|
Controller::conf.parseArgs(argc, argv);
|
||||||
if(Controller::conf.getString("logfile")!= ""){
|
if (Controller::conf.getString("logfile") != ""){
|
||||||
//open logfile, dup stdout to logfile
|
// open logfile, dup stdout to logfile
|
||||||
int output = open(Controller::conf.getString("logfile").c_str(),O_APPEND|O_CREAT|O_WRONLY,S_IRWXU);
|
int output =
|
||||||
if(output < 0){
|
open(Controller::conf.getString("logfile").c_str(), O_APPEND | O_CREAT | O_WRONLY, S_IRWXU);
|
||||||
DEBUG_MSG(DLVL_ERROR, "Could not redirect output to %s: %s",Controller::conf.getString("logfile").c_str(),strerror(errno));
|
if (output < 0){
|
||||||
|
DEBUG_MSG(DLVL_ERROR, "Could not redirect output to %s: %s",
|
||||||
|
Controller::conf.getString("logfile").c_str(), strerror(errno));
|
||||||
return 7;
|
return 7;
|
||||||
}else{
|
}else{
|
||||||
Controller::isTerminal = Controller::isColorized = false;
|
Controller::isTerminal = Controller::isColorized = false;
|
||||||
dup2(output,STDOUT_FILENO);
|
dup2(output, STDOUT_FILENO);
|
||||||
dup2(output,STDERR_FILENO);
|
dup2(output, STDERR_FILENO);
|
||||||
time_t rawtime;
|
time_t rawtime;
|
||||||
struct tm * timeinfo;
|
struct tm *timeinfo;
|
||||||
char buffer [25];
|
char buffer[25];
|
||||||
time (&rawtime);
|
time(&rawtime);
|
||||||
timeinfo = localtime (&rawtime);
|
timeinfo = localtime(&rawtime);
|
||||||
strftime (buffer,25,"%c",timeinfo);
|
strftime(buffer, 25, "%c", timeinfo);
|
||||||
std::cerr << std::endl << std::endl <<"!----MistServer Started at " << buffer << " ----!" << std::endl;
|
std::cerr << std::endl
|
||||||
|
<< std::endl
|
||||||
|
<< "!----MistServer Started at " << buffer << " ----!" << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//reload config from config file
|
// reload config from config file
|
||||||
Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile"));
|
Controller::Storage = JSON::fromFile(Controller::conf.getString("configFile"));
|
||||||
|
|
||||||
{//spawn thread that reads stderr of process
|
{// spawn thread that reads stderr of process
|
||||||
int pipeErr[2];
|
int pipeErr[2];
|
||||||
if (pipe(pipeErr) >= 0){
|
if (pipe(pipeErr) >= 0){
|
||||||
dup2(pipeErr[1], STDERR_FILENO);//cause stderr to write to the pipe
|
dup2(pipeErr[1], STDERR_FILENO); // cause stderr to write to the pipe
|
||||||
close(pipeErr[1]);//close the unneeded pipe file descriptor
|
close(pipeErr[1]); // close the unneeded pipe file descriptor
|
||||||
Util::Procs::socketList.insert(pipeErr[0]);
|
Util::Procs::socketList.insert(pipeErr[0]);
|
||||||
tthread::thread msghandler(Controller::handleMsg, (void*)(((char*)0) + pipeErr[0]));
|
tthread::thread msghandler(Controller::handleMsg, (void *)(((char *)0) + pipeErr[0]));
|
||||||
msghandler.detach();
|
msghandler.detach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Controller::conf.getOption("debug", true).size() > 1){
|
||||||
if (Controller::conf.getOption("debug",true).size() > 1){
|
|
||||||
Controller::Storage["config"]["debug"] = Controller::conf.getInteger("debug");
|
Controller::Storage["config"]["debug"] = Controller::conf.getInteger("debug");
|
||||||
}
|
}
|
||||||
if (Controller::Storage.isMember("config") && Controller::Storage["config"].isMember("debug") && Controller::Storage["config"]["debug"].isInt()){
|
if (Controller::Storage.isMember("config") && Controller::Storage["config"].isMember("debug") &&
|
||||||
|
Controller::Storage["config"]["debug"].isInt()){
|
||||||
Util::Config::printDebugLevel = Controller::Storage["config"]["debug"].asInt();
|
Util::Config::printDebugLevel = Controller::Storage["config"]["debug"].asInt();
|
||||||
}
|
}
|
||||||
//check for port, interface and username in arguments
|
// check for port, interface and username in arguments
|
||||||
//if they are not there, take them from config file, if there
|
// if they are not there, take them from config file, if there
|
||||||
if (Controller::Storage["config"]["controller"]["port"]){
|
if (Controller::Storage["config"]["controller"]["port"]){
|
||||||
Controller::conf.getOption("port", true)[0u] = Controller::Storage["config"]["controller"]["port"];
|
Controller::conf.getOption("port", true)[0u] =
|
||||||
|
Controller::Storage["config"]["controller"]["port"];
|
||||||
}
|
}
|
||||||
if (Controller::Storage["config"]["controller"]["interface"]){
|
if (Controller::Storage["config"]["controller"]["interface"]){
|
||||||
Controller::conf.getOption("interface", true)[0u] = Controller::Storage["config"]["controller"]["interface"];
|
Controller::conf.getOption("interface", true)[0u] =
|
||||||
|
Controller::Storage["config"]["controller"]["interface"];
|
||||||
}
|
}
|
||||||
if (Controller::Storage["config"]["controller"]["username"]){
|
if (Controller::Storage["config"]["controller"]["username"]){
|
||||||
Controller::conf.getOption("username", true)[0u] = 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"].isMember("prometheus")){
|
||||||
if (Controller::Storage["config"]["controller"]["prometheus"]){
|
if (Controller::Storage["config"]["controller"]["prometheus"]){
|
||||||
Controller::Storage["config"]["prometheus"] = Controller::Storage["config"]["controller"]["prometheus"];
|
Controller::Storage["config"]["prometheus"] =
|
||||||
|
Controller::Storage["config"]["controller"]["prometheus"];
|
||||||
}
|
}
|
||||||
Controller::Storage["config"]["controller"].removeMember("prometheus");
|
Controller::Storage["config"]["controller"].removeMember("prometheus");
|
||||||
}
|
}
|
||||||
if (Controller::Storage["config"]["prometheus"]){
|
if (Controller::Storage["config"]["prometheus"]){
|
||||||
Controller::conf.getOption("prometheus", true)[0u] = Controller::Storage["config"]["prometheus"];
|
Controller::conf.getOption("prometheus", true)[0u] =
|
||||||
|
Controller::Storage["config"]["prometheus"];
|
||||||
}
|
}
|
||||||
if (Controller::Storage["config"].isMember("accesslog")){
|
if (Controller::Storage["config"].isMember("accesslog")){
|
||||||
Controller::conf.getOption("accesslog", true)[0u] = Controller::Storage["config"]["accesslog"];
|
Controller::conf.getOption("accesslog", true)[0u] = Controller::Storage["config"]["accesslog"];
|
||||||
|
@ -255,152 +299,178 @@ int main_loop(int argc, char ** argv){
|
||||||
Controller::checkAvailProtocols();
|
Controller::checkAvailProtocols();
|
||||||
Controller::updateBandwidthConfig();
|
Controller::updateBandwidthConfig();
|
||||||
createAccount(Controller::conf.getString("account"));
|
createAccount(Controller::conf.getString("account"));
|
||||||
|
Controller::conf.activate(); // activate early, so threads aren't killed.
|
||||||
//if a terminal is connected and we're not logging to file
|
|
||||||
|
// if a terminal is connected and we're not logging to file
|
||||||
if (Controller::isTerminal){
|
if (Controller::isTerminal){
|
||||||
//check for username
|
// check for username
|
||||||
if ( !Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
|
if (!Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
|
||||||
std::string in_string = "";
|
std::string in_string = "";
|
||||||
while(yna(in_string) == 'x'){
|
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 << "Account not set, do you want to create an account? (y)es, (n)o, (a)bort: ";
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
std::getline(std::cin, in_string);
|
std::getline(std::cin, in_string);
|
||||||
switch (yna(in_string)){
|
switch (yna(in_string)){
|
||||||
case 'y':{
|
case 'y':{
|
||||||
//create account
|
// create account
|
||||||
std::string usr_string = "";
|
std::string usr_string = "";
|
||||||
while(!(Controller::Storage.isMember("account") && Controller::Storage["account"].size() > 0)){
|
while (!(Controller::Storage.isMember("account") &&
|
||||||
std::cout << "Please type in the username, a colon and a password in the following format; username:password" << std::endl << ": ";
|
Controller::Storage["account"].size() > 0) &&
|
||||||
std::cout.flush();
|
Controller::conf.is_active){
|
||||||
std::getline(std::cin, usr_string);
|
std::cout << "Please type in the username, a colon and a password in the following "
|
||||||
createAccount(usr_string);
|
"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;
|
}
|
||||||
case 'a': return 0; //abort bootup
|
}break;
|
||||||
case 't':
|
|
||||||
createAccount("test:test");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//check for protocols
|
// check for protocols
|
||||||
if ( !Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") || Controller::Storage["config"]["protocols"].size() < 1){
|
if ((Controller::capabilities["connectors"].size()) &&
|
||||||
|
(!Controller::Storage.isMember("config") ||
|
||||||
|
!Controller::Storage["config"].isMember("protocols") ||
|
||||||
|
Controller::Storage["config"]["protocols"].size() < 1)){
|
||||||
std::string in_string = "";
|
std::string in_string = "";
|
||||||
while(yna(in_string) == 'x'){
|
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
|
||||||
|
<< "Protocols not set, do you want to enable default protocols? (y)es, (n)o, (a)bort: ";
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
std::getline(std::cin, in_string);
|
std::getline(std::cin, in_string);
|
||||||
if (yna(in_string) == 'y'){
|
if (yna(in_string) == 'y'){
|
||||||
//create protocols
|
// create protocols
|
||||||
jsonForEach(Controller::capabilities["connectors"], it) {
|
jsonForEach(Controller::capabilities["connectors"], it){
|
||||||
if (!it->isMember("required")){
|
if (!it->isMember("required")){
|
||||||
JSON::Value newProtocol;
|
JSON::Value newProtocol;
|
||||||
newProtocol["connector"] = it.key();
|
newProtocol["connector"] = it.key();
|
||||||
Controller::Storage["config"]["protocols"].append(newProtocol);
|
Controller::Storage["config"]["protocols"].append(newProtocol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if(yna(in_string) == 'a'){
|
}else if (yna(in_string) == 'a'){
|
||||||
//abort controller startup
|
// abort controller startup
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if we have a usable server, if not, print messages with helpful hints
|
// Check if we have a usable server, if not, print messages with helpful hints
|
||||||
{
|
{
|
||||||
std::string web_port = JSON::Value((long long)Controller::conf.getInteger("port")).asString();
|
std::string web_port = JSON::Value((long long)Controller::conf.getInteger("port")).asString();
|
||||||
//check for username
|
// check for username
|
||||||
if ( !Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
|
if (!Controller::Storage.isMember("account") || Controller::Storage["account"].size() < 1){
|
||||||
Controller::Log("CONF", "No login configured. To create one, attempt to login through the web interface on port "+web_port+" and follow the instructions.");
|
Controller::Log("CONF",
|
||||||
|
"No login configured. To create one, attempt to login through the web "
|
||||||
|
"interface on port " +
|
||||||
|
web_port + " and follow the instructions.");
|
||||||
}
|
}
|
||||||
//check for protocols
|
// check for protocols
|
||||||
if ( !Controller::Storage.isMember("config") || !Controller::Storage["config"].isMember("protocols") || Controller::Storage["config"]["protocols"].size() < 1){
|
if (!Controller::Storage.isMember("config") ||
|
||||||
Controller::Log("CONF", "No protocols enabled, remember to set them up through the web interface on port "+web_port+" or API.");
|
!Controller::Storage["config"].isMember("protocols") ||
|
||||||
|
Controller::Storage["config"]["protocols"].size() < 1){
|
||||||
|
Controller::Log(
|
||||||
|
"CONF",
|
||||||
|
"No protocols enabled, remember to set them up through the web interface on port " +
|
||||||
|
web_port + " or API.");
|
||||||
}
|
}
|
||||||
//check for streams - regardless of logfile setting
|
// check for streams - regardless of logfile setting
|
||||||
if ( !Controller::Storage.isMember("streams") || Controller::Storage["streams"].size() < 1){
|
if (!Controller::Storage.isMember("streams") || Controller::Storage["streams"].size() < 1){
|
||||||
Controller::Log("CONF", "No streams configured, remember to set up streams through the web interface on port "+web_port+" or API.");
|
Controller::Log(
|
||||||
|
"CONF",
|
||||||
|
"No streams configured, remember to set up streams through the web interface on port " +
|
||||||
|
web_port + " or API.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::Log("CONF", "Controller started");
|
Controller::Log("CONF", "Controller started");
|
||||||
Controller::conf.activate();//activate early, so threads aren't killed.
|
// Generate instanceId once per boot.
|
||||||
//Generate instanceId once per boot.
|
|
||||||
if (Controller::instanceId == ""){
|
if (Controller::instanceId == ""){
|
||||||
srand(time(NULL));
|
srand(mix(clock(), time(0), getpid()));
|
||||||
do{
|
do{
|
||||||
Controller::instanceId += (char)(64 + rand() % 62);
|
Controller::instanceId += (char)(64 + rand() % 62);
|
||||||
}while(Controller::instanceId.size() < 16);
|
}while (Controller::instanceId.size() < 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*LTS-START*/
|
/*LTS-START*/
|
||||||
#ifdef UPDATER
|
#ifdef UPDATER
|
||||||
if (Controller::conf.getBool("update")){
|
if (Controller::conf.getBool("update")){Controller::checkUpdates();}
|
||||||
Controller::checkUpdates();
|
#endif
|
||||||
}
|
#ifdef LICENSING
|
||||||
#endif
|
|
||||||
#ifdef LICENSING
|
|
||||||
Controller::initLicense();
|
Controller::initLicense();
|
||||||
//start license checking thread
|
// start license checking thread
|
||||||
tthread::thread licenseThread(Controller::licenseLoop, 0);
|
tthread::thread licenseThread(Controller::licenseLoop, 0);
|
||||||
#endif
|
#endif
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
|
|
||||||
//start stats thread
|
// start stats thread
|
||||||
tthread::thread statsThread(Controller::SharedMemStats, &Controller::conf);
|
tthread::thread statsThread(Controller::SharedMemStats, &Controller::conf);
|
||||||
//start monitoring thread
|
// start monitoring thread
|
||||||
tthread::thread monitorThread(statusMonitor, 0);
|
tthread::thread monitorThread(statusMonitor, 0);
|
||||||
//start monitoring thread /*LTS*/
|
// start monitoring thread /*LTS*/
|
||||||
tthread::thread uplinkThread(Controller::uplinkConnection, 0);/*LTS*/
|
tthread::thread uplinkThread(Controller::uplinkConnection, 0); /*LTS*/
|
||||||
//start push checking thread
|
// start push checking thread
|
||||||
tthread::thread pushThread(Controller::pushCheckLoop, 0);
|
tthread::thread pushThread(Controller::pushCheckLoop, 0);
|
||||||
//start UDP API thread
|
// start UDP API thread
|
||||||
tthread::thread UDPAPIThread(Controller::handleUDPAPI, 0);
|
tthread::thread UDPAPIThread(Controller::handleUDPAPI, 0);
|
||||||
#ifdef UPDATER
|
#ifdef UPDATER
|
||||||
//start updater thread
|
// start updater thread
|
||||||
tthread::thread updaterThread(Controller::updateThread, 0);
|
tthread::thread updaterThread(Controller::updateThread, 0);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// start main loop
|
||||||
//start main loop
|
|
||||||
while (Controller::conf.is_active){/*LTS*/
|
while (Controller::conf.is_active){/*LTS*/
|
||||||
Controller::conf.serveThreadedSocket(Controller::handleAPIConnection);
|
Controller::conf.serveThreadedSocket(Controller::handleAPIConnection);
|
||||||
//print shutdown reason
|
// print shutdown reason
|
||||||
std::string shutdown_reason;
|
std::string shutdown_reason;
|
||||||
if (!Controller::conf.is_active){
|
if (!Controller::conf.is_active){
|
||||||
shutdown_reason = "user request (received shutdown signal)";
|
shutdown_reason = "user request (received shutdown signal)";
|
||||||
}else{
|
|
||||||
shutdown_reason = "socket problem (API port closed)";
|
|
||||||
}
|
|
||||||
if (Controller::restarting){
|
|
||||||
shutdown_reason = "restart (on request)";
|
|
||||||
}
|
|
||||||
/*LTS-START*/
|
|
||||||
#ifdef LICENSING
|
|
||||||
if (!Controller::isLicensed()){
|
|
||||||
shutdown_reason = "no valid license";
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if(Triggers::shouldTrigger("SYSTEM_STOP")){
|
|
||||||
if (!Triggers::doTrigger("SYSTEM_STOP", shutdown_reason)){
|
|
||||||
Controller::conf.is_active = true;
|
|
||||||
Controller::restarting = false;
|
|
||||||
Util::sleep(1000);
|
|
||||||
}else{
|
}else{
|
||||||
Controller::conf.is_active = false;
|
shutdown_reason = "socket problem (API port closed)";
|
||||||
Controller::Log("CONF", "Controller shutting down because of "+shutdown_reason);
|
|
||||||
}
|
}
|
||||||
}else{
|
if (Controller::restarting){shutdown_reason = "restart (on request)";}
|
||||||
/*LTS-END*/
|
/*LTS-START*/
|
||||||
Controller::conf.is_active = false;
|
#ifdef LICENSING
|
||||||
Controller::Log("CONF", "Controller shutting down because of "+shutdown_reason);
|
if (!Controller::isLicensed()){shutdown_reason = "no valid license";}
|
||||||
/*LTS-START*/
|
#endif
|
||||||
}
|
if (Triggers::shouldTrigger("SYSTEM_STOP")){
|
||||||
}//indentation intentionally wrong, to minimize Pro/nonPro diffs
|
if (!Triggers::doTrigger("SYSTEM_STOP", shutdown_reason)){
|
||||||
|
Controller::conf.is_active = true;
|
||||||
|
Controller::restarting = false;
|
||||||
|
Util::sleep(1000);
|
||||||
|
}else{
|
||||||
|
Controller::conf.is_active = false;
|
||||||
|
Controller::Log("CONF", "Controller shutting down because of " + shutdown_reason);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
/*LTS-END*/
|
||||||
|
Controller::conf.is_active = false;
|
||||||
|
Controller::Log("CONF", "Controller shutting down because of " + shutdown_reason);
|
||||||
|
/*LTS-START*/
|
||||||
|
}
|
||||||
|
}// indentation intentionally wrong, to minimize Pro/nonPro diffs
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
//join all joinable threads
|
// join all joinable threads
|
||||||
HIGH_MSG("Joining stats thread...");
|
HIGH_MSG("Joining stats thread...");
|
||||||
statsThread.join();
|
statsThread.join();
|
||||||
HIGH_MSG("Joining monitor thread...");
|
HIGH_MSG("Joining monitor thread...");
|
||||||
|
@ -412,47 +482,44 @@ int main_loop(int argc, char ** argv){
|
||||||
pushThread.join();
|
pushThread.join();
|
||||||
HIGH_MSG("Joining UDP API thread...");
|
HIGH_MSG("Joining UDP API thread...");
|
||||||
UDPAPIThread.join();
|
UDPAPIThread.join();
|
||||||
#ifdef LICENSING
|
#ifdef LICENSING
|
||||||
HIGH_MSG("Joining license thread...");
|
HIGH_MSG("Joining license thread...");
|
||||||
licenseThread.join();
|
licenseThread.join();
|
||||||
#endif
|
#endif
|
||||||
#ifdef UPDATER
|
#ifdef UPDATER
|
||||||
HIGH_MSG("Joining updater thread...");
|
HIGH_MSG("Joining updater thread...");
|
||||||
updaterThread.join();
|
updaterThread.join();
|
||||||
#endif
|
#endif
|
||||||
/*LTS-END*/
|
/*LTS-END*/
|
||||||
//write config
|
// write config
|
||||||
tthread::lock_guard<tthread::mutex> guard(Controller::logMutex);
|
tthread::lock_guard<tthread::mutex> guard(Controller::logMutex);
|
||||||
Controller::writeConfigToDisk();
|
Controller::writeConfigToDisk();
|
||||||
//stop all child processes
|
// stop all child processes
|
||||||
Util::Procs::StopAll();
|
Util::Procs::StopAll();
|
||||||
//give everything some time to print messages
|
// give everything some time to print messages
|
||||||
Util::wait(100);
|
Util::wait(100);
|
||||||
std::cout << "Killed all processes, wrote config to disk. Exiting." << std::endl;
|
std::cout << "Killed all processes, wrote config to disk. Exiting." << std::endl;
|
||||||
if (Controller::exitDelay){
|
if (Controller::exitDelay){
|
||||||
std::cout << "Delaying shutdown by " << Controller::exitDelay << " seconds, on license server request..." << std::endl;
|
std::cout << "Delaying shutdown by " << Controller::exitDelay
|
||||||
while (Controller::exitDelay--){
|
<< " seconds, on license server request..." << std::endl;
|
||||||
Util::wait(1000);
|
while (Controller::exitDelay--){Util::wait(1000);}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (Controller::restarting){
|
if (Controller::restarting){return 42;}
|
||||||
return 42;
|
// close stderr to make the stderr reading thread exit
|
||||||
}
|
|
||||||
//close stderr to make the stderr reading thread exit
|
|
||||||
close(STDERR_FILENO);
|
close(STDERR_FILENO);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUSR1(int signum, siginfo_t * sigInfo, void * ignore){
|
void handleUSR1(int signum, siginfo_t *sigInfo, void *ignore){
|
||||||
Controller::Log("CONF", "USR1 received - restarting controller");
|
Controller::Log("CONF", "USR1 received - restarting controller");
|
||||||
Controller::restarting = true;
|
Controller::restarting = true;
|
||||||
raise(SIGINT); //trigger restart
|
raise(SIGINT); // trigger restart
|
||||||
}
|
}
|
||||||
|
|
||||||
///\brief The controller angel process.
|
///\brief The controller angel process.
|
||||||
///Starts a forked main_loop in a loop. Yes, you read that right.
|
/// Starts a forked main_loop in a loop. Yes, you read that right.
|
||||||
int main(int argc, char ** argv){
|
int main(int argc, char **argv){
|
||||||
Util::Procs::setHandler();//set child handler
|
Util::Procs::setHandler(); // set child handler
|
||||||
{
|
{
|
||||||
struct sigaction new_action;
|
struct sigaction new_action;
|
||||||
struct sigaction cur_action;
|
struct sigaction cur_action;
|
||||||
|
@ -484,7 +551,7 @@ int main(int argc, char ** argv){
|
||||||
FAIL_MSG("Unable to spawn controller process!");
|
FAIL_MSG("Unable to spawn controller process!");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
//wait for the process to exit
|
// wait for the process to exit
|
||||||
int status;
|
int status;
|
||||||
while (waitpid(pid, &status, 0) != pid && errno == EINTR){
|
while (waitpid(pid, &status, 0) != pid && errno == EINTR){
|
||||||
if (Controller::restarting){
|
if (Controller::restarting){
|
||||||
|
@ -498,7 +565,7 @@ int main(int argc, char ** argv){
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//if the exit was clean, don't restart it
|
// if the exit was clean, don't restart it
|
||||||
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)){
|
if (WIFEXITED(status) && (WEXITSTATUS(status) == 0)){
|
||||||
MEDIUM_MSG("Controller shut down cleanly");
|
MEDIUM_MSG("Controller shut down cleanly");
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Reference in a new issue