mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2025-12-26 14:18:20 +00:00
* rtems_webserver/cgi.c, rtems_webserver/sockGen.c, rtems_webserver/umui.c, rtems_webserver/websSSL.c, rtems_webserver/websSSL.h, rtems_webserver/websda.c, rtems_webserver/websda.h: New files. Not included in previous commit.
332 lines
9.8 KiB
C
332 lines
9.8 KiB
C
/*
|
|
* cgi.c -- CGI processing (for the GoAhead Web server
|
|
*
|
|
* Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
|
|
*
|
|
* See the file "license.txt" for usage and redistribution license requirements
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
/********************************** Description *******************************/
|
|
/*
|
|
* This module implements the /cgi-bin handler. CGI processing differs from
|
|
* goforms processing in that each CGI request is executed as a separate
|
|
* process, rather than within the webserver process. For each CGI request the
|
|
* environment of the new process must be set to include all the CGI variables
|
|
* and its standard input and output must be directed to the socket. This
|
|
* is done using temporary files.
|
|
*/
|
|
|
|
/*********************************** Includes *********************************/
|
|
#include "wsIntrn.h"
|
|
#ifdef UEMF
|
|
#include "uemf.h"
|
|
#else
|
|
#include "basic/basicInternal.h"
|
|
#endif
|
|
|
|
/************************************ Locals **********************************/
|
|
typedef struct { /* Struct for CGI tasks which have completed */
|
|
webs_t wp; /* pointer to session websRec */
|
|
char_t *stdIn; /* file desc. for task's temp input fd */
|
|
char_t *stdOut; /* file desc. for task's temp output fd */
|
|
char_t *cgiPath; /* path to executable process file */
|
|
char_t **argp; /* pointer to buf containing argv tokens */
|
|
char_t **envp; /* pointer to array of environment strings */
|
|
int handle; /* process handle of the task */
|
|
long fplacemark; /* seek location for CGI output file */
|
|
} cgiRec;
|
|
static cgiRec **cgiList; /* hAlloc chain list of wp's to be closed */
|
|
static int cgiMax; /* Size of hAlloc list */
|
|
|
|
/************************************* Code ***********************************/
|
|
|
|
/*
|
|
* Process a form request. Returns 1 always to indicate it handled the URL
|
|
*/
|
|
int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
|
|
char_t *url, char_t *path, char_t* query)
|
|
{
|
|
cgiRec *cgip;
|
|
sym_t *s;
|
|
char_t cgiBuf[FNAMESIZE], *stdIn, *stdOut, cwd[FNAMESIZE];
|
|
char_t *cp, *cgiName, *cgiPath, **argp, **envp, **ep;
|
|
int n, envpsize, argpsize, pHandle, cid;
|
|
a_assert(websValid(wp));
|
|
a_assert(url && *url);
|
|
a_assert(path && *path == '/');
|
|
websStats.cgiHits++;
|
|
/*
|
|
* Extract the form name and then build the full path name. The form
|
|
* name will follow the first '/' in path.
|
|
*/
|
|
gstrncpy(cgiBuf, path, TSZ(cgiBuf));
|
|
if ((cgiName = gstrchr(&cgiBuf[1], '/')) == NULL) {
|
|
websError(wp, 200, T("Missing CGI name"));
|
|
return 1;
|
|
}
|
|
cgiName++;
|
|
if ((cp = gstrchr(cgiName, '/')) != NULL) {
|
|
*cp = '\0';
|
|
}
|
|
fmtAlloc(&cgiPath, FNAMESIZE, T("%s/%s/%s"), websGetDefaultDir(),
|
|
CGI_BIN, cgiName);
|
|
#ifndef VXWORKS
|
|
/*
|
|
* See if the file exists and is executable. If not error out.
|
|
* Don't do this step for VxWorks, since the module may already
|
|
* be part of the OS image, rather than in the file system.
|
|
*/
|
|
{
|
|
gstat_t sbuf;
|
|
if (gstat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) {
|
|
websError(wp, 200, T("CGI process file does not exist"));
|
|
bfree(B_L, cgiPath);
|
|
return 1;
|
|
}
|
|
#if (defined (WIN) || defined (CE))
|
|
if (gstrstr(cgiPath, T(".exe")) == NULL &&
|
|
gstrstr(cgiPath, T(".bat")) == NULL) {
|
|
#elif (defined (NW))
|
|
if (gstrstr(cgiPath, T(".nlm")) == NULL) {
|
|
#else
|
|
if (gaccess(cgiPath, X_OK) != 0) {
|
|
#endif /* WIN || CE */
|
|
websError(wp, 200, T("CGI process file is not executable"));
|
|
bfree(B_L, cgiPath);
|
|
return 1;
|
|
}
|
|
}
|
|
#endif /* ! VXWORKS */
|
|
|
|
|
|
/*
|
|
* Get the CWD for resetting after launching the child process CGI
|
|
*/
|
|
ggetcwd(cwd, FNAMESIZE);
|
|
/*
|
|
* Retrieve the directory of the child process CGI
|
|
*/
|
|
if ((cp = gstrrchr(cgiPath, '/')) != NULL) {
|
|
*cp = '\0';
|
|
gchdir(cgiPath);
|
|
*cp = '/';
|
|
}
|
|
/*
|
|
* Build command line arguments. Only used if there is no non-encoded
|
|
* = character. This is indicative of a ISINDEX query. POST separators
|
|
* are & and others are +. argp will point to a balloc'd array of
|
|
* pointers. Each pointer will point to substring within the
|
|
* query string. This array of string pointers is how the spawn or
|
|
* exec routines expect command line arguments to be passed. Since
|
|
* we don't know ahead of time how many individual items there are in
|
|
* the query string, the for loop includes logic to grow the array
|
|
* size via brealloc.
|
|
*/
|
|
argpsize = 10;
|
|
argp = balloc(B_L, argpsize * sizeof(char_t *));
|
|
*argp = cgiPath;
|
|
n = 1;
|
|
if (gstrchr(query, '=') == NULL) {
|
|
websDecodeUrl(query, query, gstrlen(query));
|
|
for (cp = gstrtok(query, T(" ")); cp != NULL; ) {
|
|
*(argp+n) = cp;
|
|
n++;
|
|
if (n >= argpsize) {
|
|
argpsize *= 2;
|
|
argp = brealloc(B_L, argp, argpsize * sizeof(char_t *));
|
|
}
|
|
cp = gstrtok(NULL, T(" "));
|
|
}
|
|
}
|
|
*(argp+n) = NULL;
|
|
/*
|
|
* Add all CGI variables to the environment strings to be passed
|
|
* to the spawned CGI process. This includes a few we don't
|
|
* already have in the symbol table, plus all those that are in
|
|
* the cgiVars symbol table. envp will point to a balloc'd array of
|
|
* pointers. Each pointer will point to a balloc'd string containing
|
|
* the keyword value pair in the form keyword=value. Since we don't
|
|
* know ahead of time how many environment strings there will be the
|
|
* for loop includes logic to grow the array size via brealloc.
|
|
*/
|
|
envpsize = WEBS_SYM_INIT;
|
|
envp = balloc(B_L, envpsize * sizeof(char_t *));
|
|
n = 0;
|
|
fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("PATH_TRANSLATED"), cgiPath);
|
|
n++;
|
|
fmtAlloc(envp+n, FNAMESIZE, T("%s=%s/%s"),T("SCRIPT_NAME"),
|
|
CGI_BIN, cgiName);
|
|
n++;
|
|
fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("REMOTE_USER"), wp->userName);
|
|
n++;
|
|
fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("AUTH_TYPE"), wp->authType);
|
|
n++;
|
|
for (s = symFirst(wp->cgiVars); s != NULL; s = symNext(wp->cgiVars)) {
|
|
if (s->content.valid && s->content.type == string &&
|
|
gstrcmp(s->name.value.string, T("REMOTE_HOST")) != 0 &&
|
|
gstrcmp(s->name.value.string, T("HTTP_AUTHORIZATION")) != 0) {
|
|
fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), s->name.value.string,
|
|
s->content.value.string);
|
|
n++;
|
|
if (n >= envpsize) {
|
|
envpsize *= 2;
|
|
envp = brealloc(B_L, envp, envpsize * sizeof(char_t *));
|
|
}
|
|
}
|
|
}
|
|
*(envp+n) = NULL;
|
|
/*
|
|
* Create temporary file name(s) for the child's stdin and stdout.
|
|
* For POST data the stdin temp file (and name) should already exist.
|
|
*/
|
|
if (wp->cgiStdin == NULL) {
|
|
wp->cgiStdin = websGetCgiCommName();
|
|
}
|
|
stdIn = wp->cgiStdin;
|
|
stdOut = websGetCgiCommName();
|
|
/*
|
|
* Now launch the process. If not successful, do the cleanup of resources.
|
|
* If successful, the cleanup will be done after the process completes.
|
|
*/
|
|
if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut))
|
|
== -1) {
|
|
websError(wp, 200, T("failed to spawn CGI task"));
|
|
for (ep = envp; *ep != NULL; ep++) {
|
|
bfreeSafe(B_L, *ep);
|
|
}
|
|
bfreeSafe(B_L, cgiPath);
|
|
bfreeSafe(B_L, argp);
|
|
bfreeSafe(B_L, envp);
|
|
bfreeSafe(B_L, stdOut);
|
|
} else {
|
|
/*
|
|
* If the spawn was successful, put this wp on a queue to be
|
|
* checked for completion.
|
|
*/
|
|
cid = hAllocEntry((void***) &cgiList, &cgiMax, sizeof(cgiRec));
|
|
cgip = cgiList[cid];
|
|
cgip->handle = pHandle;
|
|
cgip->stdIn = stdIn;
|
|
cgip->stdOut = stdOut;
|
|
cgip->cgiPath = cgiPath;
|
|
cgip->argp = argp;
|
|
cgip->envp = envp;
|
|
cgip->wp = wp;
|
|
cgip->fplacemark = 0;
|
|
websTimeoutCancel(wp);
|
|
}
|
|
/*
|
|
* Restore the current working directory after spawning child CGI
|
|
*/
|
|
gchdir(cwd);
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Any entry in the cgiList need to be checked to see if it has
|
|
*/
|
|
void websCgiGatherOutput (cgiRec *cgip)
|
|
{
|
|
gstat_t sbuf;
|
|
char_t cgiBuf[FNAMESIZE];
|
|
if ((gstat(cgip->stdOut, &sbuf) == 0) &&
|
|
(sbuf.st_size > cgip->fplacemark)) {
|
|
int fdout;
|
|
fdout = gopen(cgip->stdOut, O_RDONLY | O_BINARY, 0444 );
|
|
/*
|
|
* Check to see if any data is available in the
|
|
* output file and send its contents to the socket.
|
|
*/
|
|
if (fdout >= 0) {
|
|
webs_t wp = cgip->wp;
|
|
int nRead;
|
|
/*
|
|
* Write the HTTP header on our first pass
|
|
*/
|
|
if (cgip->fplacemark == 0) {
|
|
websWrite(wp, T("HTTP/1.0 200 OK\r\n"));
|
|
}
|
|
glseek(fdout, cgip->fplacemark, SEEK_SET);
|
|
while ((nRead = gread(fdout, cgiBuf, FNAMESIZE)) > 0) {
|
|
websWriteBlock(wp, cgiBuf, nRead);
|
|
cgip->fplacemark += nRead;
|
|
}
|
|
gclose(fdout);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Any entry in the cgiList need to be checked to see if it has
|
|
* completed, and if so, process its output and clean up.
|
|
*/
|
|
void websCgiCleanup()
|
|
{
|
|
cgiRec *cgip;
|
|
webs_t wp;
|
|
char_t **ep;
|
|
int cid, nTries;
|
|
for (cid = 0; cid < cgiMax; cid++) {
|
|
if ((cgip = cgiList[cid]) != NULL) {
|
|
wp = cgip->wp;
|
|
websCgiGatherOutput (cgip);
|
|
if (websCheckCgiProc(cgip->handle) == 0) {
|
|
/*
|
|
* We get here if the CGI process has terminated. Clean up.
|
|
*/
|
|
nTries = 0;
|
|
/*
|
|
* Make sure we didn't miss something during a task switch.
|
|
* Maximum wait is 100 times 10 msecs (1 second).
|
|
*/
|
|
while ((cgip->fplacemark == 0) && (nTries < 100)) {
|
|
websCgiGatherOutput(cgip);
|
|
/*
|
|
* There are some cases when we detect app exit
|
|
* before the file is ready.
|
|
*/
|
|
if (cgip->fplacemark == 0) {
|
|
#ifdef WIN
|
|
Sleep(10);
|
|
#endif /* WIN*/
|
|
}
|
|
nTries++;
|
|
}
|
|
if (cgip->fplacemark == 0) {
|
|
websError(wp, 200, T("CGI generated no output"));
|
|
} else {
|
|
websDone(wp, 200);
|
|
}
|
|
/*
|
|
* Remove the temporary re-direction files
|
|
*/
|
|
gunlink(cgip->stdIn);
|
|
gunlink(cgip->stdOut);
|
|
/*
|
|
* Free all the memory buffers pointed to by cgip.
|
|
* The stdin file name (wp->cgiStdin) gets freed as
|
|
* part of websFree().
|
|
*/
|
|
cgiMax = hFree((void***) &cgiList, cid);
|
|
for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) {
|
|
bfreeSafe(B_L, *ep);
|
|
}
|
|
bfreeSafe(B_L, cgip->cgiPath);
|
|
bfreeSafe(B_L, cgip->argp);
|
|
bfreeSafe(B_L, cgip->envp);
|
|
bfreeSafe(B_L, cgip->stdOut);
|
|
bfreeSafe(B_L, cgip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/******************************************************************************/
|