1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Ice/Application.h>
6 #include <Ice/LoggerI.h>
7 #include <Ice/LoggerUtil.h>
8 #include <Ice/ArgVector.h>
9 
10 #ifdef _WIN32
11 const DWORD SIGHUP = CTRL_LOGOFF_EVENT;
12 #else
13 #   include <csignal>
14 #endif
15 
16 using namespace std;
17 using namespace Ice;
18 using namespace IceInternal;
19 using namespace IceUtil;
20 using namespace IceUtilInternal;
21 
22 //
23 // static member initialization
24 //
25 IceUtil::Mutex Ice::Application::_mutex;
26 IceUtil::Cond Ice::Application::_condVar;
27 
28 bool Ice::Application::_callbackInProgress = false;
29 bool Ice::Application::_destroyed = false;
30 bool Ice::Application::_interrupted = false;
31 
32 string Ice::Application::_appName;
33 Ice::CommunicatorPtr Ice::Application::_communicator;
34 Ice::SignalPolicy Ice::Application::_signalPolicy = ICE_ENUM(SignalPolicy, HandleSignals);
35 Ice::Application* Ice::Application::_application = 0;
36 
37 namespace
38 {
39 
40 //
41 // Variables than can change while run() and communicator->destroy() are running!
42 //
43 bool _released = true;
44 CtrlCHandlerCallback _previousCallback = ICE_NULLPTR;
45 
46 //
47 // Variables that are immutable during run() and until communicator->destroy() has returned;
48 // before and after run(), and once communicator->destroy() has returned, we assume that
49 // only the main thread and CtrlCHandler threads are running.
50 //
51 CtrlCHandler* _ctrlCHandler = 0;
52 bool _nohup = false;
53 
54 }
55 
Application(SignalPolicy signalPolicy)56 Ice::Application::Application(SignalPolicy signalPolicy)
57 {
58     Ice::Application::_signalPolicy = signalPolicy;
59 }
60 
~Application()61 Ice::Application::~Application()
62 {
63 }
64 
65 int
main(int argc,const char * const argv[],ICE_CONFIG_FILE_STRING configFile,int version)66 Ice::Application::main(int argc, const char* const argv[], ICE_CONFIG_FILE_STRING configFile, int version)
67 {
68     _appName = "";
69     if(argc > 0)
70     {
71         _appName = argv[0];
72     }
73 
74     if(argc > 0 && argv[0] && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger()))
75     {
76         setProcessLogger(ICE_MAKE_SHARED(LoggerI, argv[0], "", true));
77     }
78 
79     InitializationData initData;
80 #ifndef ICE_CPP11_MAPPING
81     if(configFile)
82 #endif
83     {
84         try
85         {
86             initData.properties = createProperties();
87             initData.properties->load(configFile);
88         }
89         catch(const Ice::Exception& ex)
90         {
91             Error out(getProcessLogger());
92             out << ex;
93             return EXIT_FAILURE;
94         }
95         catch(const std::exception& ex)
96         {
97             Error out(getProcessLogger());
98             out << ex;
99             return EXIT_FAILURE;
100         }
101         catch(...)
102         {
103             Error out(getProcessLogger());
104             out << "unknown exception";
105             return EXIT_FAILURE;
106         }
107     }
108     return main(argc, argv, initData, version);
109 }
110 
111 #ifdef _WIN32
112 int
main(int argc,const wchar_t * const argv[],const Ice::InitializationData & initData,int version)113 Ice::Application::main(int argc, const wchar_t* const argv[], const Ice::InitializationData& initData, int version)
114 {
115     //
116     // On Windows the given wchar_t* strings are UTF16 and therefore
117     // needs to be converted to native narrow string encoding.
118     //
119     return main(argsToStringSeq(argc, argv), initData, version);
120 }
121 
122 int
main(int argc,const wchar_t * const argv[],ICE_CONFIG_FILE_STRING config,int version)123 Ice::Application::main(int argc, const wchar_t* const argv[], ICE_CONFIG_FILE_STRING config, int version)
124 {
125     return main(argsToStringSeq(argc, argv), config, version);
126 }
127 #endif
128 
129 int
main(int argc,const char * const argv[],const InitializationData & initializationData,int version)130 Ice::Application::main(int argc, const char* const argv[], const InitializationData& initializationData, int version)
131 {
132     if(argc > 0 && argv[0] && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger()))
133     {
134         const bool convert = initializationData.properties ?
135             initializationData.properties->getPropertyAsIntWithDefault("Ice.LogStdErr.Convert", 1) > 0 &&
136             initializationData.properties->getProperty("Ice.StdErr").empty() : true;
137         setProcessLogger(ICE_MAKE_SHARED(LoggerI, argv[0], "", convert));
138     }
139 
140     if(_communicator != 0)
141     {
142         Error out(getProcessLogger());
143         out << "only one instance of the Application class can be used";
144         return EXIT_FAILURE;
145     }
146     int status;
147 
148     ArgVector av(argc, argv); // copy args
149 
150     //
151     // We parse the properties here to extract Ice.ProgramName.
152     //
153     InitializationData initData = initializationData;
154     try
155     {
156         initData.properties = createProperties(av.argc, av.argv, initData.properties);
157     }
158     catch(const Ice::Exception& ex)
159     {
160         Error out(getProcessLogger());
161         out << ex;
162         return EXIT_FAILURE;
163     }
164     catch(const std::exception& ex)
165     {
166         Error out(getProcessLogger());
167         out << ex;
168         return EXIT_FAILURE;
169     }
170     catch(...)
171     {
172         Error out(getProcessLogger());
173         out << "unknown exception";
174         return EXIT_FAILURE;
175     }
176     _appName = initData.properties->getPropertyWithDefault("Ice.ProgramName", _appName);
177 
178     //
179     // Used by destroyOnInterruptCallback and shutdownOnInterruptCallback.
180     //
181     _nohup = initData.properties->getPropertyAsInt("Ice.Nohup") > 0;
182 
183     _application = this;
184 
185     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
186     {
187         try
188         {
189             //
190             // The ctrlCHandler must be created before starting any thread, in particular
191             // before initializing the communicator.
192             //
193             CtrlCHandler ctrCHandler;
194             _ctrlCHandler = &ctrCHandler;
195 
196             status = doMain(av.argc, av.argv, initData, version);
197 
198             //
199             // Set _ctrlCHandler to 0 only once communicator->destroy() has completed.
200             //
201             _ctrlCHandler = 0;
202         }
203         catch(const CtrlCHandlerException&)
204         {
205             Error out(getProcessLogger());
206             out << "only one instance of the CtrlCHandler class can be used";
207             status = EXIT_FAILURE;
208         }
209     }
210     else
211     {
212         status = doMain(av.argc, av.argv, initData, version);
213     }
214 
215     return status;
216 }
217 
218 int
main(const StringSeq & args,const InitializationData & initData,int version)219 Ice::Application::main(const StringSeq& args, const InitializationData& initData, int version)
220 {
221     ArgVector av(args);
222     return main(av.argc, av.argv, initData, version);
223 }
224 
225 int
main(const StringSeq & args,ICE_CONFIG_FILE_STRING configFile,int version)226 Ice::Application::main(const StringSeq& args, ICE_CONFIG_FILE_STRING configFile, int version)
227 {
228     ArgVector av(args);
229     return main(av.argc, av.argv, configFile, version);
230 }
231 
232 void
interruptCallback(int)233 Ice::Application::interruptCallback(int)
234 {
235 }
236 
237 const char*
appName()238 Ice::Application::appName()
239 {
240     return _appName.c_str();
241 }
242 
243 CommunicatorPtr
communicator()244 Ice::Application::communicator()
245 {
246     return _communicator;
247 }
248 
249 void
destroyOnInterrupt()250 Ice::Application::destroyOnInterrupt()
251 {
252     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
253     {
254         if(_ctrlCHandler != 0)
255         {
256             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
257             if(!_released)
258             {
259                 _released = true;
260                 _condVar.signal();
261             }
262             _ctrlCHandler->setCallback(destroyOnInterruptCallback);
263         }
264     }
265     else
266     {
267         Warning out(getProcessLogger());
268         out << "interrupt method called on Application configured to not handle interrupts.";
269     }
270 }
271 
272 void
shutdownOnInterrupt()273 Ice::Application::shutdownOnInterrupt()
274 {
275     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
276     {
277         if(_ctrlCHandler != 0)
278         {
279             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
280             if(!_released)
281             {
282                 _released = true;
283                 _condVar.signal();
284             }
285             _ctrlCHandler->setCallback(shutdownOnInterruptCallback);
286         }
287     }
288     else
289     {
290         Warning out(getProcessLogger());
291         out << "interrupt method called on Application configured to not handle interrupts.";
292     }
293 }
294 
295 void
ignoreInterrupt()296 Ice::Application::ignoreInterrupt()
297 {
298     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
299     {
300         if(_ctrlCHandler != 0)
301         {
302             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
303             if(!_released)
304             {
305                 _released = true;
306                 _condVar.signal();
307             }
308             _ctrlCHandler->setCallback(0);
309         }
310     }
311     else
312     {
313         Warning out(getProcessLogger());
314         out << "interrupt method called on Application configured to not handle interrupts.";
315     }
316 }
317 
318 void
callbackOnInterrupt()319 Ice::Application::callbackOnInterrupt()
320 {
321     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
322     {
323         if(_ctrlCHandler != 0)
324         {
325             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
326             if(!_released)
327             {
328                 _released = true;
329                 _condVar.signal();
330             }
331             _ctrlCHandler->setCallback(callbackOnInterruptCallback);
332         }
333     }
334     else
335     {
336         Warning out(getProcessLogger());
337         out << "interrupt method called on Application configured to not handle interrupts.";
338     }
339 }
340 
341 void
holdInterrupt()342 Ice::Application::holdInterrupt()
343 {
344     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
345     {
346         if(_ctrlCHandler != 0)
347         {
348             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
349             if(_released)
350             {
351                 _released = false;
352                 _previousCallback = _ctrlCHandler->setCallback(holdInterruptCallback);
353             }
354             // else, we were already holding signals
355         }
356     }
357     else
358     {
359         Warning out(getProcessLogger());
360         out << "interrupt method called on Application configured to not handle interrupts.";
361     }
362 }
363 
364 void
releaseInterrupt()365 Ice::Application::releaseInterrupt()
366 {
367     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
368     {
369         if(_ctrlCHandler != 0)
370         {
371             Mutex::Lock lock(_mutex); // we serialize all the interrupt-setting
372             if(!_released)
373             {
374                 //
375                 // Note that it's very possible no signal is held;
376                 // in this case the callback is just replaced and
377                 // setting _released to true and signalling _condVar
378                 // do no harm.
379                 //
380 
381                 _released = true;
382                 _ctrlCHandler->setCallback(_previousCallback);
383                 _condVar.signal();
384             }
385             // Else nothing to release.
386         }
387     }
388     else
389     {
390         Warning out(getProcessLogger());
391         out << "interrupt method called on Application configured to not handle interrupts.";
392     }
393 }
394 
395 bool
interrupted()396 Ice::Application::interrupted()
397 {
398     Mutex::Lock lock(_mutex);
399     return _interrupted;
400 }
401 
402 int
doMain(int argc,char * argv[],const InitializationData & initData,int version)403 Ice::Application::doMain(int argc, char* argv[], const InitializationData& initData, int version)
404 {
405     int status;
406 
407     try
408     {
409         _interrupted = false;
410 
411         //
412         // If the process logger is the default logger, we now replace it with a
413         // a logger which is using the program name for the prefix.
414         //
415         if(initData.properties->getProperty("Ice.ProgramName") != "" && ICE_DYNAMIC_CAST(LoggerI, getProcessLogger()))
416         {
417             const bool convert =
418                 initData.properties->getPropertyAsIntWithDefault("Ice.LogStdErr.Convert", 1) > 0 &&
419                 initData.properties->getProperty("Ice.StdErr").empty();
420 
421             setProcessLogger(ICE_MAKE_SHARED(LoggerI, initData.properties->getProperty("Ice.ProgramName"), "", convert));
422         }
423 
424         _communicator = initialize(argc, argv, initData, version);
425         _destroyed = false;
426 
427         //
428         // The default is to destroy when a signal is received.
429         //
430         if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
431         {
432             destroyOnInterrupt();
433         }
434 
435         status = run(argc, argv);
436     }
437     catch(const Ice::Exception& ex)
438     {
439         Error out(getProcessLogger());
440         out << ex;
441         status = EXIT_FAILURE;
442     }
443     catch(const std::exception& ex)
444     {
445         Error out(getProcessLogger());
446         out << ex;
447         status = EXIT_FAILURE;
448     }
449     catch(const std::string& msg)
450     {
451         Error out(getProcessLogger());
452         out << msg;
453         status = EXIT_FAILURE;
454     }
455     catch(const char* msg)
456     {
457         Error out(getProcessLogger());
458         out << msg;
459         status = EXIT_FAILURE;
460     }
461     catch(...)
462     {
463         Error out(getProcessLogger());
464         out << "unknown exception";
465         status = EXIT_FAILURE;
466     }
467 
468     //
469     // Don't want any new interrupt and at this point (post-run),
470     // it would not make sense to release a held signal to run
471     // shutdown or destroy.
472     //
473     if(_signalPolicy == ICE_ENUM(SignalPolicy, HandleSignals))
474     {
475         ignoreInterrupt();
476     }
477 
478     {
479         Mutex::Lock lock(_mutex);
480         while(_callbackInProgress)
481         {
482             _condVar.wait(lock);
483         }
484         if(_destroyed)
485         {
486             _communicator = 0;
487         }
488         else
489         {
490             _destroyed = true;
491             //
492             // And _communicator != 0, meaning will be destroyed
493             // next, _destroyed = true also ensures that any
494             // remaining callback won't do anything
495             //
496         }
497         _application = 0;
498     }
499 
500     if(_communicator != 0)
501     {
502         _communicator->destroy();
503         _communicator = 0;
504     }
505 
506     return status;
507 }
508 
509 //
510 // CtrlCHandler callbacks.
511 //
512 
513 void
holdInterruptCallback(int signal)514 Ice::Application::holdInterruptCallback(int signal)
515 {
516     CtrlCHandlerCallback callback = ICE_NULLPTR;
517     {
518         Mutex::Lock lock(_mutex);
519         while(!_released)
520         {
521             _condVar.wait(lock);
522         }
523 
524         if(_destroyed)
525         {
526             //
527             // Being destroyed by main thread
528             //
529             return;
530         }
531         assert(_ctrlCHandler != 0);
532         callback = _ctrlCHandler->getCallback();
533     }
534 
535     if(callback)
536     {
537         callback(signal);
538     }
539 }
540 
541 void
destroyOnInterruptCallback(int signal)542 Ice::Application::destroyOnInterruptCallback(int signal)
543 {
544     {
545         Mutex::Lock lock(_mutex);
546         if(_destroyed)
547         {
548             //
549             // Being destroyed by main thread
550             //
551             return;
552         }
553         if(_nohup && signal == static_cast<int>(SIGHUP))
554         {
555             return;
556         }
557 
558         assert(!_callbackInProgress);
559         _callbackInProgress = true;
560         _interrupted = true;
561         _destroyed = true;
562     }
563 
564     assert(_communicator != 0);
565     _communicator->destroy();
566 
567     {
568         Mutex::Lock lock(_mutex);
569         _callbackInProgress = false;
570     }
571     _condVar.signal();
572 }
573 
574 void
shutdownOnInterruptCallback(int signal)575 Ice::Application::shutdownOnInterruptCallback(int signal)
576 {
577     {
578         Mutex::Lock lock(_mutex);
579         if(_destroyed)
580         {
581             //
582             // Being destroyed by main thread
583             //
584             return;
585         }
586         if(_nohup && signal == static_cast<int>(SIGHUP))
587         {
588             return;
589         }
590 
591         assert(!_callbackInProgress);
592         _callbackInProgress = true;
593         _interrupted = true;
594     }
595 
596     assert(_communicator != 0);
597     _communicator->shutdown();
598 
599     {
600         Mutex::Lock lock(_mutex);
601         _callbackInProgress = false;
602     }
603     _condVar.signal();
604 }
605 
606 void
callbackOnInterruptCallback(int signal)607 Ice::Application::callbackOnInterruptCallback(int signal)
608 {
609     {
610         Mutex::Lock lock(_mutex);
611         if(_destroyed)
612         {
613             //
614             // Being destroyed by main thread
615             //
616             return;
617         }
618         // For SIGHUP the user callback is always called. It can
619         // decide what to do.
620         assert(!_callbackInProgress);
621         _callbackInProgress = true;
622         _interrupted = true;
623     }
624 
625     try
626     {
627         assert(_application != 0);
628         _application->interruptCallback(signal);
629     }
630     catch(const Ice::Exception& ex)
631     {
632         Error out(getProcessLogger());
633         out << "(while interrupting in response to signal " << signal << "): Ice::Exception: " << ex;
634     }
635     catch(const std::exception& ex)
636     {
637         Error out(getProcessLogger());
638         out << "(while interrupting in response to signal " << signal << "): std::exception: " << ex;
639     }
640     catch(const std::string& msg)
641     {
642         Error out(getProcessLogger());
643         out << "(while interrupting in response to signal " << signal << "): " << msg;
644     }
645     catch(const char* msg)
646     {
647         Error out(getProcessLogger());
648         out << "(while interrupting in response to signal " << signal << "): " << msg;
649     }
650     catch(...)
651     {
652         Error out(getProcessLogger());
653         out << "(while interrupting in response to signal " << signal << "): unknown exception";
654     }
655 
656     {
657         Mutex::Lock lock(_mutex);
658         _callbackInProgress = false;
659     }
660     _condVar.signal();
661 }
662