Add option to run OpenBTS in a failsafe loop.
This commit is contained in:
parent
d0c9057711
commit
7d9fa86569
|
@ -31,6 +31,11 @@
|
||||||
#Server.Daemonize
|
#Server.Daemonize
|
||||||
$optional Server.Daemonize
|
$optional Server.Daemonize
|
||||||
|
|
||||||
|
# If Server.RestartOnCrash is defined, OpenBTS forks a child
|
||||||
|
# and restart it if child dies. This is kind of failsafe.
|
||||||
|
#Server.RestartOnCrash
|
||||||
|
$optional Server.RestartOnCrash
|
||||||
|
|
||||||
# Path to OpenBTS PID file.
|
# Path to OpenBTS PID file.
|
||||||
# Filesystem Hierarchy Standard specifies it daemons should write PID
|
# Filesystem Hierarchy Standard specifies it daemons should write PID
|
||||||
# to /var/run, but you may specify any other directory if you run
|
# to /var/run, but you may specify any other directory if you run
|
||||||
|
|
|
@ -54,6 +54,7 @@ using namespace GSM;
|
||||||
using namespace CommandLine;
|
using namespace CommandLine;
|
||||||
|
|
||||||
static int daemonize(std::string &lockfile, int &lfp);
|
static int daemonize(std::string &lockfile, int &lfp);
|
||||||
|
static int forkLoop();
|
||||||
|
|
||||||
class DaemonInitializer
|
class DaemonInitializer
|
||||||
{
|
{
|
||||||
|
@ -85,12 +86,25 @@ protected:
|
||||||
int mLockFileFD;
|
int mLockFileFD;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Restarter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Restarter(bool restartOnCrash)
|
||||||
|
{
|
||||||
|
if (restartOnCrash)
|
||||||
|
if (forkLoop() != EXIT_SUCCESS)
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// Load configuration from a file.
|
/// Load configuration from a file.
|
||||||
ConfigurationTable gConfig("OpenBTS.config");
|
ConfigurationTable gConfig("OpenBTS.config");
|
||||||
/// Initialize Logger form the config.
|
/// Initialize Logger form the config.
|
||||||
static LogInitializer sgLogInitializer;
|
static LogInitializer sgLogInitializer;
|
||||||
/// Fork daemon if needed.
|
/// Fork daemon if needed.
|
||||||
static DaemonInitializer sgDaemonInitializer(gConfig.defines("Server.Daemonize"));
|
static DaemonInitializer sgDaemonInitializer(gConfig.defines("Server.Daemonize"));
|
||||||
|
/// Fork a child and restart it if it crash. Kind of failsafe.
|
||||||
|
static Restarter sgRestarter(gConfig.defines("Server.RestartOnCrash"));
|
||||||
|
|
||||||
|
|
||||||
// All of the other globals that rely on the global configuration file need to
|
// All of the other globals that rely on the global configuration file need to
|
||||||
|
@ -238,26 +252,7 @@ static void exitCLI()
|
||||||
fclose(stdin);
|
fclose(stdin);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void signalHandler(int sig)
|
static void daemonChildHandler(int signum)
|
||||||
{
|
|
||||||
COUT("Handling signal " << sig);
|
|
||||||
LOG(INFO) << "Handling signal " << sig;
|
|
||||||
switch(sig){
|
|
||||||
case SIGHUP:
|
|
||||||
// re-read the config
|
|
||||||
// TODO::
|
|
||||||
break;
|
|
||||||
case SIGTERM:
|
|
||||||
case SIGINT:
|
|
||||||
// finalize the server
|
|
||||||
exitCLI();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void childHandler(int signum)
|
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Handling signal " << signum;
|
LOG(INFO) << "Handling signal " << signum;
|
||||||
switch(signum) {
|
switch(signum) {
|
||||||
|
@ -311,9 +306,9 @@ static int daemonize(std::string &lockfile, int &lfp)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Trap signals that we expect to receive
|
// Trap signals that we expect to receive
|
||||||
signal(SIGCHLD, childHandler);
|
signal(SIGCHLD, daemonChildHandler);
|
||||||
signal(SIGUSR1, childHandler);
|
signal(SIGUSR1, daemonChildHandler);
|
||||||
signal(SIGALRM, childHandler);
|
signal(SIGALRM, daemonChildHandler);
|
||||||
|
|
||||||
// Fork off the parent process
|
// Fork off the parent process
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
@ -344,8 +339,10 @@ static int daemonize(std::string &lockfile, int &lfp)
|
||||||
// At this point we are executing as the child process
|
// At this point we are executing as the child process
|
||||||
pid_t parent = getppid();
|
pid_t parent = getppid();
|
||||||
|
|
||||||
// Return SIGCHLD to default handler
|
// Return signals to default handlers
|
||||||
signal(SIGCHLD, SIG_DFL);
|
signal(SIGCHLD, SIG_DFL);
|
||||||
|
signal(SIGUSR1, SIG_DFL);
|
||||||
|
signal(SIGALRM, SIG_DFL);
|
||||||
|
|
||||||
// Change the file mode mask
|
// Change the file mode mask
|
||||||
// This will restrict file creation mode to 750 (complement of 027).
|
// This will restrict file creation mode to 750 (complement of 027).
|
||||||
|
@ -385,6 +382,79 @@ static int daemonize(std::string &lockfile, int &lfp)
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int forkLoop()
|
||||||
|
{
|
||||||
|
bool shouldExit = false;
|
||||||
|
sigset_t chldSignalSet;
|
||||||
|
sigemptyset(&chldSignalSet);
|
||||||
|
sigaddset(&chldSignalSet, SIGCHLD);
|
||||||
|
sigaddset(&chldSignalSet, SIGTERM);
|
||||||
|
sigaddset(&chldSignalSet, SIGINT);
|
||||||
|
sigaddset(&chldSignalSet, SIGKILL);
|
||||||
|
|
||||||
|
// Block signals to avoid race condition.
|
||||||
|
// It will be delivered to us in sigwait() when we are ready to handle it.
|
||||||
|
sigprocmask(SIG_BLOCK, &chldSignalSet, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Fork off the parent process
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
// fork() failed.
|
||||||
|
LOG(ERROR) << "Unable to fork child, code=" << errno
|
||||||
|
<< " (" << strerror(errno) << ")";
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (pid > 0) {
|
||||||
|
// Parent process
|
||||||
|
// Wait for child process to exit (SIGCHLD).
|
||||||
|
LOG(INFO) << "Forked child process with PID " << pid;
|
||||||
|
int signum = -1;
|
||||||
|
while (signum != SIGCHLD) {
|
||||||
|
sigwait(&chldSignalSet, &signum);
|
||||||
|
switch(signum) {
|
||||||
|
case SIGCHLD:
|
||||||
|
LOG(ERROR) << "Child with PID " << pid << " died.";
|
||||||
|
if (shouldExit) exit(EXIT_SUCCESS);
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
case SIGKILL:
|
||||||
|
// Forward signal to the child.
|
||||||
|
kill(pid, signum);
|
||||||
|
// We will exit child exits and send us SIGCHLD.
|
||||||
|
shouldExit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Child process
|
||||||
|
// Unblock signals we blocked.
|
||||||
|
sigprocmask(SIG_UNBLOCK, &chldSignalSet, NULL);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signalHandler(int sig)
|
||||||
|
{
|
||||||
|
COUT("Handling signal " << sig);
|
||||||
|
LOG(INFO) << "Handling signal " << sig;
|
||||||
|
switch(sig){
|
||||||
|
case SIGHUP:
|
||||||
|
// re-read the config
|
||||||
|
// TODO::
|
||||||
|
break;
|
||||||
|
case SIGTERM:
|
||||||
|
case SIGINT:
|
||||||
|
// finalize the server
|
||||||
|
exitCLI();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
srandom(time(NULL));
|
srandom(time(NULL));
|
||||||
|
|
Reference in New Issue