#include #include #include #include #include #define APP_WORKERS_DIRECT_NUM 1 #define APP_SYSLOG_IDENTIFIER "beegfs-fsck" App::App(int argc, char** argv) { this->argc = argc; this->argv = argv; this->appResult = APPCODE_NO_ERROR; this->cfg = NULL; this->netFilter = NULL; this->tcpOnlyFilter = NULL; this->log = NULL; this->mgmtNodes = NULL; this->metaNodes = NULL; this->storageNodes = NULL; this->internodeSyncer = NULL; this->targetMapper = NULL; this->targetStateStore = NULL; this->buddyGroupMapper = NULL; this->metaBuddyGroupMapper = NULL; this->workQueue = NULL; this->ackStore = NULL; this->netMessageFactory = NULL; this->dgramListener = NULL; this->modificationEventHandler = NULL; this->runMode = NULL; } App::~App() { // Note: Logging of the common lib classes is not working here, because this is called // from class Program (so the thread-specific app-pointer isn't set in this context). workersDelete(); SAFE_DELETE(this->dgramListener); SAFE_DELETE(this->netMessageFactory); SAFE_DELETE(this->ackStore); SAFE_DELETE(this->workQueue); SAFE_DELETE(this->storageNodes); SAFE_DELETE(this->metaNodes); SAFE_DELETE(this->mgmtNodes); this->localNode.reset(); SAFE_DELETE(this->buddyGroupMapper); SAFE_DELETE(this->metaBuddyGroupMapper); SAFE_DELETE(this->targetStateStore); SAFE_DELETE(this->targetMapper); SAFE_DELETE(this->internodeSyncer); SAFE_DELETE(this->log); SAFE_DELETE(this->tcpOnlyFilter); SAFE_DELETE(this->netFilter); SAFE_DELETE(this->cfg); SAFE_DELETE(this->runMode); Logger::destroyLogger(); closelog(); } void App::run() { try { openlog(APP_SYSLOG_IDENTIFIER, LOG_NDELAY | LOG_PID | LOG_CONS, LOG_DAEMON); this->cfg = new Config(argc, argv); runNormal(); } catch (InvalidConfigException& e) { std::cerr << std::endl; std::cerr << "Error: " << e.what() << std::endl; std::cerr << std::endl; std::cerr << std::endl; if(this->log) log->logErr(e.what() ); this->appResult = APPCODE_INVALID_CONFIG; return; } catch (std::exception& e) { std::cerr << std::endl; std::cerr << "Unrecoverable error: " << e.what() << std::endl; std::cerr << std::endl; if(this->log) log->logErr(e.what() ); appResult = APPCODE_RUNTIME_ERROR; return; } } void App::runNormal() { bool componentsStarted = false; auto runModeEnum = Program::getApp()->getConfig()->determineRunMode(); if (runModeEnum == RunMode_CHECKFS || runModeEnum == RunMode_ENABLEQUOTA) { // check if running as root if(geteuid()) { std::cerr << std::endl << "Running beegfs-fsck requires root privileges." << std::endl << std::endl << std::endl; ModeHelp().execute(); appResult = APPCODE_INITIALIZATION_ERROR; return; } if (runModeEnum == RunMode_CHECKFS) runMode = new ModeCheckFS(); else if (runModeEnum == RunMode_ENABLEQUOTA) runMode = new ModeEnableQuota(); } else if (runModeEnum == RunMode_HELP) { appResult = ModeHelp().execute(); return; } else { ModeHelp().execute(); appResult = APPCODE_INVALID_CONFIG; return; } initDataObjects(argc, argv); // wait for mgmtd if ( !cfg->getSysMgmtdHost().length() ) throw InvalidConfigException("Management host undefined"); bool mgmtWaitRes = waitForMgmtNode(); if(!mgmtWaitRes) { // typically user just pressed ctrl+c in this case log->logErr("Waiting for beegfs-mgmtd canceled"); appResult = APPCODE_RUNTIME_ERROR; return; } // init components try { initComponents(); } catch (ComponentInitException& e) { FsckTkEx::fsckOutput(e.what(), OutputOptions_DOUBLELINEBREAK | OutputOptions_STDERR); FsckTkEx::fsckOutput("A hard error occurred. BeeGFS Fsck will abort.", OutputOptions_LINEBREAK | OutputOptions_STDERR); appResult = APPCODE_INITIALIZATION_ERROR; return; } // log system and configuration info logInfos(); // start component threads startComponents(); componentsStarted = true; try { appResult = runMode->execute(); } catch (InvalidConfigException& e) { ModeHelp modeHelp; modeHelp.execute(); appResult = APPCODE_INVALID_CONFIG; } catch (std::exception &e) { FsckTkEx::fsckOutput("Unrecoverable error. BeeGFS Fsck will abort.", OutputOptions_LINEBREAK | OutputOptions_ADDLINEBREAKBEFORE | OutputOptions_STDERR); FsckTkEx::fsckOutput(e.what(), OutputOptions_LINEBREAK | OutputOptions_STDERR); } // self-termination if(componentsStarted) { stopComponents(); joinComponents(); } } void App::initDataObjects(int argc, char** argv) { this->netFilter = new NetFilter(cfg->getConnNetFilterFile() ); this->tcpOnlyFilter = new NetFilter(cfg->getConnTcpOnlyFilterFile() ); Logger::createLogger(cfg->getLogLevel(), cfg->getLogType(), cfg->getLogNoDate(), cfg->getLogStdFile(), cfg->getLogNumLines(), cfg->getLogNumRotatedFiles()); this->log = new LogContext("App"); std::string interfacesFilename = this->cfg->getConnInterfacesFile(); if ( interfacesFilename.length() ) Config::loadStringListFile(interfacesFilename.c_str(), this->allowedInterfaces); this->targetMapper = new TargetMapper(); this->targetStateStore = new TargetStateStore(NODETYPE_Storage); this->buddyGroupMapper = new MirrorBuddyGroupMapper(targetMapper); this->metaBuddyGroupMapper = new MirrorBuddyGroupMapper(); this->mgmtNodes = new NodeStoreServers(NODETYPE_Mgmt, false); this->metaNodes = new NodeStoreServers(NODETYPE_Meta, false); this->storageNodes = new NodeStoreServers(NODETYPE_Storage, false); this->workQueue = new MultiWorkQueue(); this->ackStore = new AcknowledgmentStore(); initLocalNodeInfo(); registerSignalHandler(); this->netMessageFactory = new NetMessageFactory(); } void App::findAllowedRDMAInterfaces(NicAddressList& outList) const { Config* cfg = this->getConfig(); if(cfg->getConnUseRDMA() && RDMASocket::rdmaDevicesExist() ) { bool foundRdmaInterfaces = NetworkInterfaceCard::checkAndAddRdmaCapability(outList); if (foundRdmaInterfaces) outList.sort(NetworkInterfaceCard::NicAddrComp{&allowedInterfaces}); // re-sort the niclist } } void App::findAllowedInterfaces(NicAddressList& outList) const { // discover local NICs and filter them NetworkInterfaceCard::findAllInterfaces(allowedInterfaces, outList); outList.sort(NetworkInterfaceCard::NicAddrComp{&allowedInterfaces}); } void App::initLocalNodeInfo() { findAllowedInterfaces(localNicList); findAllowedRDMAInterfaces(localNicList); if ( this->localNicList.empty() ) throw InvalidConfigException("Couldn't find any usable NIC"); initRoutingTable(); updateRoutingTable(); std::string nodeID = System::getHostname(); this->localNode = std::make_shared(NODETYPE_Client, nodeID, NumNodeID(), 0, 0, this->localNicList); } void App::initComponents() { this->log->log(Log_DEBUG, "Initializing components..."); // Note: We choose a random udp port here to avoid conflicts with the client unsigned short udpListenPort = 0; this->dgramListener = new DatagramListener(netFilter, localNicList, ackStore, udpListenPort, this->cfg->getConnRestrictOutboundInterfaces()); // update the local node info with udp port this->localNode->updateInterfaces(dgramListener->getUDPPort(), 0, this->localNicList); this->internodeSyncer = new InternodeSyncer(); workersInit(); this->log->log(Log_DEBUG, "Components initialized."); } void App::startComponents() { log->log(Log_SPAM, "Starting up components..."); // make sure child threads don't receive SIGINT/SIGTERM (blocked signals are inherited) PThread::blockInterruptSignals(); dgramListener->start(); internodeSyncer->start(); internodeSyncer->waitForServers(); workersStart(); PThread::unblockInterruptSignals(); // main app thread may receive SIGINT/SIGTERM log->log(Log_DEBUG, "Components running."); } void App::stopComponents() { // note: this method may not wait for termination of the components, because that could // lead to a deadlock (when calling from signal handler) workersStop(); if(this->internodeSyncer) this->internodeSyncer->selfTerminate(); if ( dgramListener ) { dgramListener->selfTerminate(); dgramListener->sendDummyToSelfUDP(); } this->selfTerminate(); } void App::joinComponents() { log->log(4, "Joining component threads..."); this->internodeSyncer->join(); /* (note: we need one thread for which we do an untimed join, so this should be a quite reliably terminating thread) */ this->dgramListener->join(); workersJoin(); } void App::workersInit() { unsigned numWorkers = cfg->getTuneNumWorkers(); for(unsigned i=0; i < numWorkers; i++) { Worker* worker = new Worker( std::string("Worker") + StringTk::intToStr(i+1), workQueue, QueueWorkType_INDIRECT); workerList.push_back(worker); } for(unsigned i=0; i < APP_WORKERS_DIRECT_NUM; i++) { Worker* worker = new Worker( std::string("DirectWorker") + StringTk::intToStr(i+1), workQueue, QueueWorkType_DIRECT); workerList.push_back(worker); } } void App::workersStart() { for ( WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++ ) { (*iter)->start(); } } void App::workersStop() { // need two loops because we don't know if the worker that handles the work will be the same that // received the self-terminate-request for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++) { (*iter)->selfTerminate(); } for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++) { workQueue->addDirectWork(new DummyWork() ); } } void App::workersDelete() { for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++) { delete(*iter); } workerList.clear(); } void App::workersJoin() { for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++) { waitForComponentTermination(*iter); } } void App::logInfos() { // print software version (BEEGFS_VERSION) log->log(Log_NOTICE, std::string("Version: ") + BEEGFS_VERSION); // print debug version info LOG_DEBUG_CONTEXT(*log, Log_CRITICAL, "--DEBUG VERSION--"); // print local nodeID log->log(Log_NOTICE, std::string("LocalNode: ") + localNode->getTypedNodeID() ); // list usable network interfaces NicAddressList nicList(localNode->getNicList()); logUsableNICs(log, nicList); // print net filters if ( netFilter->getNumFilterEntries() ) { this->log->log(2, std::string("Net filters: ") + StringTk::uintToStr(netFilter->getNumFilterEntries())); } if(tcpOnlyFilter->getNumFilterEntries() ) { this->log->log(Log_WARNING, std::string("TCP-only filters: ") + StringTk::uintToStr(tcpOnlyFilter->getNumFilterEntries() ) ); } } /** * Request mgmt heartbeat and wait for the mgmt node to appear in nodestore. * * @return true if mgmt heartbeat received, false on error or thread selftermination order */ bool App::waitForMgmtNode() { const unsigned waitTimeoutMS = 0; // infinite wait const unsigned nameResolutionRetries = 3; // choose a random udp port here unsigned udpListenPort = 0; unsigned udpMgmtdPort = cfg->getConnMgmtdPort(); std::string mgmtdHost = cfg->getSysMgmtdHost(); RegistrationDatagramListener regDGramLis(this->netFilter, this->localNicList, this->ackStore, udpListenPort, this->cfg->getConnRestrictOutboundInterfaces()); regDGramLis.start(); log->log(Log_CRITICAL, "Waiting for beegfs-mgmtd@" + mgmtdHost + ":" + StringTk::uintToStr(udpMgmtdPort) + "..."); bool gotMgmtd = NodesTk::waitForMgmtHeartbeat( this, ®DGramLis, this->mgmtNodes, mgmtdHost, udpMgmtdPort, waitTimeoutMS, nameResolutionRetries); regDGramLis.selfTerminate(); regDGramLis.sendDummyToSelfUDP(); // for faster termination regDGramLis.join(); return gotMgmtd; } void App::updateLocalNicList(NicAddressList& localNicList) { std::vector allNodes({ mgmtNodes, metaNodes, storageNodes}); updateLocalNicListAndRoutes(log, localNicList, allNodes); localNode->updateInterfaces(0, 0, localNicList); dgramListener->setLocalNicList(localNicList); } /* * Handles expections that lead to the termination of a component. * Initiates an application shutdown. */ void App::handleComponentException(std::exception& e) { const char* logContext = "App (component exception handler)"; LogContext log(logContext); const auto componentName = PThread::getCurrentThreadName(); log.logErr( "The component [" + componentName + "] encountered an unrecoverable error. " + std::string("[SysErr: ") + System::getErrString() + "] " + std::string("Exception message: ") + e.what() ); log.log(2, "Shutting down..."); shallAbort.set(1); stopComponents(); } void App::handleNetworkInterfaceFailure(const std::string& devname) { LOG(GENERAL, ERR, "Network interface failure.", ("Device", devname)); internodeSyncer->setForceCheckNetwork(); } void App::registerSignalHandler() { signal(SIGINT, App::signalHandler); signal(SIGTERM, App::signalHandler); } void App::signalHandler(int sig) { App* app = Program::getApp(); Logger* log = Logger::getLogger(); const char* logContext = "App::signalHandler"; // note: this might deadlock if the signal was thrown while the logger mutex is locked by the // application thread (depending on whether the default mutex style is recursive). but // even recursive mutexes are not acceptable in this case. // we need something like a timed lock for the log mutex. if it succeeds within a // few seconds, we know that we didn't hold the mutex lock. otherwise we simply skip the // log message. this will only work if the mutex is non-recusive (which is unknown for // the default mutex style). // but it is very unlikely that the application thread holds the log mutex, because it // joins the component threads and so it doesn't do anything else but sleeping! switch(sig) { case SIGINT: { signal(sig, SIG_DFL); // reset the handler to its default log->log(1, logContext, "Received a SIGINT. Shutting down..."); } break; case SIGTERM: { signal(sig, SIG_DFL); // reset the handler to its default log->log(1, logContext, "Received a SIGTERM. Shutting down..."); } break; default: { signal(sig, SIG_DFL); // reset the handler to its default log->log(1, logContext, "Received an unknown signal. Shutting down..."); } break; } app->abort(); } void App::abort() { shallAbort.set(1); stopComponents(); }