static handler_t cgi_connection_close(server *srv, handler_ctx *hctx) { int status; pid_t pid; plugin_data *p; connection *con; if (NULL == hctx) return HANDLER_GO_ON; p = hctx->plugin_data; con = hctx->remote_conn; if (con->mode != p->id) return HANDLER_GO_ON; #ifndef __WIN32 /* the connection to the browser went away, but we still have a connection * to the CGI script * * close cgi-connection */ if (hctx->fd != -1) { /* close connection to the cgi-script */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); if (close(hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "sds", "cgi close failed ", hctx->fd, strerror(errno)); } hctx->fd = -1; hctx->fde_ndx = -1; } pid = hctx->pid; con->plugin_ctx[p->id] = NULL; /* is this a good idea ? */ cgi_handler_ctx_free(hctx); /* if waitpid hasn't been called by response.c yet, do it here */ if (pid) { /* check if the CGI-script is already gone */ switch(waitpid(pid, &status, WNOHANG)) { case 0: /* not finished yet */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid); #endif break; case -1: /* */ if (errno == EINTR) break; /* * errno == ECHILD happens if _subrequest catches the process-status before * we have read the response of the cgi process * * -> catch status * -> WAIT_FOR_EVENT * -> read response * -> we get here with waitpid == ECHILD * */ if (errno == ECHILD) return HANDLER_GO_ON; log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); return HANDLER_ERROR; default: /* Send an error if we haven't sent any data yet */ if (0 == con->file_started) { connection_set_state(srv, con, CON_STATE_HANDLE_REQUEST); con->http_status = 500; con->mode = DIRECT; } if (WIFEXITED(status)) { #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); #endif pid = 0; return HANDLER_GO_ON; } else { log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); pid = 0; return HANDLER_GO_ON; } } kill(pid, SIGTERM); /* cgi-script is still alive, queue the PID for removal */ cgi_pid_add(srv, p, pid); } #endif return HANDLER_GO_ON; }
static void cgi_connection_close(server *srv, handler_ctx *hctx) { int status; pid_t pid; plugin_data *p = hctx->plugin_data; connection *con = hctx->remote_conn; #ifndef __WIN32 /* the connection to the browser went away, but we still have a connection * to the CGI script * * close cgi-connection */ if (hctx->fd != -1) { /* close connection to the cgi-script */ fdevent_event_del(srv->ev, &(hctx->fde_ndx), hctx->fd); fdevent_unregister(srv->ev, hctx->fd); fdevent_sched_close(srv->ev, hctx->fd, 0); } if (hctx->fdtocgi != -1) { cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ } pid = hctx->pid; con->plugin_ctx[p->id] = NULL; cgi_handler_ctx_free(hctx); /* if waitpid hasn't been called by response.c yet, do it here */ if (pid) { /* check if the CGI-script is already gone */ switch(waitpid(pid, &status, WNOHANG)) { case 0: /* not finished yet */ #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) child isn't done yet, pid:", pid); #endif break; case -1: /* */ if (errno == EINTR) break; /* * errno == ECHILD happens if _subrequest catches the process-status before * we have read the response of the cgi process * * -> catch status * -> WAIT_FOR_EVENT * -> read response * -> we get here with waitpid == ECHILD * */ if (errno != ECHILD) { log_error_write(srv, __FILE__, __LINE__, "ss", "waitpid failed: ", strerror(errno)); } /* anyway: don't wait for it anymore */ pid = 0; break; default: if (WIFEXITED(status)) { #if 0 log_error_write(srv, __FILE__, __LINE__, "sd", "(debug) cgi exited fine, pid:", pid); #endif } else { log_error_write(srv, __FILE__, __LINE__, "sd", "cgi died, pid:", pid); } pid = 0; break; } if (pid) { kill(pid, SIGTERM); /* cgi-script is still alive, queue the PID for removal */ cgi_pid_add(srv, p, pid); } } #endif /* finish response (if not already con->file_started, con->file_finished) */ if (con->mode == p->id) { http_response_backend_done(srv, con); } }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { char *args[3]; int to_cgi_fds[2]; int from_cgi_fds[2]; int dfd = -1; UNUSED(p); if (!buffer_string_is_empty(cgi_handler)) { if (NULL == cgi_stat(srv, con, cgi_handler)) { log_error_write(srv, __FILE__, __LINE__, "sbss", "stat for cgi-handler", cgi_handler, "failed:", strerror(errno)); return -1; } } if (pipe_cloexec(to_cgi_fds)) { log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } if (pipe_cloexec(from_cgi_fds)) { close(to_cgi_fds[0]); close(to_cgi_fds[1]); log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed:", strerror(errno)); return -1; } fdevent_setfd_cloexec(to_cgi_fds[1]); fdevent_setfd_cloexec(from_cgi_fds[0]); { size_t i = 0; http_cgi_opts opts = { 0, 0, NULL, NULL }; env_accum *env = &p->env; env->used = 0; env->oused = 0; /* create environment */ http_cgi_headers(srv, con, &opts, cgi_env_add, env); /* for valgrind */ if (p->env.ld_preload) { cgi_env_add(env, CONST_STR_LEN("LD_PRELOAD"), CONST_BUF_LEN(p->env.ld_preload)); } if (p->env.ld_library_path) { cgi_env_add(env, CONST_STR_LEN("LD_LIBRARY_PATH"), CONST_BUF_LEN(p->env.ld_library_path)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (p->env.systemroot) { cgi_env_add(env, CONST_STR_LEN("SYSTEMROOT"), CONST_BUF_LEN(p->env.systemroot)); } #endif if (env->esize <= env->oused) { env->esize = (env->oused + 1 + 0xf) & ~(0xfuL); env->eptr = realloc(env->eptr, env->esize * sizeof(*env->eptr)); force_assert(env->eptr); } for (i = 0; i < env->oused; ++i) { env->eptr[i] = env->ptr + env->offsets[i]; } env->eptr[env->oused] = NULL; /* set up args */ i = 0; if (!buffer_string_is_empty(cgi_handler)) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i ] = NULL; } dfd = fdevent_open_dirname(con->physical.path->ptr, con->conf.follow_symlink); if (-1 == dfd) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); } hctx->pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, p->env.eptr, to_cgi_fds[0], from_cgi_fds[1], -1, dfd) : -1; if (-1 == hctx->pid) { /* log error with errno prior to calling close() (might change errno) */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); if (-1 != dfd) close(dfd); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; } else { if (-1 != dfd) close(dfd); close(from_cgi_fds[1]); close(to_cgi_fds[0]); hctx->fd = from_cgi_fds[0]; ++srv->cur_fds; cgi_pid_add(p, hctx->pid, hctx); if (0 == con->request.content_length) { close(to_cgi_fds[1]); } else { /* there is content to send */ if (-1 == fdevent_fcntl_set_nb(srv->ev, to_cgi_fds[1])) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } if (0 != cgi_write_request(srv, hctx, to_cgi_fds[1])) { close(to_cgi_fds[1]); cgi_connection_close(srv, hctx); return -1; } ++srv->cur_fds; } hctx->fdn = fdevent_register(srv->ev, hctx->fd, cgi_handle_fdevent, hctx); if (-1 == fdevent_fcntl_set_nb(srv->ev, hctx->fd)) { log_error_write(srv, __FILE__, __LINE__, "ss", "fcntl failed: ", strerror(errno)); cgi_connection_close(srv, hctx); return -1; } fdevent_fdnode_event_set(srv->ev, hctx->fdn, FDEVENT_IN | FDEVENT_RDHUP); return 0; } }