Salome HOME
Added support for Microsoft Visual C++ (also experimental)
[tools/libbatch.git] / src / Core / Batch_BatchManager_eClient.cxx
1 //  Copyright (C) 2007-2008  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 //  Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 //  This library is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU Lesser General Public
8 //  License as published by the Free Software Foundation; either
9 //  version 2.1 of the License.
10 //
11 //  This library is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 //  Lesser General Public License for more details.
15 //
16 //  You should have received a copy of the GNU Lesser General Public
17 //  License along with this library; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 //  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 /*
23 * BatchManager_eLSF.cxx : emulation of LSF client
24 *
25 * Auteur : Bernard SECHER - CEA DEN
26 * Mail   : mailto:bernard.secher@cea.fr
27 * Date   : Thu Apr 24 10:17:22 2008
28 * Projet : PAL Salome
29 *
30 */
31
32 #include "Batch_BatchManager_eClient.hxx"
33 #include "Batch_RunTimeException.hxx"
34
35 #include <ctime>
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39
40 #ifdef WIN32
41 #include <direct.h>
42 #include <io.h>
43 #endif
44
45 #include "Batch_config.h"
46
47 #ifdef MSVC
48 #define EXISTS(path) _access_s(path, 0) == 0
49 #else
50 #define EXISTS(path) access(path, F_OK) == 0
51 #endif
52
53 using namespace std;
54
55
56 namespace Batch {
57
58   BatchManager_eClient::BatchManager_eClient(const Batch::FactBatchManager * parent, const char* host,
59                                              const char* protocol, const char* mpiImpl)
60     : BatchManager(parent, host), _protocol(protocol), _username("")
61   {
62     // instanciation of mpi implementation needed to launch executable in batch script
63     _mpiImpl = FactoryMpiImpl(mpiImpl);
64   }
65
66   // Destructeur
67   BatchManager_eClient::~BatchManager_eClient()
68   {
69     // Nothing to do
70     delete _mpiImpl;
71   }
72
73   void BatchManager_eClient::exportInputFiles(const Job& job)
74   {
75     int status;
76     Parametre params = job.getParametre();
77     Versatile V = params[INFILE];
78     Versatile::iterator Vit;
79     _username = string(params[USER]);
80
81     string command = "\"";
82     string copy_command = "\"";
83
84     // Test protocol
85     if( _protocol == "rsh" ) {
86       command += RSH;
87       copy_command += RCP;
88     } else if( _protocol == "ssh" ) {
89       command += SSH;
90       copy_command += SCP;
91     } else
92       throw EmulationException("Unknown protocol : only rsh and ssh are known !");
93
94     command += "\" ";
95     copy_command += "\" ";
96
97     // First step : creating batch tmp files directory
98     if(_username != ""){
99       command += _username + "@";
100     }
101     command += _hostname;
102     command += " mkdir -p ";
103     command += string(params[TMPDIR]);
104     cerr << command.c_str() << endl;
105     status = system(command.c_str());
106     if(status) {
107       std::ostringstream oss;
108       oss << status;
109       std::string ex_mess("Error of connection on remote host ! status = ");
110       ex_mess += oss.str();
111       throw EmulationException(ex_mess.c_str());
112     }
113
114     // Second step : copy fileToExecute into
115     // batch tmp files directory
116     string executeFile = params[EXECUTABLE];
117     if( executeFile.size() > 0 ){
118       command = copy_command;
119       command += string(params[EXECUTABLE]);
120       command += " ";
121       if(_username != ""){
122         command += _username;
123         command += "@";
124       }
125       command += _hostname;
126       command += ":";
127       command += string(params[TMPDIR]);
128       cerr << command.c_str() << endl;
129       status = system(command.c_str());
130       if(status) {
131         std::ostringstream oss;
132         oss << status;
133         std::string ex_mess("Error of connection on remote host ! status = ");
134         ex_mess += oss.str();
135         throw EmulationException(ex_mess.c_str());
136       }
137
138 #ifdef WIN32
139       // On Windows, we make the remote file executable afterward because pscp does not preserve
140       // access permissions on files
141       command = "\"";
142       if( _protocol == "rsh" ) {
143         command += RSH;
144       } else if( _protocol == "ssh" ) {
145         command += SSH;
146       } else
147         throw EmulationException("Unknown protocol : only rsh and ssh are known !");
148
149       command += "\" ";
150       if(_username != ""){
151         command += _username + "@";
152       }
153       command += _hostname;
154       command += " chmod u+x ";
155       command += string(params[TMPDIR]) + "/" + string(params[EXECUTABLE]);
156       cerr << command.c_str() << endl;
157       status = system(command.c_str());
158       if(status) {
159         std::ostringstream oss;
160         oss << status;
161         std::string ex_mess("Error of connection on remote host ! status = ");
162         ex_mess += oss.str();
163         throw EmulationException(ex_mess.c_str());
164       }
165 #endif
166     }
167
168     // Third step : copy filesToExportList into
169     // batch tmp files directory
170     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
171       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
172       Couple inputFile = cpt;
173       command = copy_command;
174       command += inputFile.getLocal();
175       command += " ";
176       if(_username != ""){
177         command += _username;
178         command += "@";
179       }
180       command += _hostname;
181       command += ":";
182       command += inputFile.getRemote();
183       cerr << command.c_str() << endl;
184       status = system(command.c_str());
185       if(status) {
186         std::ostringstream oss;
187         oss << status;
188         std::string ex_mess("Error of connection on remote host ! status = ");
189         ex_mess += oss.str();
190         throw EmulationException(ex_mess.c_str());
191       }
192     }
193
194   }
195
196   void BatchManager_eClient::importOutputFiles( const Job & job, const string directory ) throw(EmulationException)
197   {
198     int status;
199
200     Parametre params = job.getParametre();
201     Versatile V = params[OUTFILE];
202     Versatile::iterator Vit;
203
204     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
205       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
206       Couple outputFile = cpt;
207
208       string command = "\"";
209
210       // Test protocol
211       if( _protocol == "rsh" ) {
212         command += RCP;
213       } else if( _protocol == "ssh" ) {
214         command += SCP;
215       } else
216         throw EmulationException("Unknown protocol : only rsh and ssh are known !");
217
218       command += "\" ";
219
220       if (_username != ""){
221         command += _username;
222         command += "@";
223       }
224       command += _hostname;
225       command += ":";
226       command += outputFile.getRemote();
227       command += " ";
228       command += directory;
229       cerr << command.c_str() << endl;
230       status = system(command.c_str());
231       if(status)
232       {
233         // Try to get what we can (logs files)
234         // throw BatchException("Error of connection on remote host");
235         std::string mess("Copy command failed ! status is :");
236         ostringstream status_str;
237         status_str << status;
238         mess += status_str.str();
239         cerr << mess << endl;
240       }
241     }
242
243   }
244
245   MpiImpl *BatchManager_eClient::FactoryMpiImpl(string mpiImpl) throw(EmulationException)
246   {
247     if(mpiImpl == "lam")
248       return new MpiImpl_LAM();
249     else if(mpiImpl == "mpich1")
250       return new MpiImpl_MPICH1();
251     else if(mpiImpl == "mpich2")
252       return new MpiImpl_MPICH2();
253     else if(mpiImpl == "openmpi")
254       return new MpiImpl_OPENMPI();
255     else if(mpiImpl == "slurm")
256       return new MpiImpl_SLURM();
257     else if(mpiImpl == "prun")
258       return new MpiImpl_PRUN();
259     else if(mpiImpl == "nompi")
260       throw EmulationException("you must specify an mpi implementation for batch manager");
261     else{
262       ostringstream oss;
263       oss << mpiImpl << " : not yet implemented";
264       throw EmulationException(oss.str().c_str());
265     }
266   }
267
268   /**
269    * This method generates a temporary file name with the pattern "<tmpdir>/<prefix>-XXXXXX" where
270    * <tmpdir> is the directory for temporary files (see BatchManager_eClient::getTmpDir()) and the
271    * X's are replaced by random characters. Note that this method is less secure than
272    * BatchManager_eClient::createAndOpenTemporaryFile, so use the latter whenever possible.
273    * \param prefix the prefix to use for the temporary file.
274    * \return a name usable for a temporary file.
275    */
276   string BatchManager_eClient::generateTemporaryFileName(const string & prefix)
277   {
278     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
279     char randstr[7];
280
281     do {
282       sprintf(randstr, "%06d", rand() % 1000000);
283       fileName.replace(fileName.size()-6, 6, randstr);
284     } while (EXISTS(fileName.c_str()));
285
286     return fileName;
287   }
288
289   /**
290    * This method creates a temporary file and opens an output stream to write into this file.
291    * The file is created with the pattern "<tmpdir>/<prefix>-XXXXXX" where <tmpdir> is the directory
292    * for temporary files (see BatchManager_eClient::getTmpDir()) and the X's are replaced by random
293    * characters. The caller is responsible for closing and deleting the file when it is no more used.
294    * \param prefix the prefix to use for the temporary file.
295    * \param outputStream an output stream that will be opened for writing in the temporary file. If
296    * the stream is already open, it will be closed first.
297    * \return the name of the created file.
298    */
299   string BatchManager_eClient::createAndOpenTemporaryFile(const string & prefix, ofstream & outputStream)
300   {
301     if (outputStream.is_open())
302       outputStream.close();
303
304 #ifdef WIN32
305
306     string fileName = generateTemporaryFileName(prefix);
307     outputStream.open(fileName.c_str());
308
309 #else
310
311     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
312     char buf[fileName.size()+1];
313     fileName.copy(buf, fileName.size());
314     buf[fileName.size()] = '\0';
315
316     int fd = mkstemp(buf);
317     if (fd == -1)
318       throw RunTimeException(string("Can't create temporary file ") + fileName);
319     fileName = buf;
320
321     outputStream.open(fileName.c_str());
322     close(fd);  // Close the file descriptor so that the file is not opened twice
323
324 #endif
325
326     if (outputStream.fail())
327       throw RunTimeException(string("Can't open temporary file ") + fileName);
328
329     return fileName;
330   }
331
332   /**
333    * This method finds the name of the directory to use for temporary files in libBatch. This name
334    * is <tempdir>/libBatch-<username>-XXXXXX. <tempdir> is found by looking for environment
335    * variables TEMP, TMP, TEMPDIR, TMPDIR, and defaults to "/tmp" if none of them is defined.
336    * <username> is found by looking for environment variables USER and USERNAME, and defaults to
337    * "unknown". XXXXXX represents random characters. The directory name is generated only once for
338    * each BatchManager_eClient instance, and the directory is created at this moment. Subsequent
339    * calls will always return the same path and the existence of the directory will not be
340    * rechecked.
341    * \return the name of the directory to use for temporary files.
342    */
343   const std::string & BatchManager_eClient::getTmpDir()
344   {
345     if (tmpDirName.empty()) {
346       char * baseDir = getenv("TEMP");
347       if (baseDir == NULL) baseDir = getenv("TMP");
348       if (baseDir == NULL) baseDir = getenv("TEMPDIR");
349       if (baseDir == NULL) baseDir = getenv("TMPDIR");
350       if (baseDir == NULL) baseDir = "/tmp";
351
352       char * userName = getenv("USER");
353       if (userName == NULL) userName = getenv("USERNAME");
354       if (userName == NULL) userName = "unknown";
355
356       string baseName = string(baseDir) + "/libBatch-" + userName + "-XXXXXX";
357       srand(time(NULL));
358
359 #ifdef WIN32
360
361       char randstr[7];
362       do {
363         sprintf(randstr, "%06d", rand() % 1000000);
364         baseName.replace(baseName.size()-6, 6, randstr);
365       } while (EXISTS(baseName.c_str()));
366       if (_mkdir(baseName.c_str()) != 0)
367         throw RunTimeException(string("Can't create temporary directory ") + baseName);
368       tmpDirName = baseName;
369
370 #else
371
372       char buf[baseName.size()+1];
373       baseName.copy(buf, baseName.size());
374       buf[baseName.size()] = '\0';
375       if (mkdtemp(buf) == NULL)
376         throw RunTimeException(string("Can't create temporary directory ") + baseName);
377       tmpDirName = buf;
378
379 #endif
380
381     }
382
383     return tmpDirName;
384   }
385
386 }