CppADCodeGen 2.4.3
A C++ Algorithmic Differentiation Package with Source Code Generation
Loading...
Searching...
No Matches
linux_system.hpp
1#ifndef CPPAD_CG_LINUX_SYSTEM_INCLUDED
2#define CPPAD_CG_LINUX_SYSTEM_INCLUDED
3/* --------------------------------------------------------------------------
4 * CppADCodeGen: C++ Algorithmic Differentiation with Source Code Generation:
5 * Copyright (C) 2019 Joao Leal
6 * Copyright (C) 2012 Ciengis
7 *
8 * CppADCodeGen is distributed under multiple licenses:
9 *
10 * - Eclipse Public License Version 1.0 (EPL1), and
11 * - GNU General Public License Version 3 (GPL3).
12 *
13 * EPL1 terms and conditions can be found in the file "epl-v10.txt", while
14 * terms and conditions for the GPL3 can be found in the file "gpl3.txt".
15 * ----------------------------------------------------------------------------
16 * Author: Joao Leal
17 */
18
19#if CPPAD_CG_SYSTEM_LINUX
20#include <unistd.h>
21#include <sys/types.h>
22#include <sys/wait.h>
23#include <sys/stat.h>
24
25namespace CppAD {
26namespace cg {
27
31namespace system {
32
33namespace {
34
38class FDHandler {
39public:
40 int fd;
41 bool closed;
42public:
43
44 inline FDHandler() : fd(0), closed(true) {
45 }
46
47 inline explicit FDHandler(int fd) : fd(fd), closed(false) {
48 }
49
50 inline void close() {
51 if (!closed) {
52 ::close(fd);
53 closed = true;
54 }
55 }
56
57 inline ~FDHandler() {
58 close();
59 }
60};
61
65class PipeHandler {
66public:
67 FDHandler read;
68 FDHandler write;
69public:
70
71 inline void create() {
72 int fd[2];
73 if (pipe(fd) < 0) {
74 throw CGException("Failed to create pipe");
75 }
76 read.fd = fd[0];
77 read.closed = false;
78 write.fd = fd[1];
79 write.closed = false;
80 }
81};
82
83}
84
85#ifdef CPPAD_CG_SYSTEM_APPLE
86template<class T>
87const std::string SystemInfo<T>::DYNAMIC_LIB_EXTENSION = ".dylib";
88#else
89template<class T>
90const std::string SystemInfo<T>::DYNAMIC_LIB_EXTENSION = ".so";
91#endif
92
93template<class T>
94const std::string SystemInfo<T>::STATIC_LIB_EXTENSION = ".a";
95
96inline std::string getWorkingDirectory() {
97 char buffer[1024];
98
99 char* ret = getcwd(buffer, 1024);
100 if (ret == nullptr) {
101 const char* error = strerror(errno);
102 throw CGException("Failed to get current working directory: ", error);
103 }
104
105 return buffer;
106}
107
108inline void createFolder(const std::string& folder) {
109 int ret = mkdir(folder.c_str(), 0755);
110 if (ret == -1) {
111 if (errno != EEXIST) {
112 const char* error = strerror(errno);
113 throw CGException("Failed to create directory '", folder + "': ", error);
114 }
115 }
116}
117
118inline std::string createPath(std::initializer_list<std::string> folders,
119 const std::string& file) {
120 std::string path;
121
122 size_t n = file.size();
123 for (const std::string& folder: folders)
124 n += folder.size() + 1;
125 path.reserve(n);
126
127 for (const std::string& folder: folders) {
128 if (!folder.empty() && folder.back() == '/') {
129 path += folder;
130 } else {
131 path += folder;
132 path += "/";
133 }
134 }
135
136 path += file;
137
138 return path;
139}
140
141inline std::string createPath(const std::string& folder,
142 const std::string& file) {
143 return createPath({folder}, file);
144}
145
146inline std::string escapePath(const std::string& path) {
147 return std::string("\"") + path + "\"";
148}
149
150inline std::string filenameFromPath(const std::string& path) {
151 size_t pos = path.rfind('/');
152 if (pos != std::string::npos) {
153 if (pos == path.size() - 1) {
154 return "";
155 } else {
156 return path.substr(pos + 1);
157 }
158 } else {
159 return path;
160 }
161}
162
163inline std::string directoryFromPath(const std::string& path) {
164 size_t found = path.find_last_of('/');
165 if (found != std::string::npos) {
166 return path.substr(0, found + 1);
167 }
168 return "./";
169}
170
171inline bool isAbsolutePath(const std::string& path) {
172 if (path.empty())
173 return false;
174
175 return path[0] == '/';
176}
177
178inline bool isDirectory(const std::string& path) {
179 struct stat info;
180
181 if (stat(path.c_str(), &info) != 0) {
182 return false;
183 } else return (info.st_mode & S_IFDIR) != 0;
184}
185
186inline bool isFile(const std::string& path) {
187 struct stat sts;
188 errno = 0;
189 if (stat(path.c_str(), &sts) == 0 && errno == 0) {
190 return S_ISREG(sts.st_mode);
191 } else if (errno == ENOENT) {
192 return false;
193 }
194 // could check for an error message...
195 return false;
196}
197
198inline void callExecutable(const std::string& executable,
199 const std::vector<std::string>& args,
200 std::string* stdOutErrMessage,
201 const std::string* stdInMessage) {
202 std::string execName = filenameFromPath(executable);
203
204 PipeHandler pipeMsg; // file descriptors used to communicate between processes
205 pipeMsg.create();
206
207 PipeHandler pipeStdOutErr; // file descriptors used to communicate between processes
208 if(stdOutErrMessage != nullptr) {
209 pipeStdOutErr.create();
210 }
211
212 PipeHandler pipeSrc;
213 if (stdInMessage != nullptr) {
214 //Create pipe for piping source to the compiler
215 pipeSrc.create();
216 }
217
218 //Fork the compiler, pipe source to it, wait for the compiler to exit
219 pid_t pid = fork();
220 if (pid < 0) {
221 throw CGException("Failed to fork process");
222 }
223
224 if (pid == 0) {
225 /***********************************************************************
226 * Child process
227 **********************************************************************/
228 pipeMsg.read.close();
229
230 if (stdInMessage != nullptr) {
231 pipeSrc.write.close(); // close write end of pipe
232 // Send pipe input to stdin
233 close(STDIN_FILENO);
234 if (dup2(pipeSrc.read.fd, STDIN_FILENO) == -1) {
235 perror("redirecting stdin");
236 exit(EXIT_FAILURE);
237 }
238 }
239
240 if(stdOutErrMessage != nullptr) {
241 pipeStdOutErr.read.close(); // close read end of pipe
242
243 // redirect stdout
244 if (dup2(pipeStdOutErr.write.fd, STDOUT_FILENO) == -1) {
245 perror("redirecting stdout");
246 exit(EXIT_FAILURE);
247 }
248
249 // redirect stderr
250 if (dup2(pipeStdOutErr.write.fd, STDERR_FILENO) == -1) {
251 perror("redirecting stderr");
252 exit(EXIT_FAILURE);
253 }
254 }
255
256 auto toCharArray = [](const std::string & args) {
257 const size_t s = args.size() + 1;
258 char* args2 = new char[s];
259 for (size_t c = 0; c < s - 1; c++) {
260 args2[c] = args.at(c);
261 }
262 args2[s - 1] = '\0';
263 return args2;
264 };
265
266 std::vector<char*> args2(args.size() + 2);
267 args2[0] = toCharArray(execName);
268 for (size_t i = 0; i < args.size(); i++) {
269 args2[i + 1] = toCharArray(args[i]);
270 }
271 args2.back() = (char *) nullptr; // END
272
273 int eCode = execv(executable.c_str(), &args2[0]);
274
275 for (size_t i = 0; i < args.size(); i++) {
276 delete [] args2[i];
277 }
278
279 if(stdOutErrMessage != nullptr) {
280 pipeStdOutErr.write.close();
281 }
282
283 if (eCode < 0) {
284 char buf[512];
285#ifndef CPPAD_CG_SYSTEM_APPLE
286 std::string error = executable + ": " + strerror_r(errno, buf, 511); // thread safe
287#else
288 std::string error = executable + ": ";
289 strerror_r(errno, buf, 511); // thread safe
290 error += std::string(buf);
291#endif
292 ssize_t size = error.size() + 1;
293 if (write(pipeMsg.write.fd, error.c_str(), size) != size) {
294 std::cerr << "Failed to send message to parent process" << std::endl;
295 }
296 std::cerr << "*** ERROR: exec failed" << std::endl;
297 exit(EXIT_FAILURE);
298 }
299
300 exit(EXIT_SUCCESS);
301 }
302
303 /***************************************************************************
304 * Parent process
305 **************************************************************************/
306 pipeMsg.write.close();
307 if(stdOutErrMessage != nullptr) {
308 pipeStdOutErr.write.close();
309 }
310
311 auto readCErrorMsg = []() {
312 int error = errno;
313 errno = 0;
314 char buf[512];
315#ifndef CPPAD_CG_SYSTEM_APPLE
316 return std::string(strerror_r(error, buf, 512));
317#else
318 strerror_r(error, buf, 512);
319 return std::string(buf);
320#endif
321 };
322
323 std::string writeError;
324 if (stdInMessage != nullptr) {
325 // close read end of pipe
326 pipeSrc.read.close();
327 //Pipe source to the executable
328 ssize_t writeFlag = write(pipeSrc.write.fd, stdInMessage->c_str(), stdInMessage->size());
329 if (writeFlag == -1)
330 writeError = readCErrorMsg() + " ";
331 pipeSrc.write.close();
332 }
333
334 //Wait for the executable to exit
335 int status;
336 // Read message from the child
337 std::ostringstream messageErr;
338 std::ostringstream messageStdOutErr;
339 size_t size = 0;
340 char buffer[128];
341 do {
342 ssize_t n;
343 if(stdOutErrMessage != nullptr) {
344 while ((n = read(pipeStdOutErr.read.fd, buffer, sizeof (buffer))) > 0) {
345 messageStdOutErr.write(buffer, n);
346 size += n;
347 if (size > 1e4) break;
348 }
349 }
350
351 while ((n = read(pipeMsg.read.fd, buffer, sizeof (buffer))) > 0) {
352 messageErr.write(buffer, n);
353 size += n;
354 if (size > 1e4) break;
355 }
356
357 if (waitpid(pid, &status, 0) < 0) {
358 throw CGException("Waitpid failed for pid ", pid, " [", readCErrorMsg(), "]");
359 }
360 } while (!WIFEXITED(status) && !WIFSIGNALED(status));
361
362 pipeMsg.read.close();
363 if(stdOutErrMessage != nullptr) {
364 pipeStdOutErr.read.close();
365 }
366
367 if (!writeError.empty()) {
368 std::ostringstream s;
369 s << "Failed to write to pipe";
370 if (size > 0) s << ": " << messageErr.str();
371 else s << ": " << writeError;
372 throw CGException(s.str());
373 }
374
375 if (WIFEXITED(status)) {
376 if (WEXITSTATUS(status) != EXIT_SUCCESS) {
377 std::ostringstream s;
378 s << "Executable '" << executable << "' (pid " << pid << ") exited with code " << WEXITSTATUS(status);
379 if (size > 0) s << ": " << messageErr.str();
380 throw CGException(s.str());
381 }
382 } else if (WIFSIGNALED(status)) {
383 std::ostringstream s;
384 s << "Executable '" << executable << "' (pid " << pid << ") terminated by signal " << WTERMSIG(status);
385 if (size > 0) s << ": " << messageErr.str();
386 throw CGException(s.str());
387 }
388
389 if (stdOutErrMessage != nullptr) {
390 *stdOutErrMessage = messageStdOutErr.str();
391 }
392}
393
394} // END system namespace
395
396} // END cg namespace
397} // END CppAD namespace
398
399#endif
400#endif
bool isDirectory(const std::string &path)
bool isFile(const std::string &path)
std::string createPath(const std::string &baseFolder, const std::string &file)
void createFolder(const std::string &folder)
bool isAbsolutePath(const std::string &path)
std::string escapePath(const std::string &path)
void callExecutable(const std::string &executable, const std::vector< std::string > &args, std::string *stdOutErrMessage=nullptr, const std::string *stdInMessage=nullptr)