From 8598e72800ed72d365b0ab47c235ec69f2d1fb91 Mon Sep 17 00:00:00 2001 From: Alexander Chemeris Date: Mon, 22 Nov 2010 16:01:25 +0300 Subject: [PATCH] Catch SIGINT, SIGTERM and SIGHUP and shutdown the server gracefully. --- public-trunk/CLI/CLI.cpp | 1 + public-trunk/CLI/CLIParser.cpp | 100 ++++++++++++++++++--------------- public-trunk/CLI/CLIParser.h | 7 +++ public-trunk/apps/OpenBTS.cpp | 56 +++++++++++++++++- 4 files changed, 117 insertions(+), 47 deletions(-) diff --git a/public-trunk/CLI/CLI.cpp b/public-trunk/CLI/CLI.cpp index 4d9e7f8..7d9ce07 100644 --- a/public-trunk/CLI/CLI.cpp +++ b/public-trunk/CLI/CLI.cpp @@ -89,6 +89,7 @@ void CommandLine::runCLI(ParserBase *processor) cout.flush(); std::string inbuf; getline(cin, inbuf, '\n'); + if (!cin.good()) break; // The parser returns -1 on exit. if (processor->process(inbuf,cout)<0) break; #endif // !HAVE_LIBREADLINE ] diff --git a/public-trunk/CLI/CLIParser.cpp b/public-trunk/CLI/CLIParser.cpp index d20b5c3..07d9811 100644 --- a/public-trunk/CLI/CLIParser.cpp +++ b/public-trunk/CLI/CLIParser.cpp @@ -67,14 +67,63 @@ static const char* errorCodeText[] = { extern TransceiverManager gTRX; CommandLine::Parser CommandLine::gParser; +/**@name Helper functions. */ +//@{ +void CommandLine::exitBTS(unsigned waitSec, ostream& os) +{ + // Block creation of new channels. + gBTS.hold(true); + + if (waitSec!=0) { + os << "waiting up to " << waitSec << " seconds for clearing of " + << gBTS.TCHActive() << " active calls" << endl; + + // Wait up to the timeout for active channels to release. + time_t finish = time(NULL) + waitSec; + while (time(NULL)0) { + LOG(WARN) << "dropping " << gBTS.SDCCHActive() << " control transactions on exit"; + loads = true; + } + if (gBTS.TCHActive()>0) { + LOG(WARN) << "dropping " << gBTS.TCHActive() << " calls on exit"; + loads = true; + } + if (loads) { + os << endl << "exiting with loads:" << endl; + printStats(os); + } + if (gConfig.defines("Control.TMSITable.SavePath")) { + gTMSITable.save(gConfig.getStr("Control.TMSITable.SavePath")); + } + os << endl << "exiting..." << endl; +} + +void CommandLine::printStats(ostream& os) +{ + os << "SDCCH load: " << gBTS.SDCCHActive() << '/' << gBTS.SDCCHTotal() << endl; + os << "TCH/F load: " << gBTS.TCHActive() << '/' << gBTS.TCHTotal() << endl; + os << "AGCH/PCH load: " << gBTS.AGCHLoad() << ',' << gBTS.PCHLoad() << endl; + // paging table size + os << "Paging table size: " << gBTS.pager().pagingEntryListSize() << endl; + os << "Transactions/TMSIs: " << gTransactionTable.size() << ',' << gTMSITable.size() << endl; + // 3122 timer current value (the number of seconds an MS should hold off the next RACH) + os << "T3122: " << gBTS.T3122() << " ms" << endl; +} + +//@} /**@name Commands for the CLI. */ //@{ -// forward refs -int printStats(int argc, char** argv, ostream& os); - /* A CLI command takes the argument in an array. It returns 0 on success. @@ -146,8 +195,6 @@ int showHelp(int argc, char** argv, ostream& os) return SUCCESS; } - - /** A function to return -1, the exit code for the caller. */ int exit_function(int argc, char** argv, ostream& os) { @@ -155,36 +202,8 @@ int exit_function(int argc, char** argv, ostream& os) if (argc>2) return BAD_NUM_ARGS; if (argc==2) wait = atoi(argv[1]); - if (wait!=0) - os << "waiting up to " << wait << " seconds for clearing of " - << gBTS.TCHActive() << " active calls" << endl; + exitBTS(wait, os); - // Block creation of new channels. - gBTS.hold(true); - // Wait up to the timeout for active channels to release. - time_t finish = time(NULL) + wait; - while (time(NULL)0) { - LOG(WARN) << "dropping " << gBTS.SDCCHActive() << " control transactions on exit"; - loads = true; - } - if (gBTS.TCHActive()>0) { - LOG(WARN) << "dropping " << gBTS.TCHActive() << " calls on exit"; - loads = true; - } - if (loads) { - os << endl << "exiting with loads:" << endl; - printStats(1,NULL,os); - } - if (gConfig.defines("Control.TMSITable.SavePath")) { - gTMSITable.save(gConfig.getStr("Control.TMSITable.SavePath")); - } - os << endl << "exiting..." << endl; return -1; } @@ -327,17 +346,10 @@ int sendrrlp(int argc, char** argv, ostream& os) /** Print current usage loads. */ -int printStats(int argc, char** argv, ostream& os) +int cliPrintStats(int argc, char** argv, ostream& os) { if (argc!=1) return BAD_NUM_ARGS; - os << "SDCCH load: " << gBTS.SDCCHActive() << '/' << gBTS.SDCCHTotal() << endl; - os << "TCH/F load: " << gBTS.TCHActive() << '/' << gBTS.TCHTotal() << endl; - os << "AGCH/PCH load: " << gBTS.AGCHLoad() << ',' << gBTS.PCHLoad() << endl; - // paging table size - os << "Paging table size: " << gBTS.pager().pagingEntryListSize() << endl; - os << "Transactions/TMSIs: " << gTransactionTable.size() << ',' << gTMSITable.size() << endl; - // 3122 timer current value (the number of seconds an MS should hold off the next RACH) - os << "T3122: " << gBTS.T3122() << " ms" << endl; + printStats(os); return SUCCESS; } @@ -813,7 +825,7 @@ Parser::Parser() addCommand("findimsi", findimsi, "[IMSIPrefix] -- prints all imsi's that are prefixed by IMSIPrefix"); addCommand("sendsms", sendsms, " -- send SMS to , addressed from ."); addCommand("sendrrlp", sendrrlp, " -- send RRLP message to ."); - addCommand("load", printStats, "-- print the current activity loads."); + addCommand("load", cliPrintStats, "-- print the current activity loads."); addCommand("cellid", cellID, "[MCC MNC LAC CI] -- get/set location area identity (MCC, MNC, LAC) and cell ID (CI)"); addCommand("calls", calls, "-- print the transaction table"); addCommand("config", config, "[] OR [patt] OR [key val(s)] -- print the current configuration, print configuration values matching a pattern, or set/change a configuration value"); diff --git a/public-trunk/CLI/CLIParser.h b/public-trunk/CLI/CLIParser.h index 70265de..37eee77 100644 --- a/public-trunk/CLI/CLIParser.h +++ b/public-trunk/CLI/CLIParser.h @@ -79,6 +79,13 @@ protected: extern CommandLine::Parser gParser; +/** Exit BTS, waiting up to waitSec seconds for active channels to release. */ +void exitBTS(unsigned waitSec, std::ostream& os); + +/** Print current usage loads. */ +void printStats(std::ostream& os); + + } // CLI diff --git a/public-trunk/apps/OpenBTS.cpp b/public-trunk/apps/OpenBTS.cpp index 330376f..207c064 100644 --- a/public-trunk/apps/OpenBTS.cpp +++ b/public-trunk/apps/OpenBTS.cpp @@ -69,7 +69,8 @@ GSMConfig gBTS; // Our interface to the software-defined radio. TransceiverManager gTRX(1, gConfig.getStr("TRX.IP"), gConfig.getNum("TRX.Port")); - +/// Pointer to the server socket if we run remote CLI. +ConnectionServerSocket *gCLIServerSock = NULL; @@ -251,18 +252,63 @@ void startBTS() void stopBTS() { - if (gConfig.defines("Control.TMSISavePath")) { - gTMSITable.save(gConfig.getStr("Control.TMSISavePath")); + if (!gBTS.hold()) { + exitBTS(0, cout); } if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); } +void exitCLI() +{ + if (gCLIServerSock != NULL) { + // Closing server sock + gCLIServerSock->close(); + gCLIServerSock = NULL; + } + + // Closing server standard input to shutdown local CLI + cin.setstate(ios::eofbit); +// cin.putback('\n'); + fclose(stdin); +} + +void signalHandler(int sig) +{ + COUT("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[]) { srandom(time(NULL)); + // Signal to re-read config + if (signal(SIGHUP, signalHandler) == SIG_ERR) { + CERR("Error while setting handler for SIGHUP."); + } + // Signal to shutdown gracefully + if (signal(SIGTERM, signalHandler) == SIG_ERR) { + CERR("Error while setting handler for SIGTERM."); + } + // Ctrl-C signal + if (signal(SIGINT, signalHandler) == SIG_ERR) { + CERR("Error while setting handler for SIGINT."); + } + + COUT("\n\n" << gOpenBTSWelcome << "\n"); if (gConfig.defines("Log.FileName")) { @@ -274,10 +320,14 @@ int main(int argc, char *argv[]) if (strcasecmp(gConfig.getStr("CLI.Type"),"TCP") == 0) { ConnectionServerSocketTCP serverSock(gConfig.getNum("CLI.TCP.Port"), gConfig.getStr("CLI.TCP.IP")); + gCLIServerSock = &serverSock; runCLIServer(&serverSock); + gCLIServerSock = NULL; } else if (strcasecmp(gConfig.getStr("CLI.Type"),"Unix") == 0) { ConnectionServerSocketUnix serverSock(gConfig.getStr("CLI.Unix.Path")); + gCLIServerSock = &serverSock; runCLIServer(&serverSock); + gCLIServerSock = NULL; } else { runCLI(&gParser); }