Annotation of embedaddon/lighttpd/src/mod_cgi.c, revision 1.1.1.1
1.1 misho 1: #include "server.h"
2: #include "stat_cache.h"
3: #include "keyvalue.h"
4: #include "log.h"
5: #include "connections.h"
6: #include "joblist.h"
7: #include "http_chunk.h"
8:
9: #include "plugin.h"
10:
11: #include <sys/types.h>
12:
13: #ifdef __WIN32
14: # include <winsock2.h>
15: #else
16: # include <sys/socket.h>
17: # include <sys/wait.h>
18: # include <sys/mman.h>
19: # include <netinet/in.h>
20: # include <arpa/inet.h>
21: #endif
22:
23: #include <unistd.h>
24: #include <errno.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <fdevent.h>
28: #include <signal.h>
29: #include <ctype.h>
30: #include <assert.h>
31:
32: #include <stdio.h>
33: #include <fcntl.h>
34:
35: #ifdef HAVE_SYS_FILIO_H
36: # include <sys/filio.h>
37: #endif
38:
39: #include "version.h"
40:
41: enum {EOL_UNSET, EOL_N, EOL_RN};
42:
43: typedef struct {
44: char **ptr;
45:
46: size_t size;
47: size_t used;
48: } char_array;
49:
50: typedef struct {
51: pid_t *ptr;
52: size_t used;
53: size_t size;
54: } buffer_pid_t;
55:
56: typedef struct {
57: array *cgi;
58: unsigned short execute_x_only;
59: } plugin_config;
60:
61: typedef struct {
62: PLUGIN_DATA;
63: buffer_pid_t cgi_pid;
64:
65: buffer *tmp_buf;
66: buffer *parse_response;
67:
68: plugin_config **config_storage;
69:
70: plugin_config conf;
71: } plugin_data;
72:
73: typedef struct {
74: pid_t pid;
75: int fd;
76: int fde_ndx; /* index into the fd-event buffer */
77:
78: connection *remote_conn; /* dumb pointer */
79: plugin_data *plugin_data; /* dumb pointer */
80:
81: buffer *response;
82: buffer *response_header;
83: } handler_ctx;
84:
85: static handler_ctx * cgi_handler_ctx_init(void) {
86: handler_ctx *hctx = calloc(1, sizeof(*hctx));
87:
88: assert(hctx);
89:
90: hctx->response = buffer_init();
91: hctx->response_header = buffer_init();
92:
93: return hctx;
94: }
95:
96: static void cgi_handler_ctx_free(handler_ctx *hctx) {
97: buffer_free(hctx->response);
98: buffer_free(hctx->response_header);
99:
100: free(hctx);
101: }
102:
103: enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
104:
105: INIT_FUNC(mod_cgi_init) {
106: plugin_data *p;
107:
108: p = calloc(1, sizeof(*p));
109:
110: assert(p);
111:
112: p->tmp_buf = buffer_init();
113: p->parse_response = buffer_init();
114:
115: return p;
116: }
117:
118:
119: FREE_FUNC(mod_cgi_free) {
120: plugin_data *p = p_d;
121: buffer_pid_t *r = &(p->cgi_pid);
122:
123: UNUSED(srv);
124:
125: if (p->config_storage) {
126: size_t i;
127: for (i = 0; i < srv->config_context->used; i++) {
128: plugin_config *s = p->config_storage[i];
129:
130: array_free(s->cgi);
131:
132: free(s);
133: }
134: free(p->config_storage);
135: }
136:
137:
138: if (r->ptr) free(r->ptr);
139:
140: buffer_free(p->tmp_buf);
141: buffer_free(p->parse_response);
142:
143: free(p);
144:
145: return HANDLER_GO_ON;
146: }
147:
148: SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
149: plugin_data *p = p_d;
150: size_t i = 0;
151:
152: config_values_t cv[] = {
153: { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
154: { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
155: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
156: };
157:
158: if (!p) return HANDLER_ERROR;
159:
160: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
161:
162: for (i = 0; i < srv->config_context->used; i++) {
163: plugin_config *s;
164:
165: s = calloc(1, sizeof(plugin_config));
166: assert(s);
167:
168: s->cgi = array_init();
169: s->execute_x_only = 0;
170:
171: cv[0].destination = s->cgi;
172: cv[1].destination = &(s->execute_x_only);
173:
174: p->config_storage[i] = s;
175:
176: if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
177: return HANDLER_ERROR;
178: }
179: }
180:
181: return HANDLER_GO_ON;
182: }
183:
184:
185: static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
186: int m = -1;
187: size_t i;
188: buffer_pid_t *r = &(p->cgi_pid);
189:
190: UNUSED(srv);
191:
192: for (i = 0; i < r->used; i++) {
193: if (r->ptr[i] > m) m = r->ptr[i];
194: }
195:
196: if (r->size == 0) {
197: r->size = 16;
198: r->ptr = malloc(sizeof(*r->ptr) * r->size);
199: } else if (r->used == r->size) {
200: r->size += 16;
201: r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
202: }
203:
204: r->ptr[r->used++] = pid;
205:
206: return m;
207: }
208:
209: static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
210: size_t i;
211: buffer_pid_t *r = &(p->cgi_pid);
212:
213: UNUSED(srv);
214:
215: for (i = 0; i < r->used; i++) {
216: if (r->ptr[i] == pid) break;
217: }
218:
219: if (i != r->used) {
220: /* found */
221:
222: if (i != r->used - 1) {
223: r->ptr[i] = r->ptr[r->used - 1];
224: }
225: r->used--;
226: }
227:
228: return 0;
229: }
230:
231: static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
232: char *ns;
233: const char *s;
234: int line = 0;
235:
236: UNUSED(srv);
237:
238: buffer_copy_string_buffer(p->parse_response, in);
239:
240: for (s = p->parse_response->ptr;
241: NULL != (ns = strchr(s, '\n'));
242: s = ns + 1, line++) {
243: const char *key, *value;
244: int key_len;
245: data_string *ds;
246:
247: /* strip the \n */
248: ns[0] = '\0';
249:
250: if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
251:
252: if (line == 0 &&
253: 0 == strncmp(s, "HTTP/1.", 7)) {
254: /* non-parsed header ... we parse them anyway */
255:
256: if ((s[7] == '1' ||
257: s[7] == '0') &&
258: s[8] == ' ') {
259: int status;
260: /* after the space should be a status code for us */
261:
262: status = strtol(s+9, NULL, 10);
263:
264: if (status >= 100 &&
265: status < 1000) {
266: /* we expected 3 digits and didn't got them */
267: con->parsed_response |= HTTP_STATUS;
268: con->http_status = status;
269: }
270: }
271: } else {
272: /* parse the headers */
273: key = s;
274: if (NULL == (value = strchr(s, ':'))) {
275: /* we expect: "<key>: <value>\r\n" */
276: continue;
277: }
278:
279: key_len = value - key;
280: value += 1;
281:
282: /* skip LWS */
283: while (*value == ' ' || *value == '\t') value++;
284:
285: if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
286: ds = data_response_init();
287: }
288: buffer_copy_string_len(ds->key, key, key_len);
289: buffer_copy_string(ds->value, value);
290:
291: array_insert_unique(con->response.headers, (data_unset *)ds);
292:
293: switch(key_len) {
294: case 4:
295: if (0 == strncasecmp(key, "Date", key_len)) {
296: con->parsed_response |= HTTP_DATE;
297: }
298: break;
299: case 6:
300: if (0 == strncasecmp(key, "Status", key_len)) {
301: con->http_status = strtol(value, NULL, 10);
302: con->parsed_response |= HTTP_STATUS;
303: }
304: break;
305: case 8:
306: if (0 == strncasecmp(key, "Location", key_len)) {
307: con->parsed_response |= HTTP_LOCATION;
308: }
309: break;
310: case 10:
311: if (0 == strncasecmp(key, "Connection", key_len)) {
312: con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
313: con->parsed_response |= HTTP_CONNECTION;
314: }
315: break;
316: case 14:
317: if (0 == strncasecmp(key, "Content-Length", key_len)) {
318: con->response.content_length = strtol(value, NULL, 10);
319: con->parsed_response |= HTTP_CONTENT_LENGTH;
320: }
321: break;
322: default:
323: break;
324: }
325: }
326: }
327:
328: /* CGI/1.1 rev 03 - 7.2.1.2 */
329: if ((con->parsed_response & HTTP_LOCATION) &&
330: !(con->parsed_response & HTTP_STATUS)) {
331: con->http_status = 302;
332: }
333:
334: return 0;
335: }
336:
337:
338: static int cgi_demux_response(server *srv, handler_ctx *hctx) {
339: plugin_data *p = hctx->plugin_data;
340: connection *con = hctx->remote_conn;
341:
342: while(1) {
343: int n;
344: int toread;
345:
346: #if defined(__WIN32)
347: buffer_prepare_copy(hctx->response, 4 * 1024);
348: #else
349: if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
350: buffer_prepare_copy(hctx->response, 4 * 1024);
351: } else {
352: if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
353: buffer_prepare_copy(hctx->response, toread + 1);
354: }
355: #endif
356:
357: if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
358: if (errno == EAGAIN || errno == EINTR) {
359: /* would block, wait for signal */
360: return FDEVENT_HANDLED_NOT_FINISHED;
361: }
362: /* error */
363: log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
364: return FDEVENT_HANDLED_ERROR;
365: }
366:
367: if (n == 0) {
368: /* read finished */
369:
370: con->file_finished = 1;
371:
372: /* send final chunk */
373: http_chunk_append_mem(srv, con, NULL, 0);
374: joblist_append(srv, con);
375:
376: return FDEVENT_HANDLED_FINISHED;
377: }
378:
379: hctx->response->ptr[n] = '\0';
380: hctx->response->used = n+1;
381:
382: /* split header from body */
383:
384: if (con->file_started == 0) {
385: int is_header = 0;
386: int is_header_end = 0;
387: size_t last_eol = 0;
388: size_t i;
389:
390: buffer_append_string_buffer(hctx->response_header, hctx->response);
391:
392: /**
393: * we have to handle a few cases:
394: *
395: * nph:
396: *
397: * HTTP/1.0 200 Ok\n
398: * Header: Value\n
399: * \n
400: *
401: * CGI:
402: * Header: Value\n
403: * Status: 200\n
404: * \n
405: *
406: * and different mixes of \n and \r\n combinations
407: *
408: * Some users also forget about CGI and just send a response and hope
409: * we handle it. No headers, no header-content seperator
410: *
411: */
412:
413: /* nph (non-parsed headers) */
414: if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
415:
416: for (i = 0; !is_header_end && i < hctx->response_header->used - 1; i++) {
417: char c = hctx->response_header->ptr[i];
418:
419: switch (c) {
420: case ':':
421: /* we found a colon
422: *
423: * looks like we have a normal header
424: */
425: is_header = 1;
426: break;
427: case '\n':
428: /* EOL */
429: if (is_header == 0) {
430: /* we got a EOL but we don't seem to got a HTTP header */
431:
432: is_header_end = 1;
433:
434: break;
435: }
436:
437: /**
438: * check if we saw a \n(\r)?\n sequence
439: */
440: if (last_eol > 0 &&
441: ((i - last_eol == 1) ||
442: (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
443: is_header_end = 1;
444: break;
445: }
446:
447: last_eol = i;
448:
449: break;
450: }
451: }
452:
453: if (is_header_end) {
454: if (!is_header) {
455: /* no header, but a body */
456:
457: if (con->request.http_version == HTTP_VERSION_1_1) {
458: con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
459: }
460:
461: http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
462: joblist_append(srv, con);
463: } else {
464: const char *bstart;
465: size_t blen;
466:
467: /**
468: * i still points to the char after the terminating EOL EOL
469: *
470: * put it on the last \n again
471: */
472: i--;
473:
474: /* the body starts after the EOL */
475: bstart = hctx->response_header->ptr + (i + 1);
476: blen = (hctx->response_header->used - 1) - (i + 1);
477:
478: /* string the last \r?\n */
479: if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
480: i--;
481: }
482:
483: hctx->response_header->ptr[i] = '\0';
484: hctx->response_header->used = i + 1; /* the string + \0 */
485:
486: /* parse the response header */
487: cgi_response_parse(srv, con, p, hctx->response_header);
488:
489: /* enable chunked-transfer-encoding */
490: if (con->request.http_version == HTTP_VERSION_1_1 &&
491: !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
492: con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
493: }
494:
495: if (blen > 0) {
496: http_chunk_append_mem(srv, con, bstart, blen + 1);
497: joblist_append(srv, con);
498: }
499: }
500:
501: con->file_started = 1;
502: }
503: } else {
504: http_chunk_append_mem(srv, con, hctx->response->ptr, hctx->response->used);
505: joblist_append(srv, con);
506: }
507:
508: #if 0
509: log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
510: #endif
511: }
512:
513: return FDEVENT_HANDLED_NOT_FINISHED;
514: }
515:
516: static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
517: int status;
518: pid_t pid;
519: plugin_data *p;
520: connection *con;
521:
522: if (NULL == hctx) return HANDLER_GO_ON;
523:
524: p = hctx->plugin_data;
525: con = hctx->remote_conn;
526:
527: if (con->mode != p->id) return HANDLER_GO_ON;
528:
529: #ifndef __WIN32
530:
531: /* the connection to the browser went away, but we still have a connection
532: * to the CGI script
533: *
534: * close cgi-connection
535: */
536:
537: if (hctx->fd != -1) {
538: /* close connection to the cgi-script */
539: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
540: fdevent_unregister(srv->ev, hctx->fd);
541:
542: if (close(hctx->fd)) {
543: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
544: }
545:
546: hctx->fd = -1;
547: hctx->fde_ndx = -1;
548: }
549:
550: pid = hctx->pid;
551:
552: con->plugin_ctx[p->id] = NULL;
553:
554: /* is this a good idea ? */
555: cgi_handler_ctx_free(hctx);
556:
557: /* if waitpid hasn't been called by response.c yet, do it here */
558: if (pid) {
559: /* check if the CGI-script is already gone */
560: switch(waitpid(pid, &status, WNOHANG)) {
561: case 0:
562: /* not finished yet */
563: #if 0
564: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
565: #endif
566: break;
567: case -1:
568: /* */
569: if (errno == EINTR) break;
570:
571: /*
572: * errno == ECHILD happens if _subrequest catches the process-status before
573: * we have read the response of the cgi process
574: *
575: * -> catch status
576: * -> WAIT_FOR_EVENT
577: * -> read response
578: * -> we get here with waitpid == ECHILD
579: *
580: */
581: if (errno == ECHILD) return HANDLER_GO_ON;
582:
583: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
584: return HANDLER_ERROR;
585: default:
586: /* Send an error if we haven't sent any data yet */
587: if (0 == con->file_started) {
588: connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
589: con->http_status = 500;
590: con->mode = DIRECT;
591: } else {
592: con->file_finished = 1;
593: }
594:
595: if (WIFEXITED(status)) {
596: #if 0
597: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
598: #endif
599: return HANDLER_GO_ON;
600: } else {
601: log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
602: return HANDLER_GO_ON;
603: }
604: }
605:
606:
607: kill(pid, SIGTERM);
608:
609: /* cgi-script is still alive, queue the PID for removal */
610: cgi_pid_add(srv, p, pid);
611: }
612: #endif
613: return HANDLER_GO_ON;
614: }
615:
616: static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
617: plugin_data *p = p_d;
618:
619: return cgi_connection_close(srv, con->plugin_ctx[p->id]);
620: }
621:
622:
623: static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
624: handler_ctx *hctx = ctx;
625: connection *con = hctx->remote_conn;
626:
627: joblist_append(srv, con);
628:
629: if (hctx->fd == -1) {
630: log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "invalid cgi-fd");
631:
632: return HANDLER_ERROR;
633: }
634:
635: if (revents & FDEVENT_IN) {
636: switch (cgi_demux_response(srv, hctx)) {
637: case FDEVENT_HANDLED_NOT_FINISHED:
638: break;
639: case FDEVENT_HANDLED_FINISHED:
640: /* we are done */
641:
642: #if 0
643: log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
644: #endif
645: cgi_connection_close(srv, hctx);
646:
647: /* if we get a IN|HUP and have read everything don't exec the close twice */
648: return HANDLER_FINISHED;
649: case FDEVENT_HANDLED_ERROR:
650: /* Send an error if we haven't sent any data yet */
651: if (0 == con->file_started) {
652: connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST);
653: con->http_status = 500;
654: con->mode = DIRECT;
655: } else {
656: con->file_finished = 1;
657: }
658:
659: log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
660: break;
661: }
662: }
663:
664: if (revents & FDEVENT_OUT) {
665: /* nothing to do */
666: }
667:
668: /* perhaps this issue is already handled */
669: if (revents & FDEVENT_HUP) {
670: /* check if we still have a unfinished header package which is a body in reality */
671: if (con->file_started == 0 &&
672: hctx->response_header->used) {
673: con->file_started = 1;
674: http_chunk_append_mem(srv, con, hctx->response_header->ptr, hctx->response_header->used);
675: joblist_append(srv, con);
676: }
677:
678: if (con->file_finished == 0) {
679: http_chunk_append_mem(srv, con, NULL, 0);
680: joblist_append(srv, con);
681: }
682:
683: con->file_finished = 1;
684:
685: if (chunkqueue_is_empty(con->write_queue)) {
686: /* there is nothing left to write */
687: connection_set_state(srv, con, CON_STATE_RESPONSE_END);
688: } else {
689: /* used the write-handler to finish the request on demand */
690:
691: }
692:
693: # if 0
694: log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
695: # endif
696:
697: /* rtsigs didn't liked the close */
698: cgi_connection_close(srv, hctx);
699: } else if (revents & FDEVENT_ERR) {
700: con->file_finished = 1;
701:
702: /* kill all connections to the cgi process */
703: cgi_connection_close(srv, hctx);
704: #if 1
705: log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
706: #endif
707: return HANDLER_ERROR;
708: }
709:
710: return HANDLER_FINISHED;
711: }
712:
713:
714: static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
715: char *dst;
716:
717: if (!key || !val) return -1;
718:
719: dst = malloc(key_len + val_len + 2);
720: memcpy(dst, key, key_len);
721: dst[key_len] = '=';
722: memcpy(dst + key_len + 1, val, val_len);
723: dst[key_len + 1 + val_len] = '\0';
724:
725: if (env->size == 0) {
726: env->size = 16;
727: env->ptr = malloc(env->size * sizeof(*env->ptr));
728: } else if (env->size == env->used) {
729: env->size += 16;
730: env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
731: }
732:
733: env->ptr[env->used++] = dst;
734:
735: return 0;
736: }
737:
738: static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
739: pid_t pid;
740:
741: #ifdef HAVE_IPV6
742: char b2[INET6_ADDRSTRLEN + 1];
743: #endif
744:
745: int to_cgi_fds[2];
746: int from_cgi_fds[2];
747: struct stat st;
748:
749: #ifndef __WIN32
750:
751: if (cgi_handler->used > 1) {
752: /* stat the exec file */
753: if (-1 == (stat(cgi_handler->ptr, &st))) {
754: log_error_write(srv, __FILE__, __LINE__, "sbss",
755: "stat for cgi-handler", cgi_handler,
756: "failed:", strerror(errno));
757: return -1;
758: }
759: }
760:
761: if (pipe(to_cgi_fds)) {
762: log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
763: return -1;
764: }
765:
766: if (pipe(from_cgi_fds)) {
767: close(to_cgi_fds[0]);
768: close(to_cgi_fds[1]);
769: log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
770: return -1;
771: }
772:
773: /* fork, execve */
774: switch (pid = fork()) {
775: case 0: {
776: /* child */
777: char **args;
778: int argc;
779: int i = 0;
780: char buf[32];
781: size_t n;
782: char_array env;
783: char *c;
784: const char *s;
785: server_socket *srv_sock = con->srv_socket;
786:
787: /* move stdout to from_cgi_fd[1] */
788: close(STDOUT_FILENO);
789: dup2(from_cgi_fds[1], STDOUT_FILENO);
790: close(from_cgi_fds[1]);
791: /* not needed */
792: close(from_cgi_fds[0]);
793:
794: /* move the stdin to to_cgi_fd[0] */
795: close(STDIN_FILENO);
796: dup2(to_cgi_fds[0], STDIN_FILENO);
797: close(to_cgi_fds[0]);
798: /* not needed */
799: close(to_cgi_fds[1]);
800:
801: /* create environment */
802: env.ptr = NULL;
803: env.size = 0;
804: env.used = 0;
805:
806: if (buffer_is_empty(con->conf.server_tag)) {
807: cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
808: } else {
809: cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
810: }
811:
812: if (!buffer_is_empty(con->server_name)) {
813: size_t len = con->server_name->used - 1;
814:
815: if (con->server_name->ptr[0] == '[') {
816: const char *colon = strstr(con->server_name->ptr, "]:");
817: if (colon) len = (colon + 1) - con->server_name->ptr;
818: } else {
819: const char *colon = strchr(con->server_name->ptr, ':');
820: if (colon) len = colon - con->server_name->ptr;
821: }
822:
823: cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
824: } else {
825: #ifdef HAVE_IPV6
826: s = inet_ntop(srv_sock->addr.plain.sa_family,
827: srv_sock->addr.plain.sa_family == AF_INET6 ?
828: (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
829: (const void *) &(srv_sock->addr.ipv4.sin_addr),
830: b2, sizeof(b2)-1);
831: #else
832: s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
833: #endif
834: cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
835: }
836: cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
837:
838: s = get_http_version_name(con->request.http_version);
839:
840: cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
841:
842: LI_ltostr(buf,
843: #ifdef HAVE_IPV6
844: ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
845: #else
846: ntohs(srv_sock->addr.ipv4.sin_port)
847: #endif
848: );
849: cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
850:
851: switch (srv_sock->addr.plain.sa_family) {
852: #ifdef HAVE_IPV6
853: case AF_INET6:
854: s = inet_ntop(srv_sock->addr.plain.sa_family,
855: (const void *) &(srv_sock->addr.ipv6.sin6_addr),
856: b2, sizeof(b2)-1);
857: break;
858: case AF_INET:
859: s = inet_ntop(srv_sock->addr.plain.sa_family,
860: (const void *) &(srv_sock->addr.ipv4.sin_addr),
861: b2, sizeof(b2)-1);
862: break;
863: #else
864: case AF_INET:
865: s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
866: break;
867: #endif
868: default:
869: s = "";
870: break;
871: }
872: cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
873:
874: s = get_http_method_name(con->request.http_method);
875: cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
876:
877: if (!buffer_is_empty(con->request.pathinfo)) {
878: cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
879: }
880: cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
881: if (!buffer_is_empty(con->uri.query)) {
882: cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
883: }
884: if (!buffer_is_empty(con->request.orig_uri)) {
885: cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
886: }
887:
888:
889: switch (con->dst_addr.plain.sa_family) {
890: #ifdef HAVE_IPV6
891: case AF_INET6:
892: s = inet_ntop(con->dst_addr.plain.sa_family,
893: (const void *) &(con->dst_addr.ipv6.sin6_addr),
894: b2, sizeof(b2)-1);
895: break;
896: case AF_INET:
897: s = inet_ntop(con->dst_addr.plain.sa_family,
898: (const void *) &(con->dst_addr.ipv4.sin_addr),
899: b2, sizeof(b2)-1);
900: break;
901: #else
902: case AF_INET:
903: s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
904: break;
905: #endif
906: default:
907: s = "";
908: break;
909: }
910: cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
911:
912: LI_ltostr(buf,
913: #ifdef HAVE_IPV6
914: ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
915: #else
916: ntohs(con->dst_addr.ipv4.sin_port)
917: #endif
918: );
919: cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
920:
921: if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
922: cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
923: }
924:
925: /* request.content_length < SSIZE_MAX, see request.c */
926: LI_ltostr(buf, con->request.content_length);
927: cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
928: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
929: cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
930: cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
931:
932: /* for valgrind */
933: if (NULL != (s = getenv("LD_PRELOAD"))) {
934: cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
935: }
936:
937: if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
938: cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
939: }
940: #ifdef __CYGWIN__
941: /* CYGWIN needs SYSTEMROOT */
942: if (NULL != (s = getenv("SYSTEMROOT"))) {
943: cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
944: }
945: #endif
946:
947: for (n = 0; n < con->request.headers->used; n++) {
948: data_string *ds;
949:
950: ds = (data_string *)con->request.headers->data[n];
951:
952: if (ds->value->used && ds->key->used) {
953: size_t j;
954:
955: buffer_reset(p->tmp_buf);
956:
957: if (0 != strcasecmp(ds->key->ptr, "CONTENT-TYPE")) {
958: buffer_copy_string_len(p->tmp_buf, CONST_STR_LEN("HTTP_"));
959: p->tmp_buf->used--; /* strip \0 after HTTP_ */
960: }
961:
962: buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
963:
964: for (j = 0; j < ds->key->used - 1; j++) {
965: char cr = '_';
966: if (light_isalpha(ds->key->ptr[j])) {
967: /* upper-case */
968: cr = ds->key->ptr[j] & ~32;
969: } else if (light_isdigit(ds->key->ptr[j])) {
970: /* copy */
971: cr = ds->key->ptr[j];
972: }
973: p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
974: }
975: p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
976:
977: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
978: }
979: }
980:
981: for (n = 0; n < con->environment->used; n++) {
982: data_string *ds;
983:
984: ds = (data_string *)con->environment->data[n];
985:
986: if (ds->value->used && ds->key->used) {
987: size_t j;
988:
989: buffer_reset(p->tmp_buf);
990:
991: buffer_prepare_append(p->tmp_buf, ds->key->used + 2);
992:
993: for (j = 0; j < ds->key->used - 1; j++) {
994: char cr = '_';
995: if (light_isalpha(ds->key->ptr[j])) {
996: /* upper-case */
997: cr = ds->key->ptr[j] & ~32;
998: } else if (light_isdigit(ds->key->ptr[j])) {
999: /* copy */
1000: cr = ds->key->ptr[j];
1001: }
1002: p->tmp_buf->ptr[p->tmp_buf->used++] = cr;
1003: }
1004: p->tmp_buf->ptr[p->tmp_buf->used++] = '\0';
1005:
1006: cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1007: }
1008: }
1009:
1010: if (env.size == env.used) {
1011: env.size += 16;
1012: env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1013: }
1014:
1015: env.ptr[env.used] = NULL;
1016:
1017: /* set up args */
1018: argc = 3;
1019: args = malloc(sizeof(*args) * argc);
1020: i = 0;
1021:
1022: if (cgi_handler->used > 1) {
1023: args[i++] = cgi_handler->ptr;
1024: }
1025: args[i++] = con->physical.path->ptr;
1026: args[i ] = NULL;
1027:
1028: /* search for the last / */
1029: if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1030: *c = '\0';
1031:
1032: /* change to the physical directory */
1033: if (-1 == chdir(con->physical.path->ptr)) {
1034: log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1035: }
1036: *c = '/';
1037: }
1038:
1039: /* we don't need the client socket */
1040: for (i = 3; i < 256; i++) {
1041: if (i != srv->errorlog_fd) close(i);
1042: }
1043:
1044: /* exec the cgi */
1045: execve(args[0], args, env.ptr);
1046:
1047: /* log_error_write(srv, __FILE__, __LINE__, "sss", "CGI failed:", strerror(errno), args[0]); */
1048:
1049: /* */
1050: SEGFAULT();
1051: break;
1052: }
1053: case -1:
1054: /* error */
1055: log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1056: close(from_cgi_fds[0]);
1057: close(from_cgi_fds[1]);
1058: close(to_cgi_fds[0]);
1059: close(to_cgi_fds[1]);
1060: return -1;
1061: break;
1062: default: {
1063: handler_ctx *hctx;
1064: /* father */
1065:
1066: close(from_cgi_fds[1]);
1067: close(to_cgi_fds[0]);
1068:
1069: if (con->request.content_length) {
1070: chunkqueue *cq = con->request_content_queue;
1071: chunk *c;
1072:
1073: assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1074:
1075: /* there is content to send */
1076: for (c = cq->first; c; c = cq->first) {
1077: int r = 0;
1078:
1079: /* copy all chunks */
1080: switch(c->type) {
1081: case FILE_CHUNK:
1082:
1083: if (c->file.mmap.start == MAP_FAILED) {
1084: if (-1 == c->file.fd && /* open the file if not already open */
1085: -1 == (c->file.fd = open(c->file.name->ptr, O_RDONLY))) {
1086: log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno));
1087:
1088: close(from_cgi_fds[0]);
1089: close(to_cgi_fds[1]);
1090: return -1;
1091: }
1092:
1093: c->file.mmap.length = c->file.length;
1094:
1095: if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_SHARED, c->file.fd, 0))) {
1096: log_error_write(srv, __FILE__, __LINE__, "ssbd", "mmap failed: ",
1097: strerror(errno), c->file.name, c->file.fd);
1098:
1099: close(from_cgi_fds[0]);
1100: close(to_cgi_fds[1]);
1101: return -1;
1102: }
1103:
1104: close(c->file.fd);
1105: c->file.fd = -1;
1106:
1107: /* chunk_reset() or chunk_free() will cleanup for us */
1108: }
1109:
1110: if ((r = write(to_cgi_fds[1], c->file.mmap.start + c->offset, c->file.length - c->offset)) < 0) {
1111: switch(errno) {
1112: case ENOSPC:
1113: con->http_status = 507;
1114: break;
1115: case EINTR:
1116: continue;
1117: default:
1118: con->http_status = 403;
1119: break;
1120: }
1121: }
1122: break;
1123: case MEM_CHUNK:
1124: if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, c->mem->used - c->offset - 1)) < 0) {
1125: switch(errno) {
1126: case ENOSPC:
1127: con->http_status = 507;
1128: break;
1129: case EINTR:
1130: continue;
1131: default:
1132: con->http_status = 403;
1133: break;
1134: }
1135: }
1136: break;
1137: case UNUSED_CHUNK:
1138: break;
1139: }
1140:
1141: if (r > 0) {
1142: c->offset += r;
1143: cq->bytes_out += r;
1144: } else {
1145: log_error_write(srv, __FILE__, __LINE__, "ss", "write() failed due to: ", strerror(errno));
1146: con->http_status = 500;
1147: break;
1148: }
1149: chunkqueue_remove_finished_chunks(cq);
1150: }
1151: }
1152:
1153: close(to_cgi_fds[1]);
1154:
1155: /* register PID and wait for them asyncronously */
1156: con->mode = p->id;
1157: buffer_reset(con->physical.path);
1158:
1159: hctx = cgi_handler_ctx_init();
1160:
1161: hctx->remote_conn = con;
1162: hctx->plugin_data = p;
1163: hctx->pid = pid;
1164: hctx->fd = from_cgi_fds[0];
1165: hctx->fde_ndx = -1;
1166:
1167: con->plugin_ctx[p->id] = hctx;
1168:
1169: fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1170: fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1171:
1172: if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1173: log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1174:
1175: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1176: fdevent_unregister(srv->ev, hctx->fd);
1177:
1178: log_error_write(srv, __FILE__, __LINE__, "sd", "cgi close:", hctx->fd);
1179:
1180: close(hctx->fd);
1181:
1182: cgi_handler_ctx_free(hctx);
1183:
1184: con->plugin_ctx[p->id] = NULL;
1185:
1186: return -1;
1187: }
1188:
1189: break;
1190: }
1191: }
1192:
1193: return 0;
1194: #else
1195: return -1;
1196: #endif
1197: }
1198:
1199: #define PATCH(x) \
1200: p->conf.x = s->x;
1201: static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1202: size_t i, j;
1203: plugin_config *s = p->config_storage[0];
1204:
1205: PATCH(cgi);
1206: PATCH(execute_x_only);
1207:
1208: /* skip the first, the global context */
1209: for (i = 1; i < srv->config_context->used; i++) {
1210: data_config *dc = (data_config *)srv->config_context->data[i];
1211: s = p->config_storage[i];
1212:
1213: /* condition didn't match */
1214: if (!config_check_cond(srv, con, dc)) continue;
1215:
1216: /* merge config */
1217: for (j = 0; j < dc->value->used; j++) {
1218: data_unset *du = dc->value->data[j];
1219:
1220: if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1221: PATCH(cgi);
1222: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1223: PATCH(execute_x_only);
1224: }
1225: }
1226: }
1227:
1228: return 0;
1229: }
1230: #undef PATCH
1231:
1232: URIHANDLER_FUNC(cgi_is_handled) {
1233: size_t k, s_len;
1234: plugin_data *p = p_d;
1235: buffer *fn = con->physical.path;
1236: stat_cache_entry *sce = NULL;
1237:
1238: if (con->mode != DIRECT) return HANDLER_GO_ON;
1239:
1240: if (fn->used == 0) return HANDLER_GO_ON;
1241:
1242: mod_cgi_patch_connection(srv, con, p);
1243:
1244: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1245: if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1246: if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1247:
1248: s_len = fn->used - 1;
1249:
1250: for (k = 0; k < p->conf.cgi->used; k++) {
1251: data_string *ds = (data_string *)p->conf.cgi->data[k];
1252: size_t ct_len = ds->key->used - 1;
1253:
1254: if (ds->key->used == 0) continue;
1255: if (s_len < ct_len) continue;
1256:
1257: if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1258: if (cgi_create_env(srv, con, p, ds->value)) {
1259: con->mode = DIRECT;
1260: con->http_status = 500;
1261:
1262: buffer_reset(con->physical.path);
1263: return HANDLER_FINISHED;
1264: }
1265: /* one handler is enough for the request */
1266: break;
1267: }
1268: }
1269:
1270: return HANDLER_GO_ON;
1271: }
1272:
1273: TRIGGER_FUNC(cgi_trigger) {
1274: plugin_data *p = p_d;
1275: size_t ndx;
1276: /* the trigger handle only cares about lonely PID which we have to wait for */
1277: #ifndef __WIN32
1278:
1279: for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1280: int status;
1281:
1282: switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1283: case 0:
1284: /* not finished yet */
1285: #if 0
1286: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1287: #endif
1288: break;
1289: case -1:
1290: if (errno == ECHILD) {
1291: /* someone else called waitpid... remove the pid to stop looping the error each time */
1292: log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1293:
1294: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1295: ndx--;
1296: continue;
1297: }
1298:
1299: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1300:
1301: return HANDLER_ERROR;
1302: default:
1303:
1304: if (WIFEXITED(status)) {
1305: #if 0
1306: log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1307: #endif
1308: } else if (WIFSIGNALED(status)) {
1309: /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1310: */
1311: if (WTERMSIG(status) != SIGTERM) {
1312: log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1313: }
1314: } else {
1315: log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1316: }
1317:
1318: cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1319: /* del modified the buffer structure
1320: * and copies the last entry to the current one
1321: * -> recheck the current index
1322: */
1323: ndx--;
1324: }
1325: }
1326: #endif
1327: return HANDLER_GO_ON;
1328: }
1329:
1330: /*
1331: * - HANDLER_GO_ON : not our job
1332: * - HANDLER_FINISHED: got response header
1333: * - HANDLER_WAIT_FOR_EVENT: waiting for response header
1334: */
1335: SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1336: int status;
1337: plugin_data *p = p_d;
1338: handler_ctx *hctx = con->plugin_ctx[p->id];
1339:
1340: if (con->mode != p->id) return HANDLER_GO_ON;
1341: if (NULL == hctx) return HANDLER_GO_ON;
1342:
1343: #if 0
1344: log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1345: #endif
1346:
1347: if (hctx->pid == 0) {
1348: /* cgi already dead */
1349: if (!con->file_started) return HANDLER_WAIT_FOR_EVENT;
1350: return HANDLER_FINISHED;
1351: }
1352:
1353: #ifndef __WIN32
1354: switch(waitpid(hctx->pid, &status, WNOHANG)) {
1355: case 0:
1356: /* we only have for events here if we don't have the header yet,
1357: * otherwise the event-handler will send us the incoming data */
1358: if (con->file_started) return HANDLER_FINISHED;
1359:
1360: return HANDLER_WAIT_FOR_EVENT;
1361: case -1:
1362: if (errno == EINTR) return HANDLER_WAIT_FOR_EVENT;
1363:
1364: if (errno == ECHILD && con->file_started == 0) {
1365: /*
1366: * second round but still not response
1367: */
1368: return HANDLER_WAIT_FOR_EVENT;
1369: }
1370:
1371: log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1372: con->mode = DIRECT;
1373: con->http_status = 500;
1374:
1375: hctx->pid = 0;
1376:
1377: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1378: fdevent_unregister(srv->ev, hctx->fd);
1379:
1380: if (close(hctx->fd)) {
1381: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1382: }
1383:
1384: cgi_handler_ctx_free(hctx);
1385:
1386: con->plugin_ctx[p->id] = NULL;
1387:
1388: return HANDLER_FINISHED;
1389: default:
1390: /* cgi process exited
1391: */
1392:
1393: hctx->pid = 0;
1394:
1395: /* we already have response headers? just continue */
1396: if (con->file_started) return HANDLER_FINISHED;
1397:
1398: if (WIFEXITED(status)) {
1399: /* clean exit - just continue */
1400: return HANDLER_WAIT_FOR_EVENT;
1401: }
1402:
1403: /* cgi proc died, and we didn't get any data yet - send error message and close cgi con */
1404: log_error_write(srv, __FILE__, __LINE__, "s", "cgi died ?");
1405:
1406: con->http_status = 500;
1407: con->mode = DIRECT;
1408:
1409: fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
1410: fdevent_unregister(srv->ev, hctx->fd);
1411:
1412: if (close(hctx->fd)) {
1413: log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
1414: }
1415:
1416: cgi_handler_ctx_free(hctx);
1417:
1418: con->plugin_ctx[p->id] = NULL;
1419: return HANDLER_FINISHED;
1420: }
1421: #else
1422: return HANDLER_ERROR;
1423: #endif
1424: }
1425:
1426:
1427: int mod_cgi_plugin_init(plugin *p);
1428: int mod_cgi_plugin_init(plugin *p) {
1429: p->version = LIGHTTPD_VERSION_ID;
1430: p->name = buffer_init_string("cgi");
1431:
1432: p->connection_reset = cgi_connection_close_callback;
1433: p->handle_subrequest_start = cgi_is_handled;
1434: p->handle_subrequest = mod_cgi_handle_subrequest;
1435: #if 0
1436: p->handle_fdevent = cgi_handle_fdevent;
1437: #endif
1438: p->handle_trigger = cgi_trigger;
1439: p->init = mod_cgi_init;
1440: p->cleanup = mod_cgi_free;
1441: p->set_defaults = mod_fastcgi_set_defaults;
1442:
1443: p->data = NULL;
1444:
1445: return 0;
1446: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>