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