📄 cgi.cpp
字号:
{ goto nt_error_return; };
CloseFileDesc( tStdin_Parent );
tStdin_Parent = INVALID_DESC;
}; /* end if ( iDoStdin ) */
/* --- setup pipes (stderr) --- */
if ( !::CreatePipe( &tStderr_Parent, &tStderr_CGI, &saAttr, 0 ) )
{ goto nt_error_return; };
if ( !::DuplicateHandle(
tProc,
tStderr_Parent,
tProc,
&tStderr_ParentDup,
0,
FALSE,
DUPLICATE_SAME_ACCESS
) )
{ goto nt_error_return; };
CloseFileDesc( tStderr_Parent );
tStderr_Parent = INVALID_HANDLE_VALUE;
tStartUp.dwFlags |= STARTF_USESTDHANDLES;
tStartUp.hStdInput = tStdin_CGI;
tStartUp.hStdOutput = tStdout_CGI;
tStartUp.hStdError = tStderr_CGI;
/*
** Execution flags
*/
if ( iFlags & FLG_16BIT )
{ dwFlags |= CREATE_SEPARATE_WOW_VDM; };
PROCESS_INFORMATION tProcessInfo;
ppEnv = CreateCGIEnvironment( tPIHTTP );
/*
** complete command line for associations
*/
AppName = (char *)PIUtil_malloc(_MAX_PATH);
// Set AppName if the script name in pPath is associated
if ((int)FindExecutable(pPath, NULL, AppName) < 33)
{
PIUtil_free(AppName);
AppName = NULL;
};
/* --- spawn the CGI program --- */
bProcessCreated = CreateProcess(
AppName, /* address of module name */
(char *)pPath, /* address of command line */
NULL, /* address of process security attributes */
NULL, /* address of thread security attributes */
TRUE, /* new process inherits handles */
dwFlags, /* creation flags */
ppEnv, /* address of new environment block */
pDirectory, /* address of current directory name */
&tStartUp, /* address of STARTUPINFO */
&tProcessInfo /* address of PROCESS_INFORMATION */
);
PIUtil_free(AppName);
if ( !bProcessCreated )
{ goto nt_error_return; };
/* --- log debug information about the process --- */
HTTPCore_logDebug( DBG_MED, "Process created!, \n\thProcess %p\
\n\thThread %p\n\tdwProcessId %lu\t\n\tdwThreadId %lu",
tProcessInfo.dwThreadId,
tProcessInfo.dwProcessId,
tProcessInfo.hThread,
tProcessInfo.hProcess );
# if VERBOSE_DEBUG
os << "Process created!" << endl;
os << " hProcess " << hex << tProcessInfo.hProcess << endl;
os << " hThread " << hex << tProcessInfo.hThread << endl;
os << " dwProcessId " << hex << tProcessInfo.dwProcessId << endl;
os << " dwThreadId " << hex << tProcessInfo.dwThreadId << endl;
# endif
/* --- Handle to main thread in new process --- */
::CloseHandle( tProcessInfo.hThread );
*pProcess = tProcessInfo.hProcess;
*pStdout_Parent = tStdout_ParentDup;
*pStdout_CGI = tStdout_CGI;
if ( iDoStdin )
{
*pStdin_Parent = tStdin_ParentDup;
*pStdin_CGI = tStdin_CGI;
};
*pStderr_Parent = tStderr_ParentDup;
*pStderr_CGI = tStderr_CGI;
/* --- OK --- */
return 0;
nt_error_return:
/* --- restore handles --- */
int iTmp = GetLastError();
goto generic_error_return;
#elif POSIX
const char **ppCommandArgs=0;
register int iPid = 0;
/* --- setup pipes (stdout) --- */
int aOutFDS[2];
if ( ::pipe( aOutFDS )==-1 )
{ goto posix_error_return; };
tStdout_Parent = aOutFDS[0];
tStdout_CGI = aOutFDS[1];
/* --- setup pipes (stderr) --- */
int aErrFDS[2];
if ( ::pipe( aErrFDS )==-1 )
{ goto posix_error_return; };
tStderr_Parent = aErrFDS[0];
tStderr_CGI = aErrFDS[1];
/* --- setup pipes (stdin) --- */
if ( iDoStdin )
{
int aInFDS[2];
if ( ::pipe( aInFDS )==-1 )
{ goto posix_error_return; };
tStdin_CGI = aInFDS[0];
tStdin_Parent = aInFDS[1];
};
ppEnv = CreateCGIEnvironment( tPIHTTP );
/* ---
Create command line
--- */
{
/* --- scan once and NULL seperate path components --- */
int i;
int j = strlen( pPath );
int k=0;
for( i=0; i<j; i++ )
{
if ( (isspace(pPath[i])) )
{
pPath[i]='\0';
k++;
};
};
/* ---
k contains number of spaces in command line.
cmd1[sp]cmd2[sp]cmd3[sp]cmd4[0],
increment k to include command terminated by final null.
increment k again to allow NULL command at end of array.
--- */
k += 2;
/* --- allocate and assign argument array --- */
ppCommandArgs = (const char **)PIHTTP_allocMem( &tPIHTTP,
sizeof( const char * ) * k );
int x = 0;
const char *pLastArg = 0;
for( i=0; i<=j; i++ )
{
if ( pLastArg==0 )
{ pLastArg=&(pPath[i]); };
if ( pPath[i]=='\0' )
{
ppCommandArgs[x++] = pLastArg;
pLastArg = 0;
};
};
ppCommandArgs[x++] = 0;
assert( x==k );
};
/* ---
Try not to write any data to the processes data space to avoid
copy-on-write penalty for CGI child process.
iPid is declared register so its assignment on fork in the child
process will not write to the child process data space.
--- */
#if 0
/*
** NOTE
*/
#define P ( PIThread_getSystemHandle( PIThread_getCurrent() ) )
cerr << P << " 1.." << tStdin_Parent << endl;
cerr << P << " 2.." << tStdin_CGI << endl;
cerr << P << " 3.." << tStdout_Parent << endl;
cerr << P << " 4.." << tStdout_CGI << endl;
cerr << P << " 5.." << tStderr_Parent << endl;
cerr << P << " 6.." << tStderr_CGI << endl;
#endif
iPid = ::fork();
switch( iPid )
{
case -1:
break;
case 0:
/* ---
This is the child
--- */
CloseFileDesc( tStdout_Parent );
CloseFileDesc( tStderr_Parent );
CloseFileDesc( tStdin_Parent );
if ( iDoStdin )
{
if ( ::dup2( tStdin_CGI, 0 )==-1 )
{
perror( "CGI child, dup2( stdin ) failed" );
goto cgi_error;
};
};
CloseFileDesc( tStdin_CGI );
if ( ::dup2( tStdout_CGI, 1 )==-1 )
{
perror( "CGI child, dup2( stdout ) failed" );
goto cgi_error;
};
CloseFileDesc( tStdout_CGI );
if ( ::dup2( tStderr_CGI, 2 )==-1 )
{
perror( "CGI child, dup2( stderr ) failed" );
goto cgi_error;
};
CloseFileDesc( tStderr_CGI );
/* --- change current directory --- */
if ( pDirectory )
{ ::chdir( pDirectory ); };
if ( ::execve( pPath, (char *const *)ppCommandArgs,
(char *const *)ppEnv )==-1 )
{
perror( "CGI child, execve() failed" );
fprintf( stderr, "Path is '%s'\n", pPath );
fflush( stderr );
goto cgi_error;
};
cgi_error:
::_exit( 1 );
/* none of the following should happen */
::_exit( -1 );
assert( 0 );
break;
default:
/* --- this is the parent --- */
*pProcess = iPid;
*pStdout_CGI = tStdout_CGI;
*pStdout_Parent = tStdout_Parent;
*pStderr_CGI = tStderr_CGI;
*pStderr_Parent = tStderr_Parent;
if ( iDoStdin )
{
*pStdin_CGI = tStdin_CGI;
*pStdin_Parent = tStdin_Parent;
};
/* --- log debug information about the process --- */
HTTPCore_logDebug( DBG_MED, "Process created!, \n\tPid %d\
\n\tStdout_Parent %d\n\tStdout_CGI %d\n\tStderr_Parent %d\n\tStderr_CGI %d",
iPid, tStdout_Parent, tStdout_CGI, tStderr_Parent,
tStderr_CGI );
/* --- OK --- */
return 0;
};
/* --- error --- */
posix_error_return:
int iTmp = errno;
goto generic_error_return;
#endif
generic_error_return:
/* --- stdout handles --- */
CloseIfValidDesc( tStdout_CGI );
CloseIfValidDesc( tStdout_Parent );
CloseIfValidDesc( tStdout_ParentDup );
/* --- stdin handles --- */
CloseIfValidDesc( tStdin_CGI );
CloseIfValidDesc( tStdin_Parent );
CloseIfValidDesc( tStdin_ParentDup );
/* --- stdin handles --- */
CloseIfValidDesc( tStderr_CGI );
CloseIfValidDesc( tStderr_Parent );
CloseIfValidDesc( tStderr_ParentDup );
HTTPCore_logError( &tPIHTTP, "CGI: Error executing CGI program, \
system specific error code is '%d'.", iTmp );
/* --- return error --- */
return iTmp ? iTmp : -1;
};
/* ---
Wait for a child process to terminate
--- */
bool WaitForChild( const char *pszCmdLine, PIHTTP &tPIHTTP,
PROCDESC tProcess )
{
int iRet = PIPlatform_waitForProcess( (int)tProcess, 0, iKillAfter );
switch( iRet )
{
case PIAPI_COMPLETED: return true;
case PIAPI_TIMEOUT:
HTTPCore_logError( &tPIHTTP, "CGI: CGI \
process did \
not terminate after %d seconds.\nCGI program command line is '%s'.\n\
The process will be terminated.", iKillAfter, pszCmdLine );
# if WIN32
::TerminateProcess( tProcess, (UINT)-1 );
# elif POSIX
::kill( (pid_t)tProcess, SIGTERM );
# endif
return false;
default:
HTTPCore_logError( &tPIHTTP, "CGI: Error waiting for \
CGI process to terminate.\nCGI program command line is '%s'",
pszCmdLine );
return false;
};
}
/* ---
Write output from one buffer into another. Transfer data until no more
data can be read.
Returns 0 on success.
--- */
int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer )
{
int iLen;
const char *pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
while( pBuffer )
{
if ( PIIOBuffer_write( &tOutBuffer, pBuffer, iLen, PIIOBUF_NONE )
<=0 )
{
assert( 0 );
return -1;
};
pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
};
return 0;
};
/* ---
Write output from one buffer into another.
iReadLen gives number of bytes to transfer, must be > 0
Returns 0 on success.
--- */
int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer,
int iReadLen )
{
assert( iReadLen>0 );
if ( iReadLen<=0 )
{ return 0; };
enum { BUF_SIZE=4096 };
char szBuffer[BUF_SIZE];
int iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
int iLen;
iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer, iAttemptToRead );
while( iLen>0 )
{
if ( PIIOBuffer_write( &tOutBuffer, szBuffer, iLen,
PIIOBUF_NOBUFFER )<=0 )
{
assert( 0 );
return -1;
};
iReadLen -= iLen;
if ( !iReadLen )
{
return 0;
};
iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer,
iAttemptToRead );
};
/* ---
Done
--- */
return iLen;
};
/* ---
Send input data to the CGI program using a copy of the pipe
IO object. Return non-zero on success, 0 on error.
--- */
int SendCGIInput( PIIOBuffer &tIn, PIIOBuffer &tOut, int iLen )
{
int iRet = 1; /* initially success */
/* ---
Write input to child stdin and close CGI side of pipe
--- */
if ( WriteBufferToBuffer( tIn, tOut, iLen ) )
{
iRet = 0;
goto SendCGIInput_error;
};
if ( iSendCRLF )
{
/* --- Write CRLF to the CGI program --- */
if ( PIIOBuffer_writeLn( &tOut, "", 0, PIIOBUF_NONE )<=0 )
{
iRet = 0;
goto SendCGIInput_error;
};
};
/* --- flush --- */
if ( !PIIOBuffer_flush( &tOut ) )
{
iRet = 0;
goto SendCGIInput_error;
};
/* --- done --- */
SendCGIInput_error:
if ( !iRet )
{
HTTPCore_logDebug( DBG_MED, "CGI: SendCGIInput() failed." );
};
return iRet;
};
/* ---
Execute the CGI program and examine the output.
--- */
bool DoExecCGI(
PIFInfo *pFile,
PIHTTP &tPIHTTP,
PROCDESC &tProcess )
{
/* ---
Normally the callee to DoExecCGI would generate an internal redirect
to handle an error code. The flag bIgnoreStatus can be set to
true to tell the callee that a redirect should not happen, because
the protocol headers have already been sent to the client. This
happens for full response
--- */
bool bIgnoreStatus = false;
PIDB *pQ = tPIHTTP.pRequestDB;
PIDB *pR = tPIHTTP.pResponseDB;
PIDB *pC = tPIHTTP.pConnectionDB;
PIIOBuffer &tB = *( tPIHTTP.pBuffer );
/* --- sanity check --- */
assert( pFile && pQ && pR );
if ( !( pFile && pQ && pR ) )
{
tPIHTTP.iStatus = ST_INTERNALERROR;
return bIgnoreStatus;
};
/* ---
Make CGI context data
--- */
CGIContextData tContext;
/* ---
Determine whether or not there is any input to be sent to the
CGI program
--- */
const char *pContentLength = (const char *)PIDB_lookup( pQ,
PIDBTYPE_RFC822, KEY_HTTP_CONTENTLENGTH, 0 );
int iStdinLen = pContentLength ? atoi( pContentLength ) : 0;
int iDoStdin = ( iStdinLen > 0 );
/* ---
See if this is an 'ISINDEX' query, and if it is, set the
ISINDEX value appropriately
An ISINDEX query will not have a '=' encoded in the query string.
--- */
const char *pQS = (const char *)PIDB_lookup( pQ, PIDBTYPE_STRING,
KEY_HTTP_QUERYSTRING, 0 );
Pi3String *pEncodedQS = Pi3String_new( pQS );
Pi3String *pDecodedQS = Pi3String_new( 0 );
HTTPUtil_urlDecode( pEncodedQS, pDecodedQS );
const char *pDQS = Pi3String_getPtr( pDecodedQS );
int i;
for( i=0; pDQS[i] && pDQS[i]!='='; i++ );
if ( !pDQS[i] )
{
/* --- '=' not found, therefore this is an ISINDEX query --- */
/* ---
Only include up to the first punctuation character, to
keep out 'special' parameters being passed to exec(). Thi
is a security message
--- */
for( i=0; pDQS[i] && !ispunct(pDQS[i]); i++ );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -