Salome HOME
Shutdown data servers scopes in driver command.
[modules/yacs.git] / src / yacsloader / driver.cxx
1 // Copyright (C) 2006-2021  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "yacsconfig.h"
21 #include "RuntimeSALOME.hxx"
22 #include "Proc.hxx"
23 #include "Logger.hxx"
24 #include "Exception.hxx"
25 #include "Executor.hxx"
26 #include "parsers.hxx"
27 #include "VisitorSalomeSaveState.hxx"
28 #include "VisitorSaveSalomeSchema.hxx"
29 #include "LoadState.hxx"
30 #include "Dispatcher.hxx"
31 #include "LinkInfo.hxx"
32 #include "ObserverAsPlugin.hxx"
33
34 #include "KernelBasis.hxx"
35 #include "SALOME_Launcher.hxx"
36 #include "ServiceUnreachable.hxx"
37 #include "SALOME_NamingService_Wrapper.hxx"
38 #include "SALOME_NamingService.hxx"
39 #include "SALOME_ModuleCatalog.hh"
40 #include "SALOMESDS_DataServerManager.hxx"
41 #include "Basics_Utils.hxx"
42
43 #include <iostream>
44 #include <fstream>
45 #include <signal.h>
46 #include <list>
47
48 #if defined WIN32 || defined __APPLE__
49 #else
50 #include <argp.h>
51 #endif
52
53 using YACS::YACSLoader;
54 using namespace YACS::ENGINE;
55 using namespace std;
56
57
58 // --- use of glibc argp interface for parsing unix-style arguments
59
60 const char *argp_program_version ="driver V0.1";
61 const char *argp_program_bug_address ="<nepal@nepal.edf.fr>";
62 static char doc[] ="driver -- a SALOME YACS graph executor";
63 static char args_doc[] = "graph.xml";
64
65 #if defined WIN32 || defined __APPLE__
66 #else
67 static struct argp_option options[] =
68   {
69     {"display",         'd', "level", 0,                   "Display dot files: 0=never to 3=very often (default 0)"},
70     {"verbose",         'v', 0,       0,                   "Produce verbose output" },
71     {"stop-on-error",   's', 0,       0,                   "Stop on first error" },
72     {"dump-on-error",   'e', "file",  OPTION_ARG_OPTIONAL, "Stop on first error and dump state"},
73     {"dump-final",      'f', "file",  OPTION_ARG_OPTIONAL, "dump final state"},
74     {"dump",            'g', "nbsec", OPTION_ARG_OPTIONAL, "dump state"},
75     {"load-state",      'l', "file",  0,                   "Load State from a previous partial execution"},
76     {"save-xml-schema", 'x', "file",  OPTION_ARG_OPTIONAL, "dump xml schema"},
77     {"shutdown",        't', "level", 0,                   "Shutdown the schema: 0=no shutdown to 3=full shutdown (default 1)"},
78     {"reset",           'r', "level", 0,                   "Reset the schema before execution: 0=nothing, 1=reset error nodes to ready state (default 0)"},
79     {"kill-port",       'k', "port",  0,                   "Kill Salome application running on the specified port if the driver process is killed (with SIGINT or SIGTERM)"},
80     {"init_port",       'i', "value", OPTION_ARG_OPTIONAL, "Initialisation value of a port, specified as bloc.node.port=value."},
81     { 0 }
82   };
83 #endif
84
85 struct arguments
86 {
87   char *args[1];
88   int display;
89   int verbose;
90   int stop;
91   char *dumpErrorFile;
92   char *finalDump;
93   int dump;
94   char *xmlSchema;
95   char *loadState;
96   int shutdown;
97   int reset;
98   int killPort;
99   std::list<std::string> init_ports;
100 };
101
102 typedef struct {
103   int nbsec;
104   string dumpFile;
105   string lockFile;
106 } thread_st;
107
108 #if defined WIN32 || defined __APPLE__
109 static int
110 #else
111 static error_t
112 #endif
113 parse_opt (int key, char *arg, struct argp_state *state)
114 {
115 #if defined WIN32 || defined __APPLE__
116 #else
117   // Get the input argument from argp_parse, which we
118   // know is a pointer to our arguments structure. 
119   struct arguments *myArgs = (arguments*)state->input;
120   
121   switch (key)
122     {
123     case 'd':
124       myArgs->display = atoi(arg);
125       break;
126     case 't':
127       myArgs->shutdown = atoi(arg);
128       break;
129     case 'r':
130       myArgs->reset = atoi(arg);
131       break;
132     case 'v':
133       myArgs->verbose = 1;
134       break;
135     case 's':
136       myArgs->stop = 1;
137       break;
138     case 'e':
139       myArgs->stop = 1;
140       if (arg)
141         myArgs->dumpErrorFile = arg;
142       else
143         myArgs->dumpErrorFile = (char *)"dumpErrorState.xml";
144       break;
145     case 'f':
146       if (arg)
147         myArgs->finalDump = arg;
148       else
149         myArgs->finalDump = (char *)"finalDumpState.xml";
150       break;      
151     case 'g':
152       if (arg)
153         myArgs->dump = atoi(arg);
154       else
155         myArgs->dump = 60;
156       break;      
157     case 'l':
158       myArgs->loadState = arg;
159       break;
160     case 'x':
161       if (arg)
162         myArgs->xmlSchema = arg;
163       else
164         myArgs->xmlSchema = (char *)"saveSchema.xml";
165       break;      
166     case 'k':
167       myArgs->killPort = atoi(arg);
168       break;
169     case 'i':
170       if (arg)
171         myArgs->init_ports.push_back(std::string(arg));
172       break;
173
174     case ARGP_KEY_ARG:
175       if (state->arg_num >=1) // Too many arguments.
176         argp_usage (state);
177       myArgs->args[state->arg_num] = arg;
178       break;
179       
180     case ARGP_KEY_END:
181       if (state->arg_num < 1) // Not enough arguments.
182         argp_usage (state);
183       break;
184      
185     default:
186       return ARGP_ERR_UNKNOWN;
187     }
188 #endif
189   return 0;
190 }
191
192 // Our argp parser.
193 #if defined WIN32 || defined __APPLE__
194 #else
195 static struct argp argp = { options, parse_opt, args_doc, doc };
196 #endif
197
198 void timer(std::string msg)
199 {
200 #if defined WIN32 || defined __APPLE__
201 #else
202   struct timeval tv;
203   gettimeofday(&tv,NULL);
204   long t=tv.tv_sec*1000+tv.tv_usec/1000;
205   static long t0=t;
206   gettimeofday(&tv,NULL);
207   std::cerr << msg << tv.tv_sec*1000+tv.tv_usec/1000-t0 << " ms" << std::endl;
208 #endif
209 }
210
211 Proc* p=0;
212 static struct arguments myArgs;
213
214 void Handler(int theSigId)
215 {
216   if(p)
217     {
218       p->cleanNodes();
219       //if requested save state
220       bool isFinalDump = (strlen(myArgs.finalDump) != 0);
221       if (isFinalDump)
222         {
223           YACS::ENGINE::VisitorSalomeSaveState vst(p);
224           vst.openFileDump(myArgs.finalDump);
225           p->accept(&vst);
226           vst.closeFileDump();
227         }
228       //if requested shutdown schema
229       if(myArgs.shutdown < 999)
230         {
231           p->shutdown(myArgs.shutdown);
232         }
233     }
234   if (myArgs.killPort)
235     {
236       ostringstream command;
237       command << "killSalomeWithPort.py " << myArgs.killPort;
238       int status = system(command.str().c_str());
239       if (status == 0)
240         cerr << "Salome application on port " << myArgs.killPort << " is killed" << endl;
241       else
242         cerr << "Error: Can't kill Salome application on port " << myArgs.killPort << endl;
243     }
244   _exit(1);
245 }
246
247 void * dumpState(void *arg)
248 {
249   thread_st *st = (thread_st*)arg;
250   YACS::StatesForNode state = p->getEffectiveState();
251   while((state != YACS::DONE) && (state != YACS::LOADFAILED) && (state != YACS::EXECFAILED) && (state != YACS::INTERNALERR) && (state != YACS::DISABLED) && (state != YACS::FAILED) && (state != YACS::ERROR)){
252 #ifdef WIN32
253     Sleep(st->nbsec);
254 #else 
255     sleep(st->nbsec);
256 #endif
257     string cmd = "touch " + st->lockFile;
258     system(cmd.c_str());
259     YACS::ENGINE::VisitorSalomeSaveState vst(p);
260     vst.openFileDump(st->dumpFile);
261     p->accept(&vst);
262     vst.closeFileDump();
263     cmd = "rm -f " + st->lockFile;
264     system(cmd.c_str());
265     state = p->getEffectiveState();
266   }
267   delete st;
268   return NULL;
269 }
270
271 #ifndef WIN32
272 typedef void (*sighandler_t)(int);
273 sighandler_t setsig(int sig, sighandler_t handler)
274 {
275   struct sigaction context, ocontext;
276   context.sa_handler = handler;
277   sigemptyset(&context.sa_mask);
278   context.sa_flags = 0;
279   if (sigaction(sig, &context, &ocontext) == -1)
280     return SIG_ERR;
281   return ocontext.sa_handler;
282 }
283 #endif
284
285 bool parse_init_port(const std::string& input, std::string& node, std::string& port, std::string& value)
286 {
287   bool ok = true;
288   size_t pos_eq = input.find('=');
289   if(pos_eq == std::string::npos || pos_eq == input.size())
290     return false;
291   value = input.substr(pos_eq+1);
292   size_t pos_dot = input.rfind('.', pos_eq);
293   if(!pos_dot || pos_dot == std::string::npos || pos_dot >= pos_eq-1)
294     return false;
295   port = input.substr(pos_dot+1, pos_eq-pos_dot-1);
296   node = input.substr(0, pos_dot);
297   return true;
298 }
299
300 void InitializeSSL()
301 {
302   setSSLMode(true);
303   KERNEL::getLauncherSA();
304 }
305
306 void shutdownServers()
307 {
308   // shutdown data server scopes
309   try
310     {
311       YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
312       runTime->loadModulCatalog();
313       CORBA::ORB_ptr orb = runTime->getOrb();
314       if (orb)
315       {
316         SALOME_NamingService_Wrapper namingService(orb);
317         CORBA::Object_var objDSM(namingService.Resolve(SALOMESDS::DataServerManager::NAME_IN_NS));
318         SALOME::DataServerManager_var dsm(SALOME::DataServerManager::_narrow(objDSM));
319         if ( !CORBA::is_nil(dsm) )
320           dsm->shutdownScopes();
321       }
322     }
323   catch(const CORBA::Exception& )
324     {
325        // ignore and continue
326     }
327   catch(ServiceUnreachable& e)
328     {
329        // ignore and continue
330     }
331 }
332
333 int main (int argc, char* argv[])
334 {
335      
336   // Default values.
337   myArgs.display = 0;
338   myArgs.verbose = 0;
339   myArgs.stop = 0;
340   myArgs.dumpErrorFile= (char *)"";
341   myArgs.finalDump = (char *)"";
342   myArgs.dump = 0;
343   myArgs.loadState = (char *)"";
344   myArgs.xmlSchema = (char *)"";
345   myArgs.shutdown = 10;
346   myArgs.reset = 0;
347   myArgs.killPort = 0;
348   myArgs.init_ports.clear();
349
350   // Parse our arguments; every option seen by parse_opt will be reflected in arguments.
351 #if defined WIN32 || defined __APPLE__
352 #else
353   argp_parse (&argp, argc, argv, 0, 0, &myArgs);
354   std::cerr << "graph = " << myArgs.args[0];
355   std::cerr << " options: display=" << myArgs.display;
356   std::cerr << " verbose="<<myArgs.verbose;
357   std::cerr << " stop-on-error=" << myArgs.stop;
358   std::cerr << " shutdown=" << myArgs.shutdown;
359   std::cerr << " reset=" << myArgs.reset;
360   if (myArgs.killPort)
361     std::cerr << " kill-port=" << myArgs.killPort;
362   if (myArgs.stop)
363     std::cerr << " dumpErrorFile=" << myArgs.dumpErrorFile << std::endl;
364   else
365     std::cerr << std::endl;
366   std::list<std::string>::const_iterator it;
367   for(it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
368   {
369     std::cerr << (*it) << std::endl;
370   }
371 #endif
372
373 #ifndef WIN32
374   setsig(SIGINT,&Handler);
375   setsig(SIGTERM,&Handler);
376 #endif
377
378   InitializeSSL();
379
380   timer("Starting ");
381   long flags = RuntimeSALOME::UsePython + RuntimeSALOME::UseCorba + RuntimeSALOME::UseXml + \
382                RuntimeSALOME::UseCpp + RuntimeSALOME::UseSalome;
383   RuntimeSALOME::setRuntime(flags, argc, argv);
384
385   // Try to load the session catalog if it exists
386   try
387     {
388       YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
389       runTime->loadModulCatalog();
390       CORBA::ORB_ptr orb = runTime->getOrb();
391       if (orb)
392         {
393           SALOME_NamingService_Wrapper namingService(orb);
394           CORBA::Object_var obj = namingService.Resolve("/Kernel/ModulCatalog");
395           SALOME_ModuleCatalog::ModuleCatalog_var aModuleCatalog = SALOME_ModuleCatalog::ModuleCatalog::_narrow(obj);
396           if (! CORBA::is_nil(aModuleCatalog))
397             {
398               CORBA::String_var anIOR = orb->object_to_string( aModuleCatalog );
399               YACS::ENGINE::Catalog* aCatalog = runTime->loadCatalog( "session", anIOR.in() );
400               runTime->addCatalog(aCatalog);
401             }
402         }
403     }
404   catch(ServiceUnreachable& e)
405     {
406       //Naming service unreachable don't add catalog
407     }
408
409   YACSLoader loader;
410   Executor executor;
411
412   try
413     {
414       timer("Elapsed time before load: ");
415       p=loader.load(myArgs.args[0]);
416       if(p==0)
417         {
418           std::cerr << "The imported file is probably not a YACS schema file" << std::endl;
419           return 1;
420         }
421       // Initialize the ports
422       for(std::list<std::string>::iterator it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
423       {
424         std::string node, port, value;
425         if(parse_init_port((*it), node, port, value))
426         {
427           std::cerr << "Initialization node=" << node
428                     << " port=" << port
429                     << " value=" << value << std::endl;
430
431           std::string init_state;
432           init_state = p->setInPortValue(node, port, value);
433           if(value.compare(init_state))
434           {
435             std::cerr << "Error on initialization:" << init_state << std::endl;
436             return 1;
437           }
438         }
439         else
440         {
441           std::cerr << "Error on parsing initialization string:" << (*it) << std::endl;
442           return 1;
443         }
444       }
445       
446       //Get the parser logger
447       Logger* logger=p->getLogger("parser");
448       //Print errors logged if any
449       if(!logger->isEmpty())
450         {
451           std::cerr << "The imported file has errors" << std::endl;
452           std::cerr << logger->getStr() << std::endl;
453         }
454       //Don't execute if there are errors
455       if(logger->hasErrors())
456         {
457           if(!p->isValid())
458             {
459               std::string report=p->getErrorReport();
460               std::cerr << "The schema is not valid and can not be executed" << std::endl;
461               std::cerr << report << std::endl;
462             }
463           delete p;
464           Runtime* r=YACS::ENGINE::getRuntime();
465           Dispatcher* disp=Dispatcher::getDispatcher();
466           r->fini();
467           delete r;
468           delete disp;
469           return 1;
470         }
471       timer("Elapsed time after load: ");
472
473       if(!p->isValid())
474         {
475           std::string report=p->getErrorReport();
476           std::cerr << "The schema is not valid and can not be executed" << std::endl;
477           std::cerr << report << std::endl;
478           Runtime* r=YACS::ENGINE::getRuntime();
479           Dispatcher* disp=Dispatcher::getDispatcher();
480           r->fini();
481           delete r;
482           delete disp;
483           return 1;
484         }
485       timer("Elapsed time after validation: ");
486
487       // Check consistency
488       LinkInfo info(LinkInfo::ALL_DONT_STOP);
489       p->checkConsistency(info);
490       if(info.areWarningsOrErrors())
491         {
492           std::cerr << "The schema is not consistent and can not be executed" << std::endl;
493           std::cerr << info.getGlobalRepr() << std::endl;
494           Runtime* r=YACS::ENGINE::getRuntime();
495           Dispatcher* disp=Dispatcher::getDispatcher();
496           r->fini();
497           delete r;
498           delete disp;
499           return 1;
500         }
501       timer("Elapsed time after check consistency: ");
502
503       //execution
504       bool isXmlSchema = (strlen(myArgs.xmlSchema) != 0);
505       if (isXmlSchema)
506       {
507         YACS::ENGINE::VisitorSaveSalomeSchema vss(p);
508         vss.openFileSchema(myArgs.xmlSchema);
509         p->accept(&vss);
510         vss.closeFileSchema();
511       }
512
513       bool fromScratch = (strlen(myArgs.loadState) == 0);
514       if (!fromScratch)
515         {
516           p->init();
517           p->exUpdateState();
518           stateParser* rootParser = new stateParser();
519           stateLoader myStateLoader(rootParser, p);
520           myStateLoader.parse(myArgs.loadState);
521           if(myArgs.reset>0)
522             {
523               p->resetState(myArgs.reset);
524               p->exUpdateState();
525             }
526         }
527
528       if (myArgs.stop)
529       {
530         if (strlen(myArgs.dumpErrorFile) >0)
531           executor.setStopOnError(true, myArgs.dumpErrorFile);
532         else
533           executor.setStopOnError(false, myArgs.dumpErrorFile);
534       }
535       if(myArgs.display>0)
536         {
537           std::ofstream f("toto");
538           p->writeDot(f);
539           f.close();
540         }
541
542       bool isDump = (myArgs.dump != 0);
543       pthread_t th;
544       if (isDump)
545         {
546           thread_st *st = new thread_st;
547           st->nbsec = myArgs.dump;
548           st->dumpFile = string("dumpState_") + myArgs.args[0];
549           string rootFile = st->dumpFile.substr(0,st->dumpFile.find("."));
550           st->lockFile = rootFile + ".lock";
551           pthread_create(&th,NULL,&dumpState,(void*)st);
552         }
553       YACS::ENGINE::LoadObserversPluginIfAny(p,&executor);
554       cerr << "+++++++++++++++++++ start calculation +++++++++++++++++++" << endl;
555       executor.RunW(p,myArgs.display, fromScratch);
556       cerr << "+++++++++++++++++++  end calculation  +++++++++++++++++++" << endl;
557       cerr << "Proc state : " << Node::getStateName(p->getEffectiveState()) << endl;
558       timer("Elapsed time after execution: ");
559
560       // Return 0 if SCHEMA OK
561       // Return 1 for YACS ERROR (EXCEPTION NOT CATCHED)
562       // Return 2 for YACS SCHEMA ERROR/FAILED
563       int return_value = 0;
564
565       if(p->getEffectiveState() != YACS::DONE)
566         {
567           std::string report=p->getErrorReport();
568           std::cerr << "Execution has ended in error" << std::endl;
569           std::cerr << report << std::endl;
570           return_value = 2;
571         }
572
573       if(myArgs.display>0)
574         {
575           std::ofstream g("titi");
576           p->writeDot(g);
577           g.close();
578         }
579
580       if (isDump)
581         {
582           pthread_join(th,NULL);
583         }
584
585       bool isFinalDump = (strlen(myArgs.finalDump) != 0);
586       if (isFinalDump)
587         {
588           YACS::ENGINE::VisitorSalomeSaveState vst(p);
589           vst.openFileDump(myArgs.finalDump);
590           p->accept(&vst);
591           vst.closeFileDump();
592         }
593       if(myArgs.shutdown < 999)
594         {
595           p->shutdown(myArgs.shutdown);
596           shutdownServers();
597         }
598       delete p;
599       Runtime* r=YACS::ENGINE::getRuntime();
600       Dispatcher* disp=Dispatcher::getDispatcher();
601       r->fini();
602       delete r;
603       delete disp;
604       YACS::ENGINE::UnLoadObserversPluginIfAny();
605       return return_value;
606     }
607   catch (YACS::Exception& e)
608     {
609       cerr << "Caught a YACS exception" << endl;
610       cerr << e.what() << endl;
611       Runtime* r=YACS::ENGINE::getRuntime();
612       Dispatcher* disp=Dispatcher::getDispatcher();
613       r->fini();
614       delete r;
615       delete disp;
616       return 1;
617     }
618   catch (const std::ios_base::failure&)
619     {
620       cerr << "Caught an io failure exception" << endl;
621       return 1;
622     }
623   catch(CORBA::SystemException& ex) 
624     {
625       cerr << "Caught a CORBA::SystemException.:" << __FILE__ << ":" << __LINE__ << ":" ;
626       CORBA::Any tmp;
627       tmp <<= ex;
628       CORBA::TypeCode_var tc = tmp.type();
629       const char *p = tc->name();
630       if ( *p != '\0' ) 
631         cerr <<p;
632       else  
633         cerr  << tc->id();
634       cerr << endl;
635       return 1;
636     }
637   catch(omniORB::fatalException& fe) 
638     {
639       cerr << "Caught omniORB::fatalException:" << endl;
640       cerr << "  file: " << fe.file() << endl;
641       cerr << "  line: " << fe.line() << endl;
642       cerr << "  mesg: " << fe.errmsg() << endl;
643       return 1;
644     }
645   catch(...) 
646     {
647       cerr << "Caught unknown exception." << endl;
648       return 1;
649     }
650 }
651