4 #include "stat_cache.h"
7 #include "connections.h"
10 #include "http_chunk.h"
11 #include "http_header.h"
15 #include <sys/types.h>
17 #include "sys-socket.h"
18 # include <sys/wait.h>
29 static int pipe_cloexec(int pipefd
[2]) {
31 if (0 == pipe2(pipefd
, O_CLOEXEC
)) return 0;
33 return 0 == pipe(pipefd
)
35 && 0 == fcntl(pipefd
[0], F_SETFD
, FD_CLOEXEC
)
36 && 0 == fcntl(pipefd
[1], F_SETFD
, FD_CLOEXEC
)
52 buffer
*ld_library_path
;
59 struct { pid_t pid
; void *ctx
; } *ptr
;
66 unsigned short execute_x_only
;
67 unsigned short local_redir
;
68 unsigned short xsendfile_allow
;
69 unsigned short upgrade
;
70 array
*xsendfile_docroot
;
75 plugin_config
**config_storage
;
88 connection
*remote_conn
; /* dumb pointer */
89 plugin_data
*plugin_data
; /* dumb pointer */
92 buffer
*cgi_handler
; /* dumb pointer */
93 http_response_opts opts
;
97 static handler_ctx
* cgi_handler_ctx_init(void) {
98 handler_ctx
*hctx
= calloc(1, sizeof(*hctx
));
102 hctx
->response
= chunk_buffer_acquire();
109 static void cgi_handler_ctx_free(handler_ctx
*hctx
) {
110 chunk_buffer_release(hctx
->response
);
114 INIT_FUNC(mod_cgi_init
) {
118 p
= calloc(1, sizeof(*p
));
123 s
= getenv("LD_PRELOAD");
124 if (s
) p
->env
.ld_preload
= buffer_init_string(s
);
125 s
= getenv("LD_LIBRARY_PATH");
126 if (s
) p
->env
.ld_library_path
= buffer_init_string(s
);
128 /* CYGWIN needs SYSTEMROOT */
129 s
= getenv("SYSTEMROOT");
130 if (s
) p
->env
.systemroot
= buffer_init_string(s
);
137 FREE_FUNC(mod_cgi_free
) {
138 plugin_data
*p
= p_d
;
139 buffer_pid_t
*r
= &(p
->cgi_pid
);
143 if (p
->config_storage
) {
145 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
146 plugin_config
*s
= p
->config_storage
[i
];
148 if (NULL
== s
) continue;
151 array_free(s
->xsendfile_docroot
);
155 free(p
->config_storage
);
159 if (r
->ptr
) free(r
->ptr
);
161 free(p
->env
.offsets
);
163 buffer_free(p
->env
.ld_preload
);
164 buffer_free(p
->env
.ld_library_path
);
166 buffer_free(p
->env
.systemroot
);
170 return HANDLER_GO_ON
;
173 SETDEFAULTS_FUNC(mod_fastcgi_set_defaults
) {
174 plugin_data
*p
= p_d
;
177 config_values_t cv
[] = {
178 { "cgi.assign", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
179 { "cgi.execute-x-only", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
180 { "cgi.x-sendfile", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
181 { "cgi.x-sendfile-docroot", NULL
, T_CONFIG_ARRAY
, T_CONFIG_SCOPE_CONNECTION
}, /* 3 */
182 { "cgi.local-redir", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 4 */
183 { "cgi.upgrade", NULL
, T_CONFIG_BOOLEAN
, T_CONFIG_SCOPE_CONNECTION
}, /* 5 */
184 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
187 if (!p
) return HANDLER_ERROR
;
189 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
190 force_assert(p
->config_storage
);
192 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
193 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
196 s
= calloc(1, sizeof(plugin_config
));
199 s
->cgi
= array_init();
200 s
->execute_x_only
= 0;
202 s
->xsendfile_allow
= 0;
203 s
->xsendfile_docroot
= array_init();
206 cv
[0].destination
= s
->cgi
;
207 cv
[1].destination
= &(s
->execute_x_only
);
208 cv
[2].destination
= &(s
->xsendfile_allow
);
209 cv
[3].destination
= s
->xsendfile_docroot
;
210 cv
[4].destination
= &(s
->local_redir
);
211 cv
[5].destination
= &(s
->upgrade
);
213 p
->config_storage
[i
] = s
;
215 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
216 return HANDLER_ERROR
;
219 if (!array_is_kvstring(s
->cgi
)) {
220 log_error_write(srv
, __FILE__
, __LINE__
, "s",
221 "unexpected value for cgi.assign; expected list of \"ext\" => \"exepath\"");
222 return HANDLER_ERROR
;
225 if (s
->xsendfile_docroot
->used
) {
227 for (j
= 0; j
< s
->xsendfile_docroot
->used
; ++j
) {
228 data_string
*ds
= (data_string
*)s
->xsendfile_docroot
->data
[j
];
229 if (ds
->type
!= TYPE_STRING
) {
230 log_error_write(srv
, __FILE__
, __LINE__
, "s",
231 "unexpected type for key cgi.x-sendfile-docroot; expected: cgi.x-sendfile-docroot = ( \"/allowed/path\", ... )");
232 return HANDLER_ERROR
;
234 if (ds
->value
->ptr
[0] != '/') {
235 log_error_write(srv
, __FILE__
, __LINE__
, "SBs",
236 "cgi.x-sendfile-docroot paths must begin with '/'; invalid: \"", ds
->value
, "\"");
237 return HANDLER_ERROR
;
239 buffer_path_simplify(ds
->value
, ds
->value
);
240 buffer_append_slash(ds
->value
);
245 return HANDLER_GO_ON
;
249 static void cgi_pid_add(plugin_data
*p
, pid_t pid
, void *ctx
) {
250 buffer_pid_t
*r
= &(p
->cgi_pid
);
252 if (r
->used
== r
->size
) {
254 r
->ptr
= realloc(r
->ptr
, sizeof(*r
->ptr
) * r
->size
);
255 force_assert(r
->ptr
);
258 r
->ptr
[r
->used
].pid
= pid
;
259 r
->ptr
[r
->used
].ctx
= ctx
;
263 static void cgi_pid_kill(plugin_data
*p
, pid_t pid
) {
264 buffer_pid_t
*r
= &(p
->cgi_pid
);
265 for (size_t i
= 0; i
< r
->used
; ++i
) {
266 if (r
->ptr
[i
].pid
== pid
) {
267 r
->ptr
[i
].ctx
= NULL
;
274 static void cgi_pid_del(plugin_data
*p
, size_t i
) {
275 buffer_pid_t
*r
= &(p
->cgi_pid
);
277 if (i
!= r
->used
- 1) {
278 r
->ptr
[i
] = r
->ptr
[r
->used
- 1];
284 static void cgi_connection_close_fdtocgi(server
*srv
, handler_ctx
*hctx
) {
285 /*(closes only hctx->fdtocgi)*/
286 fdevent_fdnode_event_del(srv
->ev
, hctx
->fdntocgi
);
287 /*fdevent_unregister(srv->ev, hctx->fdtocgi);*//*(handled below)*/
288 fdevent_sched_close(srv
->ev
, hctx
->fdtocgi
, 0);
289 hctx
->fdntocgi
= NULL
;
293 static void cgi_connection_close(server
*srv
, handler_ctx
*hctx
) {
294 plugin_data
*p
= hctx
->plugin_data
;
295 connection
*con
= hctx
->remote_conn
;
297 /* the connection to the browser went away, but we still have a connection
300 * close cgi-connection
303 if (hctx
->fd
!= -1) {
304 /* close connection to the cgi-script */
305 fdevent_fdnode_event_del(srv
->ev
, hctx
->fdn
);
306 /*fdevent_unregister(srv->ev, hctx->fd);*//*(handled below)*/
307 fdevent_sched_close(srv
->ev
, hctx
->fd
, 0);
311 if (hctx
->fdtocgi
!= -1) {
312 cgi_connection_close_fdtocgi(srv
, hctx
); /*(closes only hctx->fdtocgi)*/
316 cgi_pid_kill(p
, hctx
->pid
);
319 con
->plugin_ctx
[p
->id
] = NULL
;
321 cgi_handler_ctx_free(hctx
);
323 /* finish response (if not already con->file_started, con->file_finished) */
324 if (con
->mode
== p
->id
) {
325 http_response_backend_done(srv
, con
);
329 static handler_t
cgi_connection_close_callback(server
*srv
, connection
*con
, void *p_d
) {
330 plugin_data
*p
= p_d
;
331 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
332 if (hctx
) cgi_connection_close(srv
, hctx
);
334 return HANDLER_GO_ON
;
338 static int cgi_write_request(server
*srv
, handler_ctx
*hctx
, int fd
);
341 static handler_t
cgi_handle_fdevent_send (server
*srv
, void *ctx
, int revents
) {
342 handler_ctx
*hctx
= ctx
;
343 connection
*con
= hctx
->remote_conn
;
345 /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/
346 joblist_append(srv
, con
);
348 if (revents
& FDEVENT_OUT
) {
349 if (0 != cgi_write_request(srv
, hctx
, hctx
->fdtocgi
)) {
350 cgi_connection_close(srv
, hctx
);
351 return HANDLER_ERROR
;
353 /* more request body to be sent to CGI */
356 if (revents
& FDEVENT_HUP
) {
357 /* skip sending remaining data to CGI */
358 if (con
->request
.content_length
) {
359 chunkqueue
*cq
= con
->request_content_queue
;
360 chunkqueue_mark_written(cq
, chunkqueue_length(cq
));
361 if (cq
->bytes_in
!= (off_t
)con
->request
.content_length
) {
366 cgi_connection_close_fdtocgi(srv
, hctx
); /*(closes only hctx->fdtocgi)*/
367 } else if (revents
& FDEVENT_ERR
) {
368 /* kill all connections to the cgi process */
370 log_error_write(srv
, __FILE__
, __LINE__
, "s", "cgi-FDEVENT_ERR");
372 cgi_connection_close(srv
, hctx
);
373 return HANDLER_ERROR
;
376 return HANDLER_FINISHED
;
380 static handler_t
cgi_response_headers(server
*srv
, connection
*con
, struct http_response_opts_t
*opts
) {
381 /* response headers just completed */
382 handler_ctx
*hctx
= (handler_ctx
*)opts
->pdata
;
384 if (con
->response
.htags
& HTTP_HEADER_UPGRADE
) {
385 if (hctx
->conf
.upgrade
&& con
->http_status
== 101) {
386 /* 101 Switching Protocols; transition to transparent proxy */
387 http_response_upgrade_read_body_unknown(srv
, con
);
390 con
->response
.htags
&= ~HTTP_HEADER_UPGRADE
;
392 /* preserve prior questionable behavior; likely broken behavior
393 * anyway if backend thinks connection is being upgraded but client
394 * does not receive Connection: upgrade */
395 http_header_response_unset(con
, HTTP_HEADER_UPGRADE
,
396 CONST_STR_LEN("Upgrade"));
401 if (hctx
->conf
.upgrade
&& !(con
->response
.htags
& HTTP_HEADER_UPGRADE
)) {
402 chunkqueue
*cq
= con
->request_content_queue
;
403 hctx
->conf
.upgrade
= 0;
404 if (cq
->bytes_out
== (off_t
)con
->request
.content_length
) {
405 cgi_connection_close_fdtocgi(srv
, hctx
); /*(closes hctx->fdtocgi)*/
409 return HANDLER_GO_ON
;
413 static int cgi_recv_response(server
*srv
, handler_ctx
*hctx
) {
414 switch (http_response_read(srv
, hctx
->remote_conn
, &hctx
->opts
,
415 hctx
->response
, hctx
->fdn
)) {
417 return HANDLER_GO_ON
;
419 http_response_backend_error(srv
, hctx
->remote_conn
);
421 case HANDLER_FINISHED
:
422 cgi_connection_close(srv
, hctx
);
423 return HANDLER_FINISHED
;
424 case HANDLER_COMEBACK
:
425 /* hctx->conf.local_redir */
426 buffer_clear(hctx
->response
);
427 connection_response_reset(srv
, hctx
->remote_conn
); /*(includes con->http_status = 0)*/
428 plugins_call_connection_reset(srv
, hctx
->remote_conn
);
429 /*cgi_connection_close(srv, hctx);*//*(already cleaned up and hctx is now invalid)*/
430 return HANDLER_COMEBACK
;
435 static handler_t
cgi_handle_fdevent(server
*srv
, void *ctx
, int revents
) {
436 handler_ctx
*hctx
= ctx
;
437 connection
*con
= hctx
->remote_conn
;
439 joblist_append(srv
, con
);
441 if (revents
& FDEVENT_IN
) {
442 handler_t rc
= cgi_recv_response(srv
, hctx
);/*(might invalidate hctx)*/
443 if (rc
!= HANDLER_GO_ON
) return rc
; /*(unless HANDLER_GO_ON)*/
446 /* perhaps this issue is already handled */
447 if (revents
& (FDEVENT_HUP
|FDEVENT_RDHUP
)) {
448 if (con
->file_started
) {
449 /* drain any remaining data from kernel pipe buffers
450 * even if (con->conf.stream_response_body
451 * & FDEVENT_STREAM_RESPONSE_BUFMIN)
452 * since event loop will spin on fd FDEVENT_HUP event
453 * until unregistered. */
455 const unsigned short flags
= con
->conf
.stream_response_body
;
456 con
->conf
.stream_response_body
&= ~FDEVENT_STREAM_RESPONSE_BUFMIN
;
457 con
->conf
.stream_response_body
|= FDEVENT_STREAM_RESPONSE_POLLRDHUP
;
459 rc
= cgi_recv_response(srv
,hctx
);/*(might invalidate hctx)*/
460 } while (rc
== HANDLER_GO_ON
); /*(unless HANDLER_GO_ON)*/
461 con
->conf
.stream_response_body
= flags
;
462 return rc
; /* HANDLER_FINISHED or HANDLER_COMEBACK or HANDLER_ERROR */
463 } else if (!buffer_string_is_empty(hctx
->response
)) {
464 /* unfinished header package which is a body in reality */
465 con
->file_started
= 1;
466 if (0 != http_chunk_append_buffer(srv
, con
, hctx
->response
)) {
467 cgi_connection_close(srv
, hctx
);
468 return HANDLER_ERROR
;
470 if (0 == con
->http_status
) con
->http_status
= 200; /* OK */
472 cgi_connection_close(srv
, hctx
);
473 } else if (revents
& FDEVENT_ERR
) {
474 /* kill all connections to the cgi process */
475 cgi_connection_close(srv
, hctx
);
476 return HANDLER_ERROR
;
479 return HANDLER_FINISHED
;
483 static int cgi_env_add(void *venv
, const char *key
, size_t key_len
, const char *val
, size_t val_len
) {
484 env_accum
*env
= venv
;
487 if (!key
|| !val
) return -1;
489 if (env
->size
- env
->used
< key_len
+ val_len
+ 2) {
490 if (0 == env
->size
) env
->size
= 4096;
491 do { env
->size
*= 2; } while (env
->size
- env
->used
< key_len
+ val_len
+ 2);
492 env
->ptr
= realloc(env
->ptr
, env
->size
);
493 force_assert(env
->ptr
);
496 dst
= env
->ptr
+ env
->used
;
497 memcpy(dst
, key
, key_len
);
499 memcpy(dst
+ key_len
+ 1, val
, val_len
);
500 dst
[key_len
+ 1 + val_len
] = '\0';
502 if (env
->osize
== env
->oused
) {
504 env
->offsets
= realloc(env
->offsets
, env
->osize
* sizeof(*env
->offsets
));
505 force_assert(env
->offsets
);
507 env
->offsets
[env
->oused
++] = env
->used
;
508 env
->used
+= key_len
+ val_len
+ 2;
513 /*(improved from network_write_mmap.c)*/
514 static off_t
mmap_align_offset(off_t start
) {
515 static off_t pagemask
= 0;
517 long pagesize
= sysconf(_SC_PAGESIZE
);
518 if (-1 == pagesize
) pagesize
= 4096;
519 pagemask
= ~((off_t
)pagesize
- 1); /* pagesize always power-of-2 */
521 return (start
& pagemask
);
524 /* returns: 0: continue, -1: fatal error, -2: connection reset */
525 /* similar to network_write_file_chunk_mmap, but doesn't use send on windows (because we're on pipes),
526 * also mmaps and sends complete chunk instead of only small parts - the files
527 * are supposed to be temp files with reasonable chunk sizes.
529 * Also always use mmap; the files are "trusted", as we created them.
531 static ssize_t
cgi_write_file_chunk_mmap(server
*srv
, connection
*con
, int fd
, chunkqueue
*cq
) {
532 chunk
* const c
= cq
->first
;
533 off_t offset
, toSend
, file_end
;
535 size_t mmap_offset
, mmap_avail
;
538 force_assert(NULL
!= c
);
539 force_assert(FILE_CHUNK
== c
->type
);
540 force_assert(c
->offset
>= 0 && c
->offset
<= c
->file
.length
);
542 offset
= c
->file
.start
+ c
->offset
;
543 toSend
= c
->file
.length
- c
->offset
;
544 file_end
= c
->file
.start
+ c
->file
.length
; /* offset to file end in this chunk */
547 chunkqueue_remove_finished_chunks(cq
);
551 /*(simplified from chunk.c:chunkqueue_open_file_chunk())*/
553 if (-1 == c
->file
.fd
) {
554 if (-1 == (c
->file
.fd
= fdevent_open_cloexec(c
->mem
->ptr
, con
->conf
.follow_symlink
, O_RDONLY
, 0))) {
555 log_error_write(srv
, __FILE__
, __LINE__
, "ssb", "open failed:", strerror(errno
), c
->mem
);
560 /* (re)mmap the buffer if range is not covered completely */
561 if (MAP_FAILED
== c
->file
.mmap
.start
562 || offset
< c
->file
.mmap
.offset
563 || file_end
> (off_t
)(c
->file
.mmap
.offset
+ c
->file
.mmap
.length
)) {
565 if (MAP_FAILED
!= c
->file
.mmap
.start
) {
566 munmap(c
->file
.mmap
.start
, c
->file
.mmap
.length
);
567 c
->file
.mmap
.start
= MAP_FAILED
;
570 c
->file
.mmap
.offset
= mmap_align_offset(offset
);
571 c
->file
.mmap
.length
= file_end
- c
->file
.mmap
.offset
;
573 if (MAP_FAILED
== (c
->file
.mmap
.start
= mmap(NULL
, c
->file
.mmap
.length
, PROT_READ
, MAP_PRIVATE
, c
->file
.fd
, c
->file
.mmap
.offset
))) {
574 if (toSend
> 65536) toSend
= 65536;
575 data
= malloc(toSend
);
577 if (-1 == lseek(c
->file
.fd
, offset
, SEEK_SET
)
578 || 0 >= (toSend
= read(c
->file
.fd
, data
, toSend
))) {
580 log_error_write(srv
, __FILE__
, __LINE__
, "ssbdo", "lseek/read failed:",
581 strerror(errno
), c
->mem
, c
->file
.fd
, offset
);
582 } else { /*(0 == toSend)*/
583 log_error_write(srv
, __FILE__
, __LINE__
, "sbdo", "unexpected EOF (input truncated?):",
584 c
->mem
, c
->file
.fd
, offset
);
592 if (MAP_FAILED
!= c
->file
.mmap
.start
) {
593 force_assert(offset
>= c
->file
.mmap
.offset
);
594 mmap_offset
= offset
- c
->file
.mmap
.offset
;
595 force_assert(c
->file
.mmap
.length
> mmap_offset
);
596 mmap_avail
= c
->file
.mmap
.length
- mmap_offset
;
597 force_assert(toSend
<= (off_t
) mmap_avail
);
599 data
= c
->file
.mmap
.start
+ mmap_offset
;
602 r
= write(fd
, data
, toSend
);
604 if (MAP_FAILED
== c
->file
.mmap
.start
) free(data
);
615 log_error_write(srv
, __FILE__
, __LINE__
, "ssd",
616 "write failed:", strerror(errno
), fd
);
621 chunkqueue_mark_written(cq
, r
);
625 static int cgi_write_request(server
*srv
, handler_ctx
*hctx
, int fd
) {
626 connection
*con
= hctx
->remote_conn
;
627 chunkqueue
*cq
= con
->request_content_queue
;
630 /* old comment: windows doesn't support select() on pipes - wouldn't be easy to fix for all platforms.
631 * solution: if this is still a problem on windows, then substitute
632 * socketpair() for pipe() and closesocket() for close() on windows.
635 for (c
= cq
->first
; c
; c
= cq
->first
) {
640 r
= cgi_write_file_chunk_mmap(srv
, con
, fd
, cq
);
644 if ((r
= write(fd
, c
->mem
->ptr
+ c
->offset
, buffer_string_length(c
->mem
) - c
->offset
)) < 0) {
648 /* ignore and try again */
653 /* connection closed */
658 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "write failed due to: ", strerror(errno
));
663 chunkqueue_mark_written(cq
, r
);
668 if (0 == r
) break; /*(might block)*/
675 /* connection reset */
676 log_error_write(srv
, __FILE__
, __LINE__
, "s", "failed to send post data to cgi, connection closed by CGI");
677 /* skip all remaining data */
678 chunkqueue_mark_written(cq
, chunkqueue_length(cq
));
685 if (cq
->bytes_out
== (off_t
)con
->request
.content_length
&& !hctx
->conf
.upgrade
) {
686 /* sent all request body input */
687 /* close connection to the cgi-script */
688 if (-1 == hctx
->fdtocgi
) { /*(received request body sent in initial send to pipe buffer)*/
691 log_error_write(srv
, __FILE__
, __LINE__
, "sds", "cgi stdin close failed ", fd
, strerror(errno
));
694 cgi_connection_close_fdtocgi(srv
, hctx
); /*(closes only hctx->fdtocgi)*/
697 off_t cqlen
= cq
->bytes_in
- cq
->bytes_out
;
698 if (cq
->bytes_in
!= con
->request
.content_length
&& cqlen
< 65536 - 16384) {
699 /*(con->conf.stream_request_body & FDEVENT_STREAM_REQUEST)*/
700 if (!(con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST_POLLIN
)) {
701 con
->conf
.stream_request_body
|= FDEVENT_STREAM_REQUEST_POLLIN
;
702 con
->is_readable
= 1; /* trigger optimistic read from client */
705 if (-1 == hctx
->fdtocgi
) { /*(not registered yet)*/
707 hctx
->fdntocgi
= fdevent_register(srv
->ev
, hctx
->fdtocgi
, cgi_handle_fdevent_send
, hctx
);
709 if (0 == cqlen
) { /*(chunkqueue_is_empty(cq))*/
710 if ((fdevent_fdnode_interest(hctx
->fdntocgi
) & FDEVENT_OUT
)) {
711 fdevent_fdnode_event_set(srv
->ev
, hctx
->fdntocgi
, 0);
714 /* more request body remains to be sent to CGI so register for fdevents */
715 fdevent_fdnode_event_set(srv
->ev
, hctx
->fdntocgi
, FDEVENT_OUT
);
722 static struct stat
* cgi_stat(server
*srv
, connection
*con
, buffer
*path
) {
723 /* CGI might be executable even if it is not readable */
724 stat_cache_entry
*sce
;
725 return (HANDLER_ERROR
!= stat_cache_get_entry(srv
, con
, path
, &sce
))
730 static int cgi_create_env(server
*srv
, connection
*con
, plugin_data
*p
, handler_ctx
*hctx
, buffer
*cgi_handler
) {
737 if (!buffer_string_is_empty(cgi_handler
)) {
738 if (NULL
== cgi_stat(srv
, con
, cgi_handler
)) {
739 log_error_write(srv
, __FILE__
, __LINE__
, "sbss",
740 "stat for cgi-handler", cgi_handler
,
741 "failed:", strerror(errno
));
746 if (pipe_cloexec(to_cgi_fds
)) {
747 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "pipe failed:", strerror(errno
));
750 if (pipe_cloexec(from_cgi_fds
)) {
751 close(to_cgi_fds
[0]);
752 close(to_cgi_fds
[1]);
753 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "pipe failed:", strerror(errno
));
756 fdevent_setfd_cloexec(to_cgi_fds
[1]);
757 fdevent_setfd_cloexec(from_cgi_fds
[0]);
761 http_cgi_opts opts
= { 0, 0, NULL
, NULL
};
762 env_accum
*env
= &p
->env
;
766 /* create environment */
768 http_cgi_headers(srv
, con
, &opts
, cgi_env_add
, env
);
771 if (p
->env
.ld_preload
) {
772 cgi_env_add(env
, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p
->env
.ld_preload
));
774 if (p
->env
.ld_library_path
) {
775 cgi_env_add(env
, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p
->env
.ld_library_path
));
778 /* CYGWIN needs SYSTEMROOT */
779 if (p
->env
.systemroot
) {
780 cgi_env_add(env
, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p
->env
.systemroot
));
784 if (env
->esize
<= env
->oused
) {
785 env
->esize
= (env
->oused
+ 1 + 0xf) & ~(0xfuL
);
786 env
->eptr
= realloc(env
->eptr
, env
->esize
* sizeof(*env
->eptr
));
787 force_assert(env
->eptr
);
789 for (i
= 0; i
< env
->oused
; ++i
) {
790 env
->eptr
[i
] = env
->ptr
+ env
->offsets
[i
];
792 env
->eptr
[env
->oused
] = NULL
;
797 if (!buffer_string_is_empty(cgi_handler
)) {
798 args
[i
++] = cgi_handler
->ptr
;
800 args
[i
++] = con
->physical
.path
->ptr
;
804 dfd
= fdevent_open_dirname(con
->physical
.path
->ptr
, con
->conf
.follow_symlink
);
806 log_error_write(srv
, __FILE__
, __LINE__
, "ssb", "open dirname failed:", strerror(errno
), con
->physical
.path
);
809 hctx
->pid
= (dfd
>= 0) ? fdevent_fork_execve(args
[0], args
, p
->env
.eptr
, to_cgi_fds
[0], from_cgi_fds
[1], -1, dfd
) : -1;
811 if (-1 == hctx
->pid
) {
812 /* log error with errno prior to calling close() (might change errno) */
813 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fork failed:", strerror(errno
));
814 if (-1 != dfd
) close(dfd
);
815 close(from_cgi_fds
[0]);
816 close(from_cgi_fds
[1]);
817 close(to_cgi_fds
[0]);
818 close(to_cgi_fds
[1]);
821 if (-1 != dfd
) close(dfd
);
822 close(from_cgi_fds
[1]);
823 close(to_cgi_fds
[0]);
825 hctx
->fd
= from_cgi_fds
[0];
829 cgi_pid_add(p
, hctx
->pid
, hctx
);
831 if (0 == con
->request
.content_length
) {
832 close(to_cgi_fds
[1]);
834 /* there is content to send */
835 if (-1 == fdevent_fcntl_set_nb(srv
->ev
, to_cgi_fds
[1])) {
836 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fcntl failed: ", strerror(errno
));
837 close(to_cgi_fds
[1]);
838 cgi_connection_close(srv
, hctx
);
842 if (0 != cgi_write_request(srv
, hctx
, to_cgi_fds
[1])) {
843 close(to_cgi_fds
[1]);
844 cgi_connection_close(srv
, hctx
);
851 hctx
->fdn
= fdevent_register(srv
->ev
, hctx
->fd
, cgi_handle_fdevent
, hctx
);
852 if (-1 == fdevent_fcntl_set_nb(srv
->ev
, hctx
->fd
)) {
853 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "fcntl failed: ", strerror(errno
));
854 cgi_connection_close(srv
, hctx
);
857 fdevent_fdnode_event_set(srv
->ev
, hctx
->fdn
, FDEVENT_IN
| FDEVENT_RDHUP
);
865 static int mod_cgi_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
867 plugin_config
*s
= p
->config_storage
[0];
870 PATCH(execute_x_only
);
873 PATCH(xsendfile_allow
);
874 PATCH(xsendfile_docroot
);
876 /* skip the first, the global context */
877 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
878 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
879 s
= p
->config_storage
[i
];
881 /* condition didn't match */
882 if (!config_check_cond(srv
, con
, dc
)) continue;
885 for (j
= 0; j
< dc
->value
->used
; j
++) {
886 data_unset
*du
= dc
->value
->data
[j
];
888 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.assign"))) {
890 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.execute-x-only"))) {
891 PATCH(execute_x_only
);
892 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.local-redir"))) {
894 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.upgrade"))) {
896 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.x-sendfile"))) {
897 PATCH(xsendfile_allow
);
898 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("cgi.x-sendfile-docroot"))) {
899 PATCH(xsendfile_docroot
);
908 URIHANDLER_FUNC(cgi_is_handled
) {
909 plugin_data
*p
= p_d
;
913 if (con
->mode
!= DIRECT
) return HANDLER_GO_ON
;
914 if (buffer_is_empty(con
->physical
.path
)) return HANDLER_GO_ON
;
916 mod_cgi_patch_connection(srv
, con
, p
);
918 ds
= (data_string
*)array_match_key_suffix(p
->conf
.cgi
, con
->physical
.path
);
919 if (NULL
== ds
) return HANDLER_GO_ON
;
921 st
= cgi_stat(srv
, con
, con
->physical
.path
);
922 if (NULL
== st
) return HANDLER_GO_ON
;
924 if (!S_ISREG(st
->st_mode
)) return HANDLER_GO_ON
;
925 if (p
->conf
.execute_x_only
== 1 && (st
->st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) == 0) return HANDLER_GO_ON
;
928 handler_ctx
*hctx
= cgi_handler_ctx_init();
929 hctx
->remote_conn
= con
;
930 hctx
->plugin_data
= p
;
931 hctx
->cgi_handler
= ds
->value
;
932 memcpy(&hctx
->conf
, &p
->conf
, sizeof(plugin_config
));
935 && con
->request
.http_version
== HTTP_VERSION_1_1
936 && NULL
!= http_header_request_get(con
, HTTP_HEADER_UPGRADE
, CONST_STR_LEN("Upgrade"));
937 hctx
->opts
.fdfmt
= S_IFIFO
;
938 hctx
->opts
.backend
= BACKEND_CGI
;
939 hctx
->opts
.authorizer
= 0;
940 hctx
->opts
.local_redir
= hctx
->conf
.local_redir
;
941 hctx
->opts
.xsendfile_allow
= hctx
->conf
.xsendfile_allow
;
942 hctx
->opts
.xsendfile_docroot
= hctx
->conf
.xsendfile_docroot
;
943 hctx
->opts
.pdata
= hctx
;
944 hctx
->opts
.headers
= cgi_response_headers
;
945 con
->plugin_ctx
[p
->id
] = hctx
;
949 return HANDLER_GO_ON
;
953 * - HANDLER_GO_ON : not our job
954 * - HANDLER_FINISHED: got response
955 * - HANDLER_WAIT_FOR_EVENT: waiting for response
957 SUBREQUEST_FUNC(mod_cgi_handle_subrequest
) {
958 plugin_data
*p
= p_d
;
959 handler_ctx
*hctx
= con
->plugin_ctx
[p
->id
];
960 chunkqueue
*cq
= con
->request_content_queue
;
962 if (con
->mode
!= p
->id
) return HANDLER_GO_ON
;
963 if (NULL
== hctx
) return HANDLER_GO_ON
;
965 if ((con
->conf
.stream_response_body
& FDEVENT_STREAM_RESPONSE_BUFMIN
)
966 && con
->file_started
) {
967 if (chunkqueue_length(con
->write_queue
) > 65536 - 4096) {
968 fdevent_fdnode_event_clr(srv
->ev
,hctx
->fdn
,FDEVENT_IN
);
969 } else if (!(fdevent_fdnode_interest(hctx
->fdn
) & FDEVENT_IN
)) {
970 /* optimistic read from backend */
971 handler_t rc
= cgi_recv_response(srv
, hctx
); /*(might invalidate hctx)*/
972 if (rc
!= HANDLER_GO_ON
) return rc
; /*(unless HANDLER_GO_ON)*/
973 fdevent_fdnode_event_add(srv
->ev
, hctx
->fdn
, FDEVENT_IN
);
977 if (cq
->bytes_in
!= (off_t
)con
->request
.content_length
) {
978 /*(64k - 4k to attempt to avoid temporary files
979 * in conjunction with FDEVENT_STREAM_REQUEST_BUFMIN)*/
980 if (cq
->bytes_in
- cq
->bytes_out
> 65536 - 4096
981 && (con
->conf
.stream_request_body
& FDEVENT_STREAM_REQUEST_BUFMIN
)){
982 con
->conf
.stream_request_body
&= ~FDEVENT_STREAM_REQUEST_POLLIN
;
983 if (-1 != hctx
->fd
) return HANDLER_WAIT_FOR_EVENT
;
985 handler_t r
= connection_handle_read_post_state(srv
, con
);
986 if (!chunkqueue_is_empty(cq
)) {
987 if (fdevent_fdnode_interest(hctx
->fdntocgi
) & FDEVENT_OUT
) {
988 return (r
== HANDLER_GO_ON
) ? HANDLER_WAIT_FOR_EVENT
: r
;
991 if (r
!= HANDLER_GO_ON
) return r
;
993 /* CGI environment requires that Content-Length be set.
994 * Send 411 Length Required if Content-Length missing.
995 * (occurs here if client sends Transfer-Encoding: chunked
996 * and module is flagged to stream request body to backend) */
997 if (-1 == con
->request
.content_length
) {
998 return connection_handle_read_post_error(srv
, con
, 411);
1003 if (-1 == hctx
->fd
) {
1004 if (cgi_create_env(srv
, con
, p
, hctx
, hctx
->cgi_handler
)) {
1005 con
->http_status
= 500;
1008 return HANDLER_FINISHED
;
1010 } else if (!chunkqueue_is_empty(con
->request_content_queue
)) {
1011 if (0 != cgi_write_request(srv
, hctx
, hctx
->fdtocgi
)) {
1012 cgi_connection_close(srv
, hctx
);
1013 return HANDLER_ERROR
;
1017 /* if not done, wait for CGI to close stdout, so we read EOF on pipe */
1018 return HANDLER_WAIT_FOR_EVENT
;
1022 static handler_t
cgi_waitpid_cb(server
*srv
, void *p_d
, pid_t pid
, int status
) {
1023 plugin_data
*p
= (plugin_data
*)p_d
;
1024 for (size_t i
= 0; i
< p
->cgi_pid
.used
; ++i
) {
1026 if (pid
!= p
->cgi_pid
.ptr
[i
].pid
) continue;
1028 hctx
= (handler_ctx
*)p
->cgi_pid
.ptr
[i
].ctx
;
1029 if (hctx
) hctx
->pid
= -1;
1032 if (WIFEXITED(status
)) {
1033 /* (skip logging (non-zero) CGI exit; might be very noisy) */
1035 else if (WIFSIGNALED(status
)) {
1036 /* ignore SIGTERM if sent by cgi_connection_close() (NULL == hctx)*/
1037 if (WTERMSIG(status
) != SIGTERM
|| NULL
!= hctx
) {
1038 log_error_write(srv
, __FILE__
, __LINE__
, "sdsd", "CGI pid", pid
,
1039 "died with signal", WTERMSIG(status
));
1043 log_error_write(srv
, __FILE__
, __LINE__
, "sds",
1044 "CGI pid", pid
, "ended unexpectedly");
1047 return HANDLER_FINISHED
;
1050 return HANDLER_GO_ON
;
1054 int mod_cgi_plugin_init(plugin
*p
);
1055 int mod_cgi_plugin_init(plugin
*p
) {
1056 p
->version
= LIGHTTPD_VERSION_ID
;
1057 p
->name
= buffer_init_string("cgi");
1059 p
->connection_reset
= cgi_connection_close_callback
;
1060 p
->handle_subrequest_start
= cgi_is_handled
;
1061 p
->handle_subrequest
= mod_cgi_handle_subrequest
;
1062 p
->handle_waitpid
= cgi_waitpid_cb
;
1063 p
->init
= mod_cgi_init
;
1064 p
->cleanup
= mod_cgi_free
;
1065 p
->set_defaults
= mod_fastcgi_set_defaults
;