Remove dynamic exceptions
[anna.git] / source / app / Application.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 #include <iostream>
10 #include <algorithm>
11 #include <iomanip>
12 #include <fstream>
13
14 #include <time.h>
15 #include <unistd.h>
16 #include <signal.h>
17 #include <sys/wait.h>
18 #include <sys/types.h>
19
20 #include <anna/core/RuntimeException.hpp>
21 #include <anna/core/tracing/Logger.hpp>
22 #include <anna/core/functions.hpp>
23 #include <anna/core/tracing/TraceWriter.hpp>
24 #include <anna/core/tracing/TraceMethod.hpp>
25 #include <anna/core/internal/ModuleManager.hpp>
26 #include <anna/core/util/CommandLine.hpp>
27
28 #include <anna/app/Application.hpp>
29 #include <anna/app/Component.hpp>
30 #include <anna/app/internal/sccs.hpp>
31 #include <anna/app/functions.hpp>
32
33 #include <anna/xml/Node.hpp>
34 #include <anna/xml/Attribute.hpp>
35 #include <anna/xml/Compiler.hpp>
36
37 using namespace std;
38 using namespace anna;
39
40 // Web sites:
41 #define WEBSITE_PROJECT "http://redmine.teslayout.com/projects/anna-suite"
42 #define WEBSITE_GENERAL "http://www.teslayout.com"
43
44 app::Application* app::Application::st_application = NULL;
45
46 app::Application::Application(const char* shortName, const char* title, const char* version, const char* date, const char* time) :
47   a_shortName(shortName),
48   a_running(false),
49   a_version(version),
50   a_title(title),
51   a_enableGPL(false) {
52   sigset(SIGUSR1, handlerSignalUSR);
53   sigset(SIGUSR2, handlerSignalUSR);
54   sigset(SIGTERM, handlerSignalTerminate);
55   sigignore(SIGINT);
56   app::sccs::activate();
57   a_version += functions::getArchitecture();
58   a_pid = getpid();
59
60   if(st_application == NULL)
61     st_application = this;
62
63   cout << a_shortName << " - " << a_title << ". Version " << a_version << endl;
64
65   if(date || time) {
66     cout << "Revision date: ";
67
68     if(date) cout << date << " ";
69
70     if(time) cout << time;
71
72     cout << endl;
73   }
74
75   cout << "(c) Copyright 2001-2006 Eduardo Ramos Testillano & Francisco Antonio Ruiz Rayo" << endl;
76   cout << "                        eduardo.ramos.testillano@gmail.com & cisco.tierra@gmail.com" << endl;
77   cout << "                        (project site: " << WEBSITE_PROJECT << ")" << endl << endl;
78   //cout << "                          - Project site: " << WEBSITE_PROJECT << endl;
79   //cout << "                          - Main site:    " << WEBSITE_GENERAL << endl << endl;
80 }
81
82 app::Component* app::Application::find(const char* className)
83 {
84   Component* component;
85   for(iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
86     component = Application::component(ii);
87
88     if(anna_strcmp(className, component->getClassName()) == 0)
89       return component;
90   }
91
92   return NULL;
93 }
94
95 /**
96  * (1) Si se ejecuta el metodo clone evita que los hijos que termina se queden como zombies.
97  */
98 void app::Application::start()
99 noexcept(false) {
100   TraceMethod tm("app::Application", "start", ANNA_FILE_LOCATION);
101   ModuleManager& moduleManager = ModuleManager::instantiate();
102
103   if(a_running == true) {
104     string msg("app::Application { Name; ");
105     msg += a_title;
106     msg += " | Already on execution";
107     throw RuntimeException(msg, ANNA_FILE_LOCATION);
108   }
109
110   if(sigset(SIGCHLD, handlerChildTerminate) != 0)                   // (1)
111     throw RuntimeException("Error installing terminate handler", ANNA_FILE_LOCATION);
112
113   try {
114     if(a_enableGPL == true) {
115       cout << "This program is free software: you can redistribute it and/or modify" << endl;
116       cout << "it under the terms of the GNU General Public License as published by" << endl;
117       cout << "the Free Software Foundation, either version 3 of the License, or" << endl;
118       cout << "(at your option) any later version." << endl << endl;
119       cout << "This program is distributed in the hope that it will be useful," << endl;
120       cout << "but WITHOUT ANY WARRANTY; without even the implied warranty of" << endl;
121       cout << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the" << endl;
122       cout << "GNU General Public License for more details." << endl << endl;
123       cout << "You should have received a copy of the GNU General Public License" << endl;
124       cout << "along with this program. If not, see <http://www.gnu.org/licenses/>." << endl << endl;
125     }
126
127     cout << "ANNA Suite. Version " << functions::getVersion() << endl;
128     cout << "Revision date: " << __DATE__ << " " << __TIME__ << endl;
129     cout << "(c) Copyright 2001-2006 Eduardo Ramos Testillano & Francisco Antonio Ruiz Rayo" << endl;
130     cout << "                        eduardo.ramos.testillano@gmail.com & cisco.tierra@gmail.com" << endl << endl;
131     initialize();
132     cout << "Activating application modules ....................." << endl;
133
134     for(ModuleManager::const_iterator ii = moduleManager.begin(), maxii = moduleManager.end(); ii != maxii; ii ++) {
135       cout << "\t Module " << ModuleManager::module(ii) << endl;
136     }
137
138     cout << endl;
139     startComponents();
140     a_running = true;
141     run();
142     a_running = false;
143     stopComponents();
144   } catch(RuntimeException& ex) {
145     ex.trace();
146     stopComponents();
147     throw;
148   }
149 }
150
151 void app::Application::startComponents()
152 noexcept(false) {
153   LOGMETHOD(TraceMethod tm("app::Application", "startComponents", ANNA_FILE_LOCATION));
154   Component* component;
155
156   for(iterator ii = begin(); ii != end(); ii ++) {
157     component = Application::component(ii);
158     LOGINFORMATION(
159       std::string msg("Starting component | ");
160       msg += component->asString();
161       Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
162     );
163     component->initialize();
164   }
165 }
166
167 void app::Application::stopComponents()
168 noexcept(false) {
169   LOGMETHOD(TraceMethod tm("app::Application", "stopComponents", ANNA_FILE_LOCATION));
170   Component* component;
171
172   for(iterator ii = begin(); ii != end(); ii ++) {
173     component = Application::component(ii);
174     LOGINFORMATION(
175       std::string msg("Stopping component | ");
176       msg += component->asString();
177       Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
178     );
179     component->stop();
180   }
181 }
182
183 /*
184  * (1) Este el codigo que ejecuta la copia que acabamos de crear.
185  *     (1.1) Limpia la lista de hijos que pudiera tener el padre.
186  */
187 app::Application& app::Application::clone()
188 noexcept(false) {
189   if(anna::functions::supportMultithread() == true) {
190     string msg("app::Application::clone | pid: ");
191     msg += functions::asString((int) a_pid);
192     msg += " | Application clone is only supported for ST applications";
193     throw RuntimeException(msg, errno, ANNA_FILE_LOCATION);
194   }
195
196   const pid_t pid = ::fork();
197
198   if(pid < 0) {
199     string msg("app::Application::clone | pid: ");
200     msg += functions::asString((int) a_pid);
201     throw RuntimeException(msg, errno, ANNA_FILE_LOCATION);
202   }
203
204   Component* component;
205
206   if(pid == 0) {                                                                   // (1)
207     Logger::showPID(true);
208     a_pid = getpid();
209     a_pids.clear();                                                               // (1.1)
210
211     try {
212       for(iterator ii = begin(); ii != end(); ii ++) {
213         component = Application::component(ii);
214         LOGINFORMATION(
215           std::string msg("Cloning component on child | ");
216           msg += component->asString();
217           Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
218         );
219         component->do_cloneChild();
220       }
221
222       do_cloneChild();
223     } catch(RuntimeException& ex) {
224       ex.trace();
225       exit(1);
226     }
227   } else {
228     LOGWARNING(
229       string msg("app::Application::clone | Parent pid: ");
230       msg += functions::asString((int) a_pid);
231       msg += " | Child pid: ";
232       msg += functions::asString((int) pid);
233       Logger::warning(msg, ANNA_FILE_LOCATION);
234     );
235
236     try {
237       for(iterator ii = begin(); ii != end(); ii ++) {
238         component = Application::component(ii);
239         LOGINFORMATION(
240           std::string msg("Cloning component on parent | ");
241           msg += component->asString();
242           Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
243         );
244         component->do_cloneParent();
245       }
246
247       do_cloneParent();
248       a_pids.push_back(pid);
249     } catch(RuntimeException& ex) {
250       string msg("app::Application::clone | Abort execution: ");
251       msg  += msg += functions::asString((int) pid);
252       Logger::error(msg, ANNA_FILE_LOCATION);
253       kill(pid, SIGKILL);
254       throw;
255     }
256   }
257
258   return *this;
259 }
260
261 //---------------------------------------------------------------------------------------
262 // (1) Se invoca desde los constructores de los componentes => no se puede invocar a
263 // ningn m�odo virtual => obliga a que est� definidos antes de comenzar la
264 // ejecucin de la aplicacin => se inicilizar� en el Application::start ya que en
265 // ese momento los objetos est� completamente creados.
266 //---------------------------------------------------------------------------------------
267 void app::Application::attach(app::Component* component)
268 noexcept(false) {
269   if(component == NULL)
270     throw RuntimeException("Cannot attach a NULL component", ANNA_FILE_LOCATION);
271
272   iterator ii = std::find(begin(), end(), component);
273
274   if(ii != end()) {
275     LOGINFORMATION(
276       //string msg((*ii)->asString());
277       string msg((*ii)->getClassName());
278       msg += " | Was previously attached !";
279       Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
280     )
281     return;
282   }
283
284   LOGDEBUG(
285     string msg("app::Application::attach | ");
286     msg += component->asString();
287     Logger::debug(msg, ANNA_FILE_LOCATION);
288   );
289   a_components.push_back(component);
290
291   if(a_running) {
292     LOGINFORMATION(
293       std::string msg("Starting component | ");
294       msg += component->asString();
295       Logger::write(Logger::Information, msg, ANNA_FILE_LOCATION);
296     );
297     component->initialize();
298   }
299 }
300
301 void app::Application::detach(app::Component* component)
302 noexcept(false) {
303   LOGMETHOD(TraceMethod tm("app::Application", "detach(component)", ANNA_FILE_LOCATION));
304
305   if(component == NULL)
306     throw RuntimeException("Cannot detach a NULL component", ANNA_FILE_LOCATION);
307
308   LOGDEBUG(
309     string msg("app::Application::detach | ");
310     msg += component->asString();
311     Logger::debug(msg, ANNA_FILE_LOCATION);
312   );
313   iterator ii = std::find(begin(), end(), component);
314
315   if(ii != end()) {
316     LOGDEBUG(Logger::write(Logger::Debug, "Detached", (*ii)->asString(), ANNA_FILE_LOCATION));
317     a_components.erase(ii);
318   }
319 }
320
321 void app::Application::writeContext(const std::string& file)
322 noexcept(false) {
323   ofstream out;
324   out.open(file.c_str());
325
326   if(out.is_open() == false) {
327     string msg("Error opening '");
328     msg += file;
329     msg += "'";
330     throw RuntimeException(msg, ANNA_FILE_LOCATION);
331   }
332
333   LOGNOTICE(
334     string msg("app::Application::writeContext | File: ");
335     msg += file;
336     Logger::notice(msg, ANNA_FILE_LOCATION);
337   );
338   xml::Node root("Application");
339   out << xml::Compiler().apply(asXML(&root)) << endl;
340   out.close();
341 }
342
343 xml::Node* app::Application::asXML(xml::Node* app) const
344 {
345   xml::Node* node(NULL);
346   app->createAttribute("Name", getShortName());
347   app->createAttribute("Version", getVersion());
348   app->createAttribute("pid", getPid());
349   // CommandLine:
350   CommandLine::instantiate().asXML(app);
351   node = app->createChild("app.Clones");
352
353   for(const_pid_iterator ii = pid_begin(), maxii = pid_end(); ii != maxii; ii ++)
354     node->createChild("Clone")->createAttribute("PID", pid(ii));
355
356   ModuleManager& moduleManager = ModuleManager::instantiate();
357   node = app->createChild("app.Modules");
358
359   for(ModuleManager::const_iterator ii = moduleManager.begin(), maxii = moduleManager.end(); ii != maxii; ii ++)
360     node->createChild("Module")->createAttribute("Id", ModuleManager::module(ii));
361
362   const Component* cc;
363   node = app->createChild("app.Components");
364
365   for(const_iterator ii = begin(), maxii = end(); ii != maxii; ii ++) {
366     Guard guard(cc = component(ii));
367     cc->asXML(node);
368   }
369
370   return app;
371 }
372
373 void app::Application::signalUSR(int signal)
374 noexcept(false) {
375   if (signal == SIGUSR1) signalUSR1();
376   else if (signal == SIGUSR2) signalUSR2();
377 }
378
379 void app::Application::signalUSR1()
380 noexcept(false) {
381   writeContext(anna::functions::asString("/var/tmp/anna.context.%05d", getPid()));
382 }
383
384 void app::Application::signalUSR2()
385 noexcept(false) {
386   Logger::notice("Captured signal SIGUSR2. Nothing implemented at the moment", ANNA_FILE_LOCATION);
387 }
388
389 void app::Application::sendSignalToChilds(const int signal)
390 {
391   for(pid_iterator ii = pid_begin(), maxii = pid_end(); ii != maxii; ii ++)
392     kill(pid(ii), signal);
393 }
394
395 void app::Application::handlerSignalUSR(int signal)
396 {
397   sigignore(signal);
398
399   try {
400     Application& app = anna::app::functions::getApp();
401     app.sendSignalToChilds(signal);
402     app.signalUSR(signal);
403   } catch(Exception& ex) {
404     ex.trace();
405   }
406
407   sigset(signal, handlerSignalUSR);
408 }
409
410 void app::Application::handlerSignalTerminate(int)
411 {
412   sigignore(SIGTERM);
413
414   try {
415     LOGWARNING(
416       Logger::write(Logger::Warning, "app::Application | Received SIGTERM", ANNA_FILE_LOCATION);
417     );
418     Application& app = anna::app::functions::getApp();
419     app.sendSignalToChilds(SIGTERM);
420     app.signalTerminate();
421   } catch(Exception& ex) {
422     ex.trace();
423     exit(EXIT_SUCCESS);
424   }
425
426   sigset(SIGTERM, handlerSignalTerminate);
427 }
428
429 /**
430  * Se recoge el estado de terminacion del hijo para que no se quede considerado como un zombie y se
431  * elimina de la lista de procesos hijos.
432  */
433 void app::Application::handlerChildTerminate(int sig)
434 {
435   if(sig == SIGCHLD) {
436     int status;
437     pid_t pid = wait(&status);
438
439     if(st_application == NULL)
440       return;
441
442     pid_iterator maxii = st_application->pid_end();
443     pid_iterator ii = ::find(st_application->pid_begin(), maxii, pid);
444
445     if(ii != maxii)
446       st_application->a_pids.erase(ii);
447   }
448 }
449
450
451