0.6.1.20061108-1 release
[manu/suphp.git] / src / apache2 / mod_suphp.c
1 /*
2     suPHP - (c)2002-2005 Sebastian Marsching <sebastian@marsching.com>
3     
4     This file is part of suPHP.
5
6     suPHP is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     suPHP is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with suPHP; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "apr.h"
22 #include "apr_strings.h"
23 #include "apr_thread_proc.h"
24 #include "apr_buckets.h"
25 #include "apr_poll.h"
26
27 #define CORE_PRIVATE
28
29 #include "httpd.h"
30 #include "http_config.h"
31 #include "http_core.h"
32 #include "http_log.h"
33
34 #include "util_script.h"
35 #include "util_filter.h"
36
37 /* needed for get_suexec_identity hook */
38 #include "unixd.h"
39
40 module AP_MODULE_DECLARE_DATA suphp_module;
41
42
43 /*********************
44   Auxiliary functions
45  *********************/
46
47 static apr_status_t suphp_log_script_err(request_rec *r, apr_file_t *script_err)
48 {
49     char argsbuffer[HUGE_STRING_LEN];
50     char *newline;
51     apr_status_t rv;
52     
53     while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN,
54                          script_err)) == APR_SUCCESS) {
55         newline = strchr(argsbuffer, '\n');
56         if (newline) {
57             *newline = '\0';
58         }
59         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
60                       "%s", argsbuffer);
61     }
62     
63     return rv;
64 }
65
66 char *suphp_brigade_read(apr_pool_t *p, apr_bucket_brigade *bb, int bytes)
67 {
68     char *target_buf;
69     char *next_byte;
70     char *last_byte;
71     apr_bucket *b;
72     
73     if (bytes == 0) {
74         return NULL;
75     }
76     
77     target_buf = (char *) apr_palloc(p, bytes + 1);
78     next_byte = target_buf;
79     last_byte = target_buf + bytes;
80     
81     for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
82         char *buf;
83         apr_size_t size;
84         apr_size_t i;
85         if (apr_bucket_read(b, &buf, &size, APR_BLOCK_READ) == APR_SUCCESS) {
86             for (i = 0; i < size; i++) {
87                 *next_byte = *buf;
88                 next_byte++;
89                 buf++;
90                 if (next_byte == last_byte) {
91                     *next_byte = 0;
92                     return target_buf;
93                 }
94             }
95         }
96     }
97     next_byte = 0;
98     return target_buf;
99 }
100
101
102 /**************************
103   Configuration processing
104  **************************/
105
106 #define SUPHP_CONFIG_MODE_SERVER 1
107 #define SUPHP_CONFIG_MODE_DIRECTORY 2
108
109 #define SUPHP_ENGINE_OFF 0
110 #define SUPHP_ENGINE_ON 1
111 #define SUPHP_ENGINE_UNDEFINED 2
112
113 #ifndef SUPHP_PATH_TO_SUPHP
114 #define SUPHP_PATH_TO_SUPHP "/usr/sbin/suphp"
115 #endif
116
117 typedef struct {
118     int engine; // Status of suPHP_Engine
119     char *php_config;
120     int cmode;  // Server of directory configuration?
121 #ifdef SUPHP_USE_USERGROUP
122     char *target_user;
123     char *target_group;
124 #endif
125     apr_table_t *handlers;
126 } suphp_conf;
127
128
129 static void *suphp_create_dir_config(apr_pool_t *p, char *dir)
130 {
131     suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
132     
133     cfg->php_config = NULL;
134     cfg->engine = SUPHP_ENGINE_UNDEFINED;
135     cfg->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
136
137 #ifdef SUPHP_USE_USERGROUP
138     cfg->target_user = NULL;
139     cfg->target_group = NULL;
140 #endif
141     
142     /* Create table with 0 initial elements */
143     /* This size may be increased for performance reasons */
144     cfg->handlers = apr_table_make(p, 0);
145     
146     return (void *) cfg;
147 }
148
149
150 static void *suphp_merge_dir_config(apr_pool_t *p, void *base, 
151                                     void *overrides)
152 {
153     suphp_conf *parent = (suphp_conf *) base;
154     suphp_conf *child = (suphp_conf *) overrides;
155     suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
156     
157     merged->cmode = SUPHP_CONFIG_MODE_DIRECTORY;
158     
159     if (child->php_config)
160         merged->php_config = apr_pstrdup(p, child->php_config);
161     else if (parent->php_config)
162         merged->php_config = apr_pstrdup(p, parent->php_config);
163     else
164         merged->php_config = NULL;
165     
166     if (child->engine != SUPHP_ENGINE_UNDEFINED)
167         merged->engine = child->engine;
168     else
169         merged->engine = parent->engine;
170
171 #ifdef SUPHP_USE_USERGROUP
172     if (child->target_user)
173         merged->target_user = apr_pstrdup(p, child->target_user);
174     else if (parent->target_user)
175         merged->target_user = apr_pstrdup(p, parent->target_user);
176     else
177         merged->target_user = NULL;
178         
179     if (child->target_group)
180         merged->target_group = apr_pstrdup(p, child->target_group);
181     else if (parent->target_group)
182         merged->target_group = apr_pstrdup(p, parent->target_group);
183     else
184         merged->target_group = NULL;
185 #endif
186     
187     merged->handlers = apr_table_overlay(p, child->handlers, parent->handlers);
188     
189     return (void *) merged;  
190 }
191
192
193 static void *suphp_create_server_config(apr_pool_t *p, server_rec *s)
194 {
195     suphp_conf *cfg = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
196     
197     cfg->engine = SUPHP_ENGINE_UNDEFINED;
198     cfg->cmode = SUPHP_CONFIG_MODE_SERVER;
199     
200     return (void *) cfg;
201 }
202
203
204 static void *suphp_merge_server_config(apr_pool_t *p, void *base,
205                                        void *overrides)
206 {
207     suphp_conf *parent = (suphp_conf *) base;
208     suphp_conf *child = (suphp_conf *) overrides;
209     suphp_conf *merged = (suphp_conf *) apr_pcalloc(p, sizeof(suphp_conf));
210     
211     if (child->engine != SUPHP_ENGINE_UNDEFINED)
212         merged->engine = child->engine;
213     else
214         merged->engine = parent->engine;
215
216 #ifdef SUPHP_USE_USERGROUP
217     if (child->target_user)
218         merged->target_user = apr_pstrdup(p, child->target_user);
219     else if (parent->target_user)
220         merged->target_user = apr_pstrdup(p, parent->target_user);
221     else
222         merged->target_user = NULL;
223         
224     if (child->target_group)
225         merged->target_group = apr_pstrdup(p, child->target_group);
226     else if (parent->target_group)
227         merged->target_group = apr_pstrdup(p, parent->target_group);
228     else
229         merged->target_group = NULL;
230 #endif
231     
232     return (void*) merged;
233 }
234
235
236 /******************
237   Command handlers
238  ******************/
239
240 static const char *suphp_handle_cmd_engine(cmd_parms *cmd, void *mconfig,
241                                            int flag)
242 {
243     server_rec *s = cmd->server;
244     suphp_conf *cfg;
245     
246     if (mconfig)
247         cfg = (suphp_conf *) mconfig;
248     else
249         cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
250     
251     if (flag)
252         cfg->engine = SUPHP_ENGINE_ON;
253     else
254         cfg->engine = SUPHP_ENGINE_OFF;
255     
256     return NULL;
257 }
258
259
260 static const char *suphp_handle_cmd_config(cmd_parms *cmd, void *mconfig,
261                                            const char *arg)
262 {
263     server_rec *s = cmd->server;
264     suphp_conf *cfg;
265     
266     if (mconfig)
267         cfg = (suphp_conf *) mconfig;
268     else
269         cfg = (suphp_conf *) ap_get_module_config(s->module_config, &suphp_module);
270     
271     cfg->php_config = apr_pstrdup(cmd->pool, arg);
272     
273     return NULL;
274 }
275
276
277 #ifdef SUPHP_USE_USERGROUP
278 static const char *suphp_handle_cmd_user_group(cmd_parms *cmd, void *mconfig,
279                                            const char *arg1, const char *arg2)
280 {
281     suphp_conf *cfg = (suphp_conf *) mconfig;
282     
283     cfg->target_user = apr_pstrdup(cmd->pool, arg1);
284     cfg->target_group = apr_pstrdup(cmd->pool, arg2);
285     
286     return NULL;
287 }
288 #endif
289
290
291 static const char *suphp_handle_cmd_add_handler(cmd_parms *cmd, void *mconfig,
292                                              const char *arg)
293 {
294     suphp_conf *cfg = (suphp_conf *) mconfig;
295     // Mark active handler with '1'
296     apr_table_set(cfg->handlers, arg, "1");
297
298     return NULL;
299 }
300
301
302 static const char *suphp_handle_cmd_remove_handler(cmd_parms *cmd, 
303                                                    void *mconfig, 
304                                                    const char *arg)
305 {
306     suphp_conf *cfg = (suphp_conf *) mconfig;
307     // Mark deactivated handler with '0'
308     apr_table_set(cfg->handlers, arg, "0");
309
310     return NULL;
311 }
312
313
314 static const command_rec suphp_cmds[] =
315 {
316     AP_INIT_FLAG("suPHP_Engine", suphp_handle_cmd_engine, NULL, RSRC_CONF | ACCESS_CONF,
317                  "Whether suPHP is on or off, default is off"),
318     AP_INIT_TAKE1("suPHP_ConfigPath", suphp_handle_cmd_config, NULL, OR_OPTIONS,
319                   "Wheres the php.ini resides, default is the PHP default"),
320 #ifdef SUPHP_USE_USERGROUP
321     AP_INIT_TAKE2("suPHP_UserGroup", suphp_handle_cmd_user_group, NULL, RSRC_CONF | ACCESS_CONF,
322                   "User and group scripts shall be run as"),
323 #endif
324     AP_INIT_ITERATE("suPHP_AddHandler", suphp_handle_cmd_add_handler, NULL, ACCESS_CONF, "Tells mod_suphp to handle these MIME-types"),
325     AP_INIT_ITERATE("suPHP_RemoveHandler", suphp_handle_cmd_remove_handler, NULL, ACCESS_CONF, "Tells mod_suphp not to handle these MIME-types"),
326     {NULL}
327 };
328
329 /*****************************************
330   Code for reading script's stdout/stderr
331   based on mod_cgi's code
332  *****************************************/
333
334 #if APR_FILES_AS_SOCKETS
335
336 static const apr_bucket_type_t bucket_type_suphp;
337
338 struct suphp_bucket_data {
339     apr_pollset_t *pollset;
340     request_rec *r;
341 };
342
343 static apr_bucket *suphp_bucket_create(request_rec *r, apr_file_t *out, apr_file_t *err, apr_bucket_alloc_t *list)
344 {
345     apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
346     apr_status_t rv;
347     apr_pollfd_t fd;
348     struct suphp_bucket_data *data = apr_palloc(r->pool, sizeof(*data));
349     
350     APR_BUCKET_INIT(b);
351     b->free = apr_bucket_free;
352     b->list = list;
353     b->type = &bucket_type_suphp;
354     b->length = (apr_size_t) (-1);
355     b->start = (-1);
356     
357     /* Create the pollset */
358     rv = apr_pollset_create(&data->pollset, 2, r->pool, 0);
359     AP_DEBUG_ASSERT(rv == APR_SUCCESS);
360     
361     fd.desc_type = APR_POLL_FILE;
362     fd.reqevents = APR_POLLIN;
363     fd.p = r->pool;
364     fd.desc.f = out; /* script's stdout */
365     fd.client_data = (void *) 1;
366     rv = apr_pollset_add(data->pollset, &fd);
367     AP_DEBUG_ASSERT(rv == APR_SUCCESS);
368     
369     fd.desc.f = err; /* script's stderr */
370     fd.client_data = (void *) 2;
371     rv = apr_pollset_add(data->pollset, &fd);
372     AP_DEBUG_ASSERT(rv == APR_SUCCESS);
373     
374     data->r = r;
375     b->data = data;
376     return b;
377 }
378
379 static apr_bucket *suphp_bucket_dup(struct suphp_bucket_data *data, apr_bucket_alloc_t *list)
380 {
381     apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
382     APR_BUCKET_INIT(b);
383     b->free = apr_bucket_free;
384     b->list = list;
385     b->type = &bucket_type_suphp;
386     b->length = (apr_size_t) (-1);
387     b->start = (-1);
388     b->data = data;
389     return b;
390 }
391
392 /* This utility method is needed, because APR's implementation for the
393    pipe bucket cannot handle or special bucket type                    */
394 static apr_status_t suphp_read_fd(apr_bucket *b, apr_file_t *fd, const char **str, apr_size_t *len)
395 {
396     char *buf;
397     apr_status_t rv;
398     
399     *str = NULL;
400     *len = APR_BUCKET_BUFF_SIZE;
401     buf = apr_bucket_alloc(*len, b->list);
402     
403     rv = apr_file_read(fd, buf, len);
404     
405     if (*len > 0) {
406         /* Got data */
407         struct suphp_bucket_data *data = b->data;
408         apr_bucket_heap *h;
409         
410         /* Change the current bucket to refer to what we read
411            and append the pipe bucket after it                */
412         b = apr_bucket_heap_make(b, buf, *len, apr_bucket_free);
413         /* Here, b->data is the new heap bucket data */
414         h = b->data;
415         h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
416         *str = buf;
417         APR_BUCKET_INSERT_AFTER(b, suphp_bucket_dup(data, b->list));
418     } else {
419         /* Got no data */
420         apr_bucket_free(buf);
421         b = apr_bucket_immortal_make(b, "", 0);
422         /* Here, b->data is the reference to the empty string */
423         *str = b->data;
424     }
425     return rv;
426 }
427
428 /* Poll on stdout and stderr to make sure the process does not block
429    because of a full system (stderr) buffer                          */
430 static apr_status_t suphp_bucket_read(apr_bucket *b, const char **str, apr_size_t *len, apr_read_type_e block) {
431   struct suphp_bucket_data *data = b->data;
432   apr_interval_time_t timeout;
433   apr_status_t rv;
434   int gotdata = 0;
435   
436   timeout = (block == APR_NONBLOCK_READ) ? 0 : data->r->server->timeout;
437   
438   do {
439       const apr_pollfd_t *results;
440       apr_int32_t num;
441       
442       rv = apr_pollset_poll(data->pollset, timeout, &num, &results);
443       if (APR_STATUS_IS_TIMEUP(rv)) {
444           return (timeout == 0) ? APR_EAGAIN : rv;
445       } else if (APR_STATUS_IS_EINTR(rv)) {
446           continue;
447       } else if (rv != APR_SUCCESS) {
448           ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, "Poll failed waiting for suPHP child process");
449           return rv;
450       }
451       
452       while (num > 0) {
453         if (results[0].client_data == (void *) 1) {
454             /* handle stdout */
455             rv = suphp_read_fd(b, results[0].desc.f, str, len);
456             if (APR_STATUS_IS_EOF(rv)) {
457               rv = APR_SUCCESS;
458             }
459             gotdata = 1;
460         } else {
461             /* handle stderr */
462             apr_status_t rv2 = suphp_log_script_err(data->r, results[0].desc.f);
463             if (APR_STATUS_IS_EOF(rv2)) {
464                 apr_pollset_remove(data->pollset, &results[0]);
465             }
466         }
467         num--;
468         results++;
469       }
470   } while (!gotdata);
471   
472   return rv;
473 }
474
475 static const apr_bucket_type_t bucket_type_suphp = {
476     "SUPHP", 5, APR_BUCKET_DATA,
477     apr_bucket_destroy_noop,
478     suphp_bucket_read,
479     apr_bucket_setaside_notimpl,
480     apr_bucket_split_notimpl,
481     apr_bucket_copy_notimpl
482 };
483
484 #endif
485
486 static void suphp_discard_output(apr_bucket_brigade *bb) {
487   apr_bucket *b;
488   const char *buf;
489   apr_size_t len;
490   apr_status_t rv;
491   for (b = APR_BRIGADE_FIRST(bb); b != APR_BRIGADE_SENTINEL(bb); b = APR_BUCKET_NEXT(b)) {
492       if (APR_BUCKET_IS_EOS(b)) {
493           break;
494       }
495       rv = apr_bucket_read(b, &buf, &len, APR_BLOCK_READ);
496       if (rv != APR_SUCCESS) {
497           break;
498       }
499   }
500 }
501
502
503 /******************
504   Hooks / handlers
505  ******************/
506
507 static int suphp_handler(request_rec *r)
508 {
509     apr_pool_t *p;
510     suphp_conf *sconf;
511     suphp_conf *dconf;
512     core_dir_config *core_conf;
513     
514     apr_finfo_t finfo;
515     
516     apr_procattr_t *procattr;
517     
518     apr_proc_t *proc;
519     
520     char **argv;
521     char **env;
522     apr_status_t rv;
523     int len = 0;
524 #if MAX_STRING_LEN < 1024
525     char strbuf[1024];
526 #else
527     char strbuf[MAX_STRING_LEN];
528 #endif
529     char *tmpbuf;
530     int nph = 0;
531     int eos_reached = 0;
532     char *auth_user = NULL;
533     char *auth_pass = NULL;
534
535 #ifdef SUPHP_USE_USERGROUP
536     char *ud_user = NULL;
537     char *ud_group = NULL;
538 #endif
539     
540     apr_bucket_brigade *bb;
541     apr_bucket *b;
542     
543     /* load configuration */
544     
545     p = r->main ? r->main->pool : r->pool;
546     sconf = ap_get_module_config(r->server->module_config, &suphp_module);
547     dconf = ap_get_module_config(r->per_dir_config, &suphp_module);
548     core_conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
549     
550     /* only handle request if mod_suphp is active for this handler */
551     /* check only first byte of value (second has to be \0) */
552     if ((apr_table_get(dconf->handlers, r->handler) == NULL)
553         || (*(apr_table_get(dconf->handlers, r->handler)) == '0'))
554         return DECLINED;
555     
556     /* check if suPHP is enabled for this request */
557     
558     if (((sconf->engine != SUPHP_ENGINE_ON)
559         && (dconf->engine != SUPHP_ENGINE_ON))
560         || ((sconf->engine == SUPHP_ENGINE_ON)
561         && (dconf->engine == SUPHP_ENGINE_OFF)))
562         return DECLINED;
563     
564     /* check if file is existing and acessible */
565     
566     rv = apr_stat(&finfo, apr_pstrdup(p, r->filename), APR_FINFO_NORM, p);
567     
568     if (rv == APR_SUCCESS)
569         ; /* do nothing */
570     else if (rv == EACCES)
571     {
572         return HTTP_FORBIDDEN;
573         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "access to %s denied", r->filename);
574     }
575     else if (rv == ENOENT)
576     {
577         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "File does not exist: %s", r->filename);
578         return HTTP_NOT_FOUND;
579     }
580     else
581     {
582         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, "could not get fileinfo: %s", r->filename);
583         return HTTP_NOT_FOUND;
584     }
585     
586     if (!(r->finfo.protection & APR_UREAD))
587     {
588         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Insufficient permissions: %s", r->filename);
589         return HTTP_FORBIDDEN;
590     }
591     
592 #ifdef SUPHP_USE_USERGROUP
593     if ((sconf->target_user == NULL || sconf->target_group == NULL)
594         && (dconf->target_user == NULL || dconf->target_group == NULL))
595     {
596         /* Check for userdir request */
597         ap_unix_identity_t *userdir_id = NULL;
598         userdir_id = ap_run_get_suexec_identity(r);
599         if (userdir_id != NULL && userdir_id->userdir) {
600             ud_user = apr_psprintf(r->pool, "#%ld", (long) userdir_id->uid);
601             ud_group = apr_psprintf(r->pool, "#%ld", (long) userdir_id->gid);
602         } else {
603             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, 
604                           "No user or group set - set suPHP_UserGroup");
605             return HTTP_INTERNAL_SERVER_ERROR;
606         }
607     }
608 #endif
609         
610     /* prepare argv for new process */
611     
612     argv = apr_palloc(p, 2 * sizeof(char *));
613     argv[0] = SUPHP_PATH_TO_SUPHP;
614     argv[1] = NULL;
615     
616     /* prepare environment for new process */
617     
618     ap_add_common_vars(r);
619     ap_add_cgi_vars(r);
620     
621     apr_table_unset(r->subprocess_env, "SUPHP_PHP_CONFIG");
622     apr_table_unset(r->subprocess_env, "SUPHP_AUTH_USER");
623     apr_table_unset(r->subprocess_env, "SUPHP_AUTH_PW");
624     
625 #ifdef SUPHP_USE_USERGROUP
626     apr_table_unset(r->subprocess_env, "SUPHP_USER");
627     apr_table_unset(r->subprocess_env, "SUPHP_GROUP");
628 #endif
629     
630     if (dconf->php_config)
631     {
632         apr_table_setn(r->subprocess_env, "SUPHP_PHP_CONFIG", apr_pstrdup(p, dconf->php_config));
633     }
634     
635     apr_table_setn(r->subprocess_env, "SUPHP_HANDLER", r->handler);
636     
637     if (r->headers_in)
638     {
639         const char *auth = NULL;
640         auth = apr_table_get(r->headers_in, "Authorization");
641         if (auth && auth[0] != 0 && strncmp(auth, "Basic ", 6) == 0)
642         {
643             char *user;
644             char *pass;
645             user = ap_pbase64decode(p, auth + 6);
646             if (user)
647             {
648                 pass = strchr(user, ':');
649                 if (pass)
650                 {
651                     *pass++ = '\0';
652                     auth_user = apr_pstrdup(r->pool, user);
653                     auth_pass = apr_pstrdup(r->pool, pass);
654                 }
655             }
656         }
657     }
658     
659     if (auth_user && auth_pass)
660     {
661         apr_table_setn(r->subprocess_env, "SUPHP_AUTH_USER", auth_user);
662         apr_table_setn(r->subprocess_env, "SUPHP_AUTH_PW", auth_pass);
663     }
664
665 #ifdef SUPHP_USE_USERGROUP
666     if (dconf->target_user)
667     {
668         apr_table_setn(r->subprocess_env, "SUPHP_USER",
669                        apr_pstrdup(r->pool, dconf->target_user));
670     }
671     else if (sconf->target_user)
672     {
673         apr_table_setn(r->subprocess_env, "SUPHP_USER",
674                        apr_pstrdup(r->pool, sconf->target_user));
675     }
676     else
677     {
678         apr_table_setn(r->subprocess_env, "SUPHP_USER",
679                        apr_pstrdup(r->pool, ud_user));
680     }
681     
682     if (dconf->target_group)
683     {
684         apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
685                        apr_pstrdup(r->pool, dconf->target_group));
686     }
687     else if (sconf->target_group)
688     {
689         apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
690                        apr_pstrdup(r->pool, sconf->target_group));
691     }
692     else
693     {
694         apr_table_setn(r->subprocess_env, "SUPHP_GROUP",
695                        apr_pstrdup(r->pool, ud_group));
696     }
697 #endif
698     
699     env = ap_create_environment(p, r->subprocess_env);
700         
701     /* set attributes for new process */
702     
703     if (((rv = apr_procattr_create(&procattr, p)) != APR_SUCCESS)
704         || ((rv = apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
705         || ((rv = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS)
706     
707     /* set resource limits */
708
709 #ifdef RLIMIT_CPU
710         || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_CPU, core_conf->limit_cpu)) != APR_SUCCESS)
711 #endif
712 #if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
713         || ((rv = apr_procattr_limit_set(procattr, APR_LIMIT_MEM, core_conf->limit_mem)) != APR_SUCCESS)
714 #endif
715 #ifdef RLIMIT_NPROC
716         || ((apr_procattr_limit_set(procattr, APR_LIMIT_NPROC, core_conf->limit_nproc)) != APR_SUCCESS)
717 #endif
718
719         || ((apr_procattr_cmdtype_set(procattr, APR_PROGRAM)) != APR_SUCCESS)
720         || ((apr_procattr_error_check_set(procattr, 1)) != APR_SUCCESS)
721         || ((apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS))
722     {
723         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
724                       "couldn't set child process attributes: %s", r->filename);
725         return HTTP_INTERNAL_SERVER_ERROR;
726     }
727     
728     /* create new process */
729     
730
731     proc = apr_pcalloc(p, sizeof(*proc));
732     rv = apr_proc_create(proc, SUPHP_PATH_TO_SUPHP, (const char *const *)argv, (const char *const *)env, procattr, p);
733     if (rv != APR_SUCCESS)
734     {
735        ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
736                      "couldn't create child process: %s for %s", SUPHP_PATH_TO_SUPHP, r->filename);
737        return HTTP_INTERNAL_SERVER_ERROR;
738     }
739     apr_pool_note_subprocess(p, proc, APR_KILL_AFTER_TIMEOUT);
740
741     if (!proc->out)
742         return APR_EBADF;
743     apr_file_pipe_timeout_set(proc->out, r->server->timeout);
744     
745     if (!proc->in)
746         return APR_EBADF;
747     apr_file_pipe_timeout_set(proc->in, r->server->timeout);
748     
749     if (!proc->err)
750         return APR_EBADF;
751     apr_file_pipe_timeout_set(proc->err, r->server->timeout);
752     
753     /* send request body to script */
754     
755     bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
756     do
757     {
758         apr_bucket *bucket;
759         rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
760         
761         if (rv != APR_SUCCESS)
762         {
763             return rv;
764         }
765         
766         for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket = APR_BUCKET_NEXT(bucket))
767         {
768             const char *data;
769             apr_size_t len;
770             int child_stopped_reading = 0;
771             
772             if (APR_BUCKET_IS_EOS(bucket))
773             {
774                 eos_reached = 1;
775                 break;
776             }
777             
778             if (APR_BUCKET_IS_FLUSH(bucket) || child_stopped_reading)
779             {
780                 continue;
781             }
782             
783             apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
784             
785             rv = apr_file_write_full(proc->in, data, len, NULL);
786             if (rv != APR_SUCCESS)
787             {
788                 child_stopped_reading = 1;
789             }
790         }
791         apr_brigade_cleanup(bb);
792     }
793     while (!eos_reached);
794     
795     apr_file_flush(proc->in);
796     apr_file_close(proc->in);
797     
798     /* get output from script and check if non-parsed headers are used */
799     
800 #if APR_FILES_AS_SOCKETS
801     apr_file_pipe_timeout_set(proc->out, 0);
802     apr_file_pipe_timeout_set(proc->err, 0);
803     b = suphp_bucket_create(r, proc->out, proc->err, r->connection->bucket_alloc);
804 #else
805     b = apr_bucket_pipe_create(proc->out, r->connection->bucket_alloc);
806 #endif
807
808     APR_BRIGADE_INSERT_TAIL(bb, b);
809     
810     b = apr_bucket_eos_create(r->connection->bucket_alloc);
811     APR_BRIGADE_INSERT_TAIL(bb, b);
812
813     tmpbuf = suphp_brigade_read(p, bb, 8);
814     if (strlen(tmpbuf) == 8 && !(strncmp(tmpbuf, "HTTP/1.0", 8) && strncmp(tmpbuf, "HTTP/1.1", 8)))
815     {
816         nph = 1;
817     }
818     
819     if (!nph)
820     {
821         /* normal cgi headers, so we have to create the real headers ourselves */
822         
823         int ret;
824         const char *location;
825     
826         ret = ap_scan_script_header_err_brigade(r, bb, strbuf);
827         if (ret == HTTP_NOT_MODIFIED)
828         {
829             return ret;
830         }
831         else if (ret != APR_SUCCESS)
832         {
833             suphp_discard_output(bb);
834             apr_brigade_destroy(bb);
835             suphp_log_script_err(r, proc->err);
836             
837             /* ap_scan_script_header_err_brigade does logging itself,
838                so simply return                                       */
839                
840             return HTTP_INTERNAL_SERVER_ERROR;
841         }
842         
843         location = apr_table_get(r->headers_out, "Location");
844         if (location && location[0] == '/' && r->status == 200)
845         {
846             /* empty brigade (script output) and modify headers */
847             
848             suphp_discard_output(bb);
849             apr_brigade_destroy(bb);
850             suphp_log_script_err(r, proc->err);
851             r->method = apr_pstrdup(r->pool, "GET");
852             r->method_number = M_GET;
853             apr_table_unset(r->headers_in, "Content-Length");
854             
855             ap_internal_redirect_handler(location, r);
856             return OK;
857         }
858         else if (location && r->status == 200)
859         {
860             /* empty brigade (script output) */
861             suphp_discard_output(bb);
862             apr_brigade_destroy(bb);
863             suphp_log_script_err(r, proc->err);
864             return HTTP_MOVED_TEMPORARILY;
865         }
866         
867         /* send output to browser (through filters) */
868         
869         rv = ap_pass_brigade(r->output_filters, bb);
870         
871         /* write errors to logfile */
872         
873         if (rv == APR_SUCCESS && !r->connection->aborted)
874             suphp_log_script_err(r, proc->err);
875         
876         apr_file_close(proc->err);
877     }
878     
879     if (proc->out && nph)
880     {
881         /* use non-parsed headers (direct output) */
882         
883         struct ap_filter_t *cur;
884         
885         /* get rid of output filters */
886         
887         cur = r->proto_output_filters;
888         while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION)
889         {
890             cur = cur->next;
891         }
892         r->output_filters = r->proto_output_filters = cur;
893         
894         /* send output to browser (directly) */
895         
896         rv = ap_pass_brigade(r->output_filters, bb);
897         
898         /* log errors */
899         if (rv == APR_SUCCESS && !r->connection->aborted)
900             suphp_log_script_err(r, proc->err);
901             
902         apr_file_close(proc->err);
903     }
904
905     return OK;
906 }
907
908 static void suphp_register_hooks(apr_pool_t *p)
909 {
910     ap_hook_handler(suphp_handler, NULL, NULL, APR_HOOK_MIDDLE);
911 }
912
913
914 /********************
915   Module declaration
916  ********************/
917  
918 module AP_MODULE_DECLARE_DATA suphp_module =
919 {
920     STANDARD20_MODULE_STUFF,
921     suphp_create_dir_config,
922     suphp_merge_dir_config,
923     suphp_create_server_config,
924     suphp_merge_server_config,
925     suphp_cmds,
926     suphp_register_hooks
927 };