void GSMConfig::createCombination0(TransceiverManager& TRX, unsigned TN) { // This channel is a dummy burst generator. // This should not be applied to C0T0. LOG_ASSERT(TN!=0); LOG(NOTICE) << "Configuring dummy filling on C0T " << TN; ARFCNManager *radio = TRX.ARFCN(0); radio->setSlot(TN,0); // (pat) 0 => Transciever.h enum ChannelCombination = FILL }
int noise(int argc, char** argv, ostream& os) { if (argc!=1) return BAD_NUM_ARGS; int noise = gTRX.ARFCN(0)->getNoiseLevel(); os << "noise RSSI is -" << noise << " dB wrt full scale" << endl; os << "MS RSSI target is " << gConfig.getNum("GSM.Radio.RSSITarget") << " dB wrt full scale" << endl; return SUCCESS; }
void GSMConfig::createCombination0(TransceiverManager& TRX, unsigned CN, unsigned TN) { // This channel is a dummy burst generator. // This should not be applied to non-C0. LOG_ASSERT(CN==0); // This should not be applied to C0T0. LOG_ASSERT(TN!=0); LOG(NOTICE) << "Configuring dummy filling on C" << CN << "T " << TN; ARFCNManager *radio = TRX.ARFCN(CN); radio->setSlot(TN,0); }
int trxfactory(int argc, char** argv, ostream& os) { if (argc!=1) return BAD_NUM_ARGS; signed val = gTRX.ARFCN(0)->getFactoryCalibration("sdrsn"); if (val == 0 || val == 65535) { os << "Reading factory calibration not supported on this radio." << endl; return SUCCESS; } os << "Factory Information" << endl; os << " SDR Serial Number = " << val << endl; val = gTRX.ARFCN(0)->getFactoryCalibration("rfsn"); os << " RF Serial Number = " << val << endl; val = gTRX.ARFCN(0)->getFactoryCalibration("band"); os << " GSM.Radio.Band = "; if (val == 0) { os << "multi-band"; } else { os << val; } os << endl; val = gTRX.ARFCN(0)->getFactoryCalibration("rxgain"); os << " GSM.Radio.RxGain = " << val << endl; val = gTRX.ARFCN(0)->getFactoryCalibration("txgain"); os << " TRX.TxAttenOffset = " << val << endl; val = gTRX.ARFCN(0)->getFactoryCalibration("freq"); os << " TRX.RadioFrequencyOffset = " << val << endl; return SUCCESS; }
void GSMConfig::createCombinationI(TransceiverManager& TRX, unsigned CN, unsigned TN) { LOG_ASSERT((CN!=0)||(TN!=0)); LOG(NOTICE) << "Configuring combination I on C" << CN << "T" << TN; ARFCNManager *radio = TRX.ARFCN(CN); radio->setSlot(TN,1); // (pat) 1 => Transciever.h enum ChannelCombination = I TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(CN,TN,gTCHF_T[TN]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addTCH(chan); }
int rxgain(int argc, char** argv, ostream& os) { os << "current RX gain is " << gConfig.getNum("GSM.RxGain") << " dB" << endl; if (argc==1) return SUCCESS; if (argc!=2) return BAD_NUM_ARGS; int newGain = gTRX.ARFCN(0)->setRxGain(atoi(argv[1])); os << "new RX gain is " << newGain << " dB" << endl; gConfig.set("GSM.RxGain",newGain); return SUCCESS; }
void GSMConfig::createCombinationI(TransceiverManager& TRX, unsigned CN, unsigned TN) { LOG_ASSERT((CN!=0)||(TN!=0)); LOG(NOTICE) << "Configuring combination I on C" << CN << "T" << TN; ARFCNManager *radio = TRX.ARFCN(CN); radio->setSlot(TN,1); // (pat) 1 => Transciever.h enum ChannelCombination = I TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(CN,TN,gTCHF_T[TN]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,dynamic_cast<L3LogicalChannel*>(chan)); chan->lcinit(); if (CN == 0 && !testStart) chan->lcstart(); // Everything on C0 must broadcast continually. gBTS.addTCH(chan); }
int radio(int argc, char** argv, ostream& os) { if (argc==1) return BAD_NUM_ARGS; std::string cmd; for (argc--, argv++; argc > 0; argc--, argv++) { cmd += *argv; if (argc > 1) cmd += " "; } os << (gTRX.ARFCN(0)->runCustom(cmd.c_str()) ? "OK" : "Command rejected") << endl; return SUCCESS; }
void GSMConfig::createCombinationVII(TransceiverManager& TRX, unsigned CN, unsigned TN) { LOG_ASSERT((CN!=0)||(TN!=0)); LOG(NOTICE) << "Configuring combination VII on C" << CN << "T" << TN; ARFCNManager *radio = TRX.ARFCN(CN); radio->setSlot(TN,7); for (int i=0; i<8; i++) { SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(TN,gSDCCH8[i]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addSDCCH(chan); } }
int freqcorr(int argc, char** argv, ostream& os) { os << "current freq. offset is " << gConfig.getNum("TRX.RadioFrequencyOffset") << endl; if (argc==1) return SUCCESS; if (argc!=2) return BAD_NUM_ARGS; if (!gConfig.isValidValue("TRX.RadioFrequencyOffset", argv[1])) { os << "Invalid new value for freq. offset It must be in range ("; os << gConfig.mSchema["TRX.RadioFrequencyOffset"].getValidValues() << ")" << endl; return BAD_VALUE; } int newOffset = gTRX.ARFCN(0)->setFreqOffset(atoi(argv[1])); os << "new freq. offset is " << newOffset << endl; gConfig.set("TRX.RadioFrequencyOffset",newOffset); return SUCCESS; }
int rxgain(int argc, char** argv, ostream& os) { os << "current RX gain is " << gConfig.getNum("GSM.Radio.RxGain") << " dB" << endl; if (argc==1) return SUCCESS; if (argc!=2) return BAD_NUM_ARGS; if (!gConfig.isValidValue("GSM.Radio.RxGain", argv[1])) { os << "Invalid new value for RX gain. It must be in range ("; os << gConfig.mSchema["GSM.Radio.RxGain"].getValidValues() << ")" << endl; return BAD_VALUE; } int newGain = gTRX.ARFCN(0)->setRxGain(atoi(argv[1])); os << "new RX gain is " << newGain << " dB" << endl; gConfig.set("GSM.Radio.RxGain",newGain); return SUCCESS; }
int txatten(int argc, char** argv, ostream& os) { os << "current TX attenuation is " << gConfig.getNum("TRX.TxAttenOffset") << " dB" << endl; if (argc==1) return SUCCESS; if (argc!=2) return BAD_NUM_ARGS; if (!gConfig.isValidValue("TRX.TxAttenOffset", argv[1])) { os << "Invalid new value for TX attenuation. It must be in range ("; os << gConfig.mSchema["TRX.TxAttenOffset"].getValidValues() << ")" << endl; return BAD_VALUE; } int newAtten = gTRX.ARFCN(0)->setTxAtten(atoi(argv[1])); os << "new TX attenuation is " << newAtten << " dB" << endl; gConfig.set("TRX.TxAttenOffset",newAtten); return SUCCESS; }
int main(int argc, char *argv[]) { // TODO: Properly parse and handle any arguments if (argc > 1) { for (int argi = 0; argi < argc; argi++) { if (!strcmp(argv[argi], "--version") || !strcmp(argv[argi], "-v")) { cout << gVersionString << endl; } } return 0; } createStats(); gReports.incr("OpenBTS.Starts"); int sock = socket(AF_UNIX,SOCK_DGRAM,0); if (sock<0) { perror("creating CLI datagram socket"); LOG(ALERT) << "cannot create socket for CLI"; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); gLogInit("openbts",gConfig.getStr("Log.Level").c_str()); LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__; COUT("\n\n" << gOpenBTSWelcome << "\n"); gTMSITable.open(gConfig.getStr("Control.Reporting.TMSITable").c_str()); gTransactionTable.init(gConfig.getStr("Control.Reporting.TransactionTable").c_str()); gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gSubscriberRegistry.init(); gParser.addCommands(); COUT("\nStarting the system..."); // is the radio running? // Start the transceiver interface. LOG(INFO) << "checking transceiver"; //gTRX.ARFCN(0)->powerOn(); //sleep(gConfig.getNum("TRX.Timeout.Start",2)); bool haveTRX = gTRX.ARFCN(0)->powerOn(false); Thread transceiverThread; if (!haveTRX) { transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // sleep to let the FPGA code load // TODO: we should be "pinging" the radio instead of sleeping sleep(5); } else { LOG(NOTICE) << "transceiver already running"; } // Start the SIP interface. gSIPInterface.start(); // // Configure the radio. // gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. //C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); unsigned numARFCNs = gConfig.getNum("GSM.Radio.ARFCNs"); for (unsigned i=0; i<numARFCNs; i++) { // Tune the radios. unsigned ARFCN = C0 + i*2; LOG(INFO) << "tuning TRX " << i << " to ARFCN " << ARFCN; ARFCNManager* radio = gTRX.ARFCN(i); radio->tune(ARFCN); } // Send either TSC or full BSIC depending on radio need if (gConfig.getBool("GSM.Radio.NeedBSIC")) { // Send BSIC to C0radio->setBSIC(gBTS.BSIC()); } else { // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); } // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(true); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,0,gSDCCH_4_0), SDCCHLogicalChannel(0,0,gSDCCH_4_1), SDCCHLogicalChannel(0,0,gSDCCH_4_2), SDCCHLogicalChannel(0,0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; for (int i=0; i<4; i++) { C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount/8,sCount%8); sCount++; } if (!gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Set up idle filling on C0 as needed. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. if (gConfig.getNum("GSM.Channels.SDCCHReserve")>=(int)gBTS.SDCCHTotal()) { unsigned val = gBTS.SDCCHTotal() - 1; LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val; gConfig.set("GSM.Channels.SDCCHReserve",val); } // OK, now it is safe to start the BTS. gBTS.start(); cout << "\nsystem ready\n"; cout << "\nuse the OpenBTSCLI utility to access CLI\n"; LOG(INFO) << "system ready"; struct sockaddr_un cmdSockName; cmdSockName.sun_family = AF_UNIX; const char* sockpath = gConfig.getStr("CLI.SocketPath").c_str(); char rmcmd[strlen(sockpath)+5]; sprintf(rmcmd,"rm %s",sockpath); system(rmcmd); strcpy(cmdSockName.sun_path,sockpath); if (bind(sock, (struct sockaddr *) &cmdSockName, sizeof(struct sockaddr_un))) { perror("binding name to cmd datagram socket"); LOG(ALERT) << "cannot bind socket for CLI at " << sockpath; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } while (1) { char cmdbuf[1000]; struct sockaddr_un source; socklen_t sourceSize = sizeof(source); int nread = recvfrom(sock,cmdbuf,sizeof(cmdbuf)-1,0,(struct sockaddr*)&source,&sourceSize); gReports.incr("OpenBTS.CLI.Command"); cmdbuf[nread]='\0'; LOG(INFO) << "received command \"" << cmdbuf << "\" from " << source.sun_path; std::ostringstream sout; int res = gParser.process(cmdbuf,sout); const std::string rspString= sout.str(); const char* rsp = rspString.c_str(); LOG(INFO) << "sending " << strlen(rsp) << "-char result to " << source.sun_path; if (sendto(sock,rsp,strlen(rsp)+1,0,(struct sockaddr*)&source,sourceSize)<0) { LOG(ERR) << "can't send CLI response to " << source.sun_path; gReports.incr("OpenBTS.CLI.Command.ResponseFailure"); } // res<0 means to exit the application if (res<0) break; gReports.incr("OpenBTS.Exit.Normal.CLI"); } } // try catch (ConfigurationTableKeyNotFound e) { LOG(EMERG) << "required configuration parameter " << e.key() << " not defined, aborting"; gReports.incr("OpenBTS.Exit.Error.ConfigurationParamterNotFound"); } //if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); close(sock); }
/** Audit the current configuration. */ int audit(int argc, char** argv, ostream& os) { ConfigurationKeyMap::iterator mp; stringstream ss; // value errors mp = gConfig.mSchema.begin(); while (mp != gConfig.mSchema.end()) { if (!gConfig.isValidValue(mp->first, gConfig.getStr(mp->first))) { ss << mp->first << " \"" << gConfig.getStr(mp->first) << "\" (\"" << mp->second.getDefaultValue() << "\")" << endl; } mp++; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| ERROR : Invalid Values [key current-value (default)] |" << endl; os << "| To use the default value again, execute: rmconfig key |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } // factory calibration warnings signed sdrsn = gTRX.ARFCN(0)->getFactoryCalibration("sdrsn"); if (sdrsn != 0 && sdrsn != 65535) { string factoryValue; string configValue; factoryValue = gConfig.mSchema["GSM.Radio.Band"].getDefaultValue(); configValue = gConfig.getStr("GSM.Radio.Band"); // only warn on band changes if the unit is not multi-band if (gTRX.ARFCN(0)->getFactoryCalibration("band") != 0 && configValue.compare(factoryValue) != 0) { ss << "GSM.Radio.Band \"" << configValue << "\" (\"" << factoryValue << "\")" << endl; } factoryValue = gConfig.mSchema["GSM.Radio.RxGain"].getDefaultValue(); configValue = gConfig.getStr("GSM.Radio.RxGain"); if (configValue.compare(factoryValue) != 0) { ss << "GSM.Radio.RxGain \"" << configValue << "\" (\"" << factoryValue << "\")" << endl; } factoryValue = gConfig.mSchema["TRX.TxAttenOffset"].getDefaultValue(); configValue = gConfig.getStr("TRX.TxAttenOffset"); if (configValue.compare(factoryValue) != 0) { ss << "TRX.TxAttenOffset \"" << configValue << "\" (\"" << factoryValue << "\")" << endl; } factoryValue = gConfig.mSchema["TRX.RadioFrequencyOffset"].getDefaultValue(); configValue = gConfig.getStr("TRX.RadioFrequencyOffset"); if (configValue.compare(factoryValue) != 0) { ss << "TRX.RadioFrequencyOffset \"" << configValue << "\" (\"" << factoryValue << "\")" << endl; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| WARNING : Factory Radio Calibration [key current-value (factory)] |" << endl; os << "| To use the factory value again, execute: rmconfig key |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } } // cross check warnings vector<string> allWarnings; vector<string> warnings; vector<string>::iterator warning; mp = gConfig.mSchema.begin(); while (mp != gConfig.mSchema.end()) { warnings = gConfig.crossCheck(mp->first); allWarnings.insert(allWarnings.end(), warnings.begin(), warnings.end()); mp++; } sort(allWarnings.begin(), allWarnings.end()); allWarnings.erase(unique(allWarnings.begin(), allWarnings.end() ), allWarnings.end()); warning = allWarnings.begin(); while (warning != allWarnings.end()) { ss << *warning << endl; warning++; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| WARNING : Cross-Check Values |" << endl; os << "| To quiet these warnings, follow the advice given. |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } // site-specific values mp = gConfig.mSchema.begin(); while (mp != gConfig.mSchema.end()) { if (mp->second.getVisibility() == ConfigurationKey::CUSTOMERSITE) { if (gConfig.getStr(mp->first).compare(gConfig.mSchema[mp->first].getDefaultValue()) == 0) { ss << mp->first << " \"" << gConfig.mSchema[mp->first].getDefaultValue() << "\"" << endl; } } mp++; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| WARNING : Site Values Which Are Still Default [key current-value] |" << endl; os << "| These should be set to fit your installation: config key value |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } // non-default values mp = gConfig.mSchema.begin(); while (mp != gConfig.mSchema.end()) { if (mp->second.getVisibility() != ConfigurationKey::CUSTOMERSITE) { if (gConfig.getStr(mp->first).compare(gConfig.mSchema[mp->first].getDefaultValue()) != 0) { ss << mp->first << " \"" << gConfig.getStr(mp->first) << "\" (\"" << mp->second.getDefaultValue() << "\")" << endl; } } mp++; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| INFO : Non-Default Values [key current-value (default)] |" << endl; os << "| To use the default value again, execute: rmconfig key |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } // unknown pairs ConfigurationRecordMap pairs = gConfig.getAllPairs(); ConfigurationRecordMap::iterator mp2 = pairs.begin(); while (mp2 != pairs.end()) { if (!gConfig.keyDefinedInSchema(mp2->first)) { // also kindly ignore SIM.Prog keys for now so the users don't kill their ability to program SIMs string family = "SIM.Prog."; if (mp2->first.substr(0, family.size()) != family) { ss << mp2->first << " \"" << mp2->second.value() << "\"" << endl; } } mp2++; } if (ss.str().length()) { os << "+---------------------------------------------------------------------+" << endl; os << "| INFO : Custom/Deprecated Key/Value Pairs [key current-value] |" << endl; os << "| To clean up any extraneous keys, execute: rmconfig key |" << endl; os << "+---------------------------------------------------------------------+" << endl; os << ss.str(); os << endl; ss.str(""); } return SUCCESS; }
int main(int argc, char *argv[]) { //mtrace(); // (pat) Enable memory leak detection. Unfortunately, huge amounts of code have been started in the constructors above. gLogGroup.setAll(); // TODO: Properly parse and handle any arguments if (argc > 1) { bool testflag = false; for (int argi = 1; argi < argc; argi++) { // Skip argv[0] which is the program name. if (!strcmp(argv[argi], "--version") || !strcmp(argv[argi], "-v")) { // Print the version number and exit immediately. cout << gVersionString << endl; return 0; } if (!strcmp(argv[argi], "--test")) { testflag = true; continue; } if (!strcmp(argv[argi], "--gensql")) { cout << gConfig.getDefaultSQL(string(argv[0]), gVersionString) << endl; return 0; } if (!strcmp(argv[argi], "--gentex")) { cout << gConfig.getTeX(string(argv[0]), gVersionString) << endl; return 0; } // (pat) Adding support for specified sql file. // Unfortunately, the Config table was inited quite some time ago, // so stick this arg in the environment, whence the ConfigurationTable can find it, and then reboot. if (!strcmp(argv[argi],"--config")) { if (++argi == argc) { LOG(ALERT) <<"Missing argument to --config option"; exit(2); } setenv(cOpenBTSConfigEnv,argv[argi],1); execl(argv[0],"OpenBTS",NULL); LOG(ALERT) <<"execl failed? Exiting..."; exit(0); } if (!strcmp(argv[argi],"--help")) { printf("OpenBTS [--version --gensql --gentex] [--config file.db]\n"); printf("OpenBTS exiting...\n"); exit(0); } printf("OpenBTS: unrecognized argument: %s\nexiting...\n",argv[argi]); } if (testflag) { GSM::TestTCHL1FEC(); return 0; } } createStats(); gConfig.setCrossCheckHook(&configurationCrossCheck); gReports.incr("OpenBTS.Starts"); gNeighborTable.NeighborTableInit( gConfig.getStr("Peering.NeighborTable.Path").c_str()); int sock = socket(AF_UNIX,SOCK_DGRAM,0); if (sock<0) { perror("creating CLI datagram socket"); LOG(ALERT) << "cannot create socket for CLI"; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); LOG(ALERT) << "OpenBTS (re)starting, ver " << VERSION << " build date " << __DATE__; LOG(ALERT) << "OpenBTS reading config file "<<cOpenBTSConfigFile; COUT("\n\n" << gOpenBTSWelcome << "\n"); Control::controlInit(); // init Layer3: TMSITable, TransactionTable. gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gParser.addCommands(); COUT("\nStarting the system..."); // is the radio running? // Start the transceiver interface. LOG(INFO) << "checking transceiver"; //gTRX.ARFCN(0)->powerOn(); //sleep(gConfig.getNum("TRX.Timeout.Start")); //bool haveTRX = gTRX.ARFCN(0)->powerOn(false); This prints an inapplicable warning message. bool haveTRX = gTRX.ARFCN(0)->trxRunning(); // This does not print an inapplicable warning message. Thread transceiverThread; if (!haveTRX) { LOG(ALERT) << "starting the transceiver"; transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // sleep to let the FPGA code load // TODO: we should be "pinging" the radio instead of sleeping sleep(5); } else { LOG(NOTICE) << "transceiver already running"; } // Start the SIP interface. SIP::SIPInterfaceStart(); // Start the peer interface gPeerInterface.start(); // Sync factory calibration as defaults from radio EEPROM signed sdrsn = gTRX.ARFCN(0)->getFactoryCalibration("sdrsn"); if (sdrsn != 0 && sdrsn != 65535) { signed val; val = gTRX.ARFCN(0)->getFactoryCalibration("band"); if (gConfig.isValidValue("GSM.Radio.Band", val)) { gConfig.mSchema["GSM.Radio.Band"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("freq"); if (gConfig.isValidValue("TRX.RadioFrequencyOffset", val)) { gConfig.mSchema["TRX.RadioFrequencyOffset"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("rxgain"); if (gConfig.isValidValue("GSM.Radio.RxGain", val)) { gConfig.mSchema["GSM.Radio.RxGain"].updateDefaultValue(val); } val = gTRX.ARFCN(0)->getFactoryCalibration("txgain"); if (gConfig.isValidValue("TRX.TxAttenOffset", val)) { gConfig.mSchema["TRX.TxAttenOffset"].updateDefaultValue(val); } } // Limit valid ARFCNs to current band gConfig.mSchema["GSM.Radio.C0"].updateValidValues(getARFCNsString(gConfig.getNum("GSM.Radio.Band"))); // // Configure the radio. // gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. //C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); unsigned numARFCNs = gConfig.getNum("GSM.Radio.ARFCNs"); for (unsigned i=0; i<numARFCNs; i++) { // Tune the radios. unsigned ARFCN = C0 + i*2; LOG(INFO) << "tuning TRX " << i << " to ARFCN " << ARFCN; ARFCNManager* radio = gTRX.ARFCN(i); radio->tune(ARFCN); } // Send either TSC or full BSIC depending on radio need if (gConfig.getBool("GSM.Radio.NeedBSIC")) { // Send BSIC to C0radio->setBSIC(gBTS.BSIC()); } else { // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); } // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(true); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs // (pat) I thought config 'GSM.CCCH.CCCH-CONF' was supposed to control the number of SDCCH allocated? SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,0,gSDCCH_4_0), SDCCHLogicalChannel(0,0,gSDCCH_4_1), SDCCHLogicalChannel(0,0,gSDCCH_4_2), SDCCHLogicalChannel(0,0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; // Subchannel 2 used for CBCH if SMSCB enabled. bool SMSCB = (gConfig.getStr("Control.SMSCB.Table").length() != 0); CBCHLogicalChannel CBCH(gSDCCH_4_2); Thread CBCHControlThread; for (int i=0; i<4; i++) { if (SMSCB && (i==2)) continue; C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // Install CBCH if used. if (SMSCB) { LOG(INFO) << "creating CBCH for SMSCB"; CBCH.downstream(C0radio); CBCH.open(); gBTS.addCBCH(&CBCH); CBCHControlThread.start((void*(*)(void*))Control::SMSCBSender,NULL); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (!gConfig.defines("GSM.Channels.NumC1s")) { int numChan = numARFCNs*7; LOG(CRIT) << "GSM.Channels.NumC1s not defined. Defaulting to " << numChan << "."; gConfig.set("GSM.Channels.NumC1s",numChan); } if (!gConfig.defines("GSM.Channels.NumC7s")) { int numChan = numARFCNs-1; LOG(CRIT) << "GSM.Channels.NumC7s not defined. Defaulting to " << numChan << "."; gConfig.set("GSM.Channels.NumC7s",numChan); } // sanity check on channel counts // the clamp here could be improved to take the customer's current ratio of C1:C7 and scale it back to fit in the window if (((numARFCNs * 8) - 1) < (gConfig.getNum("GSM.Channels.NumC1s") + gConfig.getNum("GSM.Channels.NumC7s"))) { LOG(CRIT) << "scaling back GSM.Channels.NumC1s and GSM.Channels.NumC7s to fit inside number of available timeslots"; gConfig.set("GSM.Channels.NumC1s",numARFCNs*7); gConfig.set("GSM.Channels.NumC7s",numARFCNs-1); } if (gConfig.getBool("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount/8,sCount%8); sCount++; } if (!gConfig.getBool("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount/8,sCount%8); sCount++; } } if (sCount<(numARFCNs*8)) { LOG(CRIT) << "Only " << sCount << " timeslots configured in an " << numARFCNs << "-ARFCN system."; } // Set up idle filling on C0 as needed for unconfigured slots.. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* (pat) See GSM 05.02 6.5.2 and 3.3.2.3 Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. if (gConfig.getNum("GSM.Channels.SDCCHReserve")>=(int)gBTS.SDCCHTotal()) { unsigned val = gBTS.SDCCHTotal() - 1; LOG(CRIT) << "GSM.Channels.SDCCHReserve too big, changing to " << val; gConfig.set("GSM.Channels.SDCCHReserve",val); } // OK, now it is safe to start the BTS. gBTS.start(); struct sockaddr_un cmdSockName; cmdSockName.sun_family = AF_UNIX; const char* sockpath = gConfig.getStr("CLI.SocketPath").c_str(); char rmcmd[strlen(sockpath)+5]; sprintf(rmcmd,"rm -f %s",sockpath); if (system(rmcmd)) {} // The 'if' shuts up gcc warnings. strcpy(cmdSockName.sun_path,sockpath); LOG(INFO) "binding CLI datagram socket at " << sockpath; if (bind(sock, (struct sockaddr *) &cmdSockName, sizeof(struct sockaddr_un))) { perror("binding name to cmd datagram socket"); LOG(ALERT) << "cannot bind socket for CLI at " << sockpath; gReports.incr("OpenBTS.Exit.CLI.Socket"); exit(1); } COUT("\nsystem ready\n"); if (chmod(sockpath, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) { perror("sockpath"); // don't exit, at this point, we must run CLI as root COUT("\nuse the OpenBTSCLI utility to access CLI as root\n"); } else { COUT("\nuse the OpenBTSCLI utility to access CLI\n"); } LOG(INFO) << "system ready"; gParser.startCommandLine(); while (1) { char cmdbuf[1000]; struct sockaddr_un source; socklen_t sourceSize = sizeof(source); int nread = recvfrom(sock,cmdbuf,sizeof(cmdbuf)-1,0,(struct sockaddr*)&source,&sourceSize); gReports.incr("OpenBTS.CLI.Command"); cmdbuf[nread]='\0'; LOG(INFO) << "received command \"" << cmdbuf << "\" from " << source.sun_path; std::ostringstream sout; int res = gParser.process(cmdbuf,sout); const std::string rspString= sout.str(); const char* rsp = rspString.c_str(); LOG(INFO) << "sending " << strlen(rsp) << "-char result to " << source.sun_path; if (sendto(sock,rsp,strlen(rsp)+1,0,(struct sockaddr*)&source,sourceSize)<0) { LOG(ERR) << "can't send CLI response to " << source.sun_path; gReports.incr("OpenBTS.CLI.Command.ResponseFailure"); } // res<0 means to exit the application if (res<0) break; } } // try catch (ConfigurationTableKeyNotFound e) { LOG(EMERG) << "required configuration parameter " << e.key() << " not defined, aborting"; gReports.incr("OpenBTS.Exit.Error.ConfigurationParamterNotFound"); } LOG(ALERT) << "exiting OpenBTS as directed by command line..."; //if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); close(sock); }
int main(int argc, char *argv[]) { srandom(time(NULL)); COUT("\n\n" << gOpenBTSWelcome << "\n"); COUT("\nStarting the system..."); gSetLogLevel(gConfig.getStr("LogLevel")); if (gConfig.defines("LogFileName")) { gSetLogFile(gConfig.getStr("LogFileName")); } // Start the transceiver binary, if the path is defined. // If the path is not defined, the transceiver must be started by some other process. const char *TRXPath = NULL; if (gConfig.defines("TRX.Path")) TRXPath=gConfig.getStr("TRX.Path"); pid_t transceiverPid = 0; if (TRXPath) { const char *TRXLogLevel = gConfig.getStr("TRX.LogLevel"); const char *TRXLogFileName = NULL; if (gConfig.defines("TRX.LogFileName")) TRXLogFileName=gConfig.getStr("TRX.LogFileName"); transceiverPid = vfork(); assert(transceiverPid>=0); if (transceiverPid==0) { execl(TRXPath,"transceiver",TRXLogLevel,TRXLogFileName,NULL); LOG(ERROR) << "cannot start transceiver"; _exit(0); } } // Start the SIP interface. gSIPInterface.start(); // Start the transceiver interface. gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* radio = gTRX.ARFCN(0); // Tuning. // Make sure its off for tuning. radio->powerOff(); // Set TSC same as BSC everywhere. radio->setTSC(gBTS.BCC()); // Tune. radio->tune(gConfig.getNum("GSM.ARFCN")); // C-V on C0T0 radio->setSlot(0,5); // Turn on and power up. radio->powerOn(); radio->setPower(gConfig.getNum("GSM.PowerAttenDB")); // set up a combination V beacon set // SCH SCHL1FEC SCH; SCH.downstream(radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel SDCCH[4] = { SDCCHLogicalChannel(0,gSDCCH_4_0), SDCCHLogicalChannel(0,gSDCCH_4_1), SDCCHLogicalChannel(0,gSDCCH_4_2), SDCCHLogicalChannel(0,gSDCCH_4_3) }; Thread SDCCHControlThread[4]; for (int i=0; i<4; i++) { SDCCH[i].downstream(radio); SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&SDCCH[i]); SDCCH[i].open(); gBTS.addSDCCH(&SDCCH[i]); } // Count configured slots. unsigned sCount = 1; // Create C-VII slots on C0Tn for (unsigned i=0; i<gConfig.getNum("GSM.NumC7s"); i++) { radio->setSlot(sCount,7); for (unsigned sub=0; sub<8; sub++) { SDCCHLogicalChannel* chan = new SDCCHLogicalChannel(sCount,gSDCCH8[sub]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addSDCCH(chan); } sCount++; } // Create C-I slots on C0Tn for (unsigned i=0; i<gConfig.getNum("GSM.NumC1s"); i++) { radio->setSlot(sCount,1); TCHFACCHLogicalChannel* chan = new TCHFACCHLogicalChannel(sCount,gTCHF_T[sCount]); chan->downstream(radio); Thread* thread = new Thread; thread->start((void*(*)(void*))Control::DCCHDispatcher,chan); chan->open(); gBTS.addTCH(chan); sCount++; } assert(sCount<=8); /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. gBTS.addPCH(&CCCH2); // Start the paging generator // Don't start the pager until some PCHs exist!! gBTS.pager().start(); LOG(INFO) << "system ready"; COUT("\n\nWelcome to OpenBTS. Type \"help\" to see available commands."); // FIXME: We want to catch control-d (emacs keybinding for exit()) while (1) { char inbuf[1024]; cout << "\nOpenBTS> "; cin.getline(inbuf,1024,'\n'); if (strcmp(inbuf,"exit")==0) break; gParser.process(inbuf,cout,cin); } if (transceiverPid) kill(transceiverPid,SIGKILL); }
void PowerManager::start() { mRadio = gTRX.ARFCN(0); mRadio->setPower(mAtten); mThread.start((void*(*)(void*))PowerManagerServiceLoopAdapter,this); }
int main(int argc, char *argv[]) { try { srandom(time(NULL)); gConfig.setUpdateHook(purgeConfig); gLogInit("openbts",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7); LOG(ALERT) << "OpenBTS starting, ver " << VERSION << " build date " << __DATE__; COUT("\n\n" << gOpenBTSWelcome << "\n"); gTMSITable.open(gConfig.getStr("Control.Reporting.TMSITable").c_str()); gTransactionTable.init(); gPhysStatus.open(gConfig.getStr("Control.Reporting.PhysStatusTable").c_str()); gBTS.init(); gSubscriberRegistry.init(); gParser.addCommands(); COUT("\nStarting the system..."); Thread transceiverThread; transceiverThread.start((void*(*)(void*)) startTransceiver, NULL); // Start the SIP interface. gSIPInterface.start(); // // Configure the radio. // // Start the transceiver interface. // Sleep long enough for the USRP to bootload. sleep(5); gTRX.start(); // Set up the interface to the radio. // Get a handle to the C0 transceiver interface. ARFCNManager* C0radio = gTRX.ARFCN(); // Tuning. // Make sure its off for tuning. C0radio->powerOff(); // Get the ARFCN list. unsigned C0 = gConfig.getNum("GSM.Radio.C0"); // Tune the radio. LOG(INFO) << "tuning TRX to ARFCN " << C0; ARFCNManager* radio = gTRX.ARFCN(); radio->tune(C0); // Set TSC same as BCC everywhere. C0radio->setTSC(gBTS.BCC()); // Set maximum expected delay spread. C0radio->setMaxDelay(gConfig.getNum("GSM.Radio.MaxExpectedDelaySpread")); // Set Receiver Gain C0radio->setRxGain(gConfig.getNum("GSM.Radio.RxGain")); // Turn on and power up. C0radio->powerOn(); C0radio->setPower(gConfig.getNum("GSM.Radio.PowerManager.MinAttenDB")); // // Create a C-V channel set on C0T0. // // C-V on C0T0 C0radio->setSlot(0,5); // SCH SCHL1FEC SCH; SCH.downstream(C0radio); SCH.open(); // FCCH FCCHL1FEC FCCH; FCCH.downstream(C0radio); FCCH.open(); // BCCH BCCHL1FEC BCCH; BCCH.downstream(C0radio); BCCH.open(); // RACH RACHL1FEC RACH(gRACHC5Mapping); RACH.downstream(C0radio); RACH.open(); // CCCHs CCCHLogicalChannel CCCH0(gCCCH_0Mapping); CCCH0.downstream(C0radio); CCCH0.open(); CCCHLogicalChannel CCCH1(gCCCH_1Mapping); CCCH1.downstream(C0radio); CCCH1.open(); CCCHLogicalChannel CCCH2(gCCCH_2Mapping); CCCH2.downstream(C0radio); CCCH2.open(); // use CCCHs as AGCHs gBTS.addAGCH(&CCCH0); gBTS.addAGCH(&CCCH1); gBTS.addAGCH(&CCCH2); // C-V C0T0 SDCCHs SDCCHLogicalChannel C0T0SDCCH[4] = { SDCCHLogicalChannel(0,gSDCCH_4_0), SDCCHLogicalChannel(0,gSDCCH_4_1), SDCCHLogicalChannel(0,gSDCCH_4_2), SDCCHLogicalChannel(0,gSDCCH_4_3), }; Thread C0T0SDCCHControlThread[4]; for (int i=0; i<4; i++) { C0T0SDCCH[i].downstream(C0radio); C0T0SDCCHControlThread[i].start((void*(*)(void*))Control::DCCHDispatcher,&C0T0SDCCH[i]); C0T0SDCCH[i].open(); gBTS.addSDCCH(&C0T0SDCCH[i]); } // // Configure the other slots. // // Count configured slots. unsigned sCount = 1; if (gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount); sCount++; } } // Create C-VII slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC7s"); i++) { gBTS.createCombinationVII(gTRX,sCount); sCount++; } if (!gConfig.defines("GSM.Channels.C1sFirst")) { // Create C-I slots. for (int i=0; i<gConfig.getNum("GSM.Channels.NumC1s"); i++) { gBTS.createCombinationI(gTRX,sCount); sCount++; } } // Set up idle filling on C0 as needed. while (sCount<8) { gBTS.createCombination0(gTRX,sCount); sCount++; } /* Note: The number of different paging subchannels on the CCCH is: MAX(1,(3 - BS-AG-BLKS-RES)) * BS-PA-MFRMS if CCCH-CONF = "001" (9 - BS-AG-BLKS-RES) * BS-PA-MFRMS for other values of CCCH-CONF */ // Set up the pager. // Set up paging channels. // HACK -- For now, use a single paging channel, since paging groups are broken. gBTS.addPCH(&CCCH2); // Be sure we are not over-reserving. LOG_ASSERT(gConfig.getNum("GSM.CCCH.PCH.Reserve")<(int)gBTS.numAGCHs()); // OK, now it is safe to start the BTS. gBTS.start(); #ifdef HAVE_LIBREADLINE // [ // start console using_history(); static const char * const history_file_name = "/.openbts_history"; char *history_name = 0; char *home_dir = getenv("HOME"); if(home_dir) { size_t home_dir_len = strlen(home_dir); size_t history_file_len = strlen(history_file_name); size_t history_len = home_dir_len + history_file_len + 1; if(history_len > home_dir_len) { if(!(history_name = (char *)malloc(history_len))) { LOG(ERR) << "malloc failed: " << strerror(errno); exit(2); } memcpy(history_name, home_dir, home_dir_len); memcpy(history_name + home_dir_len, history_file_name, history_file_len + 1); read_history(history_name); } } #endif // HAVE_LIBREADLINE ] LOG(INFO) << "system ready"; COUT("\n\nWelcome to OpenBTS. Type \"help\" to see available commands."); // FIXME: We want to catch control-d (emacs keybinding for exit()) // The logging parts were removed from this loop. // If we want them back, they will need to go into their own thread. while (1) { #ifdef HAVE_LIBREADLINE // [ char *inbuf = readline(gConfig.getStr("CLI.Prompt").c_str()); if (!inbuf) break; if (*inbuf) { add_history(inbuf); // The parser returns -1 on exit. if (gParser.process(inbuf, cout, cin)<0) { free(inbuf); break; } } free(inbuf); #else // HAVE_LIBREADLINE ][ cout << endl << gConfig.getStr("CLI.Prompt"); cout.flush(); char inbuf[1024]; cin.getline(inbuf,1024,'\n'); // The parser returns -1 on exit. if (gParser.process(inbuf,cout,cin)<0) break; #endif // !HAVE_LIBREADLINE ] } #ifdef HAVE_LIBREADLINE // [ if(history_name) { int e = write_history(history_name); if(e) { fprintf(stderr, "error: history: %s\n", strerror(e)); } free(history_name); history_name = 0; } #endif // HAVE_LIBREADLINE ] if (gTransceiverPid) kill(gTransceiverPid, SIGKILL); } catch (ConfigurationTableKeyNotFound e) { LOG(ALERT) << "configuration key " << e.key() << " not defined"; exit(2); } }