[mod_status] page refresh option (fixes #2170)
[lighttpd.git] / src / mod_cgi.c
blob71fb5fd106fec47676c33a62c6d7dcc9cb5efa16
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 #ifdef HAVE_SYS_FILIO_H
39 # include <sys/filio.h>
40 #endif
42 #include "version.h"
44 enum {EOL_UNSET, EOL_N, EOL_RN};
46 typedef struct {
47 char **ptr;
49 size_t size;
50 size_t used;
51 } char_array;
53 typedef struct {
54 pid_t *ptr;
55 size_t used;
56 size_t size;
57 } buffer_pid_t;
59 typedef struct {
60 array *cgi;
61 unsigned short execute_x_only;
62 } plugin_config;
64 typedef struct {
65 PLUGIN_DATA;
66 buffer_pid_t cgi_pid;
68 buffer *tmp_buf;
69 buffer *parse_response;
71 plugin_config **config_storage;
73 plugin_config conf;
74 } plugin_data;
76 typedef struct {
77 pid_t pid;
78 int fd;
79 int fde_ndx; /* index into the fd-event buffer */
81 connection *remote_conn; /* dumb pointer */
82 plugin_data *plugin_data; /* dumb pointer */
84 buffer *response;
85 buffer *response_header;
86 } handler_ctx;
88 static handler_ctx * cgi_handler_ctx_init(void) {
89 handler_ctx *hctx = calloc(1, sizeof(*hctx));
91 force_assert(hctx);
93 hctx->response = buffer_init();
94 hctx->response_header = buffer_init();
96 return hctx;
99 static void cgi_handler_ctx_free(handler_ctx *hctx) {
100 buffer_free(hctx->response);
101 buffer_free(hctx->response_header);
103 free(hctx);
106 enum {FDEVENT_HANDLED_UNSET, FDEVENT_HANDLED_FINISHED, FDEVENT_HANDLED_NOT_FINISHED, FDEVENT_HANDLED_ERROR};
108 INIT_FUNC(mod_cgi_init) {
109 plugin_data *p;
111 p = calloc(1, sizeof(*p));
113 force_assert(p);
115 p->tmp_buf = buffer_init();
116 p->parse_response = buffer_init();
118 return p;
122 FREE_FUNC(mod_cgi_free) {
123 plugin_data *p = p_d;
124 buffer_pid_t *r = &(p->cgi_pid);
126 UNUSED(srv);
128 if (p->config_storage) {
129 size_t i;
130 for (i = 0; i < srv->config_context->used; i++) {
131 plugin_config *s = p->config_storage[i];
133 if (NULL == s) continue;
135 array_free(s->cgi);
137 free(s);
139 free(p->config_storage);
143 if (r->ptr) free(r->ptr);
145 buffer_free(p->tmp_buf);
146 buffer_free(p->parse_response);
148 free(p);
150 return HANDLER_GO_ON;
153 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults) {
154 plugin_data *p = p_d;
155 size_t i = 0;
157 config_values_t cv[] = {
158 { "cgi.assign", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
159 { "cgi.execute-x-only", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
160 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
163 if (!p) return HANDLER_ERROR;
165 p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
166 force_assert(p->config_storage);
168 for (i = 0; i < srv->config_context->used; i++) {
169 data_config const* config = (data_config const*)srv->config_context->data[i];
170 plugin_config *s;
172 s = calloc(1, sizeof(plugin_config));
173 force_assert(s);
175 s->cgi = array_init();
176 s->execute_x_only = 0;
178 cv[0].destination = s->cgi;
179 cv[1].destination = &(s->execute_x_only);
181 p->config_storage[i] = s;
183 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
184 return HANDLER_ERROR;
188 return HANDLER_GO_ON;
192 static int cgi_pid_add(server *srv, plugin_data *p, pid_t pid) {
193 int m = -1;
194 size_t i;
195 buffer_pid_t *r = &(p->cgi_pid);
197 UNUSED(srv);
199 for (i = 0; i < r->used; i++) {
200 if (r->ptr[i] > m) m = r->ptr[i];
203 if (r->size == 0) {
204 r->size = 16;
205 r->ptr = malloc(sizeof(*r->ptr) * r->size);
206 force_assert(r->ptr);
207 } else if (r->used == r->size) {
208 r->size += 16;
209 r->ptr = realloc(r->ptr, sizeof(*r->ptr) * r->size);
210 force_assert(r->ptr);
213 r->ptr[r->used++] = pid;
215 return m;
218 static int cgi_pid_del(server *srv, plugin_data *p, pid_t pid) {
219 size_t i;
220 buffer_pid_t *r = &(p->cgi_pid);
222 UNUSED(srv);
224 for (i = 0; i < r->used; i++) {
225 if (r->ptr[i] == pid) break;
228 if (i != r->used) {
229 /* found */
231 if (i != r->used - 1) {
232 r->ptr[i] = r->ptr[r->used - 1];
234 r->used--;
237 return 0;
240 static int cgi_response_parse(server *srv, connection *con, plugin_data *p, buffer *in) {
241 char *ns;
242 const char *s;
243 int line = 0;
245 UNUSED(srv);
247 buffer_copy_buffer(p->parse_response, in);
249 for (s = p->parse_response->ptr;
250 NULL != (ns = strchr(s, '\n'));
251 s = ns + 1, line++) {
252 const char *key, *value;
253 int key_len;
254 data_string *ds;
256 /* strip the \n */
257 ns[0] = '\0';
259 if (ns > s && ns[-1] == '\r') ns[-1] = '\0';
261 if (line == 0 &&
262 0 == strncmp(s, "HTTP/1.", 7)) {
263 /* non-parsed header ... we parse them anyway */
265 if ((s[7] == '1' ||
266 s[7] == '0') &&
267 s[8] == ' ') {
268 int status;
269 /* after the space should be a status code for us */
271 status = strtol(s+9, NULL, 10);
273 if (status >= 100 &&
274 status < 1000) {
275 /* we expected 3 digits and didn't got them */
276 con->parsed_response |= HTTP_STATUS;
277 con->http_status = status;
280 } else {
281 /* parse the headers */
282 key = s;
283 if (NULL == (value = strchr(s, ':'))) {
284 /* we expect: "<key>: <value>\r\n" */
285 continue;
288 key_len = value - key;
289 value += 1;
291 /* skip LWS */
292 while (*value == ' ' || *value == '\t') value++;
294 if (NULL == (ds = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) {
295 ds = data_response_init();
297 buffer_copy_string_len(ds->key, key, key_len);
298 buffer_copy_string(ds->value, value);
300 array_insert_unique(con->response.headers, (data_unset *)ds);
302 switch(key_len) {
303 case 4:
304 if (0 == strncasecmp(key, "Date", key_len)) {
305 con->parsed_response |= HTTP_DATE;
307 break;
308 case 6:
309 if (0 == strncasecmp(key, "Status", key_len)) {
310 int status = strtol(value, NULL, 10);
311 if (status >= 100 && status < 1000) {
312 con->http_status = status;
313 con->parsed_response |= HTTP_STATUS;
314 } else {
315 con->http_status = 502;
318 break;
319 case 8:
320 if (0 == strncasecmp(key, "Location", key_len)) {
321 con->parsed_response |= HTTP_LOCATION;
323 break;
324 case 10:
325 if (0 == strncasecmp(key, "Connection", key_len)) {
326 con->response.keep_alive = (0 == strcasecmp(value, "Keep-Alive")) ? 1 : 0;
327 con->parsed_response |= HTTP_CONNECTION;
329 break;
330 case 14:
331 if (0 == strncasecmp(key, "Content-Length", key_len)) {
332 con->response.content_length = strtoul(value, NULL, 10);
333 con->parsed_response |= HTTP_CONTENT_LENGTH;
335 break;
336 default:
337 break;
342 /* CGI/1.1 rev 03 - 7.2.1.2 */
343 if ((con->parsed_response & HTTP_LOCATION) &&
344 !(con->parsed_response & HTTP_STATUS)) {
345 con->http_status = 302;
348 return 0;
352 static int cgi_demux_response(server *srv, handler_ctx *hctx) {
353 plugin_data *p = hctx->plugin_data;
354 connection *con = hctx->remote_conn;
356 while(1) {
357 int n;
358 int toread;
360 #if defined(__WIN32)
361 buffer_string_prepare_copy(hctx->response, 4 * 1024);
362 #else
363 if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
364 buffer_string_prepare_copy(hctx->response, 4 * 1024);
365 } else {
366 if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
367 buffer_string_prepare_copy(hctx->response, toread);
369 #endif
371 if (-1 == (n = read(hctx->fd, hctx->response->ptr, hctx->response->size - 1))) {
372 if (errno == EAGAIN || errno == EINTR) {
373 /* would block, wait for signal */
374 return FDEVENT_HANDLED_NOT_FINISHED;
376 /* error */
377 log_error_write(srv, __FILE__, __LINE__, "sdd", strerror(errno), con->fd, hctx->fd);
378 return FDEVENT_HANDLED_ERROR;
381 if (n == 0) {
382 /* read finished */
384 con->file_finished = 1;
386 /* send final chunk */
387 http_chunk_close(srv, con);
388 joblist_append(srv, con);
390 return FDEVENT_HANDLED_FINISHED;
393 buffer_commit(hctx->response, n);
395 /* split header from body */
397 if (con->file_started == 0) {
398 int is_header = 0;
399 int is_header_end = 0;
400 size_t last_eol = 0;
401 size_t i, header_len;
403 buffer_append_string_buffer(hctx->response_header, hctx->response);
406 * we have to handle a few cases:
408 * nph:
410 * HTTP/1.0 200 Ok\n
411 * Header: Value\n
412 * \n
414 * CGI:
415 * Header: Value\n
416 * Status: 200\n
417 * \n
419 * and different mixes of \n and \r\n combinations
421 * Some users also forget about CGI and just send a response and hope
422 * we handle it. No headers, no header-content seperator
426 /* nph (non-parsed headers) */
427 if (0 == strncmp(hctx->response_header->ptr, "HTTP/1.", 7)) is_header = 1;
429 header_len = buffer_string_length(hctx->response_header);
430 for (i = 0; !is_header_end && i < header_len; i++) {
431 char c = hctx->response_header->ptr[i];
433 switch (c) {
434 case ':':
435 /* we found a colon
437 * looks like we have a normal header
439 is_header = 1;
440 break;
441 case '\n':
442 /* EOL */
443 if (is_header == 0) {
444 /* we got a EOL but we don't seem to got a HTTP header */
446 is_header_end = 1;
448 break;
452 * check if we saw a \n(\r)?\n sequence
454 if (last_eol > 0 &&
455 ((i - last_eol == 1) ||
456 (i - last_eol == 2 && hctx->response_header->ptr[i - 1] == '\r'))) {
457 is_header_end = 1;
458 break;
461 last_eol = i;
463 break;
467 if (is_header_end) {
468 if (!is_header) {
469 /* no header, but a body */
471 if (con->request.http_version == HTTP_VERSION_1_1) {
472 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
475 http_chunk_append_buffer(srv, con, hctx->response_header);
476 joblist_append(srv, con);
477 } else {
478 const char *bstart;
479 size_t blen;
481 /* the body starts after the EOL */
482 bstart = hctx->response_header->ptr + i;
483 blen = header_len - i;
486 * i still points to the char after the terminating EOL EOL
488 * put it on the last \n again
490 i--;
492 /* string the last \r?\n */
493 if (i > 0 && (hctx->response_header->ptr[i - 1] == '\r')) {
494 i--;
497 buffer_string_set_length(hctx->response_header, i);
499 /* parse the response header */
500 cgi_response_parse(srv, con, p, hctx->response_header);
502 /* enable chunked-transfer-encoding */
503 if (con->request.http_version == HTTP_VERSION_1_1 &&
504 !(con->parsed_response & HTTP_CONTENT_LENGTH)) {
505 con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED;
508 if (blen > 0) {
509 http_chunk_append_mem(srv, con, bstart, blen);
510 joblist_append(srv, con);
514 con->file_started = 1;
516 } else {
517 http_chunk_append_buffer(srv, con, hctx->response);
518 joblist_append(srv, con);
521 #if 0
522 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), b->ptr);
523 #endif
526 return FDEVENT_HANDLED_NOT_FINISHED;
529 static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) {
530 int status;
531 pid_t pid;
532 plugin_data *p = hctx->plugin_data;
533 connection *con = hctx->remote_conn;
535 #ifndef __WIN32
537 /* the connection to the browser went away, but we still have a connection
538 * to the CGI script
540 * close cgi-connection
543 if (hctx->fd != -1) {
544 /* close connection to the cgi-script */
545 fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd);
546 fdevent_unregister(srv->ev, hctx->fd);
548 if (close(hctx->fd)) {
549 log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno));
552 hctx->fd = -1;
553 hctx->fde_ndx = -1;
556 pid = hctx->pid;
558 con->plugin_ctx[p->id] = NULL;
560 /* is this a good idea ? */
561 cgi_handler_ctx_free(hctx);
563 /* if waitpid hasn't been called by response.c yet, do it here */
564 if (pid) {
565 /* check if the CGI-script is already gone */
566 switch(waitpid(pid, &status, WNOHANG)) {
567 case 0:
568 /* not finished yet */
569 #if 0
570 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid);
571 #endif
572 break;
573 case -1:
574 /* */
575 if (errno == EINTR) break;
578 * errno == ECHILD happens if _subrequest catches the process-status before
579 * we have read the response of the cgi process
581 * -> catch status
582 * -> WAIT_FOR_EVENT
583 * -> read response
584 * -> we get here with waitpid == ECHILD
587 if (errno != ECHILD) {
588 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
590 /* anyway: don't wait for it anymore */
591 pid = 0;
592 break;
593 default:
594 if (WIFEXITED(status)) {
595 #if 0
596 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid);
597 #endif
598 } else {
599 log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid);
602 pid = 0;
603 break;
606 if (pid) {
607 kill(pid, SIGTERM);
609 /* cgi-script is still alive, queue the PID for removal */
610 cgi_pid_add(srv, p, pid);
613 #endif
615 if (con->state == CON_STATE_HANDLE_REQUEST) {
616 /* (not CON_STATE_ERROR and not CON_STATE_RESPONSE_END,
617 * i.e. not called from cgi_connection_close_callback()) */
619 /* Send an error if we haven't sent any data yet */
620 if (0 == con->file_started) {
621 con->http_status = 500;
622 con->mode = DIRECT;
623 } else if (0 == con->file_finished) {
624 http_chunk_close(srv, con);
625 con->file_finished = 1;
629 return HANDLER_GO_ON;
632 static handler_t cgi_connection_close_callback(server *srv, connection *con, void *p_d) {
633 plugin_data *p = p_d;
634 handler_ctx *hctx = con->plugin_ctx[p->id];
636 if (con->mode != p->id) return HANDLER_GO_ON;
637 if (NULL == hctx) return HANDLER_GO_ON;
639 return cgi_connection_close(srv, hctx);
643 static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) {
644 handler_ctx *hctx = ctx;
645 connection *con = hctx->remote_conn;
647 joblist_append(srv, con);
649 if (revents & FDEVENT_IN) {
650 switch (cgi_demux_response(srv, hctx)) {
651 case FDEVENT_HANDLED_NOT_FINISHED:
652 break;
653 case FDEVENT_HANDLED_FINISHED:
654 /* we are done */
656 #if 0
657 log_error_write(srv, __FILE__, __LINE__, "ddss", con->fd, hctx->fd, connection_get_state(con->state), "finished");
658 #endif
659 cgi_connection_close(srv, hctx);
661 /* if we get a IN|HUP and have read everything don't exec the close twice */
662 return HANDLER_FINISHED;
663 case FDEVENT_HANDLED_ERROR:
664 log_error_write(srv, __FILE__, __LINE__, "s", "demuxer failed: ");
666 cgi_connection_close(srv, hctx);
667 return HANDLER_FINISHED;
671 if (revents & FDEVENT_OUT) {
672 /* nothing to do */
675 /* perhaps this issue is already handled */
676 if (revents & FDEVENT_HUP) {
677 /* check if we still have a unfinished header package which is a body in reality */
678 if (con->file_started == 0 && !buffer_string_is_empty(hctx->response_header)) {
679 con->file_started = 1;
680 http_chunk_append_buffer(srv, con, hctx->response_header);
683 # if 0
684 log_error_write(srv, __FILE__, __LINE__, "sddd", "got HUP from cgi", con->fd, hctx->fd, revents);
685 # endif
687 /* rtsigs didn't liked the close */
688 cgi_connection_close(srv, hctx);
689 } else if (revents & FDEVENT_ERR) {
690 /* kill all connections to the cgi process */
691 cgi_connection_close(srv, hctx);
692 #if 1
693 log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR");
694 #endif
695 return HANDLER_ERROR;
698 return HANDLER_FINISHED;
702 static int cgi_env_add(char_array *env, const char *key, size_t key_len, const char *val, size_t val_len) {
703 char *dst;
705 if (!key || !val) return -1;
707 dst = malloc(key_len + val_len + 2);
708 force_assert(dst);
709 memcpy(dst, key, key_len);
710 dst[key_len] = '=';
711 memcpy(dst + key_len + 1, val, val_len);
712 dst[key_len + 1 + val_len] = '\0';
714 if (env->size == 0) {
715 env->size = 16;
716 env->ptr = malloc(env->size * sizeof(*env->ptr));
717 force_assert(env->ptr);
718 } else if (env->size == env->used) {
719 env->size += 16;
720 env->ptr = realloc(env->ptr, env->size * sizeof(*env->ptr));
721 force_assert(env->ptr);
724 env->ptr[env->used++] = dst;
726 return 0;
729 /* returns: 0: continue, -1: fatal error, -2: connection reset */
730 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes),
731 * also mmaps and sends complete chunk instead of only small parts - the files
732 * are supposed to be temp files with reasonable chunk sizes.
734 * Also always use mmap; the files are "trusted", as we created them.
736 static int cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, chunkqueue *cq) {
737 chunk* const c = cq->first;
738 off_t offset, toSend, file_end;
739 ssize_t r;
740 size_t mmap_offset, mmap_avail;
741 const char *data;
743 force_assert(NULL != c);
744 force_assert(FILE_CHUNK == c->type);
745 force_assert(c->offset >= 0 && c->offset <= c->file.length);
747 offset = c->file.start + c->offset;
748 toSend = c->file.length - c->offset;
749 file_end = c->file.start + c->file.length; /* offset to file end in this chunk */
751 if (0 == toSend) {
752 chunkqueue_remove_finished_chunks(cq);
753 return 0;
756 if (0 != network_open_file_chunk(srv, con, cq)) return -1;
758 /* (re)mmap the buffer if range is not covered completely */
759 if (MAP_FAILED == c->file.mmap.start
760 || offset < c->file.mmap.offset
761 || file_end > (off_t)(c->file.mmap.offset + c->file.mmap.length)) {
763 if (MAP_FAILED != c->file.mmap.start) {
764 munmap(c->file.mmap.start, c->file.mmap.length);
765 c->file.mmap.start = MAP_FAILED;
768 c->file.mmap.offset = mmap_align_offset(offset);
769 c->file.mmap.length = file_end - c->file.mmap.offset;
771 if (MAP_FAILED == (c->file.mmap.start = mmap(NULL, c->file.mmap.length, PROT_READ, MAP_PRIVATE, c->file.fd, c->file.mmap.offset))) {
772 log_error_write(srv, __FILE__, __LINE__, "ssbdoo", "mmap failed:",
773 strerror(errno), c->file.name, c->file.fd, c->file.mmap.offset, (off_t) c->file.mmap.length);
774 return -1;
778 force_assert(offset >= c->file.mmap.offset);
779 mmap_offset = offset - c->file.mmap.offset;
780 force_assert(c->file.mmap.length > mmap_offset);
781 mmap_avail = c->file.mmap.length - mmap_offset;
782 force_assert(toSend <= (off_t) mmap_avail);
784 data = c->file.mmap.start + mmap_offset;
786 if ((r = write(fd, data, toSend)) < 0) {
787 switch (errno) {
788 case EAGAIN:
789 case EINTR:
790 return 0;
791 case EPIPE:
792 case ECONNRESET:
793 return -2;
794 default:
795 log_error_write(srv, __FILE__, __LINE__, "ssd",
796 "write failed:", strerror(errno), fd);
797 return -1;
801 if (r >= 0) {
802 chunkqueue_mark_written(cq, r);
805 return 0;
808 static int cgi_create_env(server *srv, connection *con, plugin_data *p, buffer *cgi_handler) {
809 pid_t pid;
811 #ifdef HAVE_IPV6
812 char b2[INET6_ADDRSTRLEN + 1];
813 #endif
815 int to_cgi_fds[2];
816 int from_cgi_fds[2];
817 struct stat st;
819 #ifndef __WIN32
821 if (!buffer_string_is_empty(cgi_handler)) {
822 /* stat the exec file */
823 if (-1 == (stat(cgi_handler->ptr, &st))) {
824 log_error_write(srv, __FILE__, __LINE__, "sbss",
825 "stat for cgi-handler", cgi_handler,
826 "failed:", strerror(errno));
827 return -1;
831 if (pipe(to_cgi_fds)) {
832 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
833 return -1;
836 if (pipe(from_cgi_fds)) {
837 close(to_cgi_fds[0]);
838 close(to_cgi_fds[1]);
839 log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno));
840 return -1;
843 /* fork, execve */
844 switch (pid = fork()) {
845 case 0: {
846 /* child */
847 char **args;
848 int argc;
849 int i = 0;
850 char buf[LI_ITOSTRING_LENGTH];
851 size_t n;
852 char_array env;
853 char *c;
854 const char *s;
855 server_socket *srv_sock = con->srv_socket;
857 /* move stdout to from_cgi_fd[1] */
858 close(STDOUT_FILENO);
859 dup2(from_cgi_fds[1], STDOUT_FILENO);
860 close(from_cgi_fds[1]);
861 /* not needed */
862 close(from_cgi_fds[0]);
864 /* move the stdin to to_cgi_fd[0] */
865 close(STDIN_FILENO);
866 dup2(to_cgi_fds[0], STDIN_FILENO);
867 close(to_cgi_fds[0]);
868 /* not needed */
869 close(to_cgi_fds[1]);
871 /* create environment */
872 env.ptr = NULL;
873 env.size = 0;
874 env.used = 0;
876 if (buffer_is_empty(con->conf.server_tag)) {
877 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_STR_LEN(PACKAGE_DESC));
878 } else {
879 cgi_env_add(&env, CONST_STR_LEN("SERVER_SOFTWARE"), CONST_BUF_LEN(con->conf.server_tag));
882 if (!buffer_string_is_empty(con->server_name)) {
883 size_t len = buffer_string_length(con->server_name);
885 if (con->server_name->ptr[0] == '[') {
886 const char *colon = strstr(con->server_name->ptr, "]:");
887 if (colon) len = (colon + 1) - con->server_name->ptr;
888 } else {
889 const char *colon = strchr(con->server_name->ptr, ':');
890 if (colon) len = colon - con->server_name->ptr;
893 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), con->server_name->ptr, len);
894 } else {
895 #ifdef HAVE_IPV6
896 s = inet_ntop(
897 srv_sock->addr.plain.sa_family,
898 srv_sock->addr.plain.sa_family == AF_INET6 ?
899 (const void *) &(srv_sock->addr.ipv6.sin6_addr) :
900 (const void *) &(srv_sock->addr.ipv4.sin_addr),
901 b2, sizeof(b2)-1);
902 #else
903 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
904 #endif
905 force_assert(s);
906 cgi_env_add(&env, CONST_STR_LEN("SERVER_NAME"), s, strlen(s));
908 cgi_env_add(&env, CONST_STR_LEN("GATEWAY_INTERFACE"), CONST_STR_LEN("CGI/1.1"));
910 s = get_http_version_name(con->request.http_version);
911 force_assert(s);
912 cgi_env_add(&env, CONST_STR_LEN("SERVER_PROTOCOL"), s, strlen(s));
914 li_utostrn(buf, sizeof(buf),
915 #ifdef HAVE_IPV6
916 ntohs(srv_sock->addr.plain.sa_family == AF_INET6 ? srv_sock->addr.ipv6.sin6_port : srv_sock->addr.ipv4.sin_port)
917 #else
918 ntohs(srv_sock->addr.ipv4.sin_port)
919 #endif
921 cgi_env_add(&env, CONST_STR_LEN("SERVER_PORT"), buf, strlen(buf));
923 switch (srv_sock->addr.plain.sa_family) {
924 #ifdef HAVE_IPV6
925 case AF_INET6:
926 s = inet_ntop(
927 srv_sock->addr.plain.sa_family,
928 (const void *) &(srv_sock->addr.ipv6.sin6_addr),
929 b2, sizeof(b2)-1);
930 break;
931 case AF_INET:
932 s = inet_ntop(
933 srv_sock->addr.plain.sa_family,
934 (const void *) &(srv_sock->addr.ipv4.sin_addr),
935 b2, sizeof(b2)-1);
936 break;
937 #else
938 case AF_INET:
939 s = inet_ntoa(srv_sock->addr.ipv4.sin_addr);
940 break;
941 #endif
942 default:
943 s = "";
944 break;
946 force_assert(s);
947 cgi_env_add(&env, CONST_STR_LEN("SERVER_ADDR"), s, strlen(s));
949 s = get_http_method_name(con->request.http_method);
950 force_assert(s);
951 cgi_env_add(&env, CONST_STR_LEN("REQUEST_METHOD"), s, strlen(s));
953 if (!buffer_string_is_empty(con->request.pathinfo)) {
954 cgi_env_add(&env, CONST_STR_LEN("PATH_INFO"), CONST_BUF_LEN(con->request.pathinfo));
956 cgi_env_add(&env, CONST_STR_LEN("REDIRECT_STATUS"), CONST_STR_LEN("200"));
957 if (!buffer_string_is_empty(con->uri.query)) {
958 cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_BUF_LEN(con->uri.query));
959 } else {
960 cgi_env_add(&env, CONST_STR_LEN("QUERY_STRING"), CONST_STR_LEN(""));
962 if (!buffer_string_is_empty(con->request.orig_uri)) {
963 cgi_env_add(&env, CONST_STR_LEN("REQUEST_URI"), CONST_BUF_LEN(con->request.orig_uri));
967 switch (con->dst_addr.plain.sa_family) {
968 #ifdef HAVE_IPV6
969 case AF_INET6:
970 s = inet_ntop(
971 con->dst_addr.plain.sa_family,
972 (const void *) &(con->dst_addr.ipv6.sin6_addr),
973 b2, sizeof(b2)-1);
974 break;
975 case AF_INET:
976 s = inet_ntop(
977 con->dst_addr.plain.sa_family,
978 (const void *) &(con->dst_addr.ipv4.sin_addr),
979 b2, sizeof(b2)-1);
980 break;
981 #else
982 case AF_INET:
983 s = inet_ntoa(con->dst_addr.ipv4.sin_addr);
984 break;
985 #endif
986 default:
987 s = "";
988 break;
990 force_assert(s);
991 cgi_env_add(&env, CONST_STR_LEN("REMOTE_ADDR"), s, strlen(s));
993 li_utostrn(buf, sizeof(buf),
994 #ifdef HAVE_IPV6
995 ntohs(con->dst_addr.plain.sa_family == AF_INET6 ? con->dst_addr.ipv6.sin6_port : con->dst_addr.ipv4.sin_port)
996 #else
997 ntohs(con->dst_addr.ipv4.sin_port)
998 #endif
1000 cgi_env_add(&env, CONST_STR_LEN("REMOTE_PORT"), buf, strlen(buf));
1002 if (buffer_is_equal_caseless_string(con->uri.scheme, CONST_STR_LEN("https"))) {
1003 cgi_env_add(&env, CONST_STR_LEN("HTTPS"), CONST_STR_LEN("on"));
1006 li_itostrn(buf, sizeof(buf), con->request.content_length);
1007 cgi_env_add(&env, CONST_STR_LEN("CONTENT_LENGTH"), buf, strlen(buf));
1008 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_FILENAME"), CONST_BUF_LEN(con->physical.path));
1009 cgi_env_add(&env, CONST_STR_LEN("SCRIPT_NAME"), CONST_BUF_LEN(con->uri.path));
1010 cgi_env_add(&env, CONST_STR_LEN("DOCUMENT_ROOT"), CONST_BUF_LEN(con->physical.basedir));
1012 /* for valgrind */
1013 if (NULL != (s = getenv("LD_PRELOAD"))) {
1014 cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s));
1017 if (NULL != (s = getenv("LD_LIBRARY_PATH"))) {
1018 cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s));
1020 #ifdef __CYGWIN__
1021 /* CYGWIN needs SYSTEMROOT */
1022 if (NULL != (s = getenv("SYSTEMROOT"))) {
1023 cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s));
1025 #endif
1027 for (n = 0; n < con->request.headers->used; n++) {
1028 data_string *ds;
1030 ds = (data_string *)con->request.headers->data[n];
1032 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1033 buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 1);
1035 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1039 for (n = 0; n < con->environment->used; n++) {
1040 data_string *ds;
1042 ds = (data_string *)con->environment->data[n];
1044 if (!buffer_is_empty(ds->value) && !buffer_is_empty(ds->key)) {
1045 buffer_copy_string_encoded_cgi_varnames(p->tmp_buf, CONST_BUF_LEN(ds->key), 0);
1047 cgi_env_add(&env, CONST_BUF_LEN(p->tmp_buf), CONST_BUF_LEN(ds->value));
1051 if (env.size == env.used) {
1052 env.size += 16;
1053 env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr));
1056 env.ptr[env.used] = NULL;
1058 /* set up args */
1059 argc = 3;
1060 args = malloc(sizeof(*args) * argc);
1061 force_assert(args);
1062 i = 0;
1064 if (!buffer_string_is_empty(cgi_handler)) {
1065 args[i++] = cgi_handler->ptr;
1067 args[i++] = con->physical.path->ptr;
1068 args[i ] = NULL;
1070 /* search for the last / */
1071 if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) {
1072 /* handle special case of file in root directory */
1073 const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr;
1075 /* temporarily shorten con->physical.path to directory without terminating '/' */
1076 *c = '\0';
1077 /* change to the physical directory */
1078 if (-1 == chdir(physdir)) {
1079 log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path);
1081 *c = '/';
1084 /* we don't need the client socket */
1085 for (i = 3; i < 256; i++) {
1086 if (i != srv->errorlog_fd) close(i);
1089 /* exec the cgi */
1090 execve(args[0], args, env.ptr);
1092 /* most log files may have been closed/redirected by this point,
1093 * though stderr might still point to lighttpd.breakage.log */
1094 perror(args[0]);
1095 _exit(1);
1097 case -1:
1098 /* error */
1099 log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno));
1100 close(from_cgi_fds[0]);
1101 close(from_cgi_fds[1]);
1102 close(to_cgi_fds[0]);
1103 close(to_cgi_fds[1]);
1104 return -1;
1105 default: {
1106 handler_ctx *hctx;
1107 /* parent proces */
1109 close(from_cgi_fds[1]);
1110 close(to_cgi_fds[0]);
1112 if (con->request.content_length) {
1113 chunkqueue *cq = con->request_content_queue;
1114 chunk *c;
1116 assert(chunkqueue_length(cq) == (off_t)con->request.content_length);
1118 /* NOTE: yes, this is synchronous sending of CGI post data;
1119 * if you need something asynchronous (recommended with large
1120 * request bodies), use mod_fastcgi + fcgi-cgi.
1122 * Also: windows doesn't support select() on pipes - wouldn't be
1123 * easy to fix for all platforms.
1126 /* there is content to send */
1127 for (c = cq->first; c; c = cq->first) {
1128 int r = -1;
1130 switch(c->type) {
1131 case FILE_CHUNK:
1132 r = cgi_write_file_chunk_mmap(srv, con, to_cgi_fds[1], cq);
1133 break;
1135 case MEM_CHUNK:
1136 if ((r = write(to_cgi_fds[1], c->mem->ptr + c->offset, buffer_string_length(c->mem) - c->offset)) < 0) {
1137 switch(errno) {
1138 case EAGAIN:
1139 case EINTR:
1140 /* ignore and try again */
1141 r = 0;
1142 break;
1143 case EPIPE:
1144 case ECONNRESET:
1145 /* connection closed */
1146 r = -2;
1147 break;
1148 default:
1149 /* fatal error */
1150 log_error_write(srv, __FILE__, __LINE__, "ss", "write failed due to: ", strerror(errno));
1151 r = -1;
1152 break;
1154 } else if (r > 0) {
1155 chunkqueue_mark_written(cq, r);
1157 break;
1160 switch (r) {
1161 case -1:
1162 /* fatal error */
1163 close(from_cgi_fds[0]);
1164 close(to_cgi_fds[1]);
1165 kill(pid, SIGTERM);
1166 cgi_pid_add(srv, p, pid);
1167 return -1;
1168 case -2:
1169 /* connection reset */
1170 log_error_write(srv, __FILE__, __LINE__, "s", "failed to send post data to cgi, connection closed by CGI");
1171 /* skip all remaining data */
1172 chunkqueue_mark_written(cq, chunkqueue_length(cq));
1173 break;
1174 default:
1175 break;
1180 close(to_cgi_fds[1]);
1182 /* register PID and wait for them asyncronously */
1183 con->mode = p->id;
1184 buffer_reset(con->physical.path);
1186 hctx = cgi_handler_ctx_init();
1188 hctx->remote_conn = con;
1189 hctx->plugin_data = p;
1190 hctx->pid = pid;
1191 hctx->fd = from_cgi_fds[0];
1192 hctx->fde_ndx = -1;
1194 con->plugin_ctx[p->id] = hctx;
1196 fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx);
1197 fdevent_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN);
1199 if (-1 == fdevent_fcntl_set(srv->ev, hctx->fd)) {
1200 log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno));
1201 cgi_connection_close(srv, hctx);
1202 return -1;
1205 break;
1209 return 0;
1210 #else
1211 return -1;
1212 #endif
1215 #define PATCH(x) \
1216 p->conf.x = s->x;
1217 static int mod_cgi_patch_connection(server *srv, connection *con, plugin_data *p) {
1218 size_t i, j;
1219 plugin_config *s = p->config_storage[0];
1221 PATCH(cgi);
1222 PATCH(execute_x_only);
1224 /* skip the first, the global context */
1225 for (i = 1; i < srv->config_context->used; i++) {
1226 data_config *dc = (data_config *)srv->config_context->data[i];
1227 s = p->config_storage[i];
1229 /* condition didn't match */
1230 if (!config_check_cond(srv, con, dc)) continue;
1232 /* merge config */
1233 for (j = 0; j < dc->value->used; j++) {
1234 data_unset *du = dc->value->data[j];
1236 if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.assign"))) {
1237 PATCH(cgi);
1238 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("cgi.execute-x-only"))) {
1239 PATCH(execute_x_only);
1244 return 0;
1246 #undef PATCH
1248 URIHANDLER_FUNC(cgi_is_handled) {
1249 size_t k, s_len;
1250 plugin_data *p = p_d;
1251 buffer *fn = con->physical.path;
1252 stat_cache_entry *sce = NULL;
1254 if (con->mode != DIRECT) return HANDLER_GO_ON;
1256 if (buffer_is_empty(fn)) return HANDLER_GO_ON;
1258 mod_cgi_patch_connection(srv, con, p);
1260 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) return HANDLER_GO_ON;
1261 if (!S_ISREG(sce->st.st_mode)) return HANDLER_GO_ON;
1262 if (p->conf.execute_x_only == 1 && (sce->st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0) return HANDLER_GO_ON;
1264 s_len = buffer_string_length(fn);
1266 for (k = 0; k < p->conf.cgi->used; k++) {
1267 data_string *ds = (data_string *)p->conf.cgi->data[k];
1268 size_t ct_len = buffer_string_length(ds->key);
1270 if (buffer_is_empty(ds->key)) continue;
1271 if (s_len < ct_len) continue;
1273 if (0 == strncmp(fn->ptr + s_len - ct_len, ds->key->ptr, ct_len)) {
1274 if (cgi_create_env(srv, con, p, ds->value)) {
1275 con->mode = DIRECT;
1276 con->http_status = 500;
1278 buffer_reset(con->physical.path);
1279 return HANDLER_FINISHED;
1281 /* one handler is enough for the request */
1282 break;
1286 return HANDLER_GO_ON;
1289 TRIGGER_FUNC(cgi_trigger) {
1290 plugin_data *p = p_d;
1291 size_t ndx;
1292 /* the trigger handle only cares about lonely PID which we have to wait for */
1293 #ifndef __WIN32
1295 for (ndx = 0; ndx < p->cgi_pid.used; ndx++) {
1296 int status;
1298 switch(waitpid(p->cgi_pid.ptr[ndx], &status, WNOHANG)) {
1299 case 0:
1300 /* not finished yet */
1301 #if 0
1302 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", p->cgi_pid.ptr[ndx]);
1303 #endif
1304 break;
1305 case -1:
1306 if (errno == ECHILD) {
1307 /* someone else called waitpid... remove the pid to stop looping the error each time */
1308 log_error_write(srv, __FILE__, __LINE__, "s", "cgi child vanished, probably someone else called waitpid");
1310 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1311 ndx--;
1312 continue;
1315 log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno));
1317 return HANDLER_ERROR;
1318 default:
1320 if (WIFEXITED(status)) {
1321 #if 0
1322 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", p->cgi_pid.ptr[ndx]);
1323 #endif
1324 } else if (WIFSIGNALED(status)) {
1325 /* FIXME: what if we killed the CGI script with a kill(..., SIGTERM) ?
1327 if (WTERMSIG(status) != SIGTERM) {
1328 log_error_write(srv, __FILE__, __LINE__, "sd", "cleaning up CGI: process died with signal", WTERMSIG(status));
1330 } else {
1331 log_error_write(srv, __FILE__, __LINE__, "s", "cleaning up CGI: ended unexpectedly");
1334 cgi_pid_del(srv, p, p->cgi_pid.ptr[ndx]);
1335 /* del modified the buffer structure
1336 * and copies the last entry to the current one
1337 * -> recheck the current index
1339 ndx--;
1342 #endif
1343 return HANDLER_GO_ON;
1347 * - HANDLER_GO_ON : not our job
1348 * - HANDLER_FINISHED: got response
1349 * - HANDLER_WAIT_FOR_EVENT: waiting for response
1351 SUBREQUEST_FUNC(mod_cgi_handle_subrequest) {
1352 plugin_data *p = p_d;
1353 handler_ctx *hctx = con->plugin_ctx[p->id];
1354 UNUSED(srv);
1356 if (con->mode != p->id) return HANDLER_GO_ON;
1357 if (NULL == hctx) return HANDLER_GO_ON;
1359 #if 0
1360 log_error_write(srv, __FILE__, __LINE__, "sdd", "subrequest, pid =", hctx, hctx->pid);
1361 #endif
1363 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */
1364 return con->file_finished ? HANDLER_FINISHED : HANDLER_WAIT_FOR_EVENT;
1368 int mod_cgi_plugin_init(plugin *p);
1369 int mod_cgi_plugin_init(plugin *p) {
1370 p->version = LIGHTTPD_VERSION_ID;
1371 p->name = buffer_init_string("cgi");
1373 p->connection_reset = cgi_connection_close_callback;
1374 p->handle_subrequest_start = cgi_is_handled;
1375 p->handle_subrequest = mod_cgi_handle_subrequest;
1376 p->handle_trigger = cgi_trigger;
1377 p->init = mod_cgi_init;
1378 p->cleanup = mod_cgi_free;
1379 p->set_defaults = mod_fastcgi_set_defaults;
1381 p->data = NULL;
1383 return 0;