source: src/Jobs/JobMarket/SystemCommandJob.cpp

Candidate_v1.6.1
Last change on this file was 9eb71b3, checked in by Frederik Heber <frederik.heber@…>, 8 years ago

Commented out MemDebug include and Memory::ignore.

  • MemDebug clashes with various allocation operators that use a specific placement in memory. It is so far not possible to wrap new/delete fully. Hence, we stop this effort which so far has forced us to put ever more includes (with clashes) into MemDebug and thereby bloat compilation time.
  • MemDebug does not add that much usefulness which is not also provided by valgrind.
  • Property mode set to 100644
File size: 7.3 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/** Constructor for class SystemCommandJob.
68 *
69 */
70SystemCommandJob::SystemCommandJob() :
71 FragmentJob(JobId::IllegalJob)
72{}
73
74/** Constructor for class SystemCommandJob.
75 *
76 * \param _command command to execute
77 * \param _outputfile configuration file for solver
78 * \param _JobId unique id of this job
79 * \param _suffix possible suffix for temporary filenames, may be left empty
80 */
81SystemCommandJob::SystemCommandJob(
82 const std::string &_command,
83 const std::string &_outputfile,
84 const JobId_t _JobId,
85 const std::string &_suffix) :
86 FragmentJob(_JobId),
87 command(_command),
88 suffix(_suffix),
89 outputfile(_outputfile)
90{}
91
92/** Destructor for class SystemCommandJob.
93 *
94 */
95SystemCommandJob::~SystemCommandJob()
96{}
97
98/** Work routine of this SystemCommandJob.
99 *
100 * This function encapsulates all the work that has to be done to generate
101 * a FragmentResult. Hence, the FragmentWorker does not need to know anything
102 * about the operation: it just receives it and executes this function.
103 *
104 * We obtain FragmentResult::exitflag from std::system's return value and
105 * FragmentResult::result from the contents of the piped output file.
106 *
107 * \return result of this job
108 */
109FragmentResult::ptr SystemCommandJob::Work()
110{
111// Info info((std::string(__FUNCTION__)+std::string(", id #")+toString(getId())).c_str());
112
113 // the following is taken from http://stackoverflow.com/questions/2746168/how-to-construct-a-c-fstream-from-a-posix-file-descriptor
114 char *tmpTemplate = NULL;
115 const std::string idstring(toString(getId()));
116 const size_t idlength = idstring.length();
117 {
118 std::string Template("/tmp/XXXXXXX_");
119 ASSERT(idlength <= 8,
120 "SystemCommandJob::Work() - the id contains more than 8 digits.");
121 Template += idstring;
122 if (!suffix.empty())
123 Template += suffix;
124 LOG(2, "DEBUG: Temporary template is " << Template << ".");
125 tmpTemplate = new char[Template.length()+1];
126 strncpy(tmpTemplate, Template.c_str(), Template.length()+1);
127 }
128 const int fd = mkstemps(tmpTemplate, idlength + suffix.length()+1);
129
130 // write outputfile to temporary file
131 LOG(2, "DEBUG: Temporary file is " << tmpTemplate << ".");
132 std::ofstream output(tmpTemplate);
133 ASSERT(output.is_open(),
134 "SystemCommandJob::Work() - the temporary file could not be opened.");
135 output << outputfile << std::endl;
136 output.close();
137
138 // fork into subprocess and launch command
139 const std::string WorkName = std::string("Work #")+idstring;
140 Chronos::getInstance().startTiming(WorkName);
141 FragmentResult::ptr s;
142 {
143 // open process
144 std::string command_args = command+std::string(" ")+tmpTemplate;
145 LOG(1, "INFO: Executing '" << command_args << "'.");
146 FILE *stdoutstream = NULL;
147 while (stdoutstream == NULL) {
148 stdoutstream = popen(command_args.c_str(), "r");
149 if (stdoutstream == NULL) {
150 ELOG(2, "File descriptors are full, waiting for 1 sec...");
151 sleep(1);
152 }
153 }
154
155 int exitflag;
156 // read stdout from process
157 const int fd = fileno(stdoutstream);
158 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
159 boost::iostreams::stream<boost::iostreams::file_descriptor_source> stdoutfile(fd, boost::iostreams::never_close_handle);
160 stdoutfile.set_auto_close(false); // https://svn.boost.org/trac/boost/ticket/3517
161 std::istreambuf_iterator<char> beginiter = (std::istreambuf_iterator<char>(stdoutfile));
162 std::string resultstring( beginiter, std::istreambuf_iterator<char>());
163
164 // construct result
165 LOG(2, "DEBUG: First 50 characters of output: " << resultstring.substr(0,50));
166 s = extractResult(resultstring);
167
168 // end process
169 if ((exitflag = pclose(stdoutstream)) == -1)
170 ELOG(0, "pclose error");
171
172 if (exitflag != 0)
173 ELOG(1, "Job " << getId() << " failed on executing: " << command_args);
174 s->exitflag = exitflag;
175 }
176 Chronos::getInstance().endTiming(WorkName);
177
178 // close temporary file and remove it
179 boost::filesystem::path tmpPath(tmpTemplate);
180 if (boost::filesystem::exists(boost::filesystem::status(tmpPath))) {
181 LOG(2, "DEBUG: Removing " << tmpPath.string());
182 boost::filesystem::remove(tmpPath);
183 }
184 delete[] tmpTemplate;
185 // close temporary file!
186 close(fd);
187
188 // obtain timing and place in FragmentResult
189 s->time_Work = Chronos::getInstance().getTime(WorkName);
190 LOG(1, "INFO: Work() required " << s->time_Work << " seconds to complete.");
191
192 // return result
193 return s;
194}
195
196/** Default function for result extraction is just copy.
197 *
198 * @param resultstring output of system command
199 * @return copy of \a resultstring
200 */
201FragmentResult::ptr SystemCommandJob::extractResult(const std::string &resultstring)
202{
203 return FragmentResult::ptr (new FragmentResult(getId(), resultstring) );
204}
205
206
207/** Comparator for class SystemCommandJob.
208 * \param other instance to compare to
209 * \return every member variable is the same, else - is not
210 */
211bool SystemCommandJob::operator==(const SystemCommandJob &other) const
212{
213 if (command != other.command) {
214 LOG(1, "INFO: command's of two SystemCommandJobs differ: " << command << " != " << other.command << ".");
215 return false;
216 }
217 if (outputfile != other.outputfile) {
218 LOG(1, "INFO: outputfile's of two SystemCommandJobs differ: " << outputfile << " != " << other.outputfile << ".");
219 return false;
220 }
221 return (dynamic_cast<const FragmentJob &>(*this) == dynamic_cast<const FragmentJob &>(other));
222}
223
224// we need to explicitly instantiate the serialization functions as
225// its is only serialized through its base class FragmentJob
226BOOST_CLASS_EXPORT_IMPLEMENT(SystemCommandJob)
Note: See TracBrowser for help on using the repository browser.