]> granicus.if.org Git - apache/blob - modules/generators/mod_cgid.c
Straighten up the first-time-through check in mod_cgid and add a CHANGES
[apache] / modules / generators / mod_cgid.c
1 /* ====================================================================
2  * The Apache Software License, Version 1.1
3  *
4  * Copyright (c) 2000 The Apache Software Foundation.  All rights
5  * reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  *
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  *
19  * 3. The end-user documentation included with the redistribution,
20  *    if any, must include the following acknowledgment:
21  *       "This product includes software developed by the
22  *        Apache Software Foundation (http://www.apache.org/)."
23  *    Alternately, this acknowledgment may appear in the software itself,
24  *    if and wherever such third-party acknowledgments normally appear.
25  *
26  * 4. The names "Apache" and "Apache Software Foundation" must
27  *    not be used to endorse or promote products derived from this
28  *    software without prior written permission. For written
29  *    permission, please contact apache@apache.org.
30  *
31  * 5. Products derived from this software may not be called "Apache",
32  *    nor may "Apache" appear in their name, without prior written
33  *    permission of the Apache Software Foundation.
34  *
35  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
36  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
37  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
38  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
39  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
42  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
44  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
45  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46  * SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Apache Software Foundation.  For more
51  * information on the Apache Software Foundation, please see
52  * <http://www.apache.org/>.
53  *
54  * Portions of this software are based upon public domain software
55  * originally written at the National Center for Supercomputing Applications,
56  * University of Illinois, Urbana-Champaign.
57  */
58
59 /* 
60  * http_script: keeps all script-related ramblings together. 
61  * 
62  * Compliant to cgi/1.1 spec 
63  * 
64  * Adapted by rst from original NCSA code by Rob McCool 
65  * 
66  * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for 
67  * custom error responses, and DOCUMENT_ROOT because we found it useful. 
68  * It also adds SERVER_ADMIN - useful for scripts to know who to mail when 
69  * they fail. 
70  */ 
71
72
73
74 #define CORE_PRIVATE 
75
76 #include "apr_lib.h"
77 #include "apr_strings.h"
78 #include "apr_general.h"
79 #include "apr_file_io.h"
80 #include "apr_portable.h"
81 #include "ap_buckets.h"
82 #include "util_filter.h"
83 #include "httpd.h" 
84 #include "http_config.h" 
85 #include "http_request.h" 
86 #include "http_core.h" 
87 #include "http_protocol.h" 
88 #include "http_main.h" 
89 #include "http_log.h" 
90 #include "util_script.h" 
91 #include "http_conf_globals.h" 
92 #include "ap_mpm.h"
93 #include "unixd.h"
94 #include <sys/stat.h>
95 #ifdef HAVE_SYS_SOCKET_H
96 #include <sys/socket.h>
97 #endif
98 #ifdef HAVE_UNISTD_H
99 #include <unistd.h>
100 #endif
101 #ifdef HAVE_STRINGS_H
102 #include <strings.h>
103 #endif
104 #include <sys/un.h> /* for sockaddr_un */
105 #include <sys/types.h>
106
107 module AP_MODULE_DECLARE_DATA cgid_module; 
108
109 static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server); 
110
111 static apr_pool_t *pcgi; 
112
113 /* KLUDGE --- for back-combatibility, we don't have to check Execcgid 
114  * in ScriptAliased directories, which means we need to know if this 
115  * request came through ScriptAlias or not... so the Alias module 
116  * leaves a note for us. 
117  */ 
118
119 static int is_scriptaliased(request_rec *r) 
120
121     const char *t = apr_table_get(r->notes, "alias-forced-type"); 
122     return t && (!strcasecmp(t, "cgi-script")); 
123
124
125 /* Configuration stuff */ 
126
127 #define DEFAULT_LOGBYTES 10385760 
128 #define DEFAULT_BUFBYTES 1024 
129 #define DEFAULT_SOCKET "logs/cgisock"
130
131 #define SHELL_PATH "/bin/sh"
132
133 /* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's
134  * pending connection queue.  If a bunch of cgi requests arrive at about
135  * the same time, connections from httpd threads/processes will back up
136  * in the queue while the cgid process slowly forks off a child to process
137  * each connection on the unix socket.  If the queue is too short, the
138  * httpd process will get ECONNREFUSED when trying to connect.
139  */
140 #ifndef DEFAULT_CGID_LISTENBACKLOG
141 #define DEFAULT_CGID_LISTENBACKLOG 100
142 #endif
143
144 typedef struct { 
145     const char *sockname;
146     const char *logname; 
147     long logbytes; 
148     int bufbytes; 
149 } cgid_server_conf; 
150
151 /* If a request includes query info in the URL (stuff after "?"), and
152  * the query info does not contain "=" (indicative of a FORM submission),
153  * then this routine is called to create the argument list to be passed
154  * to the CGI script.  When suexec is enabled, the suexec path, user, and
155  * group are the first three arguments to be passed; if not, all three
156  * must be NULL.  The query info is split into separate arguments, where
157  * "+" is the separator between keyword arguments.
158  *
159  * XXXX: note that the WIN32 code uses one of the suexec strings
160  * to pass an interpreter name.  Remember this if changing the way they
161  * are handled in create_argv.
162  *
163  */
164 static char **create_argv(apr_pool_t *p, char *path, char *user, char *group,
165                           char *av0, const char *args)
166 {
167     int x, numwords;
168     char **av;
169     char *w;
170     int idx = 0;
171
172     /* count the number of keywords */
173
174     for (x = 0, numwords = 1; args[x]; x++) {
175         if (args[x] == '+') {
176             ++numwords;
177         }
178     }
179
180     if (numwords > APACHE_ARG_MAX - 5) {
181         numwords = APACHE_ARG_MAX - 5;  /* Truncate args to prevent overrun */
182     }
183     av = (char **) apr_palloc(p, (numwords + 5) * sizeof(char *));
184
185     if (path) {
186         av[idx++] = path;
187     }
188     if (user) {
189         av[idx++] = user;
190     }
191     if (group) {
192         av[idx++] = group;
193      }
194
195     av[idx++] = av0;
196
197     for (x = 1; x <= numwords; x++) {
198         w = ap_getword_nulls(p, &args, '+');
199         ap_unescape_url(w);
200         av[idx++] = ap_escape_shell_cmd(p, w);
201     }
202     av[idx] = NULL;
203     return av;
204 }
205
206 static int call_exec(request_rec *r, char *argv0, char **env, int shellcmd)
207 {
208     int pid = 0;
209     int errfileno = STDERR_FILENO;
210     /* the fd on r->server->error_log is closed, but we need somewhere to            
211      * put the error messages from the log_* functions. So, we use stderr,
212      * since that is better than allowing errors to go unnoticed. 
213      */
214     apr_put_os_file(&r->server->error_log, &errfileno, r->pool);
215     if (shellcmd) {
216         execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
217     }
218
219     else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
220         execle(r->filename, argv0, NULL, env);
221     }
222
223     else {
224         execve(r->filename,
225                create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
226                env);
227     }
228     return (pid);
229 }
230
231 static void cgid_maint(int reason, void *data, apr_wait_t status)
232 {
233 #if APR_HAS_OTHER_CHILD
234     int *sd = data;
235     switch (reason) {
236         case APR_OC_REASON_DEATH:
237         case APR_OC_REASON_LOST:
238             /* stop gap to make sure everything else works.  In the end,
239              * we'll just restart the cgid server. */
240             apr_destroy_pool(pcgi);
241             kill(getppid(), SIGWINCH);
242             break;
243         case APR_OC_REASON_RESTART:
244         case APR_OC_REASON_UNREGISTER:
245             apr_destroy_pool(pcgi);
246             kill(*sd, SIGHUP);
247             break;
248     }
249 #endif
250 }
251
252 static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env) 
253
254     int i, len, j; 
255     unsigned char *data; 
256     char **environ; 
257     core_dir_config *temp_core; 
258     void **dconf; 
259
260     r->server = apr_pcalloc(r->pool, sizeof(server_rec)); 
261
262     read(fd, &j, sizeof(int)); 
263     read(fd, &len, sizeof(int)); 
264     data = apr_pcalloc(r->pool, len + 1); /* get a cleared byte for final '\0' */
265     i = read(fd, data, len); 
266
267     r->filename = ap_getword(r->pool, (const char **)&data, '\n'); 
268     *argv0 = ap_getword(r->pool, (const char **)&data, '\n'); 
269
270     r->uri = ap_getword(r->pool, (const char **)&data, '\n'); 
271     
272     environ = apr_pcalloc(r->pool, (j + 2) *sizeof(char *)); 
273     i = 0; 
274     for (i = 0; i < j; i++) { 
275         environ[i] = ap_getword(r->pool, (const char **)&data, '\n'); 
276     } 
277     *env = environ; 
278     r->args = ap_getword(r->pool, (const char **)&data, '\n'); 
279   
280     read(fd, &i, sizeof(int)); 
281      
282     /* add 1, so that if i == 0, we still malloc something. */ 
283     dconf = (void **)malloc(sizeof(void *) * i + 1); 
284
285     temp_core = (core_dir_config *)malloc(sizeof(core_module)); 
286 #if 0
287 #ifdef RLIMIT_CPU 
288     read(fd, &j, sizeof(int)); 
289     if (j) { 
290         temp_core->limit_cpu = (struct rlimit *)malloc (sizeof(struct rlimit)); 
291         read(fd, temp_core->limit_cpu, sizeof(struct rlimit)); 
292     } 
293     else { 
294         temp_core->limit_cpu = NULL; 
295     } 
296 #endif 
297
298 #if defined (RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 
299     read(fd, &j, sizeof(int)); 
300     if (j) { 
301         temp_core->limit_mem = (struct rlimit *)malloc (sizeof(struct rlimit)); 
302         read(fd, temp_core->limit_mem, sizeof(struct rlimit)); 
303     } 
304     else { 
305         temp_core->limit_mem = NULL; 
306     } 
307 #endif 
308
309 #ifdef RLIMIT_NPROC 
310     read(fd, &j, sizeof(int)); 
311     if (j) { 
312         temp_core->limit_nproc = (struct rlimit *)malloc (sizeof(struct rlimit)); 
313         read(fd, temp_core->limit_nproc, sizeof(struct rlimit)); 
314     } 
315     else { 
316         temp_core->limit_nproc = NULL; 
317     } 
318 #endif 
319 #endif
320     dconf[i] = (void *)temp_core; 
321     r->per_dir_config = dconf; 
322
323
324
325
326 static void send_req(int fd, request_rec *r, char *argv0, char **env) 
327
328     int len; 
329     int i = 0; 
330     char *data; 
331
332     data = apr_pstrcat(r->pool, r->filename, "\n", argv0, "\n", r->uri, "\n", 
333                      NULL); 
334
335     for (i =0; env[i]; i++) { 
336         continue; 
337     } 
338
339     if (write(fd, &i, sizeof(int)) < 0) {
340         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
341                      "write to cgi daemon process"); 
342         }     
343
344     for (i = 0; env[i]; i++) { 
345         data = apr_pstrcat(r->pool, data, env[i], "\n", NULL); 
346     } 
347     data = apr_pstrcat(r->pool, data, r->args, NULL); 
348     len = strlen(data); 
349     if (write(fd, &len, sizeof(int)) < 0) { 
350         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
351                      "write to cgi daemon process"); 
352         }     
353     if (write(fd, data, len) < 0) {
354         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
355                      "write to cgi daemon process"); 
356         }     
357     if (write(fd, &core_module.module_index, sizeof(int)) < 0) { 
358         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
359                      "write to cgi daemon process"); 
360         }     
361 #if 0
362 #ifdef RLIMIT_CPU 
363     if (conf->limit_cpu) { 
364         len = 1; 
365         write(fd, &len, sizeof(int)); 
366         write(fd, conf->limit_cpu, sizeof(struct rlimit)); 
367     } 
368     else { 
369         len = 0; 
370         write(fd, &len, sizeof(int)); 
371     } 
372 #endif 
373
374 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS) 
375     if (conf->limit_mem) { 
376         len = 1; 
377         write(fd, &len, sizeof(int)); 
378         write(fd, conf->limit_mem, sizeof(struct rlimit)); 
379     } 
380     else { 
381         len = 0; 
382         write(fd, &len, sizeof(int)); 
383     } 
384 #endif 
385   
386 #ifdef RLIMIT_NPROC 
387     if (conf->limit_nproc) { 
388         len = 1; 
389         write(fd, &len, sizeof(int)); 
390         write(fd, conf->limit_nproc, sizeof(struct rlimit)); 
391     } 
392     else { 
393         len = 0; 
394         write(fd, &len, sizeof(int)); 
395     } 
396 #endif
397 #endif 
398
399
400 static void cgid_server_child(int sd) 
401
402     char *argv0; 
403     char *filename; 
404     char **env; 
405     apr_pool_t *p; 
406     request_rec *r; 
407
408     apr_create_pool(&p, pcgi); 
409     r = apr_pcalloc(p, sizeof(request_rec)); 
410     r->pool = p; 
411     dup2(sd, STDIN_FILENO); 
412     dup2(sd, STDOUT_FILENO); 
413     get_req(sd, r, &filename, &argv0, &env); 
414     call_exec(r, argv0, env, 0); 
415     exit(-1);   /* We should NEVER get here */
416
417
418 static int cgid_server(void *data) 
419
420     struct sockaddr_un unix_addr;
421     int pid; 
422     int sd, sd2, rc;
423     int errfile;
424     mode_t omask;
425     apr_socklen_t len;
426     server_rec *main_server = data;
427     cgid_server_conf *sconf = (cgid_server_conf *)ap_get_module_config( 
428                        main_server->module_config, &cgid_module); 
429
430     apr_signal(SIGCHLD, SIG_IGN); 
431     if (unlink(sconf->sockname) < 0 && errno != ENOENT) {
432         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
433                      "Couldn't unlink unix domain socket %s",
434                      sconf->sockname);
435         /* just a warning; don't bail out */
436     }
437
438     if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
439         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
440                      "Couldn't create unix domain socket");
441         return errno;
442     } 
443
444     memset(&unix_addr, 0, sizeof(unix_addr));
445     unix_addr.sun_family = AF_UNIX;
446     strcpy(unix_addr.sun_path, sconf->sockname);
447
448     omask = umask(0077); /* so that only Apache can use socket */
449     rc = bind(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr));
450     umask(omask); /* can't fail, so can't clobber errno */
451     if (rc < 0) {
452         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
453                      "Couldn't bind unix domain socket %s",
454                      sconf->sockname); 
455         return errno;
456     } 
457
458     if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) {
459         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
460                      "Couldn't listen on unix domain socket"); 
461         return errno;
462     } 
463
464     if (!geteuid()) {
465         if (chown(sconf->sockname, unixd_config.user_id, -1) < 0) {
466             ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
467                          "Couldn't change owner of unix domain socket %s",
468                          sconf->sockname); 
469             return errno;
470         }
471     }
472     
473     unixd_setup_child(); /* if running as root, switch to configured user/group */
474
475     while (1) {
476         len = sizeof(unix_addr);
477         sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len);
478         if (sd2 < 0) {
479             if (errno != EINTR) {
480                 ap_log_error(APLOG_MARK, APLOG_ERR, errno, 
481                              (server_rec *)data,
482                              "Error accepting on cgid socket.");
483             }
484             continue;
485         }
486        
487         if ((pid = fork()) > 0) {
488             close(sd2);
489         } 
490         else if (pid == 0) { 
491             /* setup the STDERR here, because I have all the info
492              * for it.  I'll do the STDIN and STDOUT later, but I can't
493              * do STDERR as easily.
494              */
495             if (sconf->logname) {
496                 dup2(open(sconf->logname, O_WRONLY), STDERR_FILENO);
497             }
498             else {
499                 apr_get_os_file(&errfile, main_server->error_log);
500                 dup2(errfile, STDERR_FILENO);
501             }
502             cgid_server_child(sd2); 
503         } 
504         else { 
505             ap_log_error(APLOG_MARK, APLOG_ERR, errno, (server_rec *)data, 
506                          "Couldn't fork cgi script"); 
507         } 
508     } 
509     return -1; 
510
511
512 static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, 
513                       server_rec *main_server) 
514
515     pid_t pid; 
516     apr_proc_t *procnew;
517     void *data;
518     int first_time = 0;
519     const char *userdata_key = "cgid_init";
520
521     apr_get_userdata(&data, userdata_key, main_server->process->pool);
522     if (!data) {
523         first_time = 1;
524         apr_set_userdata((const void *)1, userdata_key,
525                          apr_null_cleanup, main_server->process->pool);
526     }
527
528     if (!first_time) {
529         apr_create_pool(&pcgi, p); 
530
531         if ((pid = fork()) < 0) {
532             ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server, 
533                          "Couldn't spawn cgid daemon process"); 
534         }
535         else if (pid == 0) {
536             cgid_server(main_server);
537             exit(-1);
538         } 
539         procnew = apr_pcalloc(p, sizeof(*procnew));        
540         procnew->pid = pid;
541         procnew->err = procnew->in = procnew->out = NULL;
542         apr_note_subprocess(p, procnew, kill_after_timeout);
543 #if APR_HAS_OTHER_CHILD
544         apr_register_other_child(procnew, cgid_maint, NULL, NULL, p);
545 #endif
546     }
547
548
549 static void *create_cgid_config(apr_pool_t *p, server_rec *s) 
550
551     cgid_server_conf *c = 
552     (cgid_server_conf *) apr_pcalloc(p, sizeof(cgid_server_conf)); 
553
554     c->logname = NULL; 
555     c->logbytes = DEFAULT_LOGBYTES; 
556     c->bufbytes = DEFAULT_BUFBYTES; 
557     c->sockname = ap_server_root_relative(p, DEFAULT_SOCKET); 
558     return c; 
559
560
561 static void *merge_cgid_config(apr_pool_t *p, void *basev, void *overridesv) 
562
563     cgid_server_conf *base = (cgid_server_conf *) basev, *overrides = (cgid_server_conf *) overridesv; 
564
565     return overrides->logname ? overrides : base; 
566
567
568 static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) 
569
570     server_rec *s = cmd->server; 
571     cgid_server_conf *conf = 
572     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
573
574     conf->logname = arg; 
575     return NULL; 
576
577
578 static const char *set_scriptlog_length(cmd_parms *cmd, void *dummy, const char *arg) 
579
580     server_rec *s = cmd->server; 
581     cgid_server_conf *conf = 
582     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
583
584     conf->logbytes = atol(arg); 
585     return NULL; 
586
587
588 static const char *set_scriptlog_buffer(cmd_parms *cmd, void *dummy, const char *arg) 
589
590     server_rec *s = cmd->server; 
591     cgid_server_conf *conf = 
592     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
593
594     conf->bufbytes = atoi(arg); 
595     return NULL; 
596
597
598 static const char *set_script_socket(cmd_parms *cmd, void *dummy, const char *arg) 
599
600     server_rec *s = cmd->server; 
601     cgid_server_conf *conf = 
602     (cgid_server_conf *) ap_get_module_config(s->module_config, &cgid_module); 
603
604     conf->sockname = ap_server_root_relative(cmd->pool, arg); 
605     return NULL; 
606
607
608 static const command_rec cgid_cmds[] = 
609
610     AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF,
611                   "the name of a log for script debugging info"), 
612     AP_INIT_TAKE1("ScriptLogLength", set_scriptlog_length, NULL, RSRC_CONF,
613                   "the maximum length (in bytes) of the script debug log"), 
614     AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF,
615                   "the maximum size (in bytes) to record of a POST request"), 
616     AP_INIT_TAKE1("Scriptsock", set_script_socket, NULL, RSRC_CONF,
617                   "the name of the socket to use for communication with "
618                   "the cgi daemon."), 
619     {NULL} 
620 }; 
621
622 static int log_scripterror(request_rec *r, cgid_server_conf * conf, int ret, 
623                            int show_errno, char *error) 
624
625     apr_file_t *f = NULL; 
626     struct stat finfo; 
627     char time_str[APR_CTIME_LEN];
628
629     ap_log_rerror(APLOG_MARK, show_errno|APLOG_ERR, errno, r, 
630                 "%s: %s", error, r->filename); 
631
632     if (!conf->logname || 
633         ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) 
634          && (finfo.st_size > conf->logbytes)) || 
635          (apr_open(&f, ap_server_root_relative(r->pool, conf->logname),
636                   APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { 
637         return ret; 
638     } 
639
640     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ 
641     apr_ctime(time_str, apr_now());
642     apr_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 
643             r->args ? "?" : "", r->args ? r->args : "", r->protocol); 
644     /* "%% 500 /usr/local/apache/cgid-bin */ 
645     apr_fprintf(f, "%%%% %d %s\n", ret, r->filename); 
646
647     apr_fprintf(f, "%%error\n%s\n", error); 
648
649     apr_close(f); 
650     return ret; 
651
652
653 static int log_script(request_rec *r, cgid_server_conf * conf, int ret, 
654                   char *dbuf, const char *sbuf, apr_file_t *script_in, apr_file_t *script_err) 
655
656     apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); 
657     apr_table_entry_t *hdrs = (apr_table_entry_t *) hdrs_arr->elts; 
658     char argsbuffer[HUGE_STRING_LEN]; 
659     apr_file_t *f = NULL; 
660     int i; 
661     struct stat finfo; 
662     char time_str[APR_CTIME_LEN];
663
664     if (!conf->logname || 
665         ((stat(ap_server_root_relative(r->pool, conf->logname), &finfo) == 0) 
666          && (finfo.st_size > conf->logbytes)) || 
667          (apr_open(&f, ap_server_root_relative(r->pool, conf->logname), 
668                   APR_APPEND|APR_WRITE|APR_CREATE, APR_OS_DEFAULT, r->pool) != APR_SUCCESS)) { 
669         /* Soak up script output */ 
670         while (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_in) == 0) 
671             continue; 
672         if (script_err) {
673             while (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_err) == 0) 
674                 continue; 
675         }
676         return ret; 
677     } 
678
679     /* "%% [Wed Jun 19 10:53:21 1996] GET /cgid-bin/printenv HTTP/1.0" */ 
680     apr_ctime(time_str, apr_now());
681     apr_fprintf(f, "%%%% [%s] %s %s%s%s %s\n", time_str, r->method, r->uri, 
682             r->args ? "?" : "", r->args ? r->args : "", r->protocol); 
683     /* "%% 500 /usr/local/apache/cgid-bin" */ 
684     apr_fprintf(f, "%%%% %d %s\n", ret, r->filename); 
685
686     apr_puts("%request\n", f); 
687     for (i = 0; i < hdrs_arr->nelts; ++i) { 
688         if (!hdrs[i].key) 
689             continue; 
690         apr_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 
691     } 
692     if ((r->method_number == M_POST || r->method_number == M_PUT) 
693         && *dbuf) { 
694         apr_fprintf(f, "\n%s\n", dbuf); 
695     } 
696
697     apr_puts("%response\n", f); 
698     hdrs_arr = apr_table_elts(r->err_headers_out); 
699     hdrs = (apr_table_entry_t *) hdrs_arr->elts; 
700
701     for (i = 0; i < hdrs_arr->nelts; ++i) { 
702         if (!hdrs[i].key) 
703             continue; 
704         apr_fprintf(f, "%s: %s\n", hdrs[i].key, hdrs[i].val); 
705     } 
706
707     if (sbuf && *sbuf) 
708         apr_fprintf(f, "%s\n", sbuf); 
709
710     if (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_in) == 0) { 
711         apr_puts("%stdout\n", f); 
712         apr_puts(argsbuffer, f); 
713         while (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_in) == 0) 
714             apr_puts(argsbuffer, f); 
715         apr_puts("\n", f); 
716     } 
717
718     if (script_err) {
719         if (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_err) == 0) { 
720             apr_puts("%stderr\n", f); 
721             apr_puts(argsbuffer, f); 
722             while (apr_fgets(argsbuffer, HUGE_STRING_LEN, script_err) == 0) 
723                 apr_puts(argsbuffer, f); 
724             apr_puts("\n", f); 
725         } 
726     }
727
728     apr_close(script_in); 
729     if (script_err) {
730         apr_close(script_err); 
731     }
732
733     apr_close(f); 
734     return ret; 
735
736
737
738
739 /**************************************************************** 
740  * 
741  * Actual cgid handling... 
742  */ 
743 static int cgid_handler(request_rec *r) 
744
745     int retval, nph, dbpos = 0; 
746     char *argv0, *dbuf = NULL; 
747     ap_bucket_brigade *bb;
748     ap_bucket *b;
749     char argsbuffer[HUGE_STRING_LEN]; 
750     void *sconf = r->server->module_config; 
751     cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module); 
752     int is_included = !strcmp(r->protocol, "INCLUDED"); 
753     int sd;
754     char **env; 
755     struct sockaddr_un unix_addr;
756     apr_file_t *tempsock = NULL;
757     apr_size_t nbytes;
758
759     if (r->method_number == M_OPTIONS) { 
760         /* 99 out of 100 cgid scripts, this is all they support */ 
761         r->allowed |= (1 << M_GET); 
762         r->allowed |= (1 << M_POST); 
763         return DECLINED; 
764     } 
765
766     if ((argv0 = strrchr(r->filename, '/')) != NULL)
767         argv0++;
768     else
769         argv0 = r->filename;
770  
771     nph = !(strncmp(argv0, "nph-", 4)); 
772
773     if ((argv0 = strrchr(r->filename, '/')) != NULL) 
774         argv0++; 
775     else 
776         argv0 = r->filename; 
777
778     if (!(ap_allow_options(r) & OPT_EXECCGI) && !is_scriptaliased(r)) 
779         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
780                                "Options ExecCGI is off in this directory"); 
781     if (nph && is_included) 
782         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
783                                "attempt to include NPH CGI script"); 
784
785 #if defined(OS2) || defined(WIN32) 
786     /* Allow for cgid files without the .EXE extension on them under OS/2 */ 
787     if (r->finfo.st_mode == 0) { 
788         struct stat statbuf; 
789         char *newfile; 
790
791         newfile = apr_pstrcat(r->pool, r->filename, ".EXE", NULL); 
792
793         if ((stat(newfile, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode))) { 
794             return log_scripterror(r, conf, HTTP_NOT_FOUND, 0, 
795                                    "script not found or unable to stat"); 
796         } else { 
797             r->filename = newfile; 
798         } 
799     } 
800 #else 
801     if (r->finfo.protection == 0) 
802         return log_scripterror(r, conf, HTTP_NOT_FOUND, APLOG_NOERRNO, 
803                                "script not found or unable to stat"); 
804 #endif 
805     if (r->finfo.filetype == APR_DIR) 
806         return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
807                                "attempt to invoke directory as script"); 
808 /*
809     if (!ap_suexec_enabled) { 
810         if (!ap_can_exec(&r->finfo)) 
811             return log_scripterror(r, conf, HTTP_FORBIDDEN, APLOG_NOERRNO, 
812                                    "file permissions deny server execution"); 
813     } 
814 */
815     ap_add_common_vars(r); 
816     ap_add_cgi_vars(r); 
817     env = ap_create_environment(r->pool, r->subprocess_env); 
818
819     if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
820             return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, 
821                                    "unable to create socket to cgi daemon");
822     } 
823     memset(&unix_addr, 0, sizeof(unix_addr));
824     unix_addr.sun_family = AF_UNIX;
825     strcpy(unix_addr.sun_path, conf->sockname);
826
827     if (connect(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) {
828             return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, 
829                                    "unable to connect to cgi daemon");
830     } 
831
832     send_req(sd, r, argv0, env); 
833
834     /* We are putting the tempsock variable into a file so that we can use
835      * a pipe bucket to send the data to the client.
836      */
837     apr_put_os_file(&tempsock, &sd, r->pool);
838
839     if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) 
840         return retval; 
841      
842     if ((argv0 = strrchr(r->filename, '/')) != NULL) 
843         argv0++; 
844     else 
845         argv0 = r->filename; 
846
847     /* Transfer any put/post args, CERN style... 
848      * Note that we already ignore SIGPIPE in the core server. 
849      */ 
850
851     if (ap_should_client_block(r)) { 
852         int dbsize, len_read; 
853
854         if (conf->logname) { 
855             dbuf = apr_pcalloc(r->pool, conf->bufbytes + 1); 
856             dbpos = 0; 
857         } 
858
859         while ((len_read = 
860                 ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { 
861             if (conf->logname) { 
862                 if ((dbpos + len_read) > conf->bufbytes) { 
863                     dbsize = conf->bufbytes - dbpos; 
864                 } 
865                 else { 
866                     dbsize = len_read; 
867                 } 
868                 memcpy(dbuf + dbpos, argsbuffer, dbsize); 
869                 dbpos += dbsize; 
870             } 
871             nbytes = len_read;
872             apr_write(tempsock, argsbuffer, &nbytes);
873             if (nbytes < len_read) { 
874                 /* silly script stopped reading, soak up remaining message */ 
875                 while (ap_get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) { 
876                     /* dump it */ 
877                 } 
878                 break; 
879             } 
880         } 
881         shutdown(sd, 1); /* done writing; force EOF on child's stdin */
882     } 
883
884     /* Handle script return... */ 
885     if (!nph) { 
886         const char *location; 
887         char sbuf[MAX_STRING_LEN]; 
888         int ret; 
889
890         if ((ret = ap_scan_script_header_err(r, tempsock, sbuf))) { 
891             return log_script(r, conf, ret, dbuf, sbuf, tempsock, NULL); 
892         } 
893
894         location = apr_table_get(r->headers_out, "Location"); 
895
896         if (location && location[0] == '/' && r->status == 200) { 
897
898             /* Soak up all the script output */ 
899             while (apr_fgets(argsbuffer, HUGE_STRING_LEN, tempsock) > 0) { 
900                 continue; 
901             } 
902             /* This redirect needs to be a GET no matter what the original 
903              * method was. 
904              */ 
905             r->method = apr_pstrdup(r->pool, "GET"); 
906             r->method_number = M_GET; 
907
908             /* We already read the message body (if any), so don't allow 
909              * the redirected request to think it has one. We can ignore 
910              * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. 
911              */ 
912             apr_table_unset(r->headers_in, "Content-Length"); 
913
914             ap_internal_redirect_handler(location, r); 
915             return OK; 
916         } 
917         else if (location && r->status == 200) { 
918             /* XX Note that if a script wants to produce its own Redirect 
919              * body, it now has to explicitly *say* "Status: 302" 
920              */ 
921             return HTTP_MOVED_TEMPORARILY; 
922         } 
923
924         ap_send_http_header(r); 
925         if (!r->header_only) { 
926             bb = ap_brigade_create(r->pool);
927             b = ap_bucket_create_pipe(tempsock);
928             AP_BRIGADE_INSERT_TAIL(bb, b);
929             b = ap_bucket_create_eos();
930             AP_BRIGADE_INSERT_TAIL(bb, b);
931             ap_pass_brigade(r->output_filters, bb);
932         } 
933     } 
934
935     if (nph) {
936         bb = ap_brigade_create(r->pool);
937         b = ap_bucket_create_pipe(tempsock);
938         AP_BRIGADE_INSERT_TAIL(bb, b);
939         b = ap_bucket_create_eos();
940         AP_BRIGADE_INSERT_TAIL(bb, b);
941         ap_pass_brigade(r->output_filters, bb);
942     } 
943
944     apr_close(tempsock);
945
946     return OK; /* NOT r->status, even if it has changed. */ 
947
948
949 static const handler_rec cgid_handlers[] = 
950
951     {CGI_MAGIC_TYPE, cgid_handler}, 
952     {"cgi-script", cgid_handler}, 
953     {NULL} 
954 };
955
956 static void register_hook(void)
957 {
958     ap_hook_post_config(cgid_init, NULL, NULL, AP_HOOK_MIDDLE);
959 }
960
961 module AP_MODULE_DECLARE_DATA cgid_module = { 
962     STANDARD20_MODULE_STUFF, 
963     NULL, /* dir config creater */ 
964     NULL, /* dir merger --- default is to override */ 
965     create_cgid_config, /* server config */ 
966     merge_cgid_config, /* merge server config */ 
967     cgid_cmds, /* command table */ 
968     cgid_handlers, /* handlers */ 
969     register_hook /* register_handlers */ 
970 }; 
971