/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2014 Frederik Heber. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MoleCuilder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MoleCuilder. If not, see .
*/
/*
* SystemCommandJob.cpp
*
* Originally taken from my JobMarket project at 1.1.4.
*
* Created on: Feb 5, 2012
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include
#include
#include "CodePatterns/MemDebug.hpp"
// include headers that implement a archive in simple text format
// otherwise BOOST_CLASS_EXPORT_IMPLEMENT has no effect
#include
#include
#ifdef HAVE_JOBMARKET
#include "JobMarket/Jobs/SystemCommandJob.hpp"
#else
#include "Jobs/JobMarket/SystemCommandJob.hpp"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include "CodePatterns/Chronos.hpp"
#include "CodePatterns/Info.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/toString.hpp"
/** Constructor for class SystemCommandJob.
*
*/
SystemCommandJob::SystemCommandJob() :
FragmentJob(JobId::IllegalJob)
{}
/** Constructor for class SystemCommandJob.
*
* \param _command command to execute
* \param _outputfile configuration file for solver
* \param _JobId unique id of this job
* \param _suffix possible suffix for temporary filenames, may be left empty
*/
SystemCommandJob::SystemCommandJob(
const std::string &_command,
const std::string &_outputfile,
const JobId_t _JobId,
const std::string &_suffix) :
FragmentJob(_JobId),
command(_command),
suffix(_suffix),
outputfile(_outputfile)
{}
/** Destructor for class SystemCommandJob.
*
*/
SystemCommandJob::~SystemCommandJob()
{}
/** Work routine of this SystemCommandJob.
*
* This function encapsulates all the work that has to be done to generate
* a FragmentResult. Hence, the FragmentWorker does not need to know anything
* about the operation: it just receives it and executes this function.
*
* We obtain FragmentResult::exitflag from std::system's return value and
* FragmentResult::result from the contents of the piped output file.
*
* \return result of this job
*/
FragmentResult::ptr SystemCommandJob::Work()
{
// Info info((std::string(__FUNCTION__)+std::string(", id #")+toString(getId())).c_str());
// the following is taken from http://stackoverflow.com/questions/2746168/how-to-construct-a-c-fstream-from-a-posix-file-descriptor
char *tmpTemplate = NULL;
const std::string idstring(toString(getId()));
const size_t idlength = idstring.length();
{
std::string Template("/tmp/XXXXXXX_");
ASSERT(idlength <= 8,
"SystemCommandJob::Work() - the id contains more than 8 digits.");
Template += idstring;
if (!suffix.empty())
Template += suffix;
LOG(2, "DEBUG: Temporary template is " << Template << ".");
tmpTemplate = new char[Template.length()+1];
strncpy(tmpTemplate, Template.c_str(), Template.length()+1);
}
const int fd = mkstemps(tmpTemplate, idlength + suffix.length()+1);
// write outputfile to temporary file
LOG(2, "DEBUG: Temporary file is " << tmpTemplate << ".");
std::ofstream output(tmpTemplate);
ASSERT(output.is_open(),
"SystemCommandJob::Work() - the temporary file could not be opened.");
output << outputfile << std::endl;
output.close();
// fork into subprocess and launch command
const std::string WorkName = std::string("Work #")+idstring;
Chronos::getInstance().startTiming(WorkName);
FragmentResult::ptr s;
{
// open process
std::string command_args = command+std::string(" ")+tmpTemplate;
LOG(1, "INFO: Executing '" << command_args << "'.");
FILE *stdoutstream = NULL;
while (stdoutstream == NULL) {
stdoutstream = popen(command_args.c_str(), "r");
if (stdoutstream == NULL) {
ELOG(2, "File descriptors are full, waiting for 1 sec...");
sleep(1);
}
}
int exitflag;
// read stdout from process
const int fd = fileno(stdoutstream);
fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
boost::iostreams::stream stdoutfile(fd, boost::iostreams::never_close_handle);
stdoutfile.set_auto_close(false); // https://svn.boost.org/trac/boost/ticket/3517
std::istreambuf_iterator beginiter = (std::istreambuf_iterator(stdoutfile));
std::string resultstring( beginiter, std::istreambuf_iterator());
// construct result
LOG(2, "DEBUG: First 50 characters of output: " << resultstring.substr(0,50));
s = extractResult(resultstring);
// end process
if ((exitflag = pclose(stdoutstream)) == -1)
ELOG(0, "pclose error");
if (exitflag != 0)
ELOG(1, "Job " << getId() << " failed on executing: " << command_args);
s->exitflag = exitflag;
}
Chronos::getInstance().endTiming(WorkName);
// close temporary file and remove it
boost::filesystem::path tmpPath(tmpTemplate);
if (boost::filesystem::exists(boost::filesystem::status(tmpPath))) {
LOG(2, "DEBUG: Removing " << tmpPath.string());
boost::filesystem::remove(tmpPath);
}
delete[] tmpTemplate;
// close temporary file!
close(fd);
// obtain timing and place in FragmentResult
s->time_Work = Chronos::getInstance().getTime(WorkName);
LOG(1, "INFO: Work() required " << s->time_Work << " seconds to complete.");
// return result
return s;
}
/** Default function for result extraction is just copy.
*
* @param resultstring output of system command
* @return copy of \a resultstring
*/
FragmentResult::ptr SystemCommandJob::extractResult(const std::string &resultstring)
{
return FragmentResult::ptr (new FragmentResult(getId(), resultstring) );
}
/** Comparator for class SystemCommandJob.
* \param other instance to compare to
* \return every member variable is the same, else - is not
*/
bool SystemCommandJob::operator==(const SystemCommandJob &other) const
{
if (command != other.command) {
LOG(1, "INFO: command's of two SystemCommandJobs differ: " << command << " != " << other.command << ".");
return false;
}
if (outputfile != other.outputfile) {
LOG(1, "INFO: outputfile's of two SystemCommandJobs differ: " << outputfile << " != " << other.outputfile << ".");
return false;
}
return (dynamic_cast(*this) == dynamic_cast(other));
}
// we need to explicitly instantiate the serialization functions as
// its is only serialized through its base class FragmentJob
BOOST_CLASS_EXPORT_IMPLEMENT(SystemCommandJob)