123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014 |
- /*
- cgi.c -- CGI processing
- 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.
- Copyright (c) All Rights Reserved. See details at the end of the file.
- */
- /*********************************** Includes *********************************/
- #include "goahead.h"
- /*********************************** Defines **********************************/
- #if ME_GOAHEAD_CGI
- #if ME_WIN_LIKE
- typedef HANDLE CgiPid;
- #else
- typedef pid_t CgiPid;
- #endif
- typedef struct Cgi { /* Struct for CGI tasks which have completed */
- Webs *wp; /* Connection object */
- char *stdIn; /* File desc. for task's temp input fd */
- char *stdOut; /* File desc. for task's temp output fd */
- char *cgiPath; /* Path to executable process file */
- char **argp; /* Pointer to buf containing argv tokens */
- char **envp; /* Pointer to array of environment strings */
- CgiPid handle; /* Process handle of the task */
- off_t fplacemark; /* Seek location for CGI output file */
- } Cgi;
- static Cgi **cgiList; /* walloc chain list of wp's to be closed */
- static int cgiMax; /* Size of walloc list */
- /************************************ Forwards ********************************/
- static int checkCgi(CgiPid handle);
- static CgiPid launchCgi(char *cgiPath, char **argp, char **envp, char *stdIn, char *stdOut);
- /************************************* Code ***********************************/
- /*
- Process a form request.
- Return true to indicate the request was handled, even for errors.
- */
- PUBLIC bool cgiHandler(Webs *wp)
- {
- Cgi *cgip;
- WebsKey *s;
- char cgiPrefix[ME_GOAHEAD_LIMIT_FILENAME], *stdIn, *stdOut, cwd[ME_GOAHEAD_LIMIT_FILENAME];
- char *cp, *cgiName, *cgiPath, **argp, **envp, **ep, *tok, *query, *dir, *extraPath, *exe;
- CgiPid pHandle;
- int n, envpsize, argpsize, cid;
- assert(websValid(wp));
- websSetEnv(wp);
- /*
- Extract the form name and then build the full path name. The form name will follow the first '/' in path.
- */
- scopy(cgiPrefix, sizeof(cgiPrefix), wp->path);
- if ((cgiName = strchr(&cgiPrefix[1], '/')) == NULL) {
- websError(wp, HTTP_CODE_NOT_FOUND, "Missing CGI name");
- return 1;
- }
- *cgiName++ = '\0';
- getcwd(cwd, ME_GOAHEAD_LIMIT_FILENAME);
- dir = wp->route->dir ? wp->route->dir : cwd;
- chdir(dir);
- extraPath = 0;
- if ((cp = strchr(cgiName, '/')) != NULL) {
- extraPath = sclone(cp);
- *cp = '\0';
- websSetVar(wp, "PATH_INFO", extraPath);
- websSetVarFmt(wp, "PATH_TRANSLATED", "%s%s%s", dir, cgiPrefix, extraPath);
- wfree(extraPath);
- } else {
- websSetVar(wp, "PATH_INFO", "");
- websSetVar(wp, "PATH_TRANSLATED", "");
- }
- cgiPath = sfmt("%s%s/%s", dir, cgiPrefix, cgiName);
- websSetVarFmt(wp, "SCRIPT_NAME", "%s/%s", cgiPrefix, cgiName);
- websSetVar(wp, "SCRIPT_FILENAME", cgiPath);
- /*
- 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.
- */
- #if !VXWORKS
- {
- WebsStat sbuf;
- if (stat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) {
- exe = sfmt("%s.exe", cgiPath);
- if (stat(exe, &sbuf) == 0 && (sbuf.st_mode & S_IFREG)) {
- wfree(cgiPath);
- cgiPath = exe;
- } else {
- error("Cannot find CGI program: ", cgiPath);
- websError(wp, HTTP_CODE_NOT_FOUND | WEBS_NOLOG, "CGI program file does not exist");
- wfree(cgiPath);
- return 1;
- }
- }
- #if ME_WIN_LIKE
- if (strstr(cgiPath, ".exe") == NULL && strstr(cgiPath, ".bat") == NULL)
- #else
- if (access(cgiPath, X_OK) != 0)
- #endif
- {
- websError(wp, HTTP_CODE_NOT_FOUND, "CGI process file is not executable");
- wfree(cgiPath);
- return 1;
- }
- }
- #endif /* ! VXWORKS */
- /*
- 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 walloc'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 wrealloc.
- */
- argpsize = 10;
- argp = walloc(argpsize * sizeof(char *));
- *argp = cgiPath;
- n = 1;
- query = 0;
- if (strchr(wp->query, '=') == NULL) {
- query = sclone(wp->query);
- websDecodeUrl(query, query, strlen(query));
- for (cp = stok(query, " ", &tok); cp != NULL; ) {
- *(argp+n) = cp;
- trace(5, "ARG[%d] %s", n, argp[n-1]);
- n++;
- if (n >= argpsize) {
- argpsize *= 2;
- argp = wrealloc(argp, argpsize * sizeof(char *));
- }
- cp = stok(NULL, " ", &tok);
- }
- }
- *(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 vars symbol table. envp will point to a walloc'd array of pointers. Each pointer will
- point to a walloc'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 wrealloc.
- */
- envpsize = 64;
- envp = walloc(envpsize * sizeof(char*));
- for (n = 0, s = hashFirst(wp->vars); s != NULL; s = hashNext(wp->vars, s)) {
- if (s->content.valid && s->content.type == string) {
- if (smatch(s->name.value.string, "REMOTE_HOST") ||
- smatch(s->name.value.string, "HTTP_AUTHORIZATION") ||
- smatch(s->name.value.string, "IFS") ||
- smatch(s->name.value.string, "CDPATH") ||
- smatch(s->name.value.string, "PATH") ||
- sstarts(s->name.value.string, "LD_")) {
- continue;
- }
- if (s->arg != 0 && *ME_GOAHEAD_CGI_VAR_PREFIX != '\0') {
- envp[n++] = sfmt("%s%s=%s", ME_GOAHEAD_CGI_VAR_PREFIX, s->name.value.string,
- s->content.value.string);
- } else {
- envp[n++] = sfmt("%s=%s", s->name.value.string, s->content.value.string);
- }
- trace(0, "Env[%d] %s", n, envp[n-1]);
- if (n >= envpsize) {
- envpsize *= 2;
- envp = wrealloc(envp, envpsize * sizeof(char *));
- }
- }
- }
- *(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();
- if (wp->cgifd >= 0) {
- close(wp->cgifd);
- wp->cgifd = -1;
- }
- /*
- 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 = launchCgi(cgiPath, argp, envp, stdIn, stdOut)) == (CgiPid) -1) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "failed to spawn CGI task");
- for (ep = envp; *ep != NULL; ep++) {
- wfree(*ep);
- }
- wfree(cgiPath);
- wfree(argp);
- wfree(envp);
- wfree(stdOut);
- wfree(query);
- } else {
- /*
- If the spawn was successful, put this wp on a queue to be checked for completion.
- */
- cid = wallocObject(&cgiList, &cgiMax, sizeof(Cgi));
- 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;
- wfree(query);
- }
- /*
- Restore the current working directory after spawning child CGI
- */
- chdir(cwd);
- return 1;
- }
- PUBLIC int websCgiOpen()
- {
- websDefineHandler("cgi", 0, cgiHandler, 0, 0);
- return 0;
- }
- PUBLIC bool websProcessCgiData(Webs *wp)
- {
- ssize nbytes;
- nbytes = bufLen(&wp->input);
- trace(5, "cgi: write %d bytes to CGI program", nbytes);
- if (write(wp->cgifd, wp->input.servp, (int) nbytes) != nbytes) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR| WEBS_CLOSE, "Cannot write to CGI gateway");
- } else {
- trace(5, "cgi: write %d bytes to CGI program", nbytes);
- }
- websConsumeInput(wp, nbytes);
- return 1;
- }
- static void writeCgiHeaders(Webs *wp, int status, ssize contentLength, char *location, char *contentType)
- {
- trace(5, "cgi: Start response headers");
- websSetStatus(wp, status);
- websWriteHeaders(wp, contentLength, location);
- websWriteHeader(wp, "Pragma", "no-cache");
- websWriteHeader(wp, "Cache-Control", "no-cache");
- if (contentType) {
- websWriteHeader(wp, "Content-Type", contentType);
- }
- }
- static ssize parseCgiHeaders(Webs *wp, char *buf)
- {
- char *end, *cp, *key, *value, *location, *contentType;
- ssize len, contentLength;
- int status, doneHeaders;
- status = HTTP_CODE_OK;
- contentLength = -1;
- contentType = 0;
- location = 0;
- doneHeaders = 0;
- /*
- Look for end of headers
- */
- if ((end = strstr(buf, "\r\n\r\n")) == NULL) {
- if ((end = strstr(buf, "\n\n")) == NULL) {
- return 0;
- }
- len = 2;
- } else {
- len = 4;
- }
- *end = '\0';
- end += len;
- cp = buf;
- if (!strchr(cp, ':')) {
- /* No headers found */
- return 0;
- }
- if (strncmp(cp, "HTTP/1.", 7) == 0) {
- ssplit(cp, "\r\n", &cp);
- }
- for (; cp && *cp && (*cp != '\r' && *cp != '\n') && cp < end; ) {
- key = slower(ssplit(cp, ":", &value));
- if (strcmp(key, "location") == 0) {
- location = value;
- } else if (strcmp(key, "status") == 0) {
- status = atoi(value);
- } else if (strcmp(key, "content-type") == 0) {
- contentType = value;
- } else if (strcmp(key, "content-length") == 0) {
- contentLength = atoi(value);
- } else {
- /*
- Now pass all other headers back to the client
- */
- if (!doneHeaders) {
- writeCgiHeaders(wp, status, contentLength, location, contentType);
- doneHeaders = 1;
- }
- if (key && value && !strspn(key, "%<>/\\")) {
- websWriteHeader(wp, key, "%s", value);
- } else {
- trace(5, "cgi: bad response http header: \"%s\": \"%s\"", key, value);
- }
- }
- stok(value, "\r\n", &cp);
- }
- if (!doneHeaders) {
- writeCgiHeaders(wp, status, contentLength, location, contentType);
- }
- websWriteEndHeaders(wp);
- return end - buf;
- }
- PUBLIC void websCgiGatherOutput(Cgi *cgip)
- {
- Webs *wp;
- WebsStat sbuf;
- char buf[ME_GOAHEAD_LIMIT_HEADERS + 2];
- ssize nbytes, skip;
- int fdout;
- /*
- OPT - currently polling and doing a stat each poll. Also doing open/close each chunk.
- If the CGI process writes partial headers, this repeatedly reads the data until complete
- headers are written or more than ME_GOAHEAD_LIMIT_HEADERS of data is received.
- */
- if ((stat(cgip->stdOut, &sbuf) == 0) && (sbuf.st_size > cgip->fplacemark)) {
- if ((fdout = open(cgip->stdOut, O_RDONLY | O_BINARY, 0444)) >= 0) {
- /*
- Check to see if any data is available in the output file and send its contents to the socket.
- Write the HTTP header on our first pass. The header must fit into ME_GOAHEAD_LIMIT_BUFFER.
- */
- wp = cgip->wp;
- lseek(fdout, cgip->fplacemark, SEEK_SET);
- while ((nbytes = read(fdout, buf, sizeof(buf))) > 0) {
- skip = 0;
- if (!(wp->flags & WEBS_HEADERS_CREATED)) {
- if ((skip = parseCgiHeaders(wp, buf)) == 0) {
- if (cgip->handle && sbuf.st_size < ME_GOAHEAD_LIMIT_HEADERS) {
- trace(5, "cgi: waiting for http headers");
- break;
- } else {
- trace(5, "cgi: missing http headers - create default headers");
- writeCgiHeaders(wp, HTTP_CODE_OK, -1, 0, 0);
- }
- }
- }
- trace(5, "cgi: write %d bytes to client", nbytes - skip);
- websWriteBlock(wp, &buf[skip], nbytes - skip);
- cgip->fplacemark += (off_t) nbytes;
- }
- close(fdout);
- } else {
- trace(5, "cgi: open failed");
- }
- }
- }
- /*
- Any entry in the cgiList need to be checked to see if it has completed, and if so, process its output and clean up.
- Return time till next poll.
- */
- int websCgiPoll()
- {
- Webs *wp;
- Cgi *cgip;
- char **ep;
- int cid;
- for (cid = 0; cid < cgiMax; cid++) {
- if ((cgip = cgiList[cid]) != NULL) {
- wp = cgip->wp;
- websCgiGatherOutput(cgip);
- if (checkCgi(cgip->handle) == 0) {
- /*
- We get here if the CGI process has terminated. Clean up.
- */
- cgip->handle = 0;
- websCgiGatherOutput(cgip);
- #if WINDOWS
- /*
- Windows can have delayed notification through the file system after process exit.
- */
- {
- int nTries;
- for (nTries = 0; (cgip->fplacemark == 0) && (nTries < 100); nTries++) {
- websCgiGatherOutput(cgip);
- if (cgip->fplacemark == 0) {
- Sleep(10);
- }
- }
- }
- #endif
- if (cgip->fplacemark == 0) {
- websError(wp, HTTP_CODE_INTERNAL_SERVER_ERROR, "CGI generated no output");
- } else {
- trace(5, "cgi: Request complete - calling websDone");
- websDone(wp);
- }
- /*
- Remove the temporary re-direction files
- */
- unlink(cgip->stdIn);
- unlink(cgip->stdOut);
- /*
- Free all the memory buffers pointed to by cgip. The stdin file name (wp->cgiStdin) gets freed as
- part of websFree().
- */
- cgiMax = wfreeHandle(&cgiList, cid);
- for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) {
- wfree(*ep);
- }
- wfree(cgip->cgiPath);
- wfree(cgip->stdOut);
- wfree(cgip->argp);
- wfree(cgip->envp);
- wfree(cgip);
- websPump(wp);
- websFree(wp);
- /* wp no longer valid */
- }
- }
- }
- return cgiMax ? 10 : MAXINT;
- }
- /*
- Returns a pointer to an allocated qualified unique temporary file name. This filename must eventually be deleted with
- wfree().
- */
- PUBLIC char *websGetCgiCommName()
- {
- return websTempFile(NULL, "cgi");
- }
- #if WINCE
- /*
- Launch the CGI process and return a handle to it. CE note: This function is not complete. The missing piece is
- the ability to redirect stdout.
- */
- static CgiPid launchCgi(char *cgiPath, char **argp, char **envp, char *stdIn, char *stdOut)
- {
- PROCESS_INFORMATION procinfo; /* Information about created proc */
- DWORD dwCreateFlags;
- char *fulldir;
- BOOL bReturn;
- int i, nLen;
- /*
- Replace directory delimiters with Windows-friendly delimiters
- */
- nLen = strlen(cgiPath);
- for (i = 0; i < nLen; i++) {
- if (cgiPath[i] == '/') {
- cgiPath[i] = '\\';
- }
- }
- fulldir = NULL;
- dwCreateFlags = CREATE_NEW_CONSOLE;
- /*
- CreateProcess returns errors sometimes, even when the process was started correctly. The cause is not evident.
- For now: we detect an error by checking the value of procinfo.hProcess after the call.
- */
- procinfo.hThread = NULL;
- bReturn = CreateProcess(
- cgiPath, /* Name of executable module */
- NULL, /* Command line string */
- NULL, /* Process security attributes */
- NULL, /* Thread security attributes */
- 0, /* Handle inheritance flag */
- dwCreateFlags, /* Creation flags */
- NULL, /* New environment block */
- NULL, /* Current directory name */
- NULL, /* STARTUPINFO */
- &procinfo); /* PROCESS_INFORMATION */
- if (bReturn == 0) {
- return -1;
- } else {
- CloseHandle(procinfo.hThread);
- }
- return (int) procinfo.dwProcessId;
- }
- /*
- Check the CGI process. Return 0 if it does not exist; non 0 if it does.
- */
- static int checkCgi(CgiPid handle)
- {
- int nReturn;
- DWORD exitCode;
- nReturn = GetExitCodeProcess((HANDLE)handle, &exitCode);
- /*
- We must close process handle to free up the window resource, but only when we're done with it.
- */
- if ((nReturn == 0) || (exitCode != STILL_ACTIVE)) {
- CloseHandle((HANDLE) handle);
- return 0;
- }
- return 1;
- }
- #endif /* WINCE */
- #if ME_UNIX_LIKE || QNX
- /*
- Launch the CGI process and return a handle to it.
- */
- static CgiPid launchCgi(char *cgiPath, char **argp, char **envp, char *stdIn, char *stdOut)
- {
- int fdin, fdout, pid;
- trace(5, "cgi: run %s", cgiPath);
- if ((fdin = open(stdIn, O_RDWR | O_CREAT | O_BINARY, 0666)) < 0) {
- error("Cannot open CGI stdin: ", cgiPath);
- return -1;
- }
- if ((fdout = open(stdOut, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0666)) < 0) {
- error("Cannot open CGI stdout: ", cgiPath);
- return -1;
- }
- pid = vfork();
- if (pid == 0) {
- /*
- Child
- */
- if (dup2(fdin, 0) < 0) {
- printf("content-type: text/html\n\nDup of stdin failed\n");
- _exit(1);
- } else if (dup2(fdout, 1) < 0) {
- printf("content-type: text/html\n\nDup of stdout failed\n");
- _exit(1);
- } else if (execve(cgiPath, argp, envp) == -1) {
- printf("content-type: text/html\n\nExecution of cgi process failed\n");
- }
- _exit(0);
- }
- /*
- Parent
- */
- if (fdout >= 0) {
- close(fdout);
- }
- if (fdin >= 0) {
- close(fdin);
- }
- return pid;
- }
- /*
- Check the CGI process. Return 0 if it does not exist; non 0 if it does.
- */
- static int checkCgi(CgiPid handle)
- {
- int pid;
- /*
- Check to see if the CGI child process has terminated or not yet.
- */
- if ((pid = waitpid((CgiPid) handle, NULL, WNOHANG)) == handle) {
- trace(5, "cgi: waited for pid %d", pid);
- return 0;
- } else {
- return 1;
- }
- }
- #endif /* LINUX || LYNX || MACOSX || QNX4 */
- #if VXWORKS
- #if _WRS_VXWORKS_MAJOR < 6 || (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR < 9)
- static int findVxSym(SYMTAB_ID sid, char *name, char **pvalue)
- {
- SYM_TYPE type;
- return symFindByName(sid, name, pvalue, &type);
- }
- #else
- static int findVxSym(SYMTAB_ID sid, char *name, char **pvalue)
- {
- SYMBOL_DESC symDesc;
- memset(&symDesc, 0, sizeof(SYMBOL_DESC));
- symDesc.mask = SYM_FIND_BY_NAME;
- symDesc.name = name;
- if (symFind(sid, &symDesc) == ERROR) {
- return ERROR;
- }
- if (pvalue != NULL) {
- *pvalue = (char*) symDesc.value;
- }
- return OK;
- }
- #endif
- static void vxWebsCgiEntry(void *entryAddr(int argc, char **argv), char **argv, char **envp, char *stdIn, char *stdOut);
- /*
- Launch the CGI process and return a handle to it. Process spawning is not supported in VxWorks. Instead, we spawn a
- "task". A major difference is that we have to know the entry point for the taskSpawn API. Also the module may have
- to be loaded before being executed; it may also be part of the OS image, in which case it cannot be loaded or
- unloaded.
- The following sequence is used:
- 1. If the module is already loaded, unload it from memory.
- 2. Search for a query string keyword=value pair in the environment variables where the keyword is cgientry. If
- found use its value as the the entry point name. If there is no such pair set the entry point name to the
- default: basename_cgientry, where basename is the name of the cgi file without the extension. Use the entry
- point name in a symbol table search for that name to use as the entry point address. If successful go to step 5.
- 3. Try to load the module into memory. If not successful error out.
- 4. If step 3 is successful repeat the entry point search from step 2. If the entry point exists, go to step 5. If
- it does not, error out.
- 5. Use taskSpawn to start a new task which uses vxWebsCgiEntry as its starting point. The five arguments to
- vxWebsCgiEntry will be the user entry point address, argp, envp, stdIn and stdOut. vxWebsCgiEntry will convert
- argp to an argc argv pair to pass to the user entry, it will initialize the task environment with envp, it will
- open and redirect stdin and stdout to stdIn and stdOut, and then it will call the user entry.
- 6. Return the taskSpawn return value.
- */
- static CgiPid launchCgi(char *cgiPath, char **argp, char **envp, char *stdIn, char *stdOut)
- {
- char *p, *basename, *pEntry, *pname, *entryAddr, **pp;
- int priority, rc, fd;
- /*
- Determine the basename, which is without path or the extension.
- */
- if ((int)(p = strrchr(cgiPath, '/') + 1) == 1) {
- p = cgiPath;
- }
- basename = sclone(p);
- if ((p = strrchr(basename, '.')) != NULL) {
- *p = '\0';
- }
- /*
- Unload the module, if it is already loaded. Get the current task priority.
- */
- unld(cgiPath, 0);
- taskPriorityGet(taskIdSelf(), &priority);
- rc = fd = -1;
- /*
- Set the entry point symbol name as described above. Look for an already loaded entry point; if it exists, spawn
- the task accordingly.
- */
- for (pp = envp, pEntry = NULL; pp != NULL && *pp != NULL; pp++) {
- if (strncmp(*pp, "cgientry=", 9) == 0) {
- pEntry = sclone(*pp + 9);
- break;
- }
- }
- if (pEntry == NULL) {
- pEntry = sfmt("%s_%s", basename, "cgientry");
- }
- entryAddr = 0;
- if (findVxSym(sysSymTbl, pEntry, &entryAddr) == -1) {
- pname = sfmt("_%s", pEntry);
- findVxSym(sysSymTbl, pname, &entryAddr);
- wfree(pname);
- }
- if (entryAddr != 0) {
- rc = taskSpawn(pEntry, priority, 0, 20000, (void*) vxWebsCgiEntry, (int) entryAddr, (int) argp,
- (int) envp, (int) stdIn, (int) stdOut, 0, 0, 0, 0, 0);
- goto done;
- }
- /*
- Try to load the module.
- */
- if ((fd = open(cgiPath, O_RDONLY | O_BINARY, 0666)) < 0 ||
- loadModule(fd, LOAD_GLOBAL_SYMBOLS) == NULL) {
- goto done;
- }
- if ((findVxSym(sysSymTbl, pEntry, &entryAddr)) == -1) {
- pname = sfmt("_%s", pEntry);
- findVxSym(sysSymTbl, pname, &entryAddr);
- wfree(pname);
- }
- if (entryAddr != 0) {
- rc = taskSpawn(pEntry, priority, 0, 20000, (void*) vxWebsCgiEntry, (int) entryAddr, (int) argp,
- (int) envp, (int) stdIn, (int) stdOut, 0, 0, 0, 0, 0);
- }
- done:
- if (fd != -1) {
- close(fd);
- }
- wfree(basename);
- wfree(pEntry);
- return rc;
- }
- /*
- This is the CGI process wrapper. It will open and redirect stdin and stdout to stdIn and stdOut. It converts argv
- to an argc, argv pair to pass to the user entry. It initializes the task environment with envp strings. Then it
- will call the user entry.
- */
- static void vxWebsCgiEntry(void *entryAddr(int argc, char **argv), char **argp, char **envp, char *stdIn, char *stdOut)
- {
- char **p;
- int argc, taskId, fdin, fdout;
- /*
- Open the stdIn and stdOut files and redirect stdin and stdout to them.
- */
- taskId = taskIdSelf();
- if ((fdout = open(stdOut, O_RDWR | O_CREAT, 0666)) < 0 &&
- (fdout = creat(stdOut, O_RDWR)) < 0) {
- exit(0);
- }
- ioTaskStdSet(taskId, 1, fdout);
- if ((fdin = open(stdIn, O_RDONLY | O_CREAT, 0666)) < 0 && (fdin = creat(stdIn, O_RDWR)) < 0) {
- printf("content-type: text/html\n\n" "Can not create CGI stdin to %s\n", stdIn);
- close(fdout);
- exit (0);
- }
- ioTaskStdSet(taskId, 0, fdin);
- /*
- Count the number of entries in argv
- */
- for (argc = 0, p = argp; p != NULL && *p != NULL; p++, argc++) { }
- /*
- Create a private envirnonment and copy the envp strings to it.
- */
- if (envPrivateCreate(taskId, -1) != OK) {
- printf("content-type: text/html\n\n" "Can not create CGI environment space\n");
- close(fdin);
- close(fdout);
- exit (0);
- }
- for (p = envp; p != NULL && *p != NULL; p++) {
- putenv(*p);
- }
- /*
- Call the user entry.
- */
- (*entryAddr)(argc, argp);
- /*
- The user code should return here for cleanup.
- */
- envPrivateDestroy(taskId);
- close(fdin);
- close(fdout);
- exit(0);
- }
- /*
- Check the CGI process. Return 0 if it does not exist; non 0 if it does.
- */
- static int checkCgi(CgiPid handle)
- {
- STATUS stat;
- /*
- Verify the existence of a VxWorks task
- */
- stat = taskIdVerify(handle);
- if (stat == OK) {
- return 1;
- } else {
- return 0;
- }
- }
- #endif /* VXWORKS */
- #if WINDOWS
- /*
- Convert a table of strings into a single block of memory. The input table consists of an array of null-terminated
- strings, terminated in a null pointer. Returns the address of a block of memory allocated using the walloc()
- function. The returned pointer must be deleted using wfree(). Returns NULL on error.
- */
- static uchar *tableToBlock(char **table)
- {
- uchar *pBlock; /* Allocated block */
- char *pEntry; /* Pointer into block */
- size_t sizeBlock; /* Size of table */
- int index; /* Index into string table */
- assert(table);
- /*
- Calculate the size of the data block. Allow for final null byte.
- */
- sizeBlock = 1;
- for (index = 0; table[index]; index++) {
- sizeBlock += strlen(table[index]) + 1;
- }
- /*
- Allocate the data block and fill it with the strings
- */
- pBlock = walloc(sizeBlock);
- if (pBlock != NULL) {
- pEntry = (char*) pBlock;
- for (index = 0; table[index]; index++) {
- strcpy(pEntry, table[index]);
- pEntry += strlen(pEntry) + 1;
- }
- /*
- Terminate the data block with an extra null string
- */
- *pEntry = '\0';
- }
- return pBlock;
- }
- /*
- Windows launchCgi
- Create a temporary stdout file and launch the CGI process. Returns a handle to the spawned CGI process.
- */
- static CgiPid launchCgi(char *cgiPath, char **argp, char **envp, char *stdIn, char *stdOut)
- {
- STARTUPINFO newinfo;
- SECURITY_ATTRIBUTES security;
- PROCESS_INFORMATION procinfo; /* Information about created proc */
- DWORD dwCreateFlags;
- char *cmdLine, **pArgs;
- BOOL bReturn;
- ssize nLen;
- int i;
- uchar *pEnvData;
- /*
- Replace directory delimiters with Windows-friendly delimiters
- */
- nLen = strlen(cgiPath);
- for (i = 0; i < nLen; i++) {
- if (cgiPath[i] == '/') {
- cgiPath[i] = '\\';
- }
- }
- /*
- Calculate length of command line
- */
- nLen = 0;
- pArgs = argp;
- while (pArgs && *pArgs && **pArgs) {
- nLen += strlen(*pArgs) + 1;
- pArgs++;
- }
- /*
- Construct command line
- */
- cmdLine = walloc(sizeof(char) * nLen);
- assert(cmdLine);
- strcpy(cmdLine, "");
- pArgs = argp;
- while (pArgs && *pArgs && **pArgs) {
- strcat(cmdLine, *pArgs);
- if (pArgs[1]) {
- strcat(cmdLine, " ");
- }
- pArgs++;
- }
- /*
- Create the process start-up information
- */
- memset (&newinfo, 0, sizeof(newinfo));
- newinfo.cb = sizeof(newinfo);
- newinfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
- newinfo.wShowWindow = SW_HIDE;
- newinfo.lpTitle = NULL;
- /*
- Create file handles for the spawned processes stdin and stdout files
- */
- security.nLength = sizeof(SECURITY_ATTRIBUTES);
- security.lpSecurityDescriptor = NULL;
- security.bInheritHandle = TRUE;
- /*
- Stdin file should already exist.
- */
- newinfo.hStdInput = CreateFile(stdIn, GENERIC_READ, FILE_SHARE_READ, &security, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
- NULL);
- if (newinfo.hStdOutput == (HANDLE) -1) {
- error("Cannot open CGI stdin file");
- return (CgiPid) -1;
- }
- /*
- Stdout file is created and file pointer is reset to start.
- */
- newinfo.hStdOutput = CreateFile(stdOut, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ + FILE_SHARE_WRITE,
- &security, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
- if (newinfo.hStdOutput == (HANDLE) -1) {
- error("Cannot create CGI stdout file");
- CloseHandle(newinfo.hStdInput);
- return (CgiPid) -1;
- }
- SetFilePointer(newinfo.hStdOutput, 0, NULL, FILE_END);
- newinfo.hStdError = newinfo.hStdOutput;
- dwCreateFlags = CREATE_NEW_CONSOLE;
- pEnvData = tableToBlock(envp);
- /*
- CreateProcess returns errors sometimes, even when the process was started correctly. The cause is not evident.
- Detect an error by checking the value of procinfo.hProcess after the call.
- */
- procinfo.hProcess = NULL;
- bReturn = CreateProcess(
- NULL, /* Name of executable module */
- cmdLine, /* Command line string */
- NULL, /* Process security attributes */
- NULL, /* Thread security attributes */
- TRUE, /* Handle inheritance flag */
- dwCreateFlags, /* Creation flags */
- pEnvData, /* New environment block */
- NULL, /* Current directory name */
- &newinfo, /* STARTUPINFO */
- &procinfo); /* PROCESS_INFORMATION */
- if (procinfo.hThread != NULL) {
- CloseHandle(procinfo.hThread);
- }
- if (newinfo.hStdInput) {
- CloseHandle(newinfo.hStdInput);
- }
- if (newinfo.hStdOutput) {
- CloseHandle(newinfo.hStdOutput);
- }
- wfree(pEnvData);
- wfree(cmdLine);
- if (bReturn == 0) {
- return (CgiPid) -1;
- } else {
- return procinfo.hProcess;
- }
- }
- /*
- Check the CGI process. Return 0 if it does not exist; non 0 if it does.
- */
- static int checkCgi(CgiPid handle)
- {
- DWORD exitCode;
- int nReturn;
- nReturn = GetExitCodeProcess((HANDLE) handle, &exitCode);
- /*
- We must close process handle to free up the window resource, but only
- when we're done with it.
- */
- if ((nReturn == 0) || (exitCode != STILL_ACTIVE)) {
- CloseHandle((HANDLE) handle);
- return 0;
- }
- return 1;
- }
- #endif /* WIN */
- #endif /* ME_GOAHEAD_CGI */
- /*
- Copyright (c) Embedthis Software. All Rights Reserved.
- This software is distributed under commercial and open source licenses.
- You may use the Embedthis GoAhead open source license or you may acquire
- a commercial license from Embedthis Software. You agree to be fully bound
- by the terms of either license. Consult the LICENSE.md distributed with
- this software for full details and other copyrights.
- */
|