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