#
MACRO(FIND_LOCAL_COMMAND VAR COMMAND)
- FIND_PROGRAM(${VAR} ${COMMAND})
+ FIND_PROGRAM(${VAR} NAMES ${COMMAND} ${ARGN})
IF (${VAR})
MESSAGE(STATUS "${COMMAND} found : ${${VAR}}")
ELSE (${VAR})
FIND_LOCAL_COMMAND(CP cp)
FIND_LOCAL_COMMAND(RSH rsh)
FIND_LOCAL_COMMAND(RCP rcp)
-FIND_LOCAL_COMMAND(SSH ssh)
-FIND_LOCAL_COMMAND(SCP scp)
+FIND_LOCAL_COMMAND(SSH ssh plink)
+FIND_LOCAL_COMMAND(SCP scp pscp)
$ cmake ../libBatch-X.X.X (X's are version numbers)
$ make package (for binary package)
$ make package_source (for source package)
+
+
+5. Note for the users of Microsoft Windows operating system
+
+
+The support for this library on Windows operating system is still partial and
+experimental. Some features are not and will not be implemented for this
+system. Nevertheless it is possible to compile the library and launch some
+batch jobs from a computer running Windows. This section only gives some
+guidelines, without any warranty, to install libBatch on Windows.
+
+First you will need to install a library providing pthread implementation. For
+this we tested Pthreads-win32 (http://sourceware.org/pthreads-win32/) but other
+implementations may exist.
+
+You will also need a compiler for Win32 platform. We tested MinGW and MSYS
+utilities (http://www.mingw.org/) but another compiler might also work.
+
+Then you will have to install and run CMake, and you should be able to compile
+libBatch and run some basic examples.
+
+If you need to use SSH protocol to submit jobs to your cluster, you will need
+a SSH implementation for Windows operating system. PuTTY is a good choice for
+that (http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html). Once
+installed, add the path to your PuTTY installation in the PATH environment
+variable and run CMake. The build system will detect it and the libBatch will
+use it for SSH-based transfers with the server.
#include "Batch_BatchManager_eClient.hxx"
#include "Batch_RunTimeException.hxx"
-#include "Batch_NotYetImplementedException.hxx"
#include <iostream>
#include <fstream>
#include <sstream>
-#include <sys/stat.h>
-#include <string.h>
-#include <stdlib.h>
+
+#ifdef WIN32
+#include <direct.h>
+#endif
+
+#include "Batch_config.h"
using namespace std;
Parametre params = job.getParametre();
Versatile V = params[INFILE];
Versatile::iterator Vit;
- string command;
- string copy_command;
_username = string(params[USER]);
+ string command = "\"";
+ string copy_command = "\"";
+
// Test protocol
- if( _protocol == "rsh" )
- copy_command = "rcp ";
- else if( _protocol == "ssh" )
- copy_command = "scp ";
- else
+ if( _protocol == "rsh" ) {
+ command += RSH;
+ copy_command += RCP;
+ } else if( _protocol == "ssh" ) {
+ command += SSH;
+ copy_command += SCP;
+ } else
throw EmulationException("Unknown protocol : only rsh and ssh are known !");
+ command += "\" ";
+ copy_command += "\" ";
+
// First step : creating batch tmp files directory
- command = _protocol;
- command += " ";
if(_username != ""){
- command += _username;
- command += "@";
+ command += _username + "@";
}
command += _hostname;
- command += " \"mkdir -p ";
+ command += " mkdir -p ";
command += string(params[TMPDIR]);
- command += "\"" ;
cerr << command.c_str() << endl;
status = system(command.c_str());
if(status) {
ex_mess += oss.str();
throw EmulationException(ex_mess.c_str());
}
+
+#ifdef WIN32
+ // On Windows, we make the remote file executable afterward because pscp does not preserve
+ // access permissions on files
+ command = "\"";
+ if( _protocol == "rsh" ) {
+ command += RSH;
+ } else if( _protocol == "ssh" ) {
+ command += SSH;
+ } else
+ throw EmulationException("Unknown protocol : only rsh and ssh are known !");
+
+ command += "\" ";
+ if(_username != ""){
+ command += _username + "@";
+ }
+ command += _hostname;
+ command += " chmod u+x ";
+ command += string(params[TMPDIR]) + "/" + string(params[EXECUTABLE]);
+ cerr << command.c_str() << endl;
+ status = system(command.c_str());
+ if(status) {
+ std::ostringstream oss;
+ oss << status;
+ std::string ex_mess("Error of connection on remote host ! status = ");
+ ex_mess += oss.str();
+ throw EmulationException(ex_mess.c_str());
+ }
+#endif
}
// Third step : copy filesToExportList into
void BatchManager_eClient::importOutputFiles( const Job & job, const string directory ) throw(EmulationException)
{
- string command;
int status;
Parametre params = job.getParametre();
for(Vit=V.begin(); Vit!=V.end(); Vit++) {
CoupleType cpt = *static_cast< CoupleType * >(*Vit);
Couple outputFile = cpt;
- if( _protocol == "rsh" )
- command = "rcp ";
- else if( _protocol == "ssh" )
- command = "scp ";
- else
- throw EmulationException("Unknown protocol");
+
+ string command = "\"";
+
+ // Test protocol
+ if( _protocol == "rsh" ) {
+ command += RCP;
+ } else if( _protocol == "ssh" ) {
+ command += SCP;
+ } else
+ throw EmulationException("Unknown protocol : only rsh and ssh are known !");
+
+ command += "\" ";
if (_username != ""){
command += _username;
}
}
+ /**
+ * This method generates a temporary file name with the pattern "<tmpdir>/<prefix>-XXXXXX" where
+ * <tmpdir> is the directory for temporary files (see BatchManager_eClient::getTmpDir()) and the
+ * X's are replaced by random characters. Note that this method is less secure than
+ * BatchManager_eClient::createAndOpenTemporaryFile, so use the latter whenever possible.
+ * \param prefix the prefix to use for the temporary file.
+ * \return a name usable for a temporary file.
+ */
+ string BatchManager_eClient::generateTemporaryFileName(const string & prefix)
+ {
+ string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
+ char randstr[7];
+
+ do {
+ sprintf(randstr, "%06d", rand() % 1000000);
+ fileName.replace(fileName.size()-6, 6, randstr);
+ } while (access(fileName.c_str(), F_OK) == 0);
+
+ return fileName;
+ }
+
/**
* This method creates a temporary file and opens an output stream to write into this file.
- * The file is created with the pattern "/tmp/batch_XXXXXX" where the X's are replaced by random
+ * The file is created with the pattern "<tmpdir>/<prefix>-XXXXXX" where <tmpdir> is the directory
+ * for temporary files (see BatchManager_eClient::getTmpDir()) and the X's are replaced by random
* characters. The caller is responsible for closing and deleting the file when it is no more used.
+ * \param prefix the prefix to use for the temporary file.
* \param outputStream an output stream that will be opened for writing in the temporary file. If
* the stream is already open, it will be closed first.
* \return the name of the created file.
*/
- string BatchManager_eClient::createAndOpenTemporaryFile(ofstream & outputStream) const
+ string BatchManager_eClient::createAndOpenTemporaryFile(const string & prefix, ofstream & outputStream)
{
- string fileName;
+ if (outputStream.is_open())
+ outputStream.close();
+
#ifdef WIN32
- throw NotYetImplementedException("Temporary file creation in Batch library has not been ported to Windows yet");
+
+ string fileName = generateTemporaryFileName(prefix);
+ outputStream.open(fileName.c_str());
+
#else
- char * tmpFileName = strdup("/tmp/batch_XXXXXX");
- int fd = mkstemp(tmpFileName);
+
+ string fileName = getTmpDir() + "/" + prefix + "-XXXXXX";
+ char buf[fileName.size()+1];
+ fileName.copy(buf, fileName.size());
+ buf[fileName.size()] = '\0';
+
+ int fd = mkstemp(buf);
if (fd == -1)
- {
- throw RunTimeException("Can't create temporary file");
- }
+ throw RunTimeException(string("Can't create temporary file ") + fileName);
+ fileName = buf;
- if (outputStream.is_open())
- outputStream.close();
- outputStream.open(tmpFileName);
+ outputStream.open(fileName.c_str());
close(fd); // Close the file descriptor so that the file is not opened twice
- fileName = tmpFileName;
- delete[] tmpFileName;
#endif
+
+ if (outputStream.fail())
+ throw RunTimeException(string("Can't open temporary file ") + fileName);
+
return fileName;
}
+ /**
+ * This method finds the name of the directory to use for temporary files in libBatch. This name
+ * is <tempdir>/libBatch-<username>-XXXXXX. <tempdir> is found by looking for environment
+ * variables TEMP, TMP, TEMPDIR, TMPDIR, and defaults to "/tmp" if none of them is defined.
+ * <username> is found by looking for environment variables USER and USERNAME, and defaults to
+ * "unknown". XXXXXX represents random characters. The directory name is generated only once for
+ * each BatchManager_eClient instance, and the directory is created at this moment. Subsequent
+ * calls will always return the same path and the existence of the directory will not be
+ * rechecked.
+ * \return the name of the directory to use for temporary files.
+ */
+ const std::string & BatchManager_eClient::getTmpDir()
+ {
+ if (tmpDirName.empty()) {
+ char * baseDir = getenv("TEMP");
+ if (baseDir == NULL) baseDir = getenv("TMP");
+ if (baseDir == NULL) baseDir = getenv("TEMPDIR");
+ if (baseDir == NULL) baseDir = getenv("TMPDIR");
+ if (baseDir == NULL) baseDir = "/tmp";
+
+ char * userName = getenv("USER");
+ if (userName == NULL) userName = getenv("USERNAME");
+ if (userName == NULL) userName = "unknown";
+
+ string baseName = string(baseDir) + "/libBatch-" + userName + "-XXXXXX";
+ srand(time(NULL));
+
+#ifdef WIN32
+
+ char randstr[7];
+ do {
+ sprintf(randstr, "%06d", rand() % 1000000);
+ baseName.replace(baseName.size()-6, 6, randstr);
+ } while (access(baseName.c_str(), F_OK) == 0);
+ if (_mkdir(baseName.c_str()) != 0)
+ throw RunTimeException(string("Can't create temporary directory ") + baseName);
+ tmpDirName = baseName;
+
+#else
+
+ char buf[baseName.size()+1];
+ baseName.copy(buf, baseName.size());
+ buf[baseName.size()] = '\0';
+ if (mkdtemp(buf) == NULL)
+ throw RunTimeException(string("Can't create temporary directory ") + baseName);
+ tmpDirName = buf;
+
+#endif
+
+ }
+
+ return tmpDirName;
+ }
+
}
std::string _username; // username to access _hostname
MpiImpl *_mpiImpl; // Mpi implementation to launch executable in batch script
- std::string createAndOpenTemporaryFile(std::ofstream & outputStream) const;
+ std::string generateTemporaryFileName(const std::string & prefix);
+ std::string createAndOpenTemporaryFile(const std::string & prefix, std::ofstream & outputStream);
MpiImpl* FactoryMpiImpl(std::string mpiImpl) throw(EmulationException);
void exportInputFiles(const Job & job);
+ const std::string & getTmpDir();
private:
+ std::string tmpDirName; // Path to the directory for temporary files
};
}
ofstream tempOutputFile;
- std::string TmpFileName = createAndOpenTemporaryFile(tempOutputFile);
+ std::string TmpFileName = createAndOpenTemporaryFile("LSF-script", tempOutputFile);
tempOutputFile << "#! /bin/sh -f" << endl ;
if (queue != "")
throw RunTimeException("Parameter \"EXECUTABLE\" is mandatory for local batch submission");
}
- string name = (param.find(NAME) != param.end()) ? param[NAME] : param[EXECUTABLE];
-
if (param.find(ARGUMENTS) != param.end()) {
Versatile V = param[ARGUMENTS];
}
}
-
UNDER_LOCK( cout << "*** exec_command = " << exec_command << endl );
// launch the new process
BOOL res = CreateProcess(NULL, buffer, NULL, NULL, FALSE,
- 0, chNewEnv, NULL, &si, &pi);
+ DETACHED_PROCESS, chNewEnv, NULL, &si, &pi);
if (!res) throw RunTimeException("Error while creating new process");
}
}
-
Versatile new_arguments;
new_arguments.setMaxSize(0);
new_arguments = string(param[EXECUTIONHOST]);
{
ostringstream fullsource;
if (host_source.size() != 0) {
+ if (user_source.size() != 0) {
+ fullsource << user_source << "@";
+ }
fullsource << host_source << ":";
}
fullsource << source;
ostringstream fulldestination;
if (host_destination.size() != 0) {
+ if (user_destination.size() != 0) {
+ fulldestination << user_destination << "@";
+ }
fulldestination << host_destination << ":";
}
fulldestination << destination;
ostringstream copy_cmd;
// Option -p is used to keep the same permissions for the destination file (particularly useful to keep scripts
// executable when copying them)
- copy_cmd << SCP << " -p " << fullsource.str() << " " << fulldestination.str();
+ copy_cmd << "\"" << SCP << "\" -p " << fullsource.str() << " " << fulldestination.str();
return copy_cmd.str();
}
string BatchManager_Local_SSH::exec_command(Parametre & param) const
{
ostringstream exec_sub_cmd;
- exec_sub_cmd << "cd " << param[WORKDIR] << ";";
- exec_sub_cmd << param[EXECUTABLE];
+#ifdef WIN32
+ exec_sub_cmd << "\"";
+#endif
+ exec_sub_cmd << "cd " << param[WORKDIR] << " && " << param[EXECUTABLE];
if (param.find(ARGUMENTS) != param.end()) {
Versatile V = param[ARGUMENTS];
exec_sub_cmd << " " << arg;
}
}
+#ifdef WIN32
+ exec_sub_cmd << "\"";
+#endif
Versatile new_arguments;
const std::string & host_destination,
const std::string & destination) const
{
- string host = (host_destination.size()) ? host_destination : "localhost:";
+ string fulldestination = (host_destination.size()) ? host_destination : "localhost";
+ if (user_destination.size() != 0) {
+ fulldestination += " -l " + user_destination;
+ }
+ // We consider here that the remote system is UNIX-like and has a "rm" command. Using the
+ // RM macro would be pointless here since the remote system is different from the local one.
ostringstream remove_cmd;
- remove_cmd << SSH << " " << host << " \"" << RM << " " << destination << "\"";
+ remove_cmd << "\"" << SSH << "\" " << fulldestination << " \"rm " << destination << "\"";
return remove_cmd.str();
}
}
SET (TEST_LOCAL_SSH_EXECUTION_HOST "localhost" CACHE STRING
"Execution host for SSH Batch test (only necessary for test target)")
+SET (TEST_LOCAL_SSH_USER $ENV{USER} CACHE STRING
+ "User name on the execution host for SSH Batch test (only necessary for test target)")
SET (TEST_LOCAL_SSH_WORK_DIR "/tmp" CACHE STRING
"Work directory for SSH Batch test (only necessary for test target)")
Job job;
// ... and its parameters ...
Parametre p;
- p["EXECUTABLE"] = "./copied-test-script.sh";
+ p["EXECUTABLE"] = "source copied-test-script.sh";
p["NAME"] = "Test_Local_SSH";
p["WORKDIR"] = TEST_LOCAL_SSH_WORK_DIR;
p["INFILE"] = Couple("seta.sh", "copied-seta.sh");
p["INFILE"] += Couple("test-script.sh", "copied-test-script.sh");
p["OUTFILE"] = Couple("result.txt", "orig-result.txt");
p["EXECUTIONHOST"] = TEST_LOCAL_SSH_EXECUTION_HOST;
+ p["USER"] = TEST_LOCAL_SSH_USER;
job.setParametre(p);
// ... and its environment (SSH_AUTH_SOCK env var is important for ssh agent authentication)
Environnement e;
#define TEST_LOCAL_RSH_WORK_DIR "${TEST_LOCAL_RSH_WORK_DIR}"
#define TEST_LOCAL_SSH_EXECUTION_HOST "${TEST_LOCAL_SSH_EXECUTION_HOST}"
+#define TEST_LOCAL_SSH_USER "${TEST_LOCAL_SSH_USER}"
#define TEST_LOCAL_SSH_WORK_DIR "${TEST_LOCAL_SSH_WORK_DIR}"
#ifdef WIN32
#include <fstream>
#include <sstream>
#include <sys/stat.h>
-#include <string.h>
-#include <stdlib.h>
+#include <libgen.h>
#include "Batch_BatchManager_ePBS.hxx"
-#ifdef WIN32
-# include <time.h>
-# include <io.h>
-#else
-# include <libgen.h>
-#endif
+#include "Batch_config.h"
using namespace std;
// Methode pour le controle des jobs : soumet un job au gestionnaire
const JobId BatchManager_ePBS::submitJob(const Job & job)
{
-#ifdef WIN32
- throw NotYetImplementedException("PBS emulation not supported on Windows platform yet");
-#else
int status;
Parametre params = job.getParametre();
const std::string dirForTmpFiles = params[TMPDIR];
// build batch script for job
buildBatchScript(job);
- // create log dir (local) and define name of log file
- string logDir = "/tmp/logs/";
- mkdir(logDir.c_str(), S_IRWXU);
- logDir += getenv("USER");
- mkdir(logDir.c_str(), S_IRWXU);
- string logFile = logDir + "/batchSalome_";
- srand ( time(NULL) );
- int ir = rand();
- ostringstream oss;
- oss << ir;
- logFile += oss.str();
- logFile += ".log";
-
- string command;
+ // define name of log file (local)
+ string logFile = generateTemporaryFileName("PBS-submitlog");
// define command to submit batch
- command = _protocol;
- command += " ";
+ string command = "\"";
+
+ // Test protocol
+ if( _protocol == "rsh" )
+ command += RSH;
+ else if( _protocol == "ssh" )
+ command += SSH;
+ else
+ throw EmulationException("Unknown protocol : only rsh and ssh are known !");
+
+ command += "\" ";
if(_username != ""){
- command += _username;
- command += "@";
+ command += _username + "@";
}
- command += _hostname;
- command += " \"cd " ;
+ command += _hostname + " ";
+#ifndef WIN32
+ command += "\"";
+#endif
+ command += "cd " ;
command += dirForTmpFiles ;
command += "; qsub " ;
command += fileNameToExecute ;
- command += "_Batch.sh\" > ";
+ command += "_Batch.sh";
+#ifndef WIN32
+ command += "\"";
+#endif
+ command += " > ";
command += logFile;
cerr << command.c_str() << endl;
status = system(command.c_str());
JobId id(this, strjob);
return id;
-#endif //WIN32
}
// Methode pour le controle des jobs : retire un job du gestionnaire
istringstream iss(jobid.getReference());
iss >> id;
- // define name of log file
- string logFile="/tmp/logs/";
- logFile += getenv("USER");
- logFile += "/batchSalome_";
+ // define name of log file (local)
+ string logFile = generateTemporaryFileName(string("PBS-querylog-id") + jobid.getReference());
- ostringstream oss;
- oss << this << "_" << id;
- logFile += oss.str();
- logFile += ".log";
+ // define command to query batch
+ string command = "\"";
- string command;
- int status;
+ // Test protocol
+ if( _protocol == "rsh" )
+ command += RSH;
+ else if( _protocol == "ssh" )
+ command += SSH;
+ else
+ throw EmulationException("Unknown protocol : only rsh and ssh are known !");
- // define command to submit batch
- command = _protocol;
- command += " ";
+ command += "\" ";
if (_username != ""){
- command += _username;
- command += "@";
+ command += _username + "@";
}
- command += _hostname;
- command += " \"qstat -f " ;
+ command += _hostname + " ";
+#ifndef WIN32
+ command += "\"";
+#endif
+ command += "qstat -f " ;
command += iss.str();
- command += "\" > ";
+#ifndef WIN32
+ command += "\"";
+#endif
+ command += " > ";
command += logFile;
cerr << command.c_str() << endl;
- status = system(command.c_str());
+ int status = system(command.c_str());
if(status && status != 153 && status != 256*153)
throw EmulationException("Error of connection on remote host");
void BatchManager_ePBS::buildBatchScript(const Job & job)
{
-#ifndef WIN32 //TODO: need for porting on Windows
int status;
Parametre params = job.getParametre();
Environnement env = job.getEnvironnement();
}
ofstream tempOutputFile;
- std::string TmpFileName = createAndOpenTemporaryFile(tempOutputFile);
+ std::string TmpFileName = createAndOpenTemporaryFile("PBS-script", tempOutputFile);
tempOutputFile << "#! /bin/sh -f" << endl;
if (queue != "")
TmpFileName.c_str(), 0x1ED);
cerr << TmpFileName.c_str() << endl;
- string command;
- if( _protocol == "rsh" )
- command = "rcp ";
- else if( _protocol == "ssh" )
- command = "scp ";
- else
- throw EmulationException("Unknown protocol");
+ string command = "\"";
+
+ // Test protocol
+ if( _protocol == "rsh" ) {
+ command += RCP;
+ } else if( _protocol == "ssh" ) {
+ command += SCP;
+ } else
+ throw EmulationException("Unknown protocol : only rsh and ssh are known !");
+
+ command += "\" ";
+
command += TmpFileName;
command += " ";
if(_username != ""){
throw EmulationException("Error of connection on remote host");
remove(TmpFileName.c_str());
-#endif
}
}
// Wait for the end of the job
string state = "Undefined";
- for (int i=0 ; i<10 && state != "U"; i++) {
+ for (int i=0 ; i<60 && state != "U"; i++) {
sleep(2);
JobInfo jinfo = jobid.queryJob();
state = jinfo.getParametre()["STATE"].str();
}
ofstream tempOutputFile;
- std::string TmpFileName = createAndOpenTemporaryFile(tempOutputFile);
+ std::string TmpFileName = createAndOpenTemporaryFile("SGE-script", tempOutputFile);
tempOutputFile << "#! /bin/sh -f" << endl;
if (queue != "")