C++ common classes
General purpose classes for use across projects (Apache license)
|
00001 /* 00002 Copyright [2010] [Richard Bross] 00003 00004 Licensed under the Apache License, Version 2.0 (the "License"); 00005 you may not use this file except in compliance with the License. 00006 You may obtain a copy of the License at 00007 00008 http://www.apache.org/licenses/LICENSE-2.0 00009 00010 Unless required by applicable law or agreed to in writing, software 00011 distributed under the License is distributed on an "AS IS" BASIS, 00012 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 See the License for the specific language governing permissions and 00014 limitations under the License. 00015 */// rbross - 12/31/09 00016 00017 #include <errno.h> 00018 #include "executeshell.h" 00019 #include <stdio.h> 00020 #include <string.h> 00021 #include <fcntl.h> 00022 00023 #define STDOUT_BUFFER_SIZE 8192 00024 00025 char ExecuteShell::szStdOut[ESHELL_FORMAT_SIZE]; 00026 00027 ExecuteShell::ExecuteShell() 00028 { 00029 ::memset(szStdOut, 0, sizeof (szStdOut)); 00030 } 00031 00032 ExecuteShell::~ExecuteShell() 00033 { 00034 } 00035 00036 // Execute a command and return stdout in a string buffer 00037 // If a command is forked, we could sit and wait for stdout forever 00038 00039 int ExecuteShell::Execute(const char *szCommand, string &sStdout, string *sStderr, const char *strForkExit) 00040 { 00041 FILE *pFile; 00042 int iRet = 0; 00043 int filedes[2]; 00044 char strCommand[64]; 00045 bool bStderr = true; 00046 bool bForkExit = false; 00047 char sReadBuffer[STDOUT_BUFFER_SIZE]; 00048 int iExit; 00049 00050 // Open file 00051 try 00052 { 00053 sStdout.clear(); 00054 string sCommand = szCommand; 00055 ::memset(filedes, 0, sizeof (filedes)); 00056 // Redirect stderr. "pipe" creates a pipe with a read end and a write end 00057 if (sStderr == NULL || pipe(filedes) == -1) 00058 bStderr = false; 00059 else 00060 { 00061 ::sprintf(strCommand, " 2>&%d", filedes[1]); 00062 sCommand += strCommand; 00063 sStderr->clear(); 00064 } 00065 00066 // Execute command 00067 pFile = ::popen(sCommand.c_str(), "r"); 00068 if (!pFile) 00069 { 00070 iRet = -1; 00071 throw (-1); 00072 } 00073 00074 // Fill buffer if command writes to stdout 00075 // No blocking 00076 int fd = fileno(pFile); // Get file descriptor 00077 int flags = fcntl(fd, F_GETFL, 0); // Get flags 00078 flags |= O_NONBLOCK; // Add non-blocking 00079 fcntl(fd, F_SETFL, flags); // Set flags 00080 // Read 00081 while (!::feof(pFile)) 00082 { 00083 if (::fgets(sReadBuffer, STDOUT_BUFFER_SIZE, pFile) != NULL) 00084 { 00085 sStdout += sReadBuffer; 00086 // If we know that we have been forked, we are waiting for an explicit string to exit 00087 if (strForkExit) 00088 { 00089 if ((iExit = sStdout.find(strForkExit)) != string::npos) 00090 { // We got that string, so set exit flag 00091 sStdout.resize(iExit); 00092 bForkExit = true; 00093 break; 00094 } 00095 } 00096 } 00097 // This SHOULD work, but it only returns errno EAGAIN 00098 if (::ferror(pFile) && errno != EAGAIN && errno != EWOULDBLOCK) 00099 break; 00100 } 00101 00102 // Fill buffer if command writes to stderr and we did NOT get the fork exit flag 00103 if (bStderr && !bForkExit) 00104 { 00105 sStderr->clear(); 00106 char cBuf; 00107 int flags = ::fcntl(filedes[0], F_GETFL, 0); 00108 ::fcntl(filedes[0], F_SETFL, flags | O_NONBLOCK); 00109 while (::read(filedes[0], &cBuf, 1) > 0) 00110 *sStderr += cBuf; 00111 } 00112 00113 // pclose returns the exit code of the process 00114 iRet = ::pclose(pFile); 00115 } 00116 catch (exception exc) 00117 { 00118 exc.what(); 00119 } 00120 00121 // Make sure that we close the pipes 00122 if (filedes[0]) 00123 ::close(filedes[0]); 00124 if (filedes[1]) 00125 ::close(filedes[1]); 00126 00127 return (iRet); 00128 } 00129 00130 00131 // Find the executables that we need 00132 00133 const char *ExecuteShell::FindBinary(const char *pName) 00134 { 00135 static string sExecPath; 00136 char sShellCmd[1024]; 00137 const char *pFormat = "find %s -type f -name %s -perm -g+x,u+x"; 00138 int iRet; 00139 00140 // Format command. First look in /usr 00141 ::memset(sShellCmd, 0, sizeof (sShellCmd)); 00142 ::sprintf(sShellCmd, pFormat, "/usr", pName); 00143 iRet = Execute(sShellCmd, sExecPath); 00144 if (iRet) // No success. Look in /etc 00145 { 00146 ::sprintf(sShellCmd, pFormat, "/etc", pName); 00147 iRet = Execute(sShellCmd, sExecPath); 00148 if (iRet) // No success. Look in /bin 00149 { 00150 ::sprintf(sShellCmd, pFormat, "/bin", pName); 00151 iRet = Execute(sShellCmd, sExecPath); 00152 } 00153 } 00154 00155 if (!iRet) // Success 00156 { 00157 int iIndex = sExecPath.find('\n', 0); 00158 if (iIndex != -1) 00159 sExecPath.resize(iIndex); 00160 return sExecPath.c_str(); 00161 } 00162 00163 // Fail 00164 return NULL; 00165 } 00166 00167 00168 // Write to standard out 00169 00170 void ExecuteShell::WriteStdout(bool bNewline) 00171 { 00172 // One way or another, this WILL be 0 terminated 00173 if (bNewline) 00174 { 00175 szStdOut[ESHELL_FORMAT_SIZE - 3] = '\0'; 00176 ::strcat(szStdOut, "\n\r"); 00177 } 00178 else 00179 szStdOut[ESHELL_FORMAT_SIZE - 1] = '\0'; 00180 00181 if (::write(STDOUT_FILENO, szStdOut, ::strlen(szStdOut) - 1)) 00182 return; 00183 } 00184