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