source: rtems/cpukit/httpd/cgi.c @ f26145b

4.104.114.84.95
Last change on this file since f26145b was 2e7f00fc, checked in by Joel Sherrill <joel.sherrill@…>, on 04/11/03 at 16:34:49

2003-04-11 Joel Sherrill <joel@…>

  • 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.
  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * cgi.c -- CGI processing (for the GoAhead Web server
3 *
4 * Copyright (c) GoAhead Software Inc., 1995-2000. All Rights Reserved.
5 *
6 * See the file "license.txt" for usage and redistribution license requirements
7 *
8 * $Id$
9 */
10
11/********************************** Description *******************************/
12/*
13 *      This module implements the /cgi-bin handler. CGI processing differs from
14 *      goforms processing in that each CGI request is executed as a separate
15 *      process, rather than within the webserver process. For each CGI request the
16 *      environment of the new process must be set to include all the CGI variables
17 *      and its standard input and output must be directed to the socket.  This
18 *      is done using temporary files.
19 */
20
21/*********************************** Includes *********************************/
22#include        "wsIntrn.h"
23#ifdef UEMF
24        #include        "uemf.h"
25#else
26        #include        "basic/basicInternal.h"
27#endif
28
29/************************************ Locals **********************************/
30typedef struct {                                /* Struct for CGI tasks which have completed */
31        webs_t  wp;                                     /* pointer to session websRec */
32        char_t  *stdIn;                         /* file desc. for task's temp input fd */
33        char_t  *stdOut;                        /* file desc. for task's temp output fd */
34        char_t  *cgiPath;                       /* path to executable process file */
35        char_t  **argp;                         /* pointer to buf containing argv tokens */
36        char_t  **envp;                         /* pointer to array of environment strings */
37        int             handle;                         /* process handle of the task */
38        long    fplacemark;                     /* seek location for CGI output file */
39} cgiRec;
40static cgiRec   **cgiList;              /* hAlloc chain list of wp's to be closed */
41static int              cgiMax;                 /* Size of hAlloc list */
42
43/************************************* Code ***********************************/
44
45/*
46 *      Process a form request. Returns 1 always to indicate it handled the URL
47 */
48int websCgiHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg,
49                char_t *url, char_t *path, char_t* query)
50{
51        cgiRec          *cgip;
52        sym_t           *s;
53        char_t          cgiBuf[FNAMESIZE], *stdIn, *stdOut, cwd[FNAMESIZE];
54        char_t          *cp, *cgiName, *cgiPath, **argp, **envp, **ep;
55        int                     n, envpsize, argpsize, pHandle, cid;
56        a_assert(websValid(wp));
57        a_assert(url && *url);
58        a_assert(path && *path == '/');
59        websStats.cgiHits++;
60/*
61 *      Extract the form name and then build the full path name.  The form
62 *      name will follow the first '/' in path.
63 */
64        gstrncpy(cgiBuf, path, TSZ(cgiBuf));
65        if ((cgiName = gstrchr(&cgiBuf[1], '/')) == NULL) {
66                websError(wp, 200, T("Missing CGI name"));
67                return 1;
68        }
69        cgiName++;
70        if ((cp = gstrchr(cgiName, '/')) != NULL) {
71                *cp = '\0';
72        }
73        fmtAlloc(&cgiPath, FNAMESIZE, T("%s/%s/%s"), websGetDefaultDir(),
74                CGI_BIN, cgiName);
75#ifndef VXWORKS
76/*
77 *      See if the file exists and is executable.  If not error out.
78 *      Don't do this step for VxWorks, since the module may already
79 *      be part of the OS image, rather than in the file system.
80 */
81        {
82                gstat_t         sbuf;
83                if (gstat(cgiPath, &sbuf) != 0 || (sbuf.st_mode & S_IFREG) == 0) {
84                        websError(wp, 200, T("CGI process file does not exist"));
85                        bfree(B_L, cgiPath);
86                        return 1;
87                }
88#if (defined (WIN) || defined (CE))
89                if (gstrstr(cgiPath, T(".exe")) == NULL &&
90                        gstrstr(cgiPath, T(".bat")) == NULL) {
91#elif (defined (NW))
92                        if (gstrstr(cgiPath, T(".nlm")) == NULL) {
93#else
94                if (gaccess(cgiPath, X_OK) != 0) {
95#endif /* WIN || CE */
96                        websError(wp, 200, T("CGI process file is not executable"));
97                        bfree(B_L, cgiPath);
98                        return 1;
99                }
100        }
101#endif /* ! VXWORKS */
102
103         
104/*
105 *      Get the CWD for resetting after launching the child process CGI
106 */
107        ggetcwd(cwd, FNAMESIZE);
108/*
109 *      Retrieve the directory of the child process CGI
110 */
111        if ((cp = gstrrchr(cgiPath, '/')) != NULL) {
112                *cp = '\0';
113                gchdir(cgiPath);
114                *cp = '/';
115        }
116/*
117 *      Build command line arguments.  Only used if there is no non-encoded
118 *      = character.  This is indicative of a ISINDEX query.  POST separators
119 *      are & and others are +.  argp will point to a balloc'd array of
120 *      pointers.  Each pointer will point to substring within the
121 *      query string.  This array of string pointers is how the spawn or
122 *      exec routines expect command line arguments to be passed.  Since
123 *      we don't know ahead of time how many individual items there are in
124 *      the query string, the for loop includes logic to grow the array
125 *      size via brealloc.
126 */
127        argpsize = 10;
128        argp = balloc(B_L, argpsize * sizeof(char_t *));
129        *argp = cgiPath;
130        n = 1;
131        if (gstrchr(query, '=') == NULL) {
132                websDecodeUrl(query, query, gstrlen(query));
133                for (cp = gstrtok(query, T(" ")); cp != NULL; ) {
134                        *(argp+n) = cp;
135                        n++;
136                        if (n >= argpsize) {
137                                argpsize *= 2;
138                                argp = brealloc(B_L, argp, argpsize * sizeof(char_t *));
139                        }
140                        cp = gstrtok(NULL, T(" "));
141                }
142        }
143        *(argp+n) = NULL;
144/*
145 *      Add all CGI variables to the environment strings to be passed
146 *      to the spawned CGI process.  This includes a few we don't
147 *      already have in the symbol table, plus all those that are in
148 *      the cgiVars symbol table.  envp will point to a balloc'd array of
149 *      pointers.  Each pointer will point to a balloc'd string containing
150 *      the keyword value pair in the form keyword=value.  Since we don't
151 *      know ahead of time how many environment strings there will be the
152 *      for loop includes logic to grow the array size via brealloc.
153 */
154        envpsize = WEBS_SYM_INIT;
155        envp = balloc(B_L, envpsize * sizeof(char_t *));
156        n = 0;
157        fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("PATH_TRANSLATED"), cgiPath);
158        n++;
159        fmtAlloc(envp+n, FNAMESIZE, T("%s=%s/%s"),T("SCRIPT_NAME"),
160                CGI_BIN, cgiName);
161        n++;
162        fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("REMOTE_USER"), wp->userName);
163        n++;
164        fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"),T("AUTH_TYPE"), wp->authType);
165        n++;
166        for (s = symFirst(wp->cgiVars); s != NULL; s = symNext(wp->cgiVars)) {
167                if (s->content.valid && s->content.type == string &&
168                        gstrcmp(s->name.value.string, T("REMOTE_HOST")) != 0 &&
169                        gstrcmp(s->name.value.string, T("HTTP_AUTHORIZATION")) != 0) {
170                        fmtAlloc(envp+n, FNAMESIZE, T("%s=%s"), s->name.value.string,
171                                s->content.value.string);
172                        n++;
173                        if (n >= envpsize) {
174                                envpsize *= 2;
175                                envp = brealloc(B_L, envp, envpsize * sizeof(char_t *));
176                        }
177                }
178        }
179        *(envp+n) = NULL;
180/*
181 *      Create temporary file name(s) for the child's stdin and stdout.
182 *      For POST data the stdin temp file (and name) should already exist.
183 */
184        if (wp->cgiStdin == NULL) {
185                wp->cgiStdin = websGetCgiCommName();
186        }
187        stdIn = wp->cgiStdin;
188        stdOut = websGetCgiCommName();
189/*
190 *      Now launch the process.  If not successful, do the cleanup of resources.
191 *      If successful, the cleanup will be done after the process completes.
192 */
193        if ((pHandle = websLaunchCgiProc(cgiPath, argp, envp, stdIn, stdOut))
194                == -1) {
195                websError(wp, 200, T("failed to spawn CGI task"));
196                for (ep = envp; *ep != NULL; ep++) {
197                        bfreeSafe(B_L, *ep);
198                }
199                bfreeSafe(B_L, cgiPath);
200                bfreeSafe(B_L, argp);
201                bfreeSafe(B_L, envp);
202                bfreeSafe(B_L, stdOut);
203        } else {
204/*
205 *              If the spawn was successful, put this wp on a queue to be
206 *              checked for completion.
207 */
208                cid = hAllocEntry((void***) &cgiList, &cgiMax, sizeof(cgiRec));
209                cgip = cgiList[cid];
210                cgip->handle = pHandle;
211                cgip->stdIn = stdIn;
212                cgip->stdOut = stdOut;
213                cgip->cgiPath = cgiPath;
214                cgip->argp = argp;
215                cgip->envp = envp;
216                cgip->wp = wp;
217                cgip->fplacemark = 0;
218                websTimeoutCancel(wp);
219        }
220/*
221 *      Restore the current working directory after spawning child CGI
222 */
223        gchdir(cwd);
224        return 1;
225}
226
227
228
229/******************************************************************************/
230/*
231 *      Any entry in the cgiList need to be checked to see if it has
232 */
233void websCgiGatherOutput (cgiRec *cgip)
234{
235        gstat_t sbuf;
236        char_t  cgiBuf[FNAMESIZE];
237        if ((gstat(cgip->stdOut, &sbuf) == 0) &&
238                (sbuf.st_size > cgip->fplacemark)) {
239                int fdout;
240                fdout = gopen(cgip->stdOut, O_RDONLY | O_BINARY, 0444 );
241/*
242 *              Check to see if any data is available in the
243 *              output file and send its contents to the socket.
244 */
245                if (fdout >= 0) {
246                        webs_t  wp = cgip->wp;
247                        int             nRead;
248/*
249 *                      Write the HTTP header on our first pass
250 */
251                        if (cgip->fplacemark == 0) {
252                                websWrite(wp, T("HTTP/1.0 200 OK\r\n"));
253                        }
254                        glseek(fdout, cgip->fplacemark, SEEK_SET);
255                        while ((nRead = gread(fdout, cgiBuf, FNAMESIZE)) > 0) {
256                                websWriteBlock(wp, cgiBuf, nRead);
257                                cgip->fplacemark += nRead;
258                        }
259                        gclose(fdout);
260                }
261        }
262}
263
264
265
266/******************************************************************************/
267/*
268 *      Any entry in the cgiList need to be checked to see if it has
269 *      completed, and if so, process its output and clean up.
270 */
271void websCgiCleanup()
272{
273        cgiRec  *cgip;
274        webs_t  wp;
275        char_t  **ep;
276        int             cid, nTries;
277        for (cid = 0; cid < cgiMax; cid++) {
278                if ((cgip = cgiList[cid]) != NULL) {
279                        wp = cgip->wp;
280                        websCgiGatherOutput (cgip);
281                        if (websCheckCgiProc(cgip->handle) == 0) {
282/*
283 *                              We get here if the CGI process has terminated.  Clean up.
284 */
285                                nTries = 0;
286/*                             
287 *                              Make sure we didn't miss something during a task switch.
288 *                              Maximum wait is 100 times 10 msecs (1 second).
289 */
290                                while ((cgip->fplacemark == 0) && (nTries < 100)) {
291                                        websCgiGatherOutput(cgip);
292/*                                     
293 *                                      There are some cases when we detect app exit
294 *                                      before the file is ready.
295 */
296                                        if (cgip->fplacemark == 0) {
297#ifdef WIN
298                                                Sleep(10);
299#endif /* WIN*/
300                                        }
301                                        nTries++;
302                                }
303                                if (cgip->fplacemark == 0) {
304                                        websError(wp, 200, T("CGI generated no output"));
305                                } else {
306                                        websDone(wp, 200);
307                                }
308/*
309 *                              Remove the temporary re-direction files
310 */
311                                gunlink(cgip->stdIn);
312                                gunlink(cgip->stdOut);
313/*
314 *                              Free all the memory buffers pointed to by cgip.
315 *                              The stdin file name (wp->cgiStdin) gets freed as
316 *                              part of websFree().
317 */
318                                cgiMax = hFree((void***) &cgiList, cid);
319                                for (ep = cgip->envp; ep != NULL && *ep != NULL; ep++) {
320                                        bfreeSafe(B_L, *ep);
321                                }
322                                bfreeSafe(B_L, cgip->cgiPath);
323                                bfreeSafe(B_L, cgip->argp);
324                                bfreeSafe(B_L, cgip->envp);
325                                bfreeSafe(B_L, cgip->stdOut);
326                                bfreeSafe(B_L, cgip);
327                        }
328                }
329        }
330}
331/******************************************************************************/
Note: See TracBrowser for help on using the repository browser.