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