static handler_t cgi_handle_fdevent_send (server *srv, void *ctx, int revents) { handler_ctx *hctx = ctx; connection *con = hctx->remote_conn; /*(joblist only actually necessary here in mod_cgi fdevent send if returning HANDLER_ERROR)*/ joblist_append(srv, con); if (revents & FDEVENT_OUT) { if (0 != cgi_write_request(srv, hctx, hctx->fdtocgi)) { cgi_connection_close(srv, hctx); return HANDLER_ERROR; } /* more request body to be sent to CGI */ } if (revents & FDEVENT_HUP) { /* skip sending remaining data to CGI */ if (con->request.content_length) { chunkqueue *cq = con->request_content_queue; chunkqueue_mark_written(cq, chunkqueue_length(cq)); if (cq->bytes_in != (off_t)con->request.content_length) { con->keep_alive = 0; } } cgi_connection_close_fdtocgi(srv, hctx); /*(closes only hctx->fdtocgi)*/ } else if (revents & FDEVENT_ERR) { /* kill all connections to the cgi process */ #if 1 log_error_write(srv, __FILE__, __LINE__, "s", "cgi-FDEVENT_ERR"); #endif cgi_connection_close(srv, hctx); return HANDLER_ERROR; } return HANDLER_FINISHED; }
static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ctx *hctx, buffer *cgi_handler) { pid_t pid; int to_cgi_fds[2]; int from_cgi_fds[2]; struct stat st; UNUSED(p); #ifndef __WIN32 if (!buffer_string_is_empty(cgi_handler)) { /* stat the exec file */ if (-1 == (stat(cgi_handler->ptr, &st))) { 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; } /* fork, execve */ switch (pid = fork()) { case 0: { /* child */ char **args; int argc; int i = 0; char_array env; char *c; const char *s; http_cgi_opts opts = { 0, 0, NULL, NULL }; /* move stdout to from_cgi_fd[1] */ dup2(from_cgi_fds[1], STDOUT_FILENO); #ifndef FD_CLOEXEC close(from_cgi_fds[1]); /* not needed */ close(from_cgi_fds[0]); #endif /* move the stdin to to_cgi_fd[0] */ dup2(to_cgi_fds[0], STDIN_FILENO); #ifndef FD_CLOEXEC close(to_cgi_fds[0]); /* not needed */ close(to_cgi_fds[1]); #endif /* create environment */ env.ptr = NULL; env.size = 0; env.used = 0; http_cgi_headers(srv, con, &opts, cgi_env_add, &env); /* for valgrind */ if (NULL != (s = getenv("LD_PRELOAD"))) { cgi_env_add(&env, CONST_STR_LEN("LD_PRELOAD"), s, strlen(s)); } if (NULL != (s = getenv("LD_LIBRARY_PATH"))) { cgi_env_add(&env, CONST_STR_LEN("LD_LIBRARY_PATH"), s, strlen(s)); } #ifdef __CYGWIN__ /* CYGWIN needs SYSTEMROOT */ if (NULL != (s = getenv("SYSTEMROOT"))) { cgi_env_add(&env, CONST_STR_LEN("SYSTEMROOT"), s, strlen(s)); } #endif if (env.size == env.used) { env.size += 16; env.ptr = realloc(env.ptr, env.size * sizeof(*env.ptr)); } env.ptr[env.used] = NULL; /* set up args */ argc = 3; args = malloc(sizeof(*args) * argc); force_assert(args); i = 0; if (!buffer_string_is_empty(cgi_handler)) { args[i++] = cgi_handler->ptr; } args[i++] = con->physical.path->ptr; args[i ] = NULL; /* search for the last / */ if (NULL != (c = strrchr(con->physical.path->ptr, '/'))) { /* handle special case of file in root directory */ const char* physdir = (c == con->physical.path->ptr) ? "/" : con->physical.path->ptr; /* temporarily shorten con->physical.path to directory without terminating '/' */ *c = '\0'; /* change to the physical directory */ if (-1 == chdir(physdir)) { log_error_write(srv, __FILE__, __LINE__, "ssb", "chdir failed:", strerror(errno), con->physical.path); } *c = '/'; } /* we don't need the client socket */ for (i = 3; i < 256; i++) { if (i != srv->errorlog_fd) close(i); } /* exec the cgi */ execve(args[0], args, env.ptr); /* most log files may have been closed/redirected by this point, * though stderr might still point to lighttpd.breakage.log */ perror(args[0]); _exit(1); } case -1: /* error */ log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed:", strerror(errno)); close(from_cgi_fds[0]); close(from_cgi_fds[1]); close(to_cgi_fds[0]); close(to_cgi_fds[1]); return -1; default: { /* parent process */ close(from_cgi_fds[1]); close(to_cgi_fds[0]); /* register PID and wait for them asynchronously */ hctx->pid = pid; hctx->fd = from_cgi_fds[0]; hctx->fde_ndx = -1; ++srv->cur_fds; 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; } 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_event_set(srv->ev, &(hctx->fde_ndx), hctx->fd, FDEVENT_IN); break; } } return 0; #else return -1; #endif }
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; } }