untangle overly complex control flow logic
[lighttpd.git] / src / mod_cgi.c
blob088de085d5a16fb1e16fc2a4f1d1c49f658fbd06
1 #include "first.h"
3 #include "server.h"
4 #include "stat_cache.h"
5 #include "keyvalue.h"
6 #include "log.h"
7 #include "connections.h"
8 #include "joblist.h"
9 #include "http_chunk.h"
10 #include "network_backends.h"
12 #include "plugin.h"
14 #include <sys/types.h>
15 #include "sys-mmap.h"
17 #ifdef __WIN32
18 # include <winsock2.h>
19 #else
20 # include <sys/socket.h>
21 # include <sys/wait.h>
22 # include <netinet/in.h>
23 # include <arpa/inet.h>
24 #endif
26 #include <unistd.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <fdevent.h>
31 #include <signal.h>
32 #include <ctype.h>
33 #include <assert.h>
35 #include <stdio.h>
36 #include <fcntl.h>
38 #include "version.h"
40 enum {EOL_UNSET, EOL_N, EOL_RN};
42 typedef struct {
43 char **ptr;
45 size_t size;
46 size_t used;
47 } char_array;
49 typedef struct {
50 pid_t *ptr;
51 size_t used;
52 size_t size;
53 } buffer_pid_t;
55 typedef struct {
56 array *cgi;
57 unsigned short execute_x_only;
58 } plugin_config;
60 typedef struct {
61 PLUGIN_DATA;
62 buffer_pid_t cgi_pid;
64 buffer *tmp_buf;
65 buffer *parse_response;
67 plugin_config **config_storage;
69 plugin_config conf;
70 } plugin_data;
72 typedef struct {
73 pid_t pid;
74 int fd;
75 int fde_ndx; /* index into the fd-event buffer */
77 connection *remote_conn; /* dumb pointer */
78 plugin_data *plugin_data; /* dumb pointer */
80 buffer *response;
81 buffer *response_header;
82 } handler_ctx;
84 static handler_ctx * cgi_handler_ctx_init(void) {
85 handler_ctx *hctx = calloc(1, sizeof(*hctx));
87 force_assert(hctx);
89 hctx->response = buffer_init();
90 hctx->response_header = buffer_init();
91 hctx->fd = -1;
93 return hctx;
96 static void cgi_handler_ctx_free(handler_ctx *hctx) {
97 buffer_free(hctx->response);
98 buffer_free(hctx->response_header);
100 free(hctx);
103 enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
105 INIT_FUNC(mod_cgi_init) {
106 plugin_data *p;
108 p = calloc(1, sizeof(*p));
110 force_assert(p);
112 p->tmp_buf = buffer_init();
113 p->parse_response = buffer_init();
115 return p;
119 FREE_FUNC(mod_cgi_free) {
120 plugin_data *p = p_d;
121 buffer_pid_t *r = &(p->cgi_pid);
123 UNUSED(srv);
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];
130 if (NULL == s) continue;
132 array_free(s->cgi);
134 free(s);
136 free(p->config_storage);
140 if (r->ptr) free(r->ptr);
142 buffer_free(p->tmp_buf);
143 buffer_free(p->parse_response);
145 free(p);
147 return HANDLER_GO_ON;
150 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
151 plugin_data *p = p_d;
152 size_t i = 0;
154 config_values_t cv[] = {
155 { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
156 { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
157 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
160 if (!p) return HANDLER_ERROR;
162 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
163 force_assert(p->config_storage);
165 for (i = 0; i < srv->config_context->used; i++) {
166 data_config const* config = (data_config const*)srv->config_context->data[i];
167 plugin_config *s;
169 s = calloc(1, sizeof(plugin_config));
170 force_assert(s);
172 s->cgi = array_init();
173 s->execute_x_only = 0;
175 cv[0].destination = s->cgi;
176 cv[1].destination = &(s->execute_x_only);
178 p->config_storage[i] = s;
180 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
181 return HANDLER_ERROR;
185 return HANDLER_GO_ON;
189 static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
190 int m = -1;
191 size_t i;
192 buffer_pid_t *r = &(p->cgi_pid);
194 UNUSED(srv);
196 for (i = 0; i < r->used; i++) {
197 if (r->ptr[i] > m) m = r->ptr[i];
200 if (r->size == 0) {
201 r->size = 16;
202 r->ptr = malloc(sizeof(*r->ptr) * r->size);
203 force_assert(r->ptr);
204 } else if (r->used == r->size) {
205 r->size += 16;
206 r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
207 force_assert(r->ptr);
210 r->ptr[r->used++] = pid;
212 return m;
215 static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
216 size_t i;
217 buffer_pid_t *r = &(p->cgi_pid);
219 UNUSED(srv);
221 for (i = 0; i < r->used; i++) {
222 if (r->ptr[i] == pid) break;
225 if (i != r->used) {
226 /* found */
228 if (i != r->used - 1) {
229 r->ptr[i] = r->ptr[r->used - 1];
231 r->used--;
234 return 0;
237 static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
238 char *ns;
239 const char *s;
240 int line = 0;
242 UNUSED(srv);
244 buffer_copy_buffer(p->parse_response, in);
246 for (s = p->parse_response->ptr;
247 NULL != (ns = strchr(s, '\n'));
248 s = ns + 1, line++) {
249 const char *key, *value;
250 int key_len;
251 data_string *ds;
253 /* strip the \n */
254 ns[0] = '\0';
256 if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
258 if (line == 0 &&
259 0 == strncmp(s, "HTTP/1.", 7)) {
260 /* non-parsed header ... we parse them anyway */
262 if ((s[7] == '1' ||
263 s[7] == '0') &&
264 s[8] == ' ') {
265 int status;
266 /* after the space should be a status code for us */
268 status = strtol(s+9, NULL, 10);
270 if (status >= 100 &&
271 status < 1000) {
272 /* we expected 3 digits and didn't got them */
273 con->parsed_response |= HTTP_STATUS;
274 con->http_status = status;
277 } else {
278 /* parse the headers */
279 key = s;
280 if (NULL == (value = strchr(s, ':'))) {
281 /* we expect: "<key>: <value>\r\n" */
282 continue;
285 key_len = value - key;
286 value += 1;
288 /* skip LWS */
289 while (*value == ' ' || *value == '\t') value++;
291 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
292 ds = data_response_init();
294 buffer_copy_string_len(ds->key, key, key_len);
295 buffer_copy_string(ds->value, value);
297 array_insert_unique(con->response.headers, (data_unset *)ds);
299 switch(key_len) {
300 case 4:
301 if (0 == strncasecmp(key, "Date", key_len)) {
302 con->parsed_response |= HTTP_DATE;
304 break;
305 case 6:
306 if (0 == strncasecmp(key, "Status", key_len)) {
307 int status = strtol(value, NULL, 10);
308 if (status >= 100 && status < 1000) {
309 con->http_status = status;
310 con->parsed_response |= HTTP_STATUS;
311 } else {
312 con->http_status = 502;
315 break;
316 case 8:
317 if (0 == strncasecmp(key, "Location", key_len)) {
318 con->parsed_response |= HTTP_LOCATION;
320 break;
321 case 10:
322 if (0 == strncasecmp(key, "Connection", key_len)) {
323 con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
324 con->parsed_response |= HTTP_CONNECTION;
326 break;
327 case 14:
328 if (0 == strncasecmp(key, "Content-Length", key_len)) {
329 con->response.content_length = strtoul(value, NULL, 10);
330 con->parsed_response |= HTTP_CONTENT_LENGTH;
332 break;
333 default:
334 break;
339 /* CGI/1.1 rev 03 - 7.2.1.2 */
340 if ((con->parsed_response & HTTP_LOCATION) &&
341 !(con->parsed_response & HTTP_STATUS)) {
342 con->http_status = 302;
345 return 0;
349 static int cgi_demux_response(server *srv, handler_ctx *hctx) {
350 plugin_data *p = hctx->plugin_data;
351 connection *con = hctx->remote_conn;
353 while(1) {
354 int n;
355 int toread;
357 #if defined(__WIN32)
358 buffer_string_prepare_copy(hctx->response, 4 * 1024);
359 #else
360 if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
361 buffer_string_prepare_copy(hctx->response, 4 * 1024);
362 } else {
363 if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
364 buffer_string_prepare_copy(hctx->response, toread);
366 #endif
368 if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
369 if (errno == EAGAIN || errno == EINTR) {
370 /* would block, wait for signal */
371 return FDEVENT_HANDLED_NOT_FINISHED;
373 /* error */
374 log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
375 return FDEVENT_HANDLED_ERROR;
378 if (n == 0) {
379 /* read finished */
381 con->file_finished = 1;
383 /* send final chunk */
384 http_chunk_close(srv, con);
386 return FDEVENT_HANDLED_FINISHED;
389 buffer_commit(hctx->response, n);
391 /* split header from body */
393 if (con->file_started == 0) {
394 int is_header = 0;
395 int is_header_end = 0;
396 size_t last_eol = 0;
397 size_t i, header_len;
399 buffer_append_string_buffer(hctx->response_header, hctx->response);
402 * we have to handle a few cases:
404 * nph:
406 * HTTP/1.0 200 Ok\n
407 * Header: Value\n
408 * \n
410 * CGI:
411 * Header: Value\n
412 * Status: 200\n
413 * \n
415 * and different mixes of \n and \r\n combinations
417 * Some users also forget about CGI and just send a response and hope
418 * we handle it. No headers, no header-content seperator
422 /* nph (non-parsed headers) */
423 if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
425 header_len = buffer_string_length(hctx->response_header);
426 for (i = 0; !is_header_end && i < header_len; i++) {
427 char c = hctx->response_header->ptr[i];
429 switch (c) {
430 case ':':
431 /* we found a colon
433 * looks like we have a normal header
435 is_header = 1;
436 break;
437 case '\n':
438 /* EOL */
439 if (is_header == 0) {
440 /* we got a EOL but we don't seem to got a HTTP header */
442 is_header_end = 1;
444 break;
448 * check if we saw a \n(\r)?\n sequence
450 if (last_eol > 0 &&
451 ((i - last_eol == 1) ||
452 (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
453 is_header_end = 1;
454 break;
457 last_eol = i;
459 break;
463 if (is_header_end) {
464 if (!is_header) {
465 /* no header, but a body */
467 if (con->request.http_version == HTTP_VERSION_1_1) {
468 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
471 http_chunk_append_buffer(srv, con, hctx->response_header);
472 } else {
473 const char *bstart;
474 size_t blen;
476 /* the body starts after the EOL */
477 bstart = hctx->response_header->ptr + i;
478 blen = header_len - i;
481 * i still points to the char after the terminating EOL EOL
483 * put it on the last \n again
485 i--;
487 /* string the last \r?\n */
488 if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
489 i--;
492 buffer_string_set_length(hctx->response_header, i);
494 /* parse the response header */
495 cgi_response_parse(srv, con, p, hctx->response_header);
497 /* enable chunked-transfer-encoding */
498 if (con->request.http_version == HTTP_VERSION_1_1 &&
499 !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
500 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
503 if (blen > 0) {
504 http_chunk_append_mem(srv, con, bstart, blen);
508 con->file_started = 1;
510 } else {
511 http_chunk_append_buffer(srv, con, hctx->response);
514 #if 0
515 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
516 #endif
519 return FDEVENT_HANDLED_NOT_FINISHED;
522 static void cgi_connection_close(server *srv, handler_ctx *hctx) {
523 int status;
524 pid_t pid;
525 plugin_data *p = hctx->plugin_data;
526 connection *con = hctx->remote_conn;
528 #ifndef __WIN32
530 /* the connection to the browser went away, but we still have a connection
531 * to the CGI script
533 * close cgi-connection
536 if (hctx->fd != -1) {
537 /* close connection to the cgi-script */
538 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
539 fdevent_unregister(srv->ev, hctx->fd);
541 if (close(hctx->fd)) {
542 log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
545 hctx->fd = -1;
546 hctx->fde_ndx = -1;
549 pid = hctx->pid;
551 con->plugin_ctx[p->id] = NULL;
553 /* is this a good idea ? */
554 cgi_handler_ctx_free(hctx);
556 /* if waitpid hasn't been called by response.c yet, do it here */
557 if (pid) {
558 /* check if the CGI-script is already gone */
559 switch(waitpid(pid, &status, WNOHANG)) {
560 case 0:
561 /* not finished yet */
562 #if 0
563 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
564 #endif
565 break;
566 case -1:
567 /* */
568 if (errno == EINTR) break;
571 * errno == ECHILD happens if _subrequest catches the process-status before
572 * we have read the response of the cgi process
574 * -> catch status
575 * -> WAIT_FOR_EVENT
576 * -> read response
577 * -> we get here with waitpid == ECHILD
580 if (errno != ECHILD) {
581 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
583 /* anyway: don't wait for it anymore */
584 pid = 0;
585 break;
586 default:
587 if (WIFEXITED(status)) {
588 #if 0
589 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
590 #endif
591 } else {
592 log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
595 pid = 0;
596 break;
599 if (pid) {
600 kill(pid, SIGTERM);
602 /* cgi-script is still alive, queue the PID for removal */
603 cgi_pid_add(srv, p, pid);
606 #endif
608 /* finish response (if not already finished) */
609 if (con->mode == p->id && con->state == CON_STATE_HANDLE_REQUEST) {
610 /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
611 * i.e. not called from cgi_connection_close_callback()) */
613 /* Send an error if we haven't sent any data yet */
614 if (0 == con->file_started) {
615 con->http_status = 500;
616 con->mode = DIRECT;
617 } else if (0 == con->file_finished) {
618 http_chunk_close(srv, con);
619 con->file_finished = 1;
624 static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
625 plugin_data *p = p_d;
626 handler_ctx *hctx = con->plugin_ctx[p->id];
627 if (hctx) cgi_connection_close(srv, hctx);
629 return HANDLER_GO_ON;
633 static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
634 handler_ctx *hctx = ctx;
635 connection *con = hctx->remote_conn;
637 joblist_append(srv, con);
639 if (revents & FDEVENT_IN) {
640 switch (cgi_demux_response(srv, hctx)) {
641 case FDEVENT_HANDLED_NOT_FINISHED:
642 break;
643 case FDEVENT_HANDLED_FINISHED:
644 /* we are done */
646 #if 0
647 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
648 #endif
649 cgi_connection_close(srv, hctx);
651 /* if we get a IN|HUP and have read everything don't exec the close twice */
652 return HANDLER_FINISHED;
653 case FDEVENT_HANDLED_ERROR:
654 log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
656 cgi_connection_close(srv, hctx);
657 return HANDLER_FINISHED;
661 if (revents & FDEVENT_OUT) {
662 /* nothing to do */
665 /* perhaps this issue is already handled */
666 if (revents & FDEVENT_HUP) {
667 /* check if we still have a unfinished header package which is a body in reality */
668 if (con->file_started == 0 && !buffer_string_is_empty(hctx->response_header)) {
669 con->file_started = 1;
670 http_chunk_append_buffer(srv, con, hctx->response_header);
673 # if 0
674 log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
675 # endif
677 /* rtsigs didn't liked the close */
678 cgi_connection_close(srv, hctx);
679 } else if (revents & FDEVENT_ERR) {
680 /* kill all connections to the cgi process */
681 cgi_connection_close(srv, hctx);
682 #if 1
683 log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
684 #endif
685 return HANDLER_ERROR;
688 return HANDLER_FINISHED;
692 static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
693 char *dst;
695 if (!key || !val) return -1;
697 dst = malloc(key_len + val_len + 2);
698 force_assert(dst);
699 memcpy(dst, key, key_len);
700 dst[key_len] = '=';
701 memcpy(dst + key_len + 1, val, val_len);
702 dst[key_len + 1 + val_len] = '\0';
704 if (env->size == 0) {
705 env->size = 16;
706 env->ptr = malloc(env->size * sizeof(*env->ptr));
707 force_assert(env->ptr);
708 } else if (env->size == env->used) {
709 env->size += 16;
710 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
711 force_assert(env->ptr);
714 env->ptr[env->used++] = dst;
716 return 0;
719 /* returns: 0: continue, -1: fatal error, -2: connection reset */
720 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes),
721 * also mmaps and sends complete chunk instead of only small parts - the files
722 * are supposed to be temp files with reasonable chunk sizes.
724 * Also always use mmap; the files are "trusted", as we created them.
726 static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
727 chunk* const c = cq->first;
728 off_t offset, toSend, file_end;
729 ssize_t r;
730 size_t mmap_offset, mmap_avail;
731 const char *data;
733 force_assert(NULL != c);
734 force_assert(FILE_CHUNK == c->type);
735 force_assert(c->offset >= 0 && c->offset <= c->file.length);
737 offset = c->file.start + c->offset;
738 toSend = c->file.length - c->offset;
739 file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
741 if (0 == toSend) {
742 chunkqueue_remove_finished_chunks(cq);
743 return 0;
746 if (0 != network_open_file_chunk(srv, con, cq)) return -1;
748 /* (re)mmap the buffer if range is not covered completely */
749 if (MAP_FAILED == c->file.mmap.start
750 || offset < c->file.mmap.offset
751 || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
753 if (MAP_FAILED != c->file.mmap.start) {
754 munmap(c->file.mmap.start, c->file.mmap.length);
755 c->file.mmap.start = MAP_FAILED;
758 c->file.mmap.offset = mmap_align_offset(offset);
759 c->file.mmap.length = file_end - c->file.mmap.offset;
761 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) {
762 log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
763 strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
764 return -1;
768 force_assert(offset >= c->file.mmap.offset);
769 mmap_offset = offset - c->file.mmap.offset;
770 force_assert(c->file.mmap.length > mmap_offset);
771 mmap_avail = c->file.mmap.length - mmap_offset;
772 force_assert(toSend <= (off_t) mmap_avail);
774 data = c->file.mmap.start + mmap_offset;
776 if ((r = write(fd, data, toSend)) < 0) {
777 switch (errno) {
778 case EAGAIN:
779 case EINTR:
780 return 0;
781 case EPIPE:
782 case ECONNRESET:
783 return -2;
784 default:
785 log_error_write(srv, __FILE__, __LINE__, "ssd",
786 "write failed:", strerror(errno), fd);
787 return -1;
791 if (r >= 0) {
792 chunkqueue_mark_written(cq, r);
795 return 0;
798 static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) {
799 pid_t pid;
801 #ifdef HAVE_IPV6
802 char b2[INET6_ADDRSTRLEN + 1];
803 #endif
805 int to_cgi_fds[2];
806 int from_cgi_fds[2];
807 struct stat st;
809 #ifndef __WIN32
811 if (!buffer_string_is_empty(cgi_handler)) {
812 /* stat the exec file */
813 if (-1 == (stat(cgi_handler->ptr, &st))) {
814 log_error_write(srv, __FILE__, __LINE__, "sbss",
815 "stat for cgi-handler", cgi_handler,
816 "failed:", strerror(errno));
817 return -1;
821 if (pipe(to_cgi_fds)) {
822 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
823 return -1;
826 if (pipe(from_cgi_fds)) {
827 close(to_cgi_fds[0]);
828 close(to_cgi_fds[1]);
829 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
830 return -1;
833 /* fork, execve */
834 switch (pid = fork()) {
835 case 0: {
836 /* child */
837 char **args;
838 int argc;
839 int i = 0;
840 char buf[LI_ITOSTRING_LENGTH];
841 size_t n;
842 char_array env;
843 char *c;
844 const char *s;
845 server_socket *srv_sock = con->srv_socket;
847 /* move stdout to from_cgi_fd[1] */
848 close(STDOUT_FILENO);
849 dup2(from_cgi_fds[1], STDOUT_FILENO);
850 close(from_cgi_fds[1]);
851 /* not needed */
852 close(from_cgi_fds[0]);
854 /* move the stdin to to_cgi_fd[0] */
855 close(STDIN_FILENO);
856 dup2(to_cgi_fds[0], STDIN_FILENO);
857 close(to_cgi_fds[0]);
858 /* not needed */
859 close(to_cgi_fds[1]);
861 /* create environment */
862 env.ptr = NULL;
863 env.size = 0;
864 env.used = 0;
866 if (buffer_is_empty(con->conf.server_tag)) {
867 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
868 } else {
869 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
872 if (!buffer_string_is_empty(con->server_name)) {
873 size_t len = buffer_string_length(con->server_name);
875 if (con->server_name->ptr[0] == '[') {
876 const char *colon = strstr(con->server_name->ptr, "]:");
877 if (colon) len = (colon + 1) - con->server_name->ptr;
878 } else {
879 const char *colon = strchr(con->server_name->ptr, ':');
880 if (colon) len = colon - con->server_name->ptr;
883 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
884 } else {
885 #ifdef HAVE_IPV6
886 s = inet_ntop(
887 srv_sock->addr.plain.sa_family,
888 srv_sock->addr.plain.sa_family == AF_INET6 ?
889 (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
890 (const void *) &(srv_sock->addr.ipv4.sin_addr),
891 b2, sizeof(b2)-1);
892 #else
893 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
894 #endif
895 force_assert(s);
896 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
898 cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
900 s = get_http_version_name(con->request.http_version);
901 force_assert(s);
902 cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
904 li_utostrn(buf, sizeof(buf),
905 #ifdef HAVE_IPV6
906 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
907 #else
908 ntohs(srv_sock->addr.ipv4.sin_port)
909 #endif
911 cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
913 switch (srv_sock->addr.plain.sa_family) {
914 #ifdef HAVE_IPV6
915 case AF_INET6:
916 s = inet_ntop(
917 srv_sock->addr.plain.sa_family,
918 (const void *) &(srv_sock->addr.ipv6.sin6_addr),
919 b2, sizeof(b2)-1);
920 break;
921 case AF_INET:
922 s = inet_ntop(
923 srv_sock->addr.plain.sa_family,
924 (const void *) &(srv_sock->addr.ipv4.sin_addr),
925 b2, sizeof(b2)-1);
926 break;
927 #else
928 case AF_INET:
929 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
930 break;
931 #endif
932 default:
933 s = "";
934 break;
936 force_assert(s);
937 cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
939 s = get_http_method_name(con->request.http_method);
940 force_assert(s);
941 cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
943 if (!buffer_string_is_empty(con->request.pathinfo)) {
944 cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
946 cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
947 if (!buffer_string_is_empty(con->uri.query)) {
948 cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
949 } else {
950 cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
952 if (!buffer_string_is_empty(con->request.orig_uri)) {
953 cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
957 switch (con->dst_addr.plain.sa_family) {
958 #ifdef HAVE_IPV6
959 case AF_INET6:
960 s = inet_ntop(
961 con->dst_addr.plain.sa_family,
962 (const void *) &(con->dst_addr.ipv6.sin6_addr),
963 b2, sizeof(b2)-1);
964 break;
965 case AF_INET:
966 s = inet_ntop(
967 con->dst_addr.plain.sa_family,
968 (const void *) &(con->dst_addr.ipv4.sin_addr),
969 b2, sizeof(b2)-1);
970 break;
971 #else
972 case AF_INET:
973 s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
974 break;
975 #endif
976 default:
977 s = "";
978 break;
980 force_assert(s);
981 cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
983 li_utostrn(buf, sizeof(buf),
984 #ifdef HAVE_IPV6
985 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
986 #else
987 ntohs(con->dst_addr.ipv4.sin_port)
988 #endif
990 cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
992 if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
993 cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
996 li_itostrn(buf, sizeof(buf), con->request.content_length);
997 cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
998 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
999 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1000 cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
1002 /* for valgrind */
1003 if (NULL != (s = getenv("LD_PRELOAD"))) {
1004 cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
1007 if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
1008 cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
1010 #ifdef __CYGWIN__
1011 /* CYGWIN needs SYSTEMROOT */
1012 if (NULL != (s = getenv("SYSTEMROOT"))) {
1013 cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
1015 #endif
1017 for (n = 0; n < con->request.headers->used; n++) {
1018 data_string *ds;
1020 ds = (data_string *)con->request.headers->data[n];
1022 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1023 buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1025 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1029 for (n = 0; n < con->environment->used; n++) {
1030 data_string *ds;
1032 ds = (data_string *)con->environment->data[n];
1034 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1035 buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1037 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1041 if (env.size == env.used) {
1042 env.size += 16;
1043 env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1046 env.ptr[env.used] = NULL;
1048 /* set up args */
1049 argc = 3;
1050 args = malloc(sizeof(*args) * argc);
1051 force_assert(args);
1052 i = 0;
1054 if (!buffer_string_is_empty(cgi_handler)) {
1055 args[i++] = cgi_handler->ptr;
1057 args[i++] = con->physical.path->ptr;
1058 args[i ] = NULL;
1060 /* search for the last / */
1061 if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1062 /* handle special case of file in root directory */
1063 const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr;
1065 /* temporarily shorten con->physical.path to directory without terminating '/' */
1066 *c = '\0';
1067 /* change to the physical directory */
1068 if (-1 == chdir(physdir)) {
1069 log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1071 *c = '/';
1074 /* we don't need the client socket */
1075 for (i = 3; i < 256; i++) {
1076 if (i != srv->errorlog_fd) close(i);
1079 /* exec the cgi */
1080 execve(args[0], args, env.ptr);
1082 /* most log files may have been closed/redirected by this point,
1083 * though stderr might still point to lighttpd.breakage.log */
1084 perror(args[0]);
1085 _exit(1);
1087 case -1:
1088 /* error */
1089 log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1090 close(from_cgi_fds[0]);
1091 close(from_cgi_fds[1]);
1092 close(to_cgi_fds[0]);
1093 close(to_cgi_fds[1]);
1094 return -1;
1095 default: {
1096 /* parent process */
1098 close(from_cgi_fds[1]);
1099 close(to_cgi_fds[0]);
1101 if (con->request.content_length) {
1102 chunkqueue *cq = con->request_content_queue;
1103 chunk *c;
1105 assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1107 /* NOTE: yes, this is synchronous sending of CGI post data;
1108 * if you need something asynchronous (recommended with large
1109 * request bodies), use mod_fastcgi + fcgi-cgi.
1111 * Also: windows doesn't support select() on pipes - wouldn't be
1112 * easy to fix for all platforms.
1115 /* there is content to send */
1116 for (c = cq->first; c; c = cq->first) {
1117 int r = -1;
1119 switch(c->type) {
1120 case FILE_CHUNK:
1121 r = cgi_write_file_chunk_mmap(srv, con, to_cgi_fds[1], cq);
1122 break;
1124 case MEM_CHUNK:
1125 if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
1126 switch(errno) {
1127 case EAGAIN:
1128 case EINTR:
1129 /* ignore and try again */
1130 r = 0;
1131 break;
1132 case EPIPE:
1133 case ECONNRESET:
1134 /* connection closed */
1135 r = -2;
1136 break;
1137 default:
1138 /* fatal error */
1139 log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
1140 r = -1;
1141 break;
1143 } else if (r > 0) {
1144 chunkqueue_mark_written(cq, r);
1146 break;
1149 switch (r) {
1150 case -1:
1151 /* fatal error */
1152 close(from_cgi_fds[0]);
1153 close(to_cgi_fds[1]);
1154 kill(pid, SIGTERM);
1155 cgi_pid_add(srv, p, pid);
1156 return -1;
1157 case -2:
1158 /* connection reset */
1159 log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
1160 /* skip all remaining data */
1161 chunkqueue_mark_written(cq, chunkqueue_length(cq));
1162 break;
1163 default:
1164 break;
1169 close(to_cgi_fds[1]);
1171 /* register PID and wait for them asyncronously */
1173 hctx->pid = pid;
1174 hctx->fd = from_cgi_fds[0];
1175 hctx->fde_ndx = -1;
1177 fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1178 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1180 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1181 log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1182 cgi_connection_close(srv, hctx);
1183 return -1;
1186 break;
1190 return 0;
1191 #else
1192 return -1;
1193 #endif
1196 static buffer * cgi_get_handler(array *a, buffer *fn) {
1197 size_t k, s_len = buffer_string_length(fn);
1198 for (k = 0; k < a->used; ++k) {
1199 data_string *ds = (data_string *)a->data[k];
1200 size_t ct_len = buffer_string_length(ds->key);
1202 if (buffer_is_empty(ds->key)) continue;
1203 if (s_len < ct_len) continue;
1205 if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1206 return ds->value;
1210 return NULL;
1213 #define PATCH(x) \
1214 p->conf.x = s->x;
1215 static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1216 size_t i, j;
1217 plugin_config *s = p->config_storage[0];
1219 PATCH(cgi);
1220 PATCH(execute_x_only);
1222 /* skip the first, the global context */
1223 for (i = 1; i < srv->config_context->used; i++) {
1224 data_config *dc = (data_config *)srv->config_context->data[i];
1225 s = p->config_storage[i];
1227 /* condition didn't match */
1228 if (!config_check_cond(srv, con, dc)) continue;
1230 /* merge config */
1231 for (j = 0; j < dc->value->used; j++) {
1232 data_unset *du = dc->value->data[j];
1234 if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1235 PATCH(cgi);
1236 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1237 PATCH(execute_x_only);
1242 return 0;
1244 #undef PATCH
1246 URIHANDLER_FUNC(cgi_is_handled) {
1247 plugin_data *p = p_d;
1248 buffer *fn = con->physical.path;
1249 stat_cache_entry *sce = NULL;
1251 if (con->mode != DIRECT) return HANDLER_GO_ON;
1253 if (buffer_is_empty(fn)) return HANDLER_GO_ON;
1255 mod_cgi_patch_connection(srv, con, p);
1257 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1258 if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1259 if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1261 if (NULL != cgi_get_handler(p->conf.cgi, fn)) {
1262 handler_ctx *hctx = cgi_handler_ctx_init();
1263 hctx->remote_conn = con;
1264 hctx->plugin_data = p;
1265 con->plugin_ctx[p->id] = hctx;
1266 con->mode = p->id;
1269 return HANDLER_GO_ON;
1272 TRIGGER_FUNC(cgi_trigger) {
1273 plugin_data *p = p_d;
1274 size_t ndx;
1275 /* the trigger handle only cares about lonely PID which we have to wait for */
1276 #ifndef __WIN32
1278 for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1279 int status;
1281 switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1282 case 0:
1283 /* not finished yet */
1284 #if 0
1285 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1286 #endif
1287 break;
1288 case -1:
1289 if (errno == ECHILD) {
1290 /* someone else called waitpid... remove the pid to stop looping the error each time */
1291 log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1293 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1294 ndx--;
1295 continue;
1298 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1300 return HANDLER_ERROR;
1301 default:
1303 if (WIFEXITED(status)) {
1304 #if 0
1305 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1306 #endif
1307 } else if (WIFSIGNALED(status)) {
1308 /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1310 if (WTERMSIG(status) != SIGTERM) {
1311 log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1313 } else {
1314 log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1317 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1318 /* del modified the buffer structure
1319 * and copies the last entry to the current one
1320 * -> recheck the current index
1322 ndx--;
1325 #endif
1326 return HANDLER_GO_ON;
1330 * - HANDLER_GO_ON : not our job
1331 * - HANDLER_FINISHED: got response
1332 * - HANDLER_WAIT_FOR_EVENT: waiting for response
1334 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1335 plugin_data *p = p_d;
1336 handler_ctx *hctx = con->plugin_ctx[p->id];
1338 if (con->mode != p->id) return HANDLER_GO_ON;
1339 if (NULL == hctx) return HANDLER_GO_ON;
1341 if (-1 == hctx->fd) {
1342 buffer *handler = cgi_get_handler(p->conf.cgi, con->physical.path);
1343 if (!handler) return HANDLER_GO_ON; /*(should not happen; checked in cgi_is_handled())*/
1344 if (cgi_create_env(srv, con, p, hctx, handler)) {
1345 con->http_status = 500;
1346 con->mode = DIRECT;
1348 return HANDLER_FINISHED;
1352 #if 0
1353 log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1354 #endif
1356 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */
1357 return con->file_finished ? HANDLER_FINISHED : HANDLER_WAIT_FOR_EVENT;
1361 int mod_cgi_plugin_init(plugin *p);
1362 int mod_cgi_plugin_init(plugin *p) {
1363 p->version = LIGHTTPD_VERSION_ID;
1364 p->name = buffer_init_string("cgi");
1366 p->connection_reset = cgi_connection_close_callback;
1367 p->handle_subrequest_start = cgi_is_handled;
1368 p->handle_subrequest = mod_cgi_handle_subrequest;
1369 p->handle_trigger = cgi_trigger;
1370 p->init = mod_cgi_init;
1371 p->cleanup = mod_cgi_free;
1372 p->set_defaults = mod_fastcgi_set_defaults;
1374 p->data = NULL;
1376 return 0;