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