CppADCodeGen  2.4.3
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) 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 
25 namespace CppAD {
26 namespace cg {
27 
31 namespace system {
32 
33 namespace {
34 
38 class FDHandler {
39 public:
40  int fd;
41  bool closed;
42 public:
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 
65 class PipeHandler {
66 public:
67  FDHandler read;
68  FDHandler write;
69 public:
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
86 template<class T>
87 const std::string SystemInfo<T>::DYNAMIC_LIB_EXTENSION = ".dylib";
88 #else
89 template<class T>
90 const std::string SystemInfo<T>::DYNAMIC_LIB_EXTENSION = ".so";
91 #endif
92 
93 template<class T>
94 const std::string SystemInfo<T>::STATIC_LIB_EXTENSION = ".a";
95 
96 inline 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 
108 inline 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 
118 inline 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 
141 inline std::string createPath(const std::string& folder,
142  const std::string& file) {
143  return createPath({folder}, file);
144 }
145 
146 inline std::string escapePath(const std::string& path) {
147  return std::string("\"") + path + "\"";
148 }
149 
150 inline 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 
163 inline 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 
171 inline bool isAbsolutePath(const std::string& path) {
172  if (path.empty())
173  return false;
174 
175  return path[0] == '/';
176 }
177 
178 inline 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 
186 inline 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 
198 inline 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
CppAD::cg::system::createFolder
void createFolder(const std::string &folder)
CppAD::cg::system::isFile
bool isFile(const std::string &path)
CppAD
Definition: abstract_atomic_fun.hpp:19
CppAD::cg::system::isDirectory
bool isDirectory(const std::string &path)
CppAD::cg::system::callExecutable
void callExecutable(const std::string &executable, const std::vector< std::string > &args, std::string *stdOutErrMessage=nullptr, const std::string *stdInMessage=nullptr)
CppAD::cg::system::escapePath
std::string escapePath(const std::string &path)
CppAD::cg::system::isAbsolutePath
bool isAbsolutePath(const std::string &path)
CppAD::cg::system::createPath
std::string createPath(const std::string &baseFolder, const std::string &file)