dlvhex
2.5.0
|
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: