source: src/Jobs/JobMarket/SystemCommandJob.cpp@ 365a44

Last change on this file since 365a44 was 365a44, checked in by Frederik Heber <heber@…>, 11 years ago

Added loop to SystemCommandJob to attempt execution three times.

  • Property mode set to 100644
File size: 7.5 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2014 Frederik Heber. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * SystemCommandJob.cpp
25 *
26 * Originally taken from my JobMarket project at 1.1.4.
27 *
28 * Created on: Feb 5, 2012
29 * Author: heber
30 */
31
32// include config.h
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include <boost/iostreams/device/file_descriptor.hpp>
38#include <boost/iostreams/stream.hpp>
39
40#include "CodePatterns/MemDebug.hpp"
41
42// include headers that implement a archive in simple text format
43// otherwise BOOST_CLASS_EXPORT_IMPLEMENT has no effect
44#include <boost/archive/text_oarchive.hpp>
45#include <boost/archive/text_iarchive.hpp>
46
47#ifdef HAVE_JOBMARKET
48#include "JobMarket/Jobs/SystemCommandJob.hpp"
49#else
50#include "Jobs/JobMarket/SystemCommandJob.hpp"
51#endif
52
53#include <cstdio>
54#include <cstdlib>
55#include <fcntl.h>
56#include <fstream>
57#include <iostream>
58#include <string>
59#include <streambuf>
60#include <boost/filesystem.hpp>
61
62#include "CodePatterns/Chronos.hpp"
63#include "CodePatterns/Info.hpp"
64#include "CodePatterns/Log.hpp"
65#include "CodePatterns/toString.hpp"
66
67// static entities
68const unsigned int SystemCommandJob::MAX_ATTEMPTS = 3;
69
70/** Constructor for class SystemCommandJob.
71 *
72 */
73SystemCommandJob::SystemCommandJob() :
74 FragmentJob(JobId::IllegalJob)
75{}
76
77/** Constructor for class SystemCommandJob.
78 *
79 * \param _command command to execute
80 * \param _outputfile configuration file for solver
81 * \param _JobId unique id of this job
82 * \param _suffix possible suffix for temporary filenames, may be left empty
83 */
84SystemCommandJob::SystemCommandJob(
85 const std::string &_command,
86 const std::string &_outputfile,
87 const JobId_t _JobId,
88 const std::string &_suffix) :
89 FragmentJob(_JobId),
90 command(_command),
91 suffix(_suffix),
92 outputfile(_outputfile)
93{}
94
95/** Destructor for class SystemCommandJob.
96 *
97 */
98SystemCommandJob::~SystemCommandJob()
99{}
100
101/** Work routine of this SystemCommandJob.
102 *
103 * This function encapsulates all the work that has to be done to generate
104 * a FragmentResult. Hence, the FragmentWorker does not need to know anything
105 * about the operation: it just receives it and executes this function.
106 *
107 * We obtain FragmentResult::exitflag from std::system's return value and
108 * FragmentResult::result from the contents of the piped output file.
109 *
110 * \return result of this job
111 */
112FragmentResult::ptr SystemCommandJob::Work()
113{
114// Info info((std::string(__FUNCTION__)+std::string(", id #")+toString(getId())).c_str());
115
116 // the following is taken from http://stackoverflow.com/questions/2746168/how-to-construct-a-c-fstream-from-a-posix-file-descriptor
117 char *tmpTemplate = NULL;
118 const std::string idstring(toString(getId()));
119 const size_t idlength = idstring.length();
120 {
121 std::string Template("/tmp/XXXXXXX_");
122 ASSERT(idlength <= 8,
123 "SystemCommandJob::Work() - the id contains more than 8 digits.");
124 Template += idstring;
125 if (!suffix.empty())
126 Template += suffix;
127 LOG(2, "DEBUG: Temporary template is " << Template << ".");
128 tmpTemplate = new char[Template.length()+1];
129 strncpy(tmpTemplate, Template.c_str(), Template.length()+1);
130 }
131 const int fd = mkstemps(tmpTemplate, idlength + suffix.length()+1);
132
133 // write outputfile to temporary file
134 LOG(2, "DEBUG: Temporary file is " << tmpTemplate << ".");
135 std::ofstream output(tmpTemplate);
136 ASSERT(output.is_open(),
137 "SystemCommandJob::Work() - the temporary file could not be opened.");
138 output << outputfile << std::endl;
139 output.close();
140
141 // fork into subprocess and launch command
142 const std::string WorkName = std::string("Work #")+idstring;
143 Chronos::getInstance().startTiming(WorkName);
144 FragmentResult::ptr s;
145 int exitflag = 255;
146 for(unsigned int counter = 0;
147 (counter < MAX_ATTEMPTS) && (exitflag != 0);
148 ++counter) {
149 // open process
150 std::string command_args = command+std::string(" ")+tmpTemplate;
151 LOG(1, "INFO: Executing '" << command_args << "'.");
152 FILE *stdoutstream = NULL;
153 while (stdoutstream == NULL) {
154 stdoutstream = popen(command_args.c_str(), "r");
155 if (stdoutstream == NULL) {
156 ELOG(2, "File descriptors are full, waiting for 1 sec...");
157 sleep(1);
158 }
159 }
160
161 // read stdout from process
162 const int fd = fileno(stdoutstream);
163 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
164 boost::iostreams::stream<boost::iostreams::file_descriptor_source> stdoutfile(fd, boost::iostreams::never_close_handle);
165 stdoutfile.set_auto_close(false); // https://svn.boost.org/trac/boost/ticket/3517
166 std::istreambuf_iterator<char> beginiter = (std::istreambuf_iterator<char>(stdoutfile));
167 std::string resultstring( beginiter, std::istreambuf_iterator<char>());
168
169 // construct result
170 LOG(2, "DEBUG: First 50 characters of output: " << resultstring.substr(0,50));
171 s = extractResult(resultstring);
172
173 // end process
174 if ((exitflag = pclose(stdoutstream)) == -1)
175 ELOG(0, "pclose error");
176
177 if (exitflag != 0)
178 ELOG(1, "Job " << getId() << " on attempt #" << counter+1
179 << " failed on executing: " << command_args);
180 }
181 s->exitflag = exitflag;
182 Chronos::getInstance().endTiming(WorkName);
183
184 // close temporary file and remove it
185 boost::filesystem::path tmpPath(tmpTemplate);
186 if (boost::filesystem::exists(boost::filesystem::status(tmpPath))) {
187 LOG(2, "DEBUG: Removing " << tmpPath.string());
188 boost::filesystem::remove(tmpPath);
189 }
190 delete[] tmpTemplate;
191 // close temporary file!
192 close(fd);
193
194 // obtain timing and place in FragmentResult
195 s->time_Work = Chronos::getInstance().getTime(WorkName);
196 LOG(1, "INFO: Work() required " << s->time_Work << " seconds to complete.");
197
198 // return result
199 return s;
200}
201
202/** Default function for result extraction is just copy.
203 *
204 * @param resultstring output of system command
205 * @return copy of \a resultstring
206 */
207FragmentResult::ptr SystemCommandJob::extractResult(const std::string &resultstring)
208{
209 return FragmentResult::ptr (new FragmentResult(getId(), resultstring) );
210}
211
212
213/** Comparator for class SystemCommandJob.
214 * \param other instance to compare to
215 * \return every member variable is the same, else - is not
216 */
217bool SystemCommandJob::operator==(const SystemCommandJob &other) const
218{
219 if (command != other.command) {
220 LOG(1, "INFO: command's of two SystemCommandJobs differ: " << command << " != " << other.command << ".");
221 return false;
222 }
223 if (outputfile != other.outputfile) {
224 LOG(1, "INFO: outputfile's of two SystemCommandJobs differ: " << outputfile << " != " << other.outputfile << ".");
225 return false;
226 }
227 return (dynamic_cast<const FragmentJob &>(*this) == dynamic_cast<const FragmentJob &>(other));
228}
229
230// we need to explicitly instantiate the serialization functions as
231// its is only serialized through its base class FragmentJob
232BOOST_CLASS_EXPORT_IMPLEMENT(SystemCommandJob)
Note: See TracBrowser for help on using the repository browser.