dlvhex  2.5.0
src/ProcessBuf.cpp
Go to the documentation of this file.
00001 /* dlvhex -- Answer-Set Programming with external interfaces.
00002  * Copyright (C) 2005-2007 Roman Schindlauer
00003  * Copyright (C) 2006-2015 Thomas Krennwallner
00004  * Copyright (C) 2009-2016 Peter Schüller
00005  * Copyright (C) 2011-2016 Christoph Redl
00006  * Copyright (C) 2015-2016 Tobias Kaminski
00007  * Copyright (C) 2015-2016 Antonius Weinzierl
00008  *
00009  * This file is part of dlvhex.
00010  *
00011  * dlvhex is free software; you can redistribute it and/or modify it
00012  * under the terms of the GNU Lesser General Public License as
00013  * published by the Free Software Foundation; either version 2.1 of
00014  * the License, or (at your option) any later version.
00015  *
00016  * dlvhex is distributed in the hope that it will be useful, but
00017  * WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019  * Lesser General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Lesser General Public
00022  * License along with dlvhex; if not, write to the Free Software
00023  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00024  * 02110-1301 USA.
00025  */
00026 
00037 #ifdef HAVE_CONFIG_H
00038 #include "config.h"
00039 #endif                           // HAVE_CONFIG_H
00040 
00041 #include "dlvhex2/ProcessBuf.h"
00042 #include "dlvhex2/Logger.h"
00043 #include "dlvhex2/Printhelpers.h"
00044 
00045 #include <boost/foreach.hpp>
00046 #include <sstream>
00047 #include <cerrno>
00048 #include <cstdio>
00049 #include <csignal>
00050 #include <cstring>
00051 #include <cstdlib>
00052 #include <sys/types.h>
00053 
00054 #ifndef WIN32
00055 #include <sys/wait.h>
00056 #endif
00057 
00058 DLVHEX_NAMESPACE_BEGIN
00059 
00060 ProcessBuf::ProcessBuf()
00061 : std::streambuf(),
00062 #ifndef WIN32
00063 process(-1),
00064 #endif
00065 bufsize(256)
00066 {
00067     #ifndef WIN32
00068     // ignore SIGPIPE
00069     struct sigaction sa;
00070     sa.sa_handler = SIG_IGN;
00071     sa.sa_flags = 0;
00072     sigemptyset(&sa.sa_mask);
00073 
00074     if (::sigaction(SIGPIPE, &sa, 0)) {
00075         ::perror("sigaction");
00076         ::exit(1);
00077     }
00078     #endif
00079 
00080     initBuffers();               // don't call virtual methods in the ctor
00081 }
00082 
00083 
00084 ProcessBuf::ProcessBuf(const ProcessBuf& sb)
00085 : std::streambuf(),
00086 #ifdef WIN32
00087 processInformation(sb.processInformation),
00088 #elif defined(POSIX)
00089 process(sb.process),
00090 #else
00091 #error Either POSIX or WIN32 must be defined
00092 #endif
00093 status(sb.status),
00094 bufsize(sb.bufsize)
00095 {
00096     #ifndef WIN32
00097     ::memcpy(inpipes, sb.inpipes, 2);
00098     ::memcpy(outpipes, sb.outpipes, 2);
00099     #endif
00100     initBuffers();               // don't call virtual methods in the ctor
00101 }
00102 
00103 
00104 ProcessBuf::~ProcessBuf()
00105 {
00106     close();
00107 
00108     if (ibuf) {
00109         delete[] ibuf;
00110         ibuf = 0;
00111     }
00112 
00113     if (obuf) {
00114         delete[] obuf;
00115         obuf = 0;
00116     }
00117 }
00118 
00119 
00120 void
00121 ProcessBuf::initBuffers()
00122 {
00123     obuf = new std::streambuf::char_type[bufsize];
00124     ibuf = new std::streambuf::char_type[bufsize];
00125     setp(obuf, obuf + bufsize);
00126     setg(ibuf, ibuf, ibuf);
00127 }
00128 
00129 
00130 pid_t
00131 ProcessBuf::open(const std::vector<std::string>& av)
00132 {
00133     LOG(DBG,"ProcessBuf::open" << printvector(av));
00134 
00135     #ifdef WIN32
00136     SECURITY_ATTRIBUTES saAttr;
00137     ZeroMemory(&saAttr, sizeof(saAttr));
00138 
00139     // Set the bInheritHandle flag so pipe handles are inherited.
00140 
00141     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
00142     saAttr.bInheritHandle = TRUE;
00143     saAttr.lpSecurityDescriptor = NULL;
00144 
00145     // Create a pipe for the child process's STDOUT.
00146     if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) return 0;
00147 
00148     // Ensure the read handle to the pipe for STDOUT is not inherited.
00149     if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) return 0;
00150 
00151     // Create a pipe for the child process's STDIN.
00152     if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) return 0;
00153 
00154     // Ensure the write handle to the pipe for STDIN is not inherited.
00155     if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) return 0;
00156 
00157     STARTUPINFO startupInfo;
00158     ZeroMemory(&processInformation, sizeof(processInformation));
00159     ZeroMemory(&startupInfo, sizeof(startupInfo));
00160     startupInfo.cb = sizeof(startupInfo);
00161     startupInfo.hStdOutput = g_hChildStd_OUT_Wr;
00162     startupInfo.hStdError = g_hChildStd_OUT_Wr;
00163     startupInfo.hStdInput = g_hChildStd_IN_Rd;
00164     startupInfo.dwFlags |= STARTF_USESTDHANDLES;
00165     std::stringstream args;
00166     for (uint32_t i = 1; i < av.size(); i++) {
00167         args << " " << av[i];
00168     }
00169     std::string argstr = args.str();
00170 
00171     // get PATH variable
00172     DWORD bufferSize = 65535;    //Limit according to http://msdn.microsoft.com/en-us/library/ms683188.aspx
00173     std::string buff;
00174     buff.resize(bufferSize);
00175 
00176     std::string paths = std::string(::getenv("PATH")) + ";;";
00177 
00178     // search for dlv in all directories listed in PATH
00179     bool result = false;
00180     while (!result && paths.find(";") != std::wstring::npos) {
00181         std::string path = buff.substr(0, paths.find(";"));
00182         path += (path.size() > 0 ? "\\" : "") + av[0];
00183         paths = paths.substr(buff.find(";") + 1);
00184 
00185         std::string cmdstr = path;
00186         result = (CreateProcess((LPCSTR)cmdstr.c_str(), (LPSTR)argstr.c_str(), NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS, NULL, NULL, &startupInfo, &processInformation) == TRUE);
00187     }
00188 
00189     if (!result) {
00190         std::cout << GetLastError() << std::endl;
00191     }
00192 
00193     return processInformation.dwProcessId;
00194 
00195     #elif defined(POSIX)
00196     // close before re-open it
00197     if (process != -1) {
00198         int ret = close();
00199         if (ret != 0) {
00200             return ret < 0 ? ret : -ret;
00201         }
00202     }
00203 
00204     outpipes[0] = 0;
00205     outpipes[1] = 0;
00206     inpipes[0] = 0;
00207     inpipes[1] = 0;
00208 
00209     // we want a full-duplex stream -> create two pairs of pipes
00210 
00211     if (::pipe(outpipes) < 0) {
00212         ::perror("pipes");
00213         return -1;
00214     }
00215 
00216     if (::pipe(inpipes) < 0) {
00217         ::perror("pipes");
00218         return -1;
00219     }
00220 
00221     // create a new process
00222     process = ::fork();
00223 
00224     switch (process) {
00225         case -1:                 // error
00226             ::perror("fork");
00227             ::exit(process);
00228             break;
00229 
00230         case 0:                  // child
00231         {
00232             // setup argv
00233 
00234             char* argv[av.size() + 1];
00235             int i = 0;
00236 
00237             for (std::vector<std::string>::const_iterator it = av.begin();
00238             it != av.end(); it++) {
00239                 std::string::size_type size = it->size();
00240                 argv[i] = new char[size + 1];
00241                 it->copy(argv[i], size);
00242                 argv[i][size] = '\0';
00243                 i++;
00244             }
00245 
00246             argv[i] = NULL;
00247 
00248             // redirect stdin and stdout and stderr
00249 
00250             if (::dup2(outpipes[1], STDOUT_FILENO) < 0) {
00251                 ::perror("dup2");
00252                 ::exit(1);
00253             }
00254 
00255             if (::dup2(outpipes[1], STDERR_FILENO) < 0) {
00256                 ::perror("dup2");
00257                 ::exit(1);
00258             }
00259 
00260             if (::dup2(inpipes[0], STDIN_FILENO) < 0) {
00261                 ::perror("dup2");
00262                 ::exit(1);
00263             }
00264 
00265             // stdout and stdin is redirected, close unneeded filedescr.
00266             ::close(outpipes[0]);
00267             ::close(outpipes[1]);
00268             ::close(inpipes[0]);
00269             ::close(inpipes[1]);
00270 
00271             // execute command, should not return
00272             WARNING("TODO handle signals to parent process (pass on to children s.t. child process is not reparented to init)")
00273                 ::execvp(*argv, argv);
00274 
00275             // just in case we couldn't execute the command
00276             ::exit(127);
00277         }
00278         break;
00279 
00280         default:                 // parent
00281 
00282             // close writing end of the output pipe
00283             ::close(outpipes[1]);
00284             outpipes[1] = -1;
00285             // close reading end of the input pipe
00286             ::close(inpipes[0]);
00287             inpipes[0] = -1;
00288 
00289             break;
00290     }
00291 
00292     return process;
00293     #else
00294     #error Either POSIX or WIN32 must be defined
00295     #endif
00296 }
00297 
00298 
00299 void
00300 ProcessBuf::endoffile()
00301 {
00302     #ifdef WIN32
00303     // reset output buffer
00304     setp(obuf, obuf + bufsize);
00305 
00306     if (g_hChildStd_IN_Wr != 0) {
00307                                  // send EOF to stdin of child process
00308         CloseHandle(g_hChildStd_IN_Wr);
00309         g_hChildStd_IN_Wr = 0;
00310     }
00311 
00312     // close handles to allow child process termination
00313     if (g_hChildStd_IN_Rd != 0) {
00314         CloseHandle(g_hChildStd_IN_Rd);
00315         g_hChildStd_IN_Rd = 0;
00316     }
00317     if (g_hChildStd_OUT_Wr != 0) {
00318         CloseHandle(g_hChildStd_OUT_Wr);
00319         g_hChildStd_OUT_Wr = 0;
00320     }
00321     #elif defined(POSIX)
00322     // reset output buffer
00323     setp(obuf, obuf + bufsize);
00324 
00325     if (inpipes[1] != -1) {
00326         ::close(inpipes[1]);     // send EOF to stdin of child process
00327         inpipes[1] = -1;
00328     }
00329     #else
00330     #error Either POSIX or WIN32 must be defined
00331     #endif
00332 }
00333 
00334 
00335 // wait for end of process
00336 // if kill is true, kill if not already ended
00337 int
00338 ProcessBuf::close(bool kill)
00339 {
00340     #ifdef WIN32
00341     if (processInformation.hProcess == 0)
00342         return -1;
00343 
00344     LOG(DBG,"ProcessBuf::close for process " << processInformation.hProcess << "(" << kill << ")");
00345     if (processInformation.hProcess != 0) {
00346         if (kill) {
00347             TerminateProcess(processInformation.hProcess, 1);
00348         }
00349         else {
00350             WaitForSingleObject(processInformation.hProcess, INFINITE);
00351         }
00352                                  // send EOF to stdin of child process
00353         CloseHandle(processInformation.hProcess);
00354         processInformation.hProcess = 0;
00355         if (processInformation.hThread != 0) {
00356             CloseHandle(processInformation.hThread);
00357             processInformation.hThread = 0;
00358         }
00359     }
00360 
00361     if (g_hChildStd_IN_Rd != 0) {
00362         CloseHandle(g_hChildStd_IN_Rd);
00363         g_hChildStd_IN_Rd = 0;
00364     }
00365     if (g_hChildStd_OUT_Wr != 0) {
00366         CloseHandle(g_hChildStd_OUT_Wr);
00367         g_hChildStd_OUT_Wr = 0;
00368     }
00369 
00370     return 0;
00371 
00372     #elif defined(POSIX)
00373     if( process == -1 )
00374         return -1;
00375 
00376     LOG(DBG,"ProcessBuf::close for process " << process << "(" << kill << ")");
00377 
00378     // we're done writing
00379     endoffile();
00380 
00381     // reset input buffer
00382     setg(ibuf, ibuf, ibuf);
00383 
00384     // we're done reading
00385     if (outpipes[0] != -1) {
00386         ::close(outpipes[0]);
00387         outpipes[0] = -1;
00388     }
00389 
00390     // try to waitpid without waiting (just see if the process is still there)
00391     if( ::waitpid(process, &status, WNOHANG) == 0 ) {
00392         int sig = SIGTERM;
00393         LOG(INFO,"sending signal " << sig << " to process " << process);
00394         ::kill(process, sig);
00395     }
00396 
00397     // obviously we do not want to leave zombies around, so get status
00398     // code of the process
00399     // (if the process no longer exists, this will simply fail,
00400     // if a new process grabbed the same pid, we are doomed and will wait for that
00401     // unrelated process to exit)
00402     ::waitpid(process, &status, 0);
00403     int exitstatus = WEXITSTATUS(status);
00404     LOG(DBG,"ProcessBuf::close for process " << process << ": exit status " << exitstatus);
00405     process = -1;
00406 
00407     // exit code of process
00408     return exitstatus;
00409     #else
00410     #error Either POSIX or WIN32 must be defined
00411     #endif
00412 }
00413 
00414 
00415 std::streambuf::int_type
00416 ProcessBuf::overflow(std::streambuf::int_type c)
00417 {
00418     if (pptr() >= epptr()) {     // full obuf -> write buffer
00419         if (sync() == -1) {
00420             return traits_type::eof();
00421         }
00422     }
00423 
00424     // if c != EOF, put c into output buffer so next call to sync() will
00425     // write it
00426     if (!traits_type::eq_int_type(c, traits_type::eof())) {
00427         *pptr() = traits_type::to_char_type(c);
00428         pbump(1);                // increase put pointer by one
00429     }
00430 
00431     return traits_type::not_eof(c);
00432 }
00433 
00434 
00435 std::streambuf::int_type
00436 ProcessBuf::underflow()
00437 {
00438     #ifdef WIN32
00439     // try to receive at most bufsize bytes
00440     DWORD dwRead;
00441     bool bSuccess = (ReadFile(g_hChildStd_OUT_Rd, ibuf, bufsize, &dwRead, NULL) == TRUE);
00442     if ( ! bSuccess || dwRead == 0 ) {
00443         return traits_type::eof();
00444     }
00445                                  // set new input buffer boundaries
00446     setg(ibuf, ibuf, ibuf + dwRead);
00447     return traits_type::to_int_type(*gptr());
00448 
00449     #elif defined(POSIX)
00450     if (gptr() >= egptr()) {     // empty ibuf -> get data
00451         errno = 0;
00452 
00453         // try to receive at most bufsize bytes
00454         ssize_t n = ::read(outpipes[0], ibuf, bufsize);
00455 
00456         if (n == 0) {            // EOF
00457             return traits_type::eof();
00458         }
00459         else if (n < 0) {        // a failure occured while receiving from the stream
00460             std::ostringstream oss;
00461             oss << "Process prematurely closed pipe before I could read (errno = " << errno << ").";
00462             throw std::ios_base::failure(oss.str());
00463         }
00464 
00465                                  // set new input buffer boundaries
00466         setg(ibuf, ibuf, ibuf + n);
00467     }
00468 
00469     return traits_type::to_int_type(*gptr());
00470     #else
00471     #error Either POSIX or WIN32 must be defined
00472     #endif
00473 }
00474 
00475 
00476 std::streambuf::int_type
00477 ProcessBuf::sync()
00478 {
00479     #ifdef WIN32
00480     // reset input buffer
00481     setg(ibuf, ibuf, ibuf);
00482 
00483     const int len = pptr() - pbase();
00484 
00485     if (len) {                   // non-empty obuf -> send data
00486         errno = 0;
00487 
00488         // loops until whole obuf is sent
00489         //
00490         // Warning: when peer disconnects during the sending we receive
00491         // a SIGPIPE and the default signal handler exits the program.
00492         // Therefore we have to ignore SIGPIPE (in ctor) and reset the
00493         // obuf followed by an error return value. See chapter 5.13 of
00494         // W.R. Stevens: Unix Network Programming Vol.1.
00495 
00496         DWORD bwritten = 0;
00497         bool ret;
00498 
00499         for (int written = 0; written < len; written += bwritten) {
00500             ret = (WriteFile(g_hChildStd_IN_Wr, pbase() + written, len - written, &bwritten, NULL) == TRUE);
00501 
00502             if (!ret || bwritten == 0) break;
00503         }
00504 
00505         // reset output buffer right after sending to the stream
00506         setp(obuf, obuf + bufsize);
00507 
00508         if (bwritten == 0) {     // EOF
00509             return -1;
00510         }
00511         else if (!ret) {         // failure
00512             std::ostringstream oss;
00513             oss << "Process prematurely closed pipe before I could write (errno = " << errno << ").";
00514             throw std::ios_base::failure(oss.str());
00515         }
00516     }
00517 
00518     return 0;
00519 
00520     #elif defined(POSIX)
00521     // reset input buffer
00522     setg(ibuf, ibuf, ibuf);
00523 
00524     const ssize_t len = pptr() - pbase();
00525 
00526     if (len) {                   // non-empty obuf -> send data
00527         errno = 0;
00528 
00529         // loops until whole obuf is sent
00530         //
00531         // Warning: when peer disconnects during the sending we receive
00532         // a SIGPIPE and the default signal handler exits the program.
00533         // Therefore we have to ignore SIGPIPE (in ctor) and reset the
00534         // obuf followed by an error return value. See chapter 5.13 of
00535         // W.R. Stevens: Unix Network Programming Vol.1.
00536 
00537         ssize_t ret = 0;
00538 
00539         for (ssize_t written = 0; written < len; written += ret) {
00540             ret = ::write (inpipes[1], pbase() + written, len - written);
00541             if (ret == -1 || ret == 0) break;
00542         }
00543 
00544         // reset output buffer right after sending to the stream
00545         setp(obuf, obuf + bufsize);
00546 
00547         if (ret == 0) {          // EOF
00548             return -1;
00549         }
00550                                  // failure
00551         else if (ret < 0 || errno == EPIPE) {
00552             std::ostringstream oss;
00553             oss << "Process prematurely closed pipe before I could write (errno = " << errno << ").";
00554             throw std::ios_base::failure(oss.str());
00555         }
00556     }
00557 
00558     return 0;
00559     #else
00560     #error Either POSIX or WIN32 must be defined
00561     #endif
00562 }
00563 
00564 
00565 DLVHEX_NAMESPACE_END
00566 
00567 
00568 // vim:expandtab:ts=4:sw=4:
00569 // mode: C++
00570 // End: