Salome HOME
Fix compilation with gcc 4.7
[tools/libbatch.git] / src / Core / Batch_BatchManager_eClient.cxx
1 //  Copyright (C) 2007-2012  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 <unistd.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <ctime>
37 #include <iostream>
38 #include <fstream>
39 #include <sstream>
40
41 #include <stdlib.h>
42
43 #ifdef WIN32
44 #include <direct.h>
45 #include <io.h>
46 #endif
47
48 #include <Batch_config.h>
49
50 #include "Batch_Constants.hxx"
51 #include "Batch_BatchManager_eClient.hxx"
52 #include "Batch_RunTimeException.hxx"
53
54 #ifdef MSVC
55 #define EXISTS(path) _access_s(path, 0) == 0
56 #else
57 #define EXISTS(path) access(path, F_OK) == 0
58 #endif
59
60 using namespace std;
61
62
63 namespace Batch {
64
65   BatchManager_eClient::BatchManager_eClient(const Batch::FactBatchManager * parent, const char* host,
66                                              const char * username,
67                                              CommunicationProtocolType protocolType, const char* mpiImpl)
68     : BatchManager(parent, host), _protocol(CommunicationProtocol::getInstance(protocolType)),
69       _username(username)
70   {
71     // instanciation of mpi implementation needed to launch executable in batch script
72     _mpiImpl = FactoryMpiImpl(mpiImpl);
73   }
74
75   // Destructeur
76   BatchManager_eClient::~BatchManager_eClient()
77   {
78     if (_mpiImpl)
79       delete _mpiImpl;
80   }
81
82   void BatchManager_eClient::exportInputFiles(const Job& job)
83   {
84     int status;
85     Parametre params = job.getParametre();
86     const Versatile & V = params[INFILE];
87     Versatile::const_iterator Vit;
88
89     status = _protocol.makeDirectory(string(params[TMPDIR]) + "/logs", _hostname, _username);
90     if(status) {
91       std::ostringstream oss;
92       oss << status;
93       std::string ex_mess("Error of connection on remote host ! status = ");
94       ex_mess += oss.str();
95       throw EmulationException(ex_mess.c_str());
96     }
97
98     // Second step : copy fileToExecute into
99     // batch tmp files directory
100     string executeFile = params[EXECUTABLE];
101     if (executeFile.size() != 0) {
102       status = _protocol.copyFile(executeFile, "", "",
103                                   params[TMPDIR], _hostname, _username);
104       if(status) {
105         std::ostringstream oss;
106         oss << status;
107         std::string ex_mess("Error of connection on remote host ! status = ");
108         ex_mess += oss.str();
109         throw EmulationException(ex_mess.c_str());
110       }
111
112 #ifdef WIN32
113       // On Windows, we make the remote file executable afterward because
114       // pscp does not preserve access permissions on files
115       string subCommand = string("chmod u+x ") + string(params[TMPDIR]) + "/" +
116                           string(params[EXECUTABLE]);
117       string command = _protocol.getExecCommand(subCommand, _hostname, _username);
118       cerr << command.c_str() << endl;
119       status = system(command.c_str());
120       if(status) {
121         std::ostringstream oss;
122         oss << status;
123         std::string ex_mess("Error of connection on remote host ! status = ");
124         ex_mess += oss.str();
125         throw EmulationException(ex_mess.c_str());
126       }
127 #endif
128     }
129
130     // Third step : copy filesToExportList into
131     // batch tmp files directory
132     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
133       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
134       Couple inputFile = cpt;
135       status = _protocol.copyFile(inputFile.getLocal(), "", "",
136                                   inputFile.getRemote(), _hostname, _username);
137       if(status) {
138         std::ostringstream oss;
139         oss << status;
140         std::string ex_mess("Error of connection on remote host ! status = ");
141         ex_mess += oss.str();
142         throw EmulationException(ex_mess.c_str());
143       }
144     }
145
146   }
147
148   void BatchManager_eClient::importOutputFiles( const Job & job, const string directory )
149   {
150     Parametre params = job.getParametre();
151     const Versatile & V = params[OUTFILE];
152     Versatile::const_iterator Vit;
153
154     // Create local result directory
155     int status = CommunicationProtocol::getInstance(SH).makeDirectory(directory, "", "");
156     if (status) {
157       string mess("Directory creation failed. Status is :");
158       ostringstream status_str;
159       status_str << status;
160       mess += status_str.str();
161       cerr << mess << endl;
162     }
163
164     for(Vit=V.begin(); Vit!=V.end(); Vit++) {
165       CoupleType cpt  = *static_cast< CoupleType * >(*Vit);
166       Couple outputFile = cpt;
167       status = _protocol.copyFile(outputFile.getRemote(), _hostname, _username,
168                                   directory, "", "");
169       if (status) {
170         // Try to get what we can (logs files)
171         // throw BatchException("Error of connection on remote host");
172         std::string mess("Copy command failed ! status is :");
173         ostringstream status_str;
174         status_str << status;
175         mess += status_str.str();
176         cerr << mess << endl;
177       }
178     }
179
180     // Copy logs
181     status = _protocol.copyFile(string(params[TMPDIR]) + string("/logs"), _hostname, _username,
182                                 directory, "", "");
183     if (status) {
184       std::string mess("Copy logs directory failed ! status is :");
185       ostringstream status_str;
186       status_str << status;
187       mess += status_str.str();
188       cerr << mess << endl;
189     }
190
191   }
192
193   bool BatchManager_eClient::importDumpStateFile( const Job & job, const string directory )
194   {
195     Parametre params = job.getParametre();
196
197     // Create local result directory
198     int status = CommunicationProtocol::getInstance(SH).makeDirectory(directory, "", "");
199     if (status) {
200       string mess("Directory creation failed. Status is :");
201       ostringstream status_str;
202       status_str << status;
203       mess += status_str.str();
204       cerr << mess << endl;
205     }
206
207     bool ret = true;
208     status = _protocol.copyFile(string(params[TMPDIR]) + string("/dumpState*.xml"), _hostname, _username,
209                                 directory, "", "");
210     if (status) {
211       // Try to get what we can (logs files)
212       // throw BatchException("Error of connection on remote host");
213       std::string mess("Copy command failed ! status is :");
214       ostringstream status_str;
215       status_str << status;
216       mess += status_str.str();
217       cerr << mess << endl;
218       ret = false;
219     }
220     return ret;
221   }
222
223   MpiImpl *BatchManager_eClient::FactoryMpiImpl(string mpiImpl)
224   {
225     if(mpiImpl == "lam")
226       return new MpiImpl_LAM();
227     else if(mpiImpl == "mpich1")
228       return new MpiImpl_MPICH1();
229     else if(mpiImpl == "mpich2")
230       return new MpiImpl_MPICH2();
231     else if(mpiImpl == "openmpi")
232       return new MpiImpl_OPENMPI();
233     else if(mpiImpl == "slurm")
234       return new MpiImpl_SLURM();
235     else if(mpiImpl == "prun")
236       return new MpiImpl_PRUN();
237     else if(mpiImpl == "nompi")
238       return NULL;
239     else{
240       ostringstream oss;
241       oss << mpiImpl << " : not yet implemented";
242       throw EmulationException(oss.str().c_str());
243     }
244   }
245
246   /**
247    * This method generates a temporary file name with the pattern "<tmpdir>/<prefix>-XXXXXX" where
248    * <tmpdir> is the directory for temporary files (see BatchManager_eClient::getTmpDir()) and the
249    * X's are replaced by random characters. Note that this method is less secure than
250    * BatchManager_eClient::createAndOpenTemporaryFile, so use the latter whenever possible.
251    * \param prefix the prefix to use for the temporary file.
252    * \return a name usable for a temporary file.
253    */
254   string BatchManager_eClient::generateTemporaryFileName(const string & prefix)
255   {
256     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
257     char randstr[7];
258
259     do {
260       sprintf(randstr, "%06d", rand() % 1000000);
261       fileName.replace(fileName.size()-6, 6, randstr);
262     } while (EXISTS(fileName.c_str()));
263
264     return fileName;
265   }
266
267   /**
268    * This method creates a temporary file and opens an output stream to write into this file.
269    * The file is created with the pattern "<tmpdir>/<prefix>-XXXXXX" where <tmpdir> is the directory
270    * for temporary files (see BatchManager_eClient::getTmpDir()) and the X's are replaced by random
271    * characters. The caller is responsible for closing and deleting the file when it is no more used.
272    * \param prefix the prefix to use for the temporary file.
273    * \param outputStream an output stream that will be opened for writing in the temporary file. If
274    * the stream is already open, it will be closed first.
275    * \return the name of the created file.
276    */
277   string BatchManager_eClient::createAndOpenTemporaryFile(const string & prefix, ofstream & outputStream)
278   {
279     if (outputStream.is_open())
280       outputStream.close();
281
282 #ifdef WIN32
283
284     string fileName = generateTemporaryFileName(prefix);
285     // Open the file as binary to avoid problems with Windows newlines
286     outputStream.open(fileName.c_str(), ios_base::binary | ios_base::out);
287
288 #else
289
290     string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
291     char * buf = new char[fileName.size()+1];
292     fileName.copy(buf, fileName.size());
293     buf[fileName.size()] = '\0';
294
295     int fd = mkstemp(buf);
296     if (fd == -1) {
297       delete[] buf;
298       throw RunTimeException(string("Can't create temporary file ") + fileName);
299     }
300     fileName = buf;
301     delete[] buf;
302
303     outputStream.open(fileName.c_str());
304     close(fd);  // Close the file descriptor so that the file is not opened twice
305
306 #endif
307
308     if (outputStream.fail())
309       throw RunTimeException(string("Can't open temporary file ") + fileName);
310
311     return fileName;
312   }
313
314   /**
315    * This method finds the name of the directory to use for temporary files in libBatch. This name
316    * is <tempdir>/libBatch-<username>-XXXXXX. <tempdir> is found by looking for environment
317    * variables TEMP, TMP, TEMPDIR, TMPDIR, and defaults to "/tmp" if none of them is defined.
318    * <username> is found by looking for environment variables USER and USERNAME, and defaults to
319    * "unknown". XXXXXX represents random characters. The directory name is generated only once for
320    * each BatchManager_eClient instance, and the directory is created at this moment. Subsequent
321    * calls will always return the same path and the existence of the directory will not be
322    * rechecked.
323    * \return the name of the directory to use for temporary files.
324    */
325   const std::string & BatchManager_eClient::getTmpDir()
326   {
327     if (tmpDirName.empty()) {
328       const char * baseDir = getenv("TEMP");
329       if (baseDir == NULL) baseDir = getenv("TMP");
330       if (baseDir == NULL) baseDir = getenv("TEMPDIR");
331       if (baseDir == NULL) baseDir = getenv("TMPDIR");
332       if (baseDir == NULL) baseDir = "/tmp";
333
334       const char * userName = getenv("USER");
335       if (userName == NULL) userName = getenv("USERNAME");
336       if (userName == NULL) userName = "unknown";
337
338       string baseName = string(baseDir) + "/libBatch-" + userName + "-XXXXXX";
339       srand(time(NULL));
340
341 #ifdef WIN32
342
343       char randstr[7];
344       do {
345         sprintf(randstr, "%06d", rand() % 1000000);
346         baseName.replace(baseName.size()-6, 6, randstr);
347       } while (EXISTS(baseName.c_str()));
348       if (_mkdir(baseName.c_str()) != 0)
349         throw RunTimeException(string("Can't create temporary directory ") + baseName);
350       tmpDirName = baseName;
351
352 #else
353
354       char * buf = new char[baseName.size() + 1];
355       baseName.copy(buf, baseName.size());
356       buf[baseName.size()] = '\0';
357       if (mkdtemp(buf) == NULL) {
358         delete[] buf;
359         throw RunTimeException(string("Can't create temporary directory ") + baseName);
360       }
361       tmpDirName = buf;
362       delete[] buf;
363
364 #endif
365
366     }
367
368     return tmpDirName;
369   }
370
371 }