ret_t cherokee_handler_tmi_init (cherokee_handler_tmi_t *hdl) { ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(hdl); cherokee_buffer_t *tmp = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_handler_tmi_props_t *props = HANDLER_TMI_PROPS(hdl); /* We are going to look for gzipped encoding */ cherokee_buffer_clean (tmp); ret = cherokee_header_copy_known (&conn->header, header_content_encoding, tmp); if (ret == ret_ok && cherokee_buffer_cmp_str(tmp, "gzip") == 0) { TRACE(ENTRIES, "ZeroMQ: incomming header '%s'\n", tmp->buf); hdl->inflated = true; } else { cherokee_buffer_clean (tmp); ret = cherokee_header_copy_known (&conn->header, header_content_type, tmp); if (ret == ret_ok && (cherokee_buffer_cmp_str(tmp, "application/gzip") == 0 || cherokee_buffer_cmp_str(tmp, "application/zip") == 0)) { TRACE(ENTRIES, "ZeroMQ: incomming header '%s'\n", tmp->buf); hdl->inflated = true; } else { hdl->inflated = false; } } #ifdef LIBXML_PUSH_ENABLED if (props->validate_xml) { hdl->validate_xml = true; hdl->ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL); xmlCtxtUseOptions(hdl->ctxt, XML_PARSE_NOERROR | XML_PARSE_NOWARNING | XML_PARSE_NONET | XML_PARSE_COMPACT); if (hdl->inflated) { /* allocate inflate state */ hdl->strm.zalloc = Z_NULL; hdl->strm.zfree = Z_NULL; hdl->strm.opaque = Z_NULL; hdl->strm.avail_in = 0; hdl->strm.next_in = Z_NULL; hdl->z_ret = inflateInit2(&(hdl->strm), 16+MAX_WBITS); if (hdl->z_ret != Z_OK) hdl->validate_xml = false; } } #endif if (!hdl->inflated) { /* If we end up here that means content is plain, lets set up an encoder */ ret = props->encoder_props->instance_func((void **)&hdl->encoder, props->encoder_props); if (unlikely (ret != ret_ok)) { return ret_error; } ret = cherokee_encoder_init (hdl->encoder, conn); if (unlikely (ret != ret_ok)) { return ret_error; } } return ret_ok; }
ret_t cherokee_handler_dbslayer_init (cherokee_handler_dbslayer_t *hdl) { ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(hdl); cherokee_handler_dbslayer_props_t *props = HANDLER_DBSLAYER_PROPS(hdl); /* Check client headers */ cherokee_client_headers (hdl); /* Get a reference to the target host */ if (hdl->src_ref == NULL) { ret = cherokee_balancer_dispatch (props->balancer, conn, &hdl->src_ref); if (ret != ret_ok) return ret; } /* Connect to the MySQL server */ ret = connect_to_database(hdl); if (unlikely (ret != ret_ok)) return ret; /* Send query: * Do not check whether it failed, ::step() will do * it and send an error message if needed. */ send_query(hdl); return ret_ok; }
static ret_t send_query (cherokee_handler_dbslayer_t *hdl) { int re; cuint_t len; cherokee_connection_t *conn = HANDLER_CONN(hdl); cherokee_buffer_t *tmp = &HANDLER_THREAD(hdl)->tmp_buf1; /* Extract the SQL query */ if ((cherokee_buffer_is_empty (&conn->web_directory)) || (cherokee_buffer_is_ending (&conn->web_directory, '/'))) { len = conn->web_directory.len; } else { len = conn->web_directory.len + 1; } cherokee_buffer_clean (tmp); cherokee_buffer_add (tmp, conn->request.buf + len, conn->request.len - len); cherokee_buffer_unescape_uri (tmp); /* Send the query */ re = mysql_real_query (hdl->conn, tmp->buf, tmp->len); if (re != 0) return ret_error; return ret_ok; }
ret_t cherokee_handler_ssi_init (cherokee_handler_ssi_t *hdl) { ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* Build the local directory */ cherokee_buffer_add_buffer (&hdl->dir, &conn->local_directory); cherokee_buffer_add_buffer (&hdl->dir, &conn->request); while (true) { if (cherokee_buffer_is_empty (&hdl->dir)) return ret_error; if (cherokee_buffer_is_ending (&hdl->dir, '/')) break; cherokee_buffer_drop_ending (&hdl->dir, 1); } /* Real init function */ cherokee_buffer_add_buffer (&conn->local_directory, &conn->request); ret = init (hdl, &conn->local_directory); cherokee_buffer_drop_ending (&conn->local_directory, conn->request.len); return ret; }
static ret_t fork_and_execute_cgi_unix (cherokee_handler_cgi_t *cgi) { int re; int pid; cherokee_connection_t *conn = HANDLER_CONN(cgi); struct { int cgi[2]; int server[2]; } pipes; /* Creates the pipes ... */ re = pipe (pipes.cgi); re |= pipe (pipes.server); if (re != 0) { conn->error_code = http_internal_error; return ret_error; } /* .. and fork the process */ pid = fork(); if (pid == 0) { /* CGI process */ manage_child_cgi_process (cgi, pipes.cgi, pipes.server); } else if (pid < 0) { /* Error */ cherokee_fd_close (pipes.cgi[0]); cherokee_fd_close (pipes.cgi[1]); cherokee_fd_close (pipes.server[0]); cherokee_fd_close (pipes.server[1]); conn->error_code = http_internal_error; return ret_error; } TRACE (ENTRIES, "pid %d\n", pid); cherokee_fd_close (pipes.server[0]); cherokee_fd_close (pipes.cgi[1]); cgi->pid = pid; cgi->pipeInput = pipes.cgi[0]; cgi->pipeOutput = pipes.server[1]; /* Set to Input to NON-BLOCKING */ _fd_set_properties (cgi->pipeInput, O_NDELAY|O_NONBLOCK, 0); return ret_ok; }
ret_t cherokee_handler_admin_add_headers (cherokee_handler_admin_t *hdl, cherokee_buffer_t *buffer) { cherokee_connection_t *conn = HANDLER_CONN(hdl); if (cherokee_connection_should_include_length(conn)) { HANDLER(hdl)->support = hsupport_length; cherokee_buffer_add_va (buffer, "Content-Length: %lu" CRLF, hdl->reply.len); } return ret_ok; }
ret_t cherokee_handler_cgi_read_post (cherokee_handler_cgi_t *cgi) { ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(cgi); cherokee_socket_status_t blocking = socket_closed; cherokee_boolean_t did_IO = false; if (! conn->post.has_info) { return ret_ok; } ret = cherokee_post_send_to_fd (&conn->post, &conn->socket, cgi->pipeOutput, NULL, &blocking, &did_IO); if (did_IO) { cherokee_connection_update_timeout (conn); } switch (ret) { case ret_ok: break; case ret_eagain: if (blocking == socket_writing) { cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi), conn, cgi->pipeOutput, FDPOLL_MODE_WRITE, false); return ret_deny; } /* ret_eagain - Block on read * ret_deny - Block on back-end write */ if (cherokee_post_has_buffered_info (&conn->post)) { return ret_deny; } return ret_eagain; default: return ret; } TRACE (ENTRIES",post", "%s\n", "finished"); cherokee_fd_close (cgi->pipeOutput); cgi->pipeOutput = -1; return ret_ok; }
ret_t cherokee_handler_error_init (cherokee_handler_error_t *hdl) { ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* If needed then generate the error web page. * Some HTTP response codes should not include body * because it's forbidden by the RFC. */ if (http_code_with_body (conn->error_code)) { ret = build_hardcoded_response_page (conn, &hdl->content); if (unlikely(ret < ret_ok)) return ret; } return ret_ok; }
ret_t cherokee_handler_cgi_init (cherokee_handler_cgi_t *cgi) { ret_t ret; cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(cgi); cherokee_connection_t *conn = HANDLER_CONN(cgi); switch (cgi_base->init_phase) { case hcgi_phase_build_headers: /* Extracts PATH_INFO and filename from request uri */ if (cherokee_buffer_is_empty (&cgi_base->executable)) { ret = cherokee_handler_cgi_base_extract_path (cgi_base, true); if (unlikely (ret < ret_ok)) { return ret; } } /* It has to update the timeout of the connection, * otherwhise the server will drop it for the CGI * isn't fast enough */ conn->timeout = cherokee_bogonow_now + CGI_TIMEOUT; cgi_base->init_phase = hcgi_phase_connect; case hcgi_phase_connect: /* Launch the CGI */ ret = fork_and_execute_cgi_via_spawner(cgi); if (unlikely (ret != ret_ok)) { ret = fork_and_execute_cgi(cgi); if (unlikely (ret != ret_ok)) { return ret; } } default: break; } TRACE (ENTRIES, "finishing %s\n", "ret_ok"); return ret_ok; }
static void cherokee_client_headers (cherokee_handler_dbslayer_t *hdl) { ret_t ret; char *hdr = NULL; cuint_t len = 0; cherokee_connection_t *conn = HANDLER_CONN(hdl); ret = cherokee_header_get_unknown (&conn->header, "X-Beautify", 10, &hdr, &len); if ((ret == ret_ok) && hdr) { hdl->writer.pretty = !! atoi(hdr); } hdr = NULL; ret = cherokee_header_get_unknown (&conn->header, "X-Rollback", 10, &hdr, &len); if ((ret == ret_ok) && hdr) { hdl->rollback = !! atoi(hdr); } }
static ret_t read_from_cgi (cherokee_handler_cgi_base_t *cgi_base, cherokee_buffer_t *buffer) { ret_t ret; size_t read_ = 0; cherokee_handler_cgi_t *cgi = HDL_CGI(cgi_base); /* Sanity check: pipe() accessed */ if (unlikely (cgi->pipeInput < 0)) return ret_eof; /* Read the data from the pipe: */ ret = cherokee_buffer_read_from_fd (buffer, cgi->pipeInput, 4096, &read_); TRACE (ENTRIES, "read... ret=%d %d\n", ret, read_); switch (ret) { case ret_eagain: cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi), HANDLER_CONN(cgi), cgi->pipeInput, FDPOLL_MODE_READ, false); return ret_eagain; case ret_ok: TRACE (ENTRIES, "%d bytes read\n", read_); return ret_ok; case ret_eof: case ret_error: cgi_base->got_eof = true; return ret; default: RET_UNKNOWN(ret); } SHOULDNT_HAPPEN; return ret_error; }
static ret_t read_from_fcgi (cherokee_handler_cgi_base_t *cgi, cherokee_buffer_t *buffer) { ret_t ret; size_t read = 0; cherokee_handler_fcgi_t *fcgi = HDL_FCGI(cgi); ret = cherokee_socket_bufread (&fcgi->socket, &fcgi->write_buffer, DEFAULT_READ_SIZE, &read); switch (ret) { case ret_eagain: ret = cherokee_thread_deactive_to_polling (HANDLER_THREAD(cgi), HANDLER_CONN(cgi), fcgi->socket.socket, FDPOLL_MODE_READ, false); if (unlikely (ret != ret_ok)) { cgi->got_eof = true; return ret_error; } return ret_eagain; case ret_ok: ret = process_buffer (fcgi, &fcgi->write_buffer, buffer); TRACE (ENTRIES, "%d bytes read, buffer.len %d\n", read, buffer->len); if ((ret == ret_ok) && cgi->got_eof && (buffer->len > 0)) { return ret_eof_have_data; } return ret; case ret_eof: case ret_error: cgi->got_eof = true; return ret; default: RET_UNKNOWN(ret); } SHOULDNT_HAPPEN; return ret_error; }
ret_t cherokee_handler_admin_init (cherokee_handler_admin_t *hdl) { cherokee_connection_t *conn = HANDLER_CONN(hdl); #define finishes_by(s) ((conn->request.len > sizeof(s)-1) && \ (!strncmp (conn->request.buf + conn->request.len - (sizeof(s)-1), s, sizeof(s)-1))) if (finishes_by ("/py")) { hdl->dwriter.lang = dwriter_python; } else if (finishes_by ("/js")) { hdl->dwriter.lang = dwriter_json; } else if (finishes_by ("/php")) { hdl->dwriter.lang = dwriter_php; } else if (finishes_by ("/ruby")) { hdl->dwriter.lang = dwriter_ruby; } #undef finishes_by return ret_ok; }
ret_t cherokee_handler_ssi_add_headers (cherokee_handler_ssi_t *hdl, cherokee_buffer_t *buffer) { ret_t ret; char *ext; cherokee_buffer_t *mime = NULL; cherokee_server_t *srv = HANDLER_SRV(hdl); cherokee_connection_t *conn = HANDLER_CONN(hdl); /* MIME type */ if (srv->mime != NULL) { ext = strrchr (conn->request.buf, '.'); if (ext == NULL) return ret_ok; ret = cherokee_mime_get_by_suffix (srv->mime, ext+1, &hdl->mime); if (ret == ret_ok) { cherokee_mime_entry_get_type (hdl->mime, &mime); cherokee_buffer_add_str (buffer, "Content-Type: "); cherokee_buffer_add_buffer (buffer, mime); cherokee_buffer_add_str (buffer, CRLF); } } /* Length */ if (cherokee_connection_should_include_length(conn)) { HANDLER(hdl)->support = hsupport_length; cherokee_buffer_add_str (buffer, "Content-Length: "); cherokee_buffer_add_ullong10(buffer, (cullong_t) hdl->render.len); cherokee_buffer_add_str (buffer, CRLF); } return ret_ok; }
static ret_t init (cherokee_handler_ssi_t *hdl, cherokee_buffer_t *local_path) { int re; ret_t ret; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* Stat the file */ re = cherokee_stat (local_path->buf, &hdl->cache_info); if (re < 0) { switch (errno) { case ENOENT: conn->error_code = http_not_found; break; case EACCES: conn->error_code = http_access_denied; break; default: conn->error_code = http_internal_error; } return ret_error; } /* Read the file */ ret = cherokee_buffer_read_file (&hdl->source, local_path->buf); if (ret != ret_ok) return ret_error; /* Render */ ret = parse (hdl, &hdl->source, &hdl->render); if (ret != ret_ok) return ret; return ret_ok; }
ret_t cherokee_handler_zeromq_init (cherokee_handler_zeromq_t *hdl) { ret_t ret; cherokee_buffer_t *tmp = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_connection_t *conn = HANDLER_CONN(hdl); cherokee_handler_zeromq_props_t *props = HANDLER_ZEROMQ_PROPS(hdl); /* We are going to look for gzipped encoding */ cherokee_buffer_clean (tmp); ret = cherokee_header_copy_known (&conn->header, header_content_encoding, tmp); if (ret == ret_ok && cherokee_buffer_cmp_str(tmp, "gzip") == 0) { TRACE(ENTRIES, "ZeroMQ: incomming header '%s'\n", tmp->buf); return ret_ok; } else { cherokee_buffer_clean (tmp); ret = cherokee_header_copy_known (&conn->header, header_content_type, tmp); if (ret == ret_ok && cherokee_buffer_cmp_str(tmp, "application/gzip") == 0) { TRACE(ENTRIES, "ZeroMQ: incomming header '%s'\n", tmp->buf); return ret_ok; } } /* If we end up here that means content is plain, lets set up an encoder */ ret = props->encoder_props->instance_func((void **)&hdl->encoder, props->encoder_props); if (unlikely (ret != ret_ok)) { return ret_error; } ret = cherokee_encoder_init (hdl->encoder, conn); /* TODO: this is a great idea for KV78turbo, but being able to configure * the reply (KV6, 15, 17) sounds like a good idea too. */ conn->error_code = http_no_content; return ret_error; }
static ret_t connect_to_database (cherokee_handler_dbslayer_t *hdl) { MYSQL *conn; cherokee_handler_dbslayer_props_t *props = HANDLER_DBSLAYER_PROPS(hdl); cherokee_connection_t *connection = HANDLER_CONN(hdl); conn = mysql_real_connect (hdl->conn, hdl->src_ref->host.buf, props->user.buf, props->password.buf, props->db.buf, hdl->src_ref->port, hdl->src_ref->unix_socket.buf, CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS); if (conn == NULL) { cherokee_balancer_report_fail (props->balancer, connection, hdl->src_ref); connection->error_code = http_bad_gateway; return ret_error; } return ret_ok; }
ret_t cherokee_handler_tmi_read_post (cherokee_handler_tmi_t *hdl) { zmq_msg_t message; int re; ret_t ret; ret_t ret_final; cherokee_buffer_t *post = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_buffer_t *encoded = &HANDLER_THREAD(hdl)->tmp_buf2; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* Check for the post info */ if (! conn->post.has_info) { conn->error_code = http_bad_request; return ret_error; } cherokee_buffer_clean (post); ret = cherokee_post_read (&conn->post, &conn->socket, post); switch (ret) { case ret_ok: cherokee_connection_update_timeout (conn); break; case ret_eagain: ret = cherokee_thread_deactive_to_polling (HANDLER_THREAD(hdl), HANDLER_CONN(hdl), conn->socket.socket, FDPOLL_MODE_READ, false); if (ret != ret_ok) { return ret_error; } else { return ret_eagain; } default: conn->error_code = http_bad_request; return ret_error; } TRACE (ENTRIES, "Post contains: '%s'\n", post->buf); re = cherokee_post_read_finished (&conn->post); ret_final = re ? ret_ok : ret_eagain; cherokee_buffer_clean(encoded); if (hdl->encoder != NULL) { if (ret == ret_ok) { cherokee_encoder_flush(hdl->encoder, post, encoded); } else { cherokee_encoder_encode(hdl->encoder, post, encoded); } } else { encoded = post; } cherokee_buffer_add_buffer(&hdl->output, post); if (ret_final == ret_ok) { cherokee_buffer_t *tmp = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_handler_tmi_props_t *props = HANDLER_TMI_PROPS(hdl); zmq_msg_t envelope; zmq_msg_t message; cuint_t len; if ((cherokee_buffer_is_empty (&conn->web_directory)) || (cherokee_buffer_is_ending (&conn->web_directory, '/'))) { len = conn->web_directory.len; } else { len = conn->web_directory.len + 1; } cherokee_buffer_clean (tmp); cherokee_buffer_add (tmp, conn->request.buf + len, conn->request.len - len); TRACE(ENTRIES, "ZeroMQ: incomming path '%s'\n", tmp->buf); zmq_msg_init_size (&envelope, tmp->len); memcpy (zmq_msg_data (&envelope), tmp->buf, tmp->len); zmq_msg_init_size (&message, hdl->output.len); memcpy (zmq_msg_data (&message), hdl->output.buf, hdl->output.len); /* Atomic Section */ CHEROKEE_MUTEX_LOCK (&props->mutex); zmq_msg_send (&envelope, props->socket, ZMQ_DONTWAIT | ZMQ_SNDMORE); zmq_msg_send (&message, props->socket, ZMQ_DONTWAIT); CHEROKEE_MUTEX_UNLOCK (&props->mutex); zmq_msg_close (&envelope); zmq_msg_close (&message); #ifdef LIBXML_PUSH_ENABLED if (hdl->validate_xml) { if (hdl->inflated) { hdl->strm.avail_in = hdl->output.len; hdl->strm.next_in = hdl->output.buf; /* run inflate() on input until output buffer not full */ do { #define CHUNK 131072 int have; char out[CHUNK]; hdl->strm.avail_out = CHUNK; hdl->strm.next_out = out; hdl->z_ret = inflate(&(hdl->strm), Z_NO_FLUSH); switch (hdl->z_ret) { case Z_NEED_DICT: hdl->z_ret = Z_DATA_ERROR; /* and fall through */ case Z_DATA_ERROR: case Z_MEM_ERROR: case Z_STREAM_ERROR: hdl->z_ret = Z_STREAM_ERROR; return ret_ok; } have = CHUNK - hdl->strm.avail_out; xmlParseChunk(hdl->ctxt, out, have, 0); } while (hdl->strm.avail_out == 0); } else { xmlParseChunk(hdl->ctxt, hdl->output.buf, hdl->output.len, 0); } } #endif } return ret_final; }
static ret_t fork_and_execute_cgi_via_spawner(cherokee_handler_cgi_t *cgi) { int re; int pid = -1; cherokee_connection_t *conn = HANDLER_CONN(cgi); cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(cgi); ret_t ret; uid_t uid; gid_t gid; cherokee_buffer_t empty = CHEROKEE_BUF_INIT; cherokee_buffer_t username = CHEROKEE_BUF_INIT; cherokee_services_fdmap_t fd_map; cherokee_buffer_t *chdir = NULL; cherokee_buffer_t chdir_backing = CHEROKEE_BUF_INIT; struct passwd ent; char ent_tmp[1024]; struct { int cgi[2]; int server[2]; } pipes; TRACE (ENTRIES, "Trying to create CGI via spawner\n"); if (! cherokee_buffer_is_empty (&conn->effective_directory)) { chdir = &conn->effective_directory; } else { int ofs = cgi_base->executable.len - 1; while (ofs >= 0 && cgi_base->executable.buf[ofs] != '/') { ofs--; } TRACE (ENTRIES, "While building chdir, ofs=%d\n", ofs); if (ofs < 0 || cherokee_buffer_add (&chdir_backing, cgi_base->executable.buf, ofs + 1) != ret_ok) { conn->error_code = http_internal_error; TRACE (ENTRIES, "Failed, cannot build chdir entry\n"); cherokee_buffer_mrproper(&chdir_backing); return ret_error; } chdir = &chdir_backing; } /* Creates the pipes ... */ re = cherokee_pipe (pipes.cgi); re |= cherokee_pipe (pipes.server); if (re != 0) { conn->error_code = http_internal_error; cherokee_buffer_mrproper(&chdir_backing); TRACE (ENTRIES, "Failed, cannot build pipes\n"); return ret_error; } if (HANDLER_CGI_PROPS(cgi_base)->change_user) { struct stat nocache_info; struct stat *info; cherokee_iocache_entry_t *io_entry = NULL; cherokee_server_t *srv = CONN_SRV(conn); cherokee_handler_cgi_base_props_t *props = HANDLER_CGI_BASE_PROPS(cgi); ret = cherokee_io_stat (srv->iocache, &cgi_base->executable, props->use_cache, &nocache_info, &io_entry, &info); if (ret != ret_ok) { info = &nocache_info; nocache_info.st_uid = getuid(); nocache_info.st_gid = getgid(); } uid = info->st_uid; gid = info->st_gid; cherokee_iocache_entry_unref(&io_entry); } else { /* Not changing, so launch as the same uid/gid as the worker */ uid = getuid(); gid = getgid(); } /* Determine the username of the owner of the file */ ret = cherokee_getpwuid(uid, &ent, ent_tmp, sizeof (ent_tmp)); if (ret != ret_ok || cherokee_buffer_add(&username, ent.pw_name, strlen(ent.pw_name) != ret_ok)) { cherokee_fd_close(pipes.cgi[0]); cherokee_fd_close(pipes.cgi[1]); cherokee_fd_close(pipes.server[0]); cherokee_fd_close(pipes.server[1]); conn->error_code = http_internal_error; cherokee_buffer_mrproper(&chdir_backing); cherokee_buffer_mrproper(&username); TRACE (ENTRIES, "Failed, Unable to retrieve username for uid %d\n", uid); return ret_error; } /* Update the environment ready to run */ add_environment (cgi, conn); /* Set up the FD map */ fd_map.fd_in = pipes.server[0]; fd_map.fd_out = pipes.cgi[1]; if ((CONN_VSRV(conn)->error_writer != NULL) && (CONN_VSRV(conn)->error_writer->fd != -1)) { fd_map.fd_err = CONN_VSRV(conn)->error_writer->fd; } else { fd_map.fd_err = fd_map.fd_out; } TRACE (ENTRIES, "Doing Spawn\n"); ret = cherokee_services_client_spawn (&cgi_base->executable, &username, uid, gid, &empty, chdir, false, cgi->envp, CONN_VSRV(conn)->error_writer, &pid, &fd_map); cherokee_buffer_mrproper(&chdir_backing); cherokee_buffer_mrproper(&username); /* Close the client FDs */ cherokee_fd_close (pipes.server[0]); cherokee_fd_close (pipes.cgi[1]); /* Did we fail to try to spawn? */ if (ret != ret_ok) { /* Close the server FDs too */ cherokee_fd_close (pipes.server[1]); cherokee_fd_close (pipes.cgi[0]); TRACE (ENTRIES, "Failed to spawn\n"); return ret; } /* Did we try, but fail, to spawn? */ if (pid == -1) { /* Close the server FDs too */ cherokee_fd_close (pipes.server[1]); cherokee_fd_close (pipes.cgi[0]); TRACE (ENTRIES, "Spawned, but failed server side\n"); return ret_error; } /* Successfully launched */ cgi->pid = pid; cgi->pipeInput = pipes.cgi[0]; cgi->pipeOutput = pipes.server[1]; /* Set to Input to NON-BLOCKING */ _fd_set_properties (cgi->pipeInput, O_NDELAY|O_NONBLOCK, 0); TRACE (ENTRIES, "CGI running, PID=%d\n", pid); return ret_ok; }
ret_t cherokee_handler_error_add_headers (cherokee_handler_error_t *hdl, cherokee_buffer_t *buffer) { cherokee_connection_t *conn = HANDLER_CONN(hdl); /* It has special headers on protocol upgrading */ switch (conn->upgrade) { case http_upgrade_nothing: break; case http_upgrade_tls10: cherokee_buffer_add_str (buffer, "Upgrade: TLS/1.0, HTTP/1.1"CRLF); break; case http_upgrade_http11: cherokee_buffer_add_str (buffer, "Upgrade: HTTP/1.1"CRLF); break; default: SHOULDNT_HAPPEN; } /* 1xx, 204 and 304 (Not Modified) responses have to be managed * by "content" handlers, anyway this test ensures that * it'll never send wrong and unrelated headers in case that * a 1xx, 204 or 304 response is managed by this handler. * 304 responses should only include the * Last-Modified, ETag, Expires and Cache-Control headers. */ if (!http_code_with_body (conn->error_code)) { return ret_ok; } if (cherokee_connection_should_include_length(conn)) { HANDLER(hdl)->support |= hsupport_length; if (conn->error_code == http_range_not_satisfiable) { /* The handler that attended the request has put the content * length in conn->range_end in order to allow it to send the * right length to the client. * * "Content-Range: bytes *" "/" FMT_OFFSET CRLF */ cherokee_buffer_add_str (buffer, "Content-Range: bytes */"); cherokee_buffer_add_ullong10(buffer, (cullong_t)conn->range_end); cherokee_buffer_add_str (buffer, CRLF); } cherokee_buffer_add_str (buffer, "Content-Length: "); cherokee_buffer_add_ulong10 (buffer, (culong_t) hdl->content.len); cherokee_buffer_add_str (buffer, CRLF); } /* Usual headers */ cherokee_buffer_add_str (buffer, "Content-Type: text/html"CRLF); if (http_type_400(conn->error_code) || http_type_500(conn->error_code)) { cherokee_buffer_add_str (buffer, "Cache-Control: no-cache"CRLF); cherokee_buffer_add_str (buffer, "Pragma: no-cache"CRLF); } return ret_ok; }
static NORETURN void manage_child_cgi_process (cherokee_handler_cgi_t *cgi, int pipe_cgi[2], int pipe_server[2]) { /* Child process */ int re; char *script; cherokee_connection_t *conn = HANDLER_CONN(cgi); cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(cgi); char *absolute_path = cgi_base->executable.buf; char *argv[2] = { NULL, NULL }; #ifdef TRACE_ENABLED TRACE(ENTRIES, "About to execute: '%s'\n", absolute_path); if (! cherokee_buffer_is_empty (&conn->effective_directory)) TRACE(ENTRIES, "Effective directory: '%s'\n", conn->effective_directory.buf); else TRACE(ENTRIES, "No Effective directory %s", "\n"); #endif /* Close useless sides */ cherokee_fd_close (pipe_cgi[0]); cherokee_fd_close (pipe_server[1]); /* Change stdin and out */ re = dup2 (pipe_server[0], STDIN_FILENO); cherokee_fd_close (pipe_server[0]); if (unlikely (re != 0)) { printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__); exit(1); } re |= dup2 (pipe_cgi[1], STDOUT_FILENO); cherokee_fd_close (pipe_cgi[1]); /* Redirect the stderr */ if ((CONN_VSRV(conn)->error_writer != NULL) && (CONN_VSRV(conn)->error_writer->fd != -1)) { dup2 (CONN_VSRV(conn)->error_writer->fd, STDERR_FILENO); } # if 0 /* Set unbuffered */ setvbuf (stdin, NULL, _IONBF, 0); setvbuf (stdout, NULL, _IONBF, 0); # endif /* Enable blocking mode */ _fd_set_properties (STDIN_FILENO, 0, O_NONBLOCK); _fd_set_properties (STDOUT_FILENO, 0, O_NONBLOCK); _fd_set_properties (STDERR_FILENO, 0, O_NONBLOCK); /* Sets the new environ. */ add_environment (cgi, conn); /* Change the directory */ if (! cherokee_buffer_is_empty (&conn->effective_directory)) { re = chdir (conn->effective_directory.buf); } else { char *file = strrchr (absolute_path, '/'); *file = '\0'; re = chdir (absolute_path); *file = '/'; } if (re < 0) { printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__); exit(1); } /* Build de argv array */ script = absolute_path; argv[0] = absolute_path; /* Change the execution user? */ if (HANDLER_CGI_PROPS(cgi_base)->change_user) { struct stat info; re = stat (script, &info); if (re >= 0) { re = setuid (info.st_uid); if (re != 0) { LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_SETID, script, info.st_uid); } } } /* Reset the server-wide signal handlers */ #ifdef SIGPIPE signal (SIGPIPE, SIG_DFL); #endif #ifdef SIGHUP signal (SIGHUP, SIG_DFL); #endif #ifdef SIGSEGV signal (SIGSEGV, SIG_DFL); #endif #ifdef SIGBUS signal (SIGBUS, SIG_DFL); #endif #ifdef SIGTERM signal (SIGTERM, SIG_DFL); #endif /* Lets go.. execute it! */ re = execve (absolute_path, argv, cgi->envp); if (re < 0) { int err = errno; char buferr[ERROR_MAX_BUFSIZE]; switch (err) { case ENOENT: printf ("Status: 404" CRLF_CRLF); break; default: printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d cmd=%s: %s" CRLF_CRLF, __FILE__, __LINE__, absolute_path, strerror(err)); } /* Don't use the logging system (concurrency issues) */ LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_EXECUTE, absolute_path, cherokee_strerror_r(err, buferr, sizeof(buferr))); exit(1); } /* There is no way, it could reach this point. */ SHOULDNT_HAPPEN; exit(2); }
static ret_t fork_and_execute_cgi_win32 (cherokee_handler_cgi_t *cgi) { int re; PROCESS_INFORMATION pi; STARTUPINFO si; char *cmd; cherokee_buffer_t cmd_line = CHEROKEE_BUF_INIT; cherokee_buffer_t exec_dir = CHEROKEE_BUF_INIT; cherokee_connection_t *conn = HANDLER_CONN(cgi); SECURITY_ATTRIBUTES saSecAtr; HANDLE hProc; HANDLE hChildStdinRd = INVALID_HANDLE_VALUE; HANDLE hChildStdinWr = INVALID_HANDLE_VALUE; HANDLE hChildStdoutRd = INVALID_HANDLE_VALUE; HANDLE hChildStdoutWr = INVALID_HANDLE_VALUE; /* Create the environment for the process */ add_environment (cgi, conn); cherokee_buffer_add (&cgi->envp, "\0", 1); /* Command line */ cmd = HDL_CGI_BASE(cgi)->executable.buf; cherokee_buffer_add (&cmd_line, cmd, strlen(cmd)); // cherokee_buffer_add_va (&cmd_line, " \"%s\"", HDL_CGI_BASE(cgi)->param.buf); /* Execution directory */ if (! cherokee_buffer_is_empty (&conn->effective_directory)) { cherokee_buffer_add_buffer (&exec_dir, &conn->effective_directory); } else { char *file = strrchr (cmd, '/'); char *end = HDL_CGI_BASE(cgi)->executable.buf + HDL_CGI_BASE(cgi)->executable.len; cherokee_buffer_add (&exec_dir, cmd, HDL_CGI_BASE(cgi)->executable.len - (end - file)); } /* Set the bInheritHandle flag so pipe handles are inherited. */ memset(&saSecAtr, 0, sizeof(SECURITY_ATTRIBUTES)); saSecAtr.nLength = sizeof(SECURITY_ATTRIBUTES); saSecAtr.lpSecurityDescriptor = NULL; saSecAtr.bInheritHandle = TRUE; /* Create the pipes */ hProc = GetCurrentProcess(); re = CreatePipe (&hChildStdoutRd, &hChildStdoutWr, &saSecAtr, 0); if (!re) return ret_error; re = CreatePipe (&hChildStdinRd, &hChildStdinWr, &saSecAtr, 0); if (!re) return ret_error; /* Make them inheritable */ re = DuplicateHandle (hProc, hChildStdoutRd, hProc, &hChildStdoutRd, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); if (!re) return ret_error; re = DuplicateHandle (hProc, hChildStdinWr, hProc, &hChildStdinWr, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS); if (!re) return ret_error; /* Starting information */ ZeroMemory (&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.hStdOutput = hChildStdoutWr; si.hStdError = hChildStdoutWr; si.hStdInput = hChildStdinRd; si.dwFlags |= STARTF_USESTDHANDLES; TRACE (ENTRIES, "exec %s dir %s\n", cmd_line.buf, exec_dir.buf); /* Launch the child process */ re = CreateProcess (cmd, /* ApplicationName */ cmd_line.buf, /* Command line */ NULL, /* Process handle not inheritable */ NULL, /* Thread handle not inheritable */ TRUE, /* Handle inheritance */ 0, /* Creation flags */ cgi->envp.buf, /* Use parent's environment block */ exec_dir.buf, /* Use parent's starting directory */ &si, /* Pointer to STARTUPINFO structure */ &pi); /* Pointer to PROCESS_INFORMATION structure */ CloseHandle (hChildStdinRd); CloseHandle (hChildStdoutWr); if (!re) { LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_CREATEPROCESS, GetLastError()); CloseHandle (pi.hProcess); CloseHandle (pi.hThread); conn->error_code = http_internal_error; return ret_error; } cherokee_buffer_mrproper (&cmd_line); cherokee_buffer_mrproper (&exec_dir); cgi->thread = pi.hThread; cgi->process = pi.hProcess; /* Wait for the CGI process to be ready */ WaitForInputIdle (pi.hProcess, INFINITE); /* Extract the file descriptors */ cgi->pipeInput = _open_osfhandle((LONG)hChildStdoutRd, O_BINARY|_O_RDONLY); if (! conn->post.len <= 0) { CloseHandle (hChildStdinWr); } else { cgi->pipeOutput = _open_osfhandle((LONG)hChildStdinWr, O_BINARY|_O_WRONLY); } TRACE (ENTRIES, "In fd %d, Out fd %d\n", cgi->pipeInput, cgi->pipeOutput); return ret_ok; }
ret_t cherokee_handler_zeromq_read_post (cherokee_handler_zeromq_t *hdl) { zmq_msg_t message; int re; ret_t ret; cherokee_buffer_t *post = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_buffer_t *out = &HANDLER_THREAD(hdl)->tmp_buf2; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* Check for the post info */ if (! conn->post.has_info) { conn->error_code = http_bad_request; return ret_error; } cherokee_buffer_clean (post); ret = cherokee_post_read (&conn->post, &conn->socket, post); switch (ret) { case ret_ok: cherokee_connection_update_timeout (conn); break; case ret_eagain: ret = cherokee_thread_deactive_to_polling (HANDLER_THREAD(hdl), HANDLER_CONN(hdl), conn->socket.socket, FDPOLL_MODE_READ, false); if (ret != ret_ok) { return ret_error; } else { return ret_eagain; } default: conn->error_code = http_bad_request; return ret_error; } TRACE (ENTRIES, "Post contains: '%s'\n", post->buf); re = cherokee_post_read_finished (&conn->post); ret = re ? ret_ok : ret_eagain; if (hdl->encoder != NULL) { cherokee_buffer_clean(out); if (ret == ret_ok) { cherokee_encoder_flush(hdl->encoder, post, out); } else { cherokee_encoder_encode(hdl->encoder, post, out); } post = out; } cherokee_buffer_add_buffer(&hdl->output, post); if (ret == ret_ok) { cherokee_buffer_t *tmp = &HANDLER_THREAD(hdl)->tmp_buf1; cherokee_handler_zeromq_props_t *props = HANDLER_ZEROMQ_PROPS(hdl); zmq_msg_t envelope; zmq_msg_t message; cuint_t len; if ((cherokee_buffer_is_empty (&conn->web_directory)) || (cherokee_buffer_is_ending (&conn->web_directory, '/'))) { len = conn->web_directory.len; } else { len = conn->web_directory.len + 1; } cherokee_buffer_clean (tmp); cherokee_buffer_add (tmp, conn->request.buf + len, conn->request.len - len); TRACE(ENTRIES, "ZeroMQ: incomming path '%s'\n", tmp->buf); zmq_msg_init_size (&envelope, tmp->len); memcpy (zmq_msg_data (&envelope), tmp->buf, tmp->len); zmq_msg_init_size (&message, hdl->output.len); memcpy (zmq_msg_data (&message), hdl->output.buf, hdl->output.len); /* Atomic Section */ CHEROKEE_MUTEX_LOCK (&props->mutex); zmq_msg_send (&envelope, props->socket, ZMQ_DONTWAIT | ZMQ_SNDMORE); zmq_msg_send (&message, props->socket, ZMQ_DONTWAIT); CHEROKEE_MUTEX_UNLOCK (&props->mutex); zmq_msg_close (&envelope); zmq_msg_close (&message); } return ret; }
static NORETURN void manage_child_cgi_process (cherokee_handler_cgi_t *cgi, int pipe_cgi[2], int pipe_server[2]) { /* Child process */ int re; cherokee_connection_t *conn = HANDLER_CONN(cgi); cherokee_handler_cgi_base_t *cgi_base = HDL_CGI_BASE(cgi); char *absolute_path = cgi_base->executable.buf; char *argv[2] = { NULL, NULL }; #ifdef TRACE_ENABLED TRACE(ENTRIES, "About to execute: '%s'\n", absolute_path); if (! cherokee_buffer_is_empty (&conn->effective_directory)) TRACE(ENTRIES, "Effective directory: '%s'\n", conn->effective_directory.buf); else TRACE(ENTRIES, "No Effective directory %s", "\n"); #endif /* Close useless sides */ cherokee_fd_close (pipe_cgi[0]); cherokee_fd_close (pipe_server[1]); /* Change stdin and out */ cherokee_fd_close (STDIN_FILENO); re = dup2 (pipe_server[0], STDIN_FILENO); cherokee_fd_close (pipe_server[0]); if (unlikely (re != 0)) { printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__); exit(1); } cherokee_fd_close (STDOUT_FILENO); re |= dup2 (pipe_cgi[1], STDOUT_FILENO); cherokee_fd_close (pipe_cgi[1]); /* Redirect the stderr */ if ((CONN_VSRV(conn)->error_writer != NULL) && (CONN_VSRV(conn)->error_writer->fd != -1)) { cherokee_fd_close (STDERR_FILENO); dup2 (CONN_VSRV(conn)->error_writer->fd, STDERR_FILENO); } # if 0 /* Set unbuffered */ setvbuf (stdin, NULL, _IONBF, 0); setvbuf (stdout, NULL, _IONBF, 0); # endif /* Enable blocking mode */ _fd_set_properties (STDIN_FILENO, 0, O_NONBLOCK); _fd_set_properties (STDOUT_FILENO, 0, O_NONBLOCK); _fd_set_properties (STDERR_FILENO, 0, O_NONBLOCK); /* Sets the new environ. */ add_environment (cgi, conn); /* Change the directory */ if (! cherokee_buffer_is_empty (&conn->effective_directory)) { re = chdir (conn->effective_directory.buf); } else { char *file = strrchr (absolute_path, '/'); if (file != NULL) { *file = '\0'; re = chdir (absolute_path); *file = '/'; } else { re = -1; } } if (re < 0) { printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d" CRLF_CRLF, __FILE__, __LINE__); exit(1); } /* Build de argv array */ argv[0] = absolute_path; /* Change the execution user? */ if (HANDLER_CGI_PROPS(cgi_base)->change_user) { struct stat nocache_info; struct stat *info; cherokee_iocache_entry_t *io_entry = NULL; cherokee_server_t *srv = CONN_SRV(conn); cherokee_handler_cgi_base_props_t *props = HANDLER_CGI_BASE_PROPS(cgi); ret_t ret = cherokee_io_stat (srv->iocache, &cgi_base->executable, props->use_cache, &nocache_info, &io_entry, &info); if (ret != ret_ok) { info = &nocache_info; } re = setuid (info->st_uid); if (re != 0) { LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_SETID, absolute_path, info->st_uid); } cherokee_iocache_entry_unref(&io_entry); } /* Reset the server-wide signal handlers */ cherokee_reset_signals(); /* Lets go.. execute it! */ do { re = execve (absolute_path, argv, cgi->envp); } while ((re == -1) && (errno == EINTR)); if (re < 0) { int err = errno; char buferr[ERROR_MAX_BUFSIZE]; switch (err) { case ENODEV: case ENOTDIR: case ENOENT: printf ("Status: 404" CRLF_CRLF); exit(0); case EPERM: case EACCES: case ENOEXEC: printf ("Status: 403" CRLF_CRLF); exit(0); default: printf ("Status: 500" CRLF_CRLF); printf ("X-Debug: file=%s line=%d cmd=%s errno=%d: %s" CRLF_CRLF, __FILE__, __LINE__, absolute_path, err, strerror(err)); } /* Don't use the logging system (concurrency issues) */ LOG_ERROR (CHEROKEE_ERROR_HANDLER_CGI_EXECUTE, absolute_path, cherokee_strerror_r(err, buferr, sizeof(buferr))); exit(1); } /* There is no way, it could reach this point. */ SHOULDNT_HAPPEN; exit(2); }
ret_t cherokee_handler_admin_read_post (cherokee_handler_admin_t *hdl) { int re; ret_t ret; char *tmp; cherokee_buffer_t post = CHEROKEE_BUF_INIT; cherokee_buffer_t line = CHEROKEE_BUF_INIT; cherokee_connection_t *conn = HANDLER_CONN(hdl); /* Check for the post info */ if (! conn->post.has_info) { conn->error_code = http_bad_request; return ret_error; } /* Process line per line */ ret = cherokee_post_read (&conn->post, &conn->socket, &post); switch (ret) { case ret_ok: case ret_eagain: break; default: conn->error_code = http_bad_request; return ret_error; } /* Parse */ TRACE (ENTRIES, "Post contains: '%s'\n", post.buf); cherokee_dwriter_list_open (&hdl->dwriter); for (tmp = post.buf;;) { char *end1 = strchr (tmp, CHR_LF); char *end2 = strchr (tmp, CHR_CR); char *end = cherokee_min_str (end1, end2); if (end == NULL) break; if (end - tmp < 2) break; /* Copy current line and go to the next one */ cherokee_buffer_add (&line, tmp, end - tmp); while ((*end == CHR_CR) || (*end == CHR_LF)) end++; tmp = end; /* Process current line */ ret = process_request_line (hdl, &line); if (ret == ret_error) { conn->error_code = http_bad_request; ret = ret_error; goto exit2; } /* Clean up for the next iteration */ cherokee_buffer_clean (&line); } cherokee_dwriter_list_close (&hdl->dwriter); /* There might be more POST to read */ re = cherokee_post_read_finished (&conn->post); ret = re ? ret_ok : ret_eagain; exit2: cherokee_buffer_mrproper (&post); cherokee_buffer_mrproper (&line); return ret; }