// // Copyright (c) ZeroC, Inc. All rights reserved. // #include #include #include #include #ifdef _WIN32 const DWORD SIGHUP = CTRL_LOGOFF_EVENT; #else # include #endif using namespace std; using namespace Ice; using namespace IceInternal; using namespace IceUtil; using namespace IceUtilInternal; // // static member initialization // IceUtil::Mutex Ice::Application::_mutex; IceUtil::Cond Ice::Application::_condVar; bool Ice::Application::_callbackInProgress = false; bool Ice::Application::_destroyed = false; bool Ice::Application::_interrupted = false; string Ice::Application::_appName; Ice::CommunicatorPtr Ice::Application::_communicator; Ice::SignalPolicy Ice::Application::_signalPolicy = ICE_ENUM(SignalPolicy, HandleSignals); Ice::Application* Ice::Application::_application = 0; namespace { // // Variables than can change while run() and communicator->destroy() are running! // bool _released = true; CtrlCHandlerCallback _previousCallback = ICE_NULLPTR; // // Variables that are immutable during run() and until communicator->destroy() has returned; // before and after run(), and once communicator->destroy() has returned, we assume that // only the main thread and CtrlCHandler threads are running. // CtrlCHandler* _ctrlCHandler = 0; bool _nohup = false; } Ice::Application::Application(SignalPolicy signalPolicy) { Ice::Application::_signalPolicy = signalPolicy; } Ice::Application::~Application() { } int Ice::Application::main(int argc, const char* const argv[], ICE_CONFIG_FILE_STRING configFile, int version) { _appName = ""; if(argc > 0) { _appName = argv[0]; } if(argc > 0 && argv[0] && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger())) { setProcessLogger(ICE_MAKE_SHARED(LoggerI, argv[0], "", true)); } InitializationData initData; #ifndef ICE_CPP11_MAPPING if(configFile) #endif { try { initData.properties = createProperties(); initData.properties->load(configFile); } catch(const Ice::Exception& ex) { Error out(getProcessLogger()); out << ex; return EXIT_FAILURE; } catch(const std::exception& ex) { Error out(getProcessLogger()); out << ex; return EXIT_FAILURE; } catch(...) { Error out(getProcessLogger()); out << "unknown exception"; return EXIT_FAILURE; } } return main(argc, argv, initData, version); } #ifdef _WIN32 int Ice::Application::main(int argc, const wchar_t* const argv[], const Ice::InitializationData& initData, int version) { // // On Windows the given wchar_t* strings are UTF16 and therefore // needs to be converted to native narrow string encoding. // return main(argsToStringSeq(argc, argv), initData, version); } int Ice::Application::main(int argc, const wchar_t* const argv[], ICE_CONFIG_FILE_STRING config, int version) { return main(argsToStringSeq(argc, argv), config, version); } #endif int Ice::Application::main(int argc, const char* const argv[], const InitializationData& initializationData, int version) { if(argc > 0 && argv[0] && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger())) { const bool convert = initializationData.properties ? initializationData.properties->getPropertyAsIntWithDefault("Ice.LogStdErr.Convert", 1) > 0 && initializationData.properties->getProperty("Ice.StdErr").empty() : true; setProcessLogger(ICE_MAKE_SHARED(LoggerI, argv[0], "", convert)); } if(_communicator != 0) { Error out(getProcessLogger()); out << "only one instance of the Application class can be used"; return EXIT_FAILURE; } int status; ArgVector av(argc, argv); // copy args // // We parse the properties here to extract Ice.ProgramName. // InitializationData initData = initializationData; try { initData.properties = createProperties(av.argc, av.argv, initData.properties); } catch(const Ice::Exception& ex) { Error out(getProcessLogger()); out << ex; return EXIT_FAILURE; } catch(const std::exception& ex) { Error out(getProcessLogger()); out << ex; return EXIT_FAILURE; } catch(...) { Error out(getProcessLogger()); out << "unknown exception"; return EXIT_FAILURE; } _appName = initData.properties->getPropertyWithDefault("Ice.ProgramName", _appName); // // Used by destroyOnInterruptCallback and shutdownOnInterruptCallback. // _nohup = initData.properties->getPropertyAsInt("Ice.Nohup") > 0; _application = this; if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { try { // // The ctrlCHandler must be created before starting any thread, in particular // before initializing the communicator. // CtrlCHandler ctrCHandler; _ctrlCHandler = &ctrCHandler; status = doMain(av.argc, av.argv, initData, version); // // Set _ctrlCHandler to 0 only once communicator->destroy() has completed. // _ctrlCHandler = 0; } catch(const CtrlCHandlerException&) { Error out(getProcessLogger()); out << "only one instance of the CtrlCHandler class can be used"; status = EXIT_FAILURE; } } else { status = doMain(av.argc, av.argv, initData, version); } return status; } int Ice::Application::main(const StringSeq& args, const InitializationData& initData, int version) { ArgVector av(args); return main(av.argc, av.argv, initData, version); } int Ice::Application::main(const StringSeq& args, ICE_CONFIG_FILE_STRING configFile, int version) { ArgVector av(args); return main(av.argc, av.argv, configFile, version); } void Ice::Application::interruptCallback(int) { } const char* Ice::Application::appName() { return _appName.c_str(); } CommunicatorPtr Ice::Application::communicator() { return _communicator; } void Ice::Application::destroyOnInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(!_released) { _released = true; _condVar.signal(); } _ctrlCHandler->setCallback(destroyOnInterruptCallback); } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } void Ice::Application::shutdownOnInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(!_released) { _released = true; _condVar.signal(); } _ctrlCHandler->setCallback(shutdownOnInterruptCallback); } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } void Ice::Application::ignoreInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(!_released) { _released = true; _condVar.signal(); } _ctrlCHandler->setCallback(0); } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } void Ice::Application::callbackOnInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(!_released) { _released = true; _condVar.signal(); } _ctrlCHandler->setCallback(callbackOnInterruptCallback); } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } void Ice::Application::holdInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(_released) { _released = false; _previousCallback = _ctrlCHandler->setCallback(holdInterruptCallback); } // else, we were already holding signals } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } void Ice::Application::releaseInterrupt() { if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { if(_ctrlCHandler != 0) { Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting if(!_released) { // // Note that it's very possible no signal is held; // in this case the callback is just replaced and // setting _released to true and signalling _condVar // do no harm. // _released = true; _ctrlCHandler->setCallback(_previousCallback); _condVar.signal(); } // Else nothing to release. } } else { Warning out(getProcessLogger()); out << "interrupt method called on Application configured to not handle interrupts."; } } bool Ice::Application::interrupted() { Mutex::Lock lock(_mutex); return _interrupted; } int Ice::Application::doMain(int argc, char* argv[], const InitializationData& initData, int version) { int status; try { _interrupted = false; // // If the process logger is the default logger, we now replace it with a // a logger which is using the program name for the prefix. // if(initData.properties->getProperty("Ice.ProgramName") != "" && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger())) { const bool convert = initData.properties->getPropertyAsIntWithDefault("Ice.LogStdErr.Convert", 1) > 0 && initData.properties->getProperty("Ice.StdErr").empty(); setProcessLogger(ICE_MAKE_SHARED(LoggerI, initData.properties->getProperty("Ice.ProgramName"), "", convert)); } _communicator = initialize(argc, argv, initData, version); _destroyed = false; // // The default is to destroy when a signal is received. // if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { destroyOnInterrupt(); } status = run(argc, argv); } catch(const Ice::Exception& ex) { Error out(getProcessLogger()); out << ex; status = EXIT_FAILURE; } catch(const std::exception& ex) { Error out(getProcessLogger()); out << ex; status = EXIT_FAILURE; } catch(const std::string& msg) { Error out(getProcessLogger()); out << msg; status = EXIT_FAILURE; } catch(const char* msg) { Error out(getProcessLogger()); out << msg; status = EXIT_FAILURE; } catch(...) { Error out(getProcessLogger()); out << "unknown exception"; status = EXIT_FAILURE; } // // Don't want any new interrupt and at this point (post-run), // it would not make sense to release a held signal to run // shutdown or destroy. // if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals)) { ignoreInterrupt(); } { Mutex::Lock lock(_mutex); while(_callbackInProgress) { _condVar.wait(lock); } if(_destroyed) { _communicator = 0; } else { _destroyed = true; // // And _communicator != 0, meaning will be destroyed // next, _destroyed = true also ensures that any // remaining callback won't do anything // } _application = 0; } if(_communicator != 0) { _communicator->destroy(); _communicator = 0; } return status; } // // CtrlCHandler callbacks. // void Ice::Application::holdInterruptCallback(int signal) { CtrlCHandlerCallback callback = ICE_NULLPTR; { Mutex::Lock lock(_mutex); while(!_released) { _condVar.wait(lock); } if(_destroyed) { // // Being destroyed by main thread // return; } assert(_ctrlCHandler != 0); callback = _ctrlCHandler->getCallback(); } if(callback) { callback(signal); } } void Ice::Application::destroyOnInterruptCallback(int signal) { { Mutex::Lock lock(_mutex); if(_destroyed) { // // Being destroyed by main thread // return; } if(_nohup && signal == static_cast(SIGHUP)) { return; } assert(!_callbackInProgress); _callbackInProgress = true; _interrupted = true; _destroyed = true; } assert(_communicator != 0); _communicator->destroy(); { Mutex::Lock lock(_mutex); _callbackInProgress = false; } _condVar.signal(); } void Ice::Application::shutdownOnInterruptCallback(int signal) { { Mutex::Lock lock(_mutex); if(_destroyed) { // // Being destroyed by main thread // return; } if(_nohup && signal == static_cast(SIGHUP)) { return; } assert(!_callbackInProgress); _callbackInProgress = true; _interrupted = true; } assert(_communicator != 0); _communicator->shutdown(); { Mutex::Lock lock(_mutex); _callbackInProgress = false; } _condVar.signal(); } void Ice::Application::callbackOnInterruptCallback(int signal) { { Mutex::Lock lock(_mutex); if(_destroyed) { // // Being destroyed by main thread // return; } // For SIGHUP the user callback is always called. It can // decide what to do. assert(!_callbackInProgress); _callbackInProgress = true; _interrupted = true; } try { assert(_application != 0); _application->interruptCallback(signal); } catch(const Ice::Exception& ex) { Error out(getProcessLogger()); out << "(while interrupting in response to signal " << signal << "): Ice::Exception: " << ex; } catch(const std::exception& ex) { Error out(getProcessLogger()); out << "(while interrupting in response to signal " << signal << "): std::exception: " << ex; } catch(const std::string& msg) { Error out(getProcessLogger()); out << "(while interrupting in response to signal " << signal << "): " << msg; } catch(const char* msg) { Error out(getProcessLogger()); out << "(while interrupting in response to signal " << signal << "): " << msg; } catch(...) { Error out(getProcessLogger()); out << "(while interrupting in response to signal " << signal << "): unknown exception"; } { Mutex::Lock lock(_mutex); _callbackInProgress = false; } _condVar.signal(); }