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