host/executecommand/unix/executecommand.cpp (171 lines of code) (raw):

// // Copyright (c) 2005 InMage. // This file contains proprietary and confidential information and // remains the unpublished property of InMage. Use, // disclosure, or reproduction is prohibited except as permitted // by express written license agreement with InMage. // // File : executecommand.cpp // #include <fcntl.h> #include <time.h> #include <sys/wait.h> #include "executecommand.h" #include "logger.h" #include "portable.h" #include <boost/thread.hpp> boost::mutex g_MutexForkProcess; #define READ 0 #define WRITE 1 #define READ_SUCCESS 0 #define READ_EAGAIN -1 #define READ_ERROR -2 static const char* gBinPath = "/bin/sh"; /** * Set the `FD_CLOEXEC' flag of DESC if VALUE is nonzero, * or clear the flag if VALUE is 0. * * @returns 0 on success, or -1 on error. **/ int setCloexecFlag (const int desc, const bool value) { int oldflags = fcntl (desc, F_GETFD, 0); /* If reading the flags failed, return error indication now. */ if (oldflags == -1) { return oldflags; } /* Set just the flag we want to set. */ if (value) { oldflags |= FD_CLOEXEC; } else { oldflags &= ~FD_CLOEXEC; } /* Store modified flag word in the descriptor. */ return fcntl (desc, F_SETFD, oldflags); } /* reads from read fd into results * returns READ_EAGAIN if errno is EAGAIN * returns READ_ERROR if read fails for any other error * returns READ_SUCCESS upon successful read till EOF */ int readFromPipe(const int pipefdRead, std::stringstream & results) { int bytesRead = 0; char buffer[512]; do { bytesRead = read(pipefdRead, buffer, sizeof(buffer)); if (bytesRead == -1) { if (EAGAIN == errno || EINTR == errno) { break; /// go to select } else { DebugPrintf(SV_LOG_ERROR, "executePipe read failed, error no %d\n", errno); bytesRead = READ_ERROR; break; /// Return if can't read } } if (bytesRead) { results.write(buffer, bytesRead); } } while (bytesRead); return bytesRead; } /// Reads from the given fd using select. Select timeout is used to log delays to cx. void SelectNRead(const int pipefdRead, std::stringstream & results, std::string const & command) { fd_set rfds, exfds; struct timeval tv; int retval = -1; int bytesRead = 0; const int WAITTIME = 300; bool b_ReadyToReadFromPipe = false; do { FD_ZERO(&rfds); FD_ZERO(&exfds); FD_SET(pipefdRead, &rfds); FD_SET(pipefdRead, &exfds); memset(&tv, 0, sizeof(tv)); tv.tv_sec = WAITTIME; tv.tv_usec = 0; retval = select(pipefdRead + 1, &rfds, NULL, &exfds, &tv); if (retval == -1) { if (EINTR == errno) { DebugPrintf(SV_LOG_DEBUG,"executePipe select interrupted\n"); continue; } else { DebugPrintf(SV_LOG_ERROR,"select failed, errno %d, command %s\n", errno, command.c_str()); return; } } else if (retval == 0) { DebugPrintf(SV_LOG_ERROR, "executePipe %s not returned for %d minutes, fd %d.\n", command.c_str(), WAITTIME/60, pipefdRead); } else if (FD_ISSET(pipefdRead, &rfds)) { b_ReadyToReadFromPipe = true; } else if (FD_ISSET(pipefdRead, &exfds)) { DebugPrintf(SV_LOG_ERROR, "executePipe exception set for fd %d\n", pipefdRead); break; } else { /* impossible case: record error */ DebugPrintf(SV_LOG_ERROR, "select returned %d but fd is not ready: command %s\n", retval, command.c_str()); break; } if (b_ReadyToReadFromPipe) { b_ReadyToReadFromPipe = false; bytesRead = readFromPipe(pipefdRead, results); if (bytesRead == READ_SUCCESS) { break;/// Read completely, so break out of select and return } else if (bytesRead == READ_ERROR) { break; /// read error, return } /* Default is to continue select() call as there is data to read */ } } while(true); } bool executePipe(std::string const & command, std::stringstream & results) { DebugPrintf(SV_LOG_DEBUG,"ENTERED %s %s \n",FUNCTION_NAME, command.c_str()); // note: this is unix only int pipefd[2]; pid_t chpid; do {/// do while controls the scoped_lock duration boost::mutex::scoped_lock guard(g_MutexForkProcess); if (pipe(pipefd) != 0) { results << "pipe failed, errno " << errno << '\n'; return false; } chpid = fork(); if (-1 == chpid) { /// fork error close(pipefd[READ]); close(pipefd[WRITE]); DebugPrintf(SV_LOG_ERROR, "fork failed, errno %d \n", errno); return false; } else if (0 == chpid) { /// child process close(pipefd[READ]); dup2(pipefd[WRITE], STDOUT_FILENO); close(pipefd[WRITE]); close(STDERR_FILENO); close(STDIN_FILENO); execl(gBinPath, "sh", "-c", command.c_str(), NULL); DebugPrintf(SV_LOG_ERROR, "exec failed, errno %d \n", errno); _exit(1); } else { /// parent process /// Close write end of pipe as parent is interested ONLY in read end close(pipefd[WRITE]); /// pipefd shouldn't be inherited by other children of svagents setCloexecFlag(pipefd[READ], 1); } } while (0);/// Unlock the scoped_lock SelectNRead(pipefd[READ], results, command); close(pipefd[READ]); /* Wait for the child*/ int chstatus; pid_t wpid = waitpid(chpid, &chstatus, 0); // reset for user to read from begining results.clear(); results.seekg(0, std::ios::beg); DebugPrintf(SV_LOG_DEBUG,"EXITED %s %s\n",FUNCTION_NAME, command.c_str()); return true; }