diff --git a/core/alarm.c b/core/alarm.c index 3a1fe46c..eb50ea7b 100644 --- a/core/alarm.c +++ b/core/alarm.c @@ -18,18 +18,18 @@ void uwsgi_alarm_init_log(struct uwsgi_alarm_instance *uai) { void uwsgi_alarm_func_log(struct uwsgi_alarm_instance *uai, char *msg, size_t len) { if (msg[len-1] != '\n') { if (uai->arg && strlen(uai->arg) > 0) { - uwsgi_log_alarm("] %s %.*s\n", uai->arg, len, msg); + uwsgi_log_verbose("ALARM: %s %.*s\n", uai->arg, len, msg); } else { - uwsgi_log_alarm("] %.*s\n", len, msg); + uwsgi_log_verbose("ALARM: %.*s\n", len, msg); } } else { if (uai->arg && strlen(uai->arg) > 0) { - uwsgi_log_alarm("] %s %.*s", uai->arg, len, msg); + uwsgi_log_verbose("ALARM: %s %.*s", uai->arg, len, msg); } else { - uwsgi_log_alarm("] %.*s", len, msg); + uwsgi_log_verbose("ALARM: %.*s", len, msg); } } } diff --git a/core/async.c b/core/async.c index bb8ff412..41651fdf 100644 --- a/core/async.c +++ b/core/async.c @@ -387,6 +387,23 @@ end: } +static int uwsgi_async_wait_milliseconds_hook(int timeout) { + struct wsgi_request *wsgi_req = current_wsgi_req(); + timeout = timeout / 1000; + if (!timeout) timeout = 1; + async_add_timeout(wsgi_req, timeout); + wsgi_req->async_force_again = 1; + if (uwsgi.schedule_to_main) { + uwsgi.schedule_to_main(wsgi_req); + } + if (wsgi_req->async_timed_out) { + wsgi_req->async_timed_out = 0; + return 0; + } + + return -1; +} + void async_loop() { if (uwsgi.async < 2) { @@ -414,6 +431,7 @@ void async_loop() { uwsgi.wait_write_hook = async_wait_fd_write; uwsgi.wait_read_hook = async_wait_fd_read; uwsgi.wait_read2_hook = async_wait_fd_read2; + uwsgi.wait_milliseconds_hook = uwsgi_async_wait_milliseconds_hook; if (uwsgi.signal_socket > -1) { event_queue_add_fd_read(uwsgi.async_queue, uwsgi.signal_socket); diff --git a/core/emperor.c b/core/emperor.c index d30a4955..3b8676b6 100644 --- a/core/emperor.c +++ b/core/emperor.c @@ -39,6 +39,65 @@ struct uwsgi_emperor_blacklist_item { struct uwsgi_emperor_blacklist_item *emperor_blacklist; +/* +this should be placed in core/socket.c but we realized it was needed +only after 2.0 so we cannot change uwsgi.h + +basically it is a stripped down bind_to_tcp/bind_to_unix with rollback +*/ +static int on_demand_bind(char *socket_name) { + union uwsgi_sockaddr us; + socklen_t addr_len = sizeof(struct sockaddr_un); + char *is_tcp = strchr(socket_name, ':'); + int af_family = is_tcp ? AF_INET : AF_UNIX; + int fd = socket(af_family, SOCK_STREAM, 0); + if (fd < 0) return -1; + + memset(&us, 0, sizeof(union uwsgi_sockaddr)); + + if (is_tcp) { + int reuse = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &reuse, sizeof(int)) < 0) { + goto error; + } + us.sa_in.sin_family = AF_INET; + us.sa_in.sin_port = htons(atoi(is_tcp+1)); + *is_tcp = 0; + us.sa_in.sin_addr.s_addr = inet_addr(socket_name); + *is_tcp = ':'; + addr_len = sizeof(struct sockaddr_in); + } + else { + if (unlink(socket_name) != 0 && errno != ENOENT) { + goto error; + } + + us.sa_un.sun_family = AF_UNIX; + memcpy(us.sa_un.sun_path, socket_name, UMIN(strlen(socket_name), 102)); + addr_len = strlen(socket_name) + ((void *) us.sa_un.sun_path - (void *) &us.sa_un); + } + + if (bind(fd, (struct sockaddr *) &us, addr_len) != 0) { + goto error; + } + + if (!is_tcp) { + if (chmod(socket_name, 0666)) { + goto error; + } + } + + if (listen(fd, uwsgi.listen_queue) != 0) { + goto error; + } + + return fd; + +error: + close(fd); + return -1; +} + struct uwsgi_emperor_blacklist_item *uwsgi_emperor_blacklist_check(char *id) { struct uwsgi_emperor_blacklist_item *uebi = emperor_blacklist; while (uebi) { @@ -157,7 +216,7 @@ static char *emperor_check_on_demand_socket(char *filename) { if (fd < 0) return NULL; char *ret = uwsgi_read_fd(fd, &len, 1); close(fd); - // change the first non prinabel character to 0 + // change the first non printable character to 0 size_t i; for(i=0;isocket_name); } + if (c_ui->on_demand_fd != -1) { + close(c_ui->on_demand_fd); + } + free(c_ui); } @@ -637,8 +700,10 @@ void emperor_stop(struct uwsgi_instance *c_ui) { if (c_ui->status == 1) return; // remove uWSGI instance - if (write(c_ui->pipe[0], "\0", 1) != 1) { - uwsgi_error("emperor_stop()/write()"); + if (c_ui->pid != -1) { + if (write(c_ui->pipe[0], "\0", 1) != 1) { + uwsgi_error("emperor_stop()/write()"); + } } c_ui->status = 1; @@ -797,18 +862,7 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha // ok here we check if we need to bind to the specified socket or continue with the activation if (socket_name) { - char *tcp_port = strchr(socket_name, ':'); - if (tcp_port) { - // disable deferred accept for this socket - int current_defer_accept = uwsgi.no_defer_accept; - uwsgi.no_defer_accept = 1; - n_ui->on_demand_fd = bind_to_tcp(socket_name, uwsgi.listen_queue, tcp_port); - uwsgi.no_defer_accept = current_defer_accept; - } - else { - n_ui->on_demand_fd = bind_to_unix(socket_name, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); - } - + n_ui->on_demand_fd = on_demand_bind(socket_name); if (n_ui->on_demand_fd < 0) { uwsgi_error("emperor_add()/bind()"); free(n_ui); @@ -1743,12 +1797,18 @@ void emperor_loop() { break; } } - else if (ui_current->cursed_at > 0 && now - ui_current->cursed_at >= uwsgi.emperor_curse_tolerance) { - ui_current->cursed_at = now; - if (kill(ui_current->pid, SIGKILL)) { - uwsgi_error("[emperor] kill"); + else if (ui_current->cursed_at > 0) { + if (ui_current->pid == -1) { + emperor_del(ui_current); + break; + } + else if (now - ui_current->cursed_at >= uwsgi.emperor_curse_tolerance) { + ui_current->cursed_at = now; + if (kill(ui_current->pid, SIGKILL)) { + uwsgi_error("[emperor] kill"); + } + break; } - break; } } diff --git a/core/io.c b/core/io.c index 03560b86..30334dce 100644 --- a/core/io.c +++ b/core/io.c @@ -967,6 +967,7 @@ int uwsgi_read_nb(int fd, char *buf, size_t remains, int timeout) { ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) { int ret; + errno = 0; ssize_t rlen = read(fd, buf, len); if (rlen > 0) { return rlen; @@ -977,8 +978,10 @@ ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) { } return -1; wait: + errno = 0; ret = uwsgi.wait_read_hook(fd, timeout); if (ret > 0) { + errno = 0; rlen = read(fd, buf, len); if (rlen > 0) { return rlen; diff --git a/core/spooler.c b/core/spooler.c index 2245ecee..cc92c701 100644 --- a/core/spooler.c +++ b/core/spooler.c @@ -190,6 +190,11 @@ static void spooler_req_parser_hook(char *key, uint16_t key_len, char *value, ui } if (!uwsgi_strncmp(key, key_len, "at", 2)) { + // at can be a float... + char *dot = memchr(value, '.', value_len); + if (dot) { + value_len = dot - value; + } sr->at = uwsgi_str_num(value, value_len); return; } diff --git a/plugins/cgi/cgi_plugin.c b/plugins/cgi/cgi_plugin.c index 0518081e..2dcaa90e 100644 --- a/plugins/cgi/cgi_plugin.c +++ b/plugins/cgi/cgi_plugin.c @@ -2,6 +2,8 @@ extern struct uwsgi_server uwsgi; +#define kill_on_error if (!uc.do_not_kill_on_error) { if (kill(cgi_pid, SIGKILL)) uwsgi_error("kill()");} + struct uwsgi_cgi { struct uwsgi_dyn_dict *mountpoint; struct uwsgi_dyn_dict *helpers; @@ -16,6 +18,8 @@ struct uwsgi_cgi { int has_mountpoints; struct uwsgi_dyn_dict *default_cgi; int path_info; + int do_not_kill_on_error; + int async_max_attempts; } uc ; static void uwsgi_opt_add_cgi(char *opt, char *value, void *foobar) { @@ -62,6 +66,9 @@ struct uwsgi_option uwsgi_cgi_options[] = { {"cgi-path-info", no_argument, 0, "disable PATH_INFO management in cgi scripts", uwsgi_opt_true, &uc.path_info, 0}, + {"cgi-do-not-kill-on-error", no_argument, 0, "do not send SIGKILL to cgi script on errors", uwsgi_opt_true, &uc.do_not_kill_on_error, 0}, + {"cgi-async-max-attempts", no_argument, 0, "max waitpid() attempts in cgi async mode (default 10)", uwsgi_opt_set_int, &uc.async_max_attempts, 0}, + {0, 0, 0, 0, 0, 0, 0}, }; @@ -179,118 +186,154 @@ static char *uwsgi_cgi_get_helper(char *filename) { } -static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, char *buf, size_t len) { - - size_t i; +/* +start reading each line until Status or Location are found +-1 error +0 not found +1 found +*/ +static int uwsgi_cgi_check_status(struct wsgi_request *wsgi_req, char *buf, size_t len) { char *key = buf, *value = NULL; size_t header_size = 0; - int status_sent = 0; + size_t i; - // Search for Status/Location headers for(i=0;i buf) { - if ((buf[i-1]) == '\r') { - header_size--; - } + // invalid header + else if (value == NULL) return -1; + header_size = (buf+i) - key; + // security check + if (buf+i > buf) { + // remove \r + if ((buf[i-1]) == '\r') { + header_size--; + } + } + + // enough space for Status ? + if (header_size >= 11) { + // "Status: NNN" + if (!strncasecmp("Status: ", key, 8)) { +#ifdef UWSGI_DEBUG + uwsgi_log("found Status header: %.*s\n", header_size, key); +#endif + if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1; + return 1; + } + // Location: X + if (!strncasecmp("Location: ", key, 10)) { +#ifdef UWSGI_DEBUG + uwsgi_log("found Location header: %.*s\n", header_size, key); +#endif + if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1; + return 1; + } } - if (header_size >= 11) { - // "Status: NNN" - if (!strncasecmp("Status: ", key, 8)) { -#ifdef UWSGI_DEBUG - uwsgi_log("found Status header: %.*s\n", header_size, key); -#endif - if (uwsgi_response_prepare_headers(wsgi_req, key+8, header_size - 8)) return -1; - break; - } - // Location: X - if (!strncasecmp("Location: ", key, 10)) { -#ifdef UWSGI_DEBUG - uwsgi_log("found Location header: %.*s\n", header_size, key); -#endif - if (uwsgi_response_prepare_headers(wsgi_req, "302 Found", 9)) return -1; - break; - } - } - - key = NULL; - value = NULL; + key = NULL; + value = NULL; } - else if (buf[i] == ':') { + else if (buf[i] == ':') { value = buf+i; - } - else if (buf[i] != '\r') { - if (key == NULL) { - key = buf + i; - } - } + } + else if (buf[i] != '\r') { + if (key == NULL) key = buf + i; + } + } - key = buf; - value = NULL; + // no Status/Location found + return 0; - for(i=0;i buf) { - if ((buf[i-1]) == '\r') { - header_size--; +} + +static int uwsgi_cgi_parse(struct wsgi_request *wsgi_req, int fd, char *buf, size_t blen) { + + size_t i; + size_t header_size = 0; + int status_sent = 0; + size_t remains = blen; + char *ptr = buf; + size_t len = 0; + + while(remains > 0) { + ssize_t rlen = uwsgi_read_true_nb(fd, ptr, remains, uc.timeout); + if (rlen < 0) { + if (!errno) return 1; + return -1; + } + // timed out + if (rlen == 0) return -1; + remains -= rlen; + len += rlen; + ptr += rlen; + + // Search for Status/Location headers + if (!status_sent) { + status_sent = uwsgi_cgi_check_status(wsgi_req, buf, len); + if (status_sent < 0) return -1; + // need more data ? + if (status_sent == 0) continue; + } + + // send headers + char *key = buf; + char *value = NULL; + + for(i=0;i buf) { + if ((buf[i-1]) == '\r') { + header_size--; + } } - } #ifdef UWSGI_DEBUG - uwsgi_log("found CGI header: %.*s\n", header_size, key); + uwsgi_log("found CGI header: %.*s\n", header_size, key); #endif - // Ignore "Status: NNN" header - if (status_sent == 0 && header_size >= 11) { - if (!strncasecmp("Status: ", key, 8)) { - status_sent = 1; - key = NULL; - value = NULL; - continue; + // Ignore "Status: NNN" header + if (header_size >= 11) { + if (!strncasecmp("Status: ", key, 8)) { + key = NULL; + value = NULL; + continue; + } } + + uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size); + key = NULL; + value = NULL; } - - uwsgi_response_add_header(wsgi_req, NULL, 0, key, header_size); - - key = NULL; - value = NULL; - } - else if (buf[i] == ':') { - value = buf+i; - } - else if (buf[i] != '\r') { - if (key == NULL) { - key = buf + i; + else if (buf[i] == ':') { + value = buf+i; + } + else if (buf[i] != '\r') { + if (key == NULL) { + key = buf + i; + } } } } @@ -592,7 +635,6 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do int post_pipe[2]; int nargs = 0; int waitpid_status; - ssize_t len; int i; char **argv; @@ -630,126 +672,96 @@ static int uwsgi_cgi_run(struct wsgi_request *wsgi_req, char *docroot, size_t do close(cgi_pipe[1]); close(post_pipe[0]); + uwsgi_socket_nb(cgi_pipe[0]); + uwsgi_socket_nb(post_pipe[1]); + // ok start sending post data... size_t remains = wsgi_req->post_cl; while(remains > 0) { ssize_t rlen = 0; char *buf = uwsgi_request_body_read(wsgi_req, 8192, &rlen); if (!buf) { - close(post_pipe[1]); goto clear2; } if (buf == uwsgi.empty) break; // write data to the node if (uwsgi_write_true_nb(post_pipe[1], buf, rlen, uc.timeout)) { - close(post_pipe[1]); goto clear2; } remains -= rlen; } - close(post_pipe[1]); // wait for data - char *headers_buf = uwsgi_malloc(uc.buffer_size); - char *ptr = headers_buf; - remains = uc.buffer_size; - int completed = 0; - while(remains > 0) { - int ret = uwsgi.wait_read_hook(cgi_pipe[0], uc.timeout); - if (ret > 0) { - len = read(cgi_pipe[0], ptr, remains); - if (len > 0) { - ptr+=len; - remains -= len; - } - else if (len == 0) { - completed = 1; - break; - } - else { - uwsgi_error("read()"); - goto clear; - } - continue; - } - else if (ret == 0) { - uwsgi_log("CGI timeout !!!\n"); - goto clear; - } - break; - } + char *buf = uwsgi_malloc(uc.buffer_size); - if (uwsgi_cgi_parse(wsgi_req, headers_buf, uc.buffer_size-remains)) { - uwsgi_log("invalid CGI output !!!\n"); + int completed = uwsgi_cgi_parse(wsgi_req, cgi_pipe[0], buf, uc.buffer_size); + if (completed < 0) { + uwsgi_log("invalid CGI response !!!\n"); + kill_on_error goto clear; } while (!completed) { - int ret = uwsgi.wait_read_hook(cgi_pipe[0], uc.timeout); - if (ret > 0) { - len = read(cgi_pipe[0], headers_buf, uc.buffer_size); - if (len > 0) { - uwsgi_response_write_body_do(wsgi_req, headers_buf, len); - } - // end of output - else if (len == 0) { - break; - } - else { - uwsgi_error("read()"); + ssize_t rlen = uwsgi_read_true_nb(cgi_pipe[0], buf, uc.buffer_size, uc.timeout); + if (rlen > 0) { + if (uwsgi_response_write_body_do(wsgi_req, buf, rlen)) { + kill_on_error goto clear; } - continue; } - else if (ret == 0) { - uwsgi_log("CGI timeout !!!\n"); - goto clear; - } - break; + else if (rlen == 0) { + uwsgi_log("CGI timeout !!!\n"); + kill_on_error + goto clear; + } + else { + if (errno) { + uwsgi_req_error("error reading CGI response\n"); + kill_on_error + } + goto clear; + } } clear: - free(headers_buf); + free(buf); clear2: close(cgi_pipe[0]); close(post_pipe[1]); // now wait for process exit/death - if (waitpid(cgi_pid, &waitpid_status, 0) < 0) { - uwsgi_error("waitpid()"); + // in async mode we need a trick... + if (uwsgi.async > 1) { + pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG); + if (diedpid < 0) { + uwsgi_error("waitpid()"); + } + else if (diedpid == 0) { + // pass the pid of the cgi to async_plagued (the after request hook will clear the process) + wsgi_req->async_plagued = (int) cgi_pid; + } + } + else { + if (waitpid(cgi_pid, &waitpid_status, 0) < 0) { + uwsgi_error("waitpid()"); + } } return UWSGI_OK; } - // close all the fd except wsgi_req->poll.fd and 2; - - for(i=0;i< (int)uwsgi.max_fd;i++) { - if (post_pipe[0] == i) { - continue; - } - if (wsgi_req->post_file) { - if (fileno(wsgi_req->post_file) == i) { - continue; - } - } - if (i != wsgi_req->fd && i != 2 && i != cgi_pipe[1]) { - close(i); - } - } - // now map wsgi_req->poll.fd (or async_post) to 0 & cgi_pipe[1] to 1 - if (post_pipe[0] != 0) { - dup2(post_pipe[0], 0); - close(post_pipe[0]); - } - -#ifdef UWSGI_DEBUG - uwsgi_log("mapping cgi_pipe %d to 1\n", cgi_pipe[1]); -#endif + dup2(post_pipe[0], 0); + close(post_pipe[0]); dup2(cgi_pipe[1],1); - + close(cgi_pipe[1]); + + // close all the fd > 2 + for(i=3;i<(int)uwsgi.max_fd;i++) { + close(i); + } + // fill cgi env for(i=0;ivar_cnt;i++) { // no need to free the putenv() memory @@ -913,6 +925,39 @@ clear2: static void uwsgi_cgi_after_request(struct wsgi_request *wsgi_req) { + if (wsgi_req->async_plagued > 0) { + int waitpid_status; + pid_t cgi_pid = (pid_t) wsgi_req->async_plagued; + int max_attempts = uc.async_max_attempts; + if (!max_attempts) max_attempts = 10; + while(max_attempts) { + pid_t diedpid = waitpid(cgi_pid, &waitpid_status, WNOHANG); + if (diedpid < 0) { + uwsgi_error("waitpid()"); + break; + } + else if (diedpid == 0) { + int ret = uwsgi.wait_milliseconds_hook(1000); + if (ret < 0) { + kill_on_error + if (waitpid(cgi_pid, &waitpid_status, 0) < 0) { + uwsgi_error("waitpid()"); + } + } + } + else { + break; + } + max_attempts--; + } + if (max_attempts == 0) { + kill_on_error + if (waitpid(cgi_pid, &waitpid_status, 0) < 0) { + uwsgi_error("waitpid()"); + } + } + } + log_request(wsgi_req); } diff --git a/plugins/emperor_mongodb/emperor_mongodb.cc b/plugins/emperor_mongodb/emperor_mongodb.cc index 0719cb9a..7960e42d 100644 --- a/plugins/emperor_mongodb/emperor_mongodb.cc +++ b/plugins/emperor_mongodb/emperor_mongodb.cc @@ -20,7 +20,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues try { // requested fields - mongo::BSONObj p = BSON( "name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 ); + mongo::BSONObj p = BSON( "name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 << "socket" << 1 ); mongo::BSONObj q = mongo::fromjson(uems->json); // the connection object (will be automatically destroyed at each cycle) mongo::DBClientConnection c; @@ -31,7 +31,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues // run the query std::auto_ptr cursor = c.query(uems->collection, q, 0, 0, &p); - while( cursor->more() ) { + while(cursor.get() && cursor->more() ) { mongo::BSONObj p = cursor->next(); // checking for an empty string is not required, but we reduce the load @@ -40,6 +40,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues if (strlen(name) == 0) continue; const char *config = p.getStringField("config"); + if (strlen(config) == 0) config = NULL; time_t vassal_ts = 0; // ts must be a Date object !!! @@ -61,6 +62,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues } const char *socket_name = p.getStringField("socket"); + if (strlen(socket_name) == 0) socket_name = NULL; uwsgi_emperor_simple_do(ues, (char *) name, (char *) config, vassal_ts/1000, vassal_uid, vassal_gid, (char *) socket_name); } @@ -76,6 +78,7 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues b.append("name", c_ui->name); mongo::BSONObj q2 = b.obj(); cursor = c.query(uems->collection, q2, 0, 0, &p); + if (!cursor.get()) return; #ifdef UWSGI_DEBUG uwsgi_log("JSON: %s\n", q2.toString().c_str()); #endif diff --git a/plugins/mongrel2/mongrel2.c b/plugins/mongrel2/mongrel2.c index 198ac1d3..708a758f 100644 --- a/plugins/mongrel2/mongrel2.c +++ b/plugins/mongrel2/mongrel2.c @@ -574,6 +574,7 @@ static void mongrel2_register_proto() { static void mongrel2_connect() { struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; while(uwsgi_sock) { + if (uwsgi_sock->proto != uwsgi_proto_zeromq_parser) goto next; uwsgi_sock->ctx = zmq_init(1); if (!uwsgi_sock->ctx) { uwsgi_error("mongrel2_connect()/zmq_init()"); @@ -581,7 +582,7 @@ static void mongrel2_connect() { } char *responder = strchr(uwsgi_sock->name, ','); if (!responder) { - uwsgi_log("invalid zeromq address\n"); + uwsgi_log("invalid zeromq address: %s\n", uwsgi_sock->name); exit(1); } uwsgi_sock->receiver = uwsgi_concat2n(uwsgi_sock->name, responder - uwsgi_sock->name, "", 0); @@ -653,6 +654,7 @@ static void mongrel2_connect() { #else uwsgi_sock->recv_flag = ZMQ_NOBLOCK; #endif +next: uwsgi_sock = uwsgi_sock->next; } } diff --git a/plugins/psgi/psgi.h b/plugins/psgi/psgi.h index 264704b4..92e6b588 100644 --- a/plugins/psgi/psgi.h +++ b/plugins/psgi/psgi.h @@ -65,6 +65,8 @@ struct uwsgi_perl { int shell_oneshot; CV *spooler; + + int no_plack; }; void init_perl_embedded_module(void); diff --git a/plugins/psgi/psgi_loader.c b/plugins/psgi/psgi_loader.c index f8cc4b8f..bc2515a0 100644 --- a/plugins/psgi/psgi_loader.c +++ b/plugins/psgi/psgi_loader.c @@ -313,9 +313,6 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe char *app_name = uwsgi_concat2n(app, app_len, "", 0); - size_t size; - char *buf = uwsgi_open_and_read(app_name, &size, 1, NULL); - if (uwsgi_file_exists(app_name)) { // prepare for $0 (if the file is local) uperl.embedding[1] = app_name; @@ -368,38 +365,30 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe uperl.tmp_current_i = i; - - if (uperl.locallib) { - uwsgi_log("using %s as local::lib directory\n", uperl.locallib); - uperl.embedding[1] = uwsgi_concat2("-Mlocal::lib=", uperl.locallib); - uperl.embedding[2] = app_name; - if (perl_parse(interpreters[i], xs_init, 3, uperl.embedding, NULL)) { - // what to do here ? i hope no-one will use threads with dynamic apps... but clear the whole stuff... - free(uperl.embedding[1]); - uperl.embedding[1] = app_name; - free(callables); - uwsgi_perl_free_stashes(); - goto clear; - } - free(uperl.embedding[1]); - uperl.embedding[1] = app_name; - } - else { - if (perl_parse(interpreters[i], xs_init, 2, uperl.embedding, NULL)) { + // We need to initialize the interpreter to execute + // our xs_init hook, but we're *not* calling it with + // uperl.embedding as an argument so we won't execute + // BEGIN blocks in app_name twice. + { + char *perl_init_arg[] = { "", "-e", "0" }; + if (perl_parse(interpreters[i], xs_init, 3, perl_init_arg, NULL)) { // what to do here ? i hope no-one will use threads with dynamic apps... but clear the whole stuff... free(callables); uwsgi_perl_free_stashes(); goto clear; - } + } } + if (uperl.locallib) { + uwsgi_log("using %s as local::lib directory\n", uperl.locallib); + char *local_lib_use = uwsgi_concat3("use local::lib qw(", uperl.locallib, ");"); + perl_eval_pv(local_lib_use, 1); + free(local_lib_use); + } perl_eval_pv("use IO::Handle;", 1); perl_eval_pv("use IO::File;", 1); perl_eval_pv("use IO::Socket;", 1); perl_eval_pv("use Scalar::Util;", 1); - if (!uperl.no_die_catch) { - perl_eval_pv("use Devel::StackTrace; $SIG{__DIE__} = sub { print Devel::StackTrace->new()->as_string() };", 0); - } if (uperl.argv_items || uperl.argv_item) { AV *uperl_argv = GvAV(PL_argvgv); @@ -417,12 +406,32 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe } } - SV *dollar_zero = get_sv("0", GV_ADD); sv_setsv(dollar_zero, newSVpv(app, app_len)); - callables[i] = perl_eval_pv(uwsgi_concat4("#line 1 ", app_name, "\n", buf), 0); - if (!callables[i]) { + SV *has_plack = NULL; + if (!uperl.no_plack) { + has_plack = perl_eval_pv("use Plack::Util;", 0); + } + + if (!has_plack || SvTRUE(ERRSV)) { + if (!uperl.no_plack) { + uwsgi_log("Plack::Util is not installed, using \"do\" instead of \"load_psgi\"\n"); + } + char *code = uwsgi_concat3("my $app = do '", app_name, "'; if ( !$app && ( my $error = $@ || $! )) { die $error; }; $app"); + callables[i] = perl_eval_pv(code, 0); + free(code); + } + else { + char *code = uwsgi_concat3("Plack::Util::load_psgi '", app_name , "';"); + callables[i] = perl_eval_pv(code, 0); + free(code); + } + + if (!callables[i] || SvTYPE(callables[i]) == SVt_NULL || SvTRUE(ERRSV)) { + if (SvTRUE(ERRSV)) { + uwsgi_log("%s", SvPV_nolen(ERRSV)); + } uwsgi_log("unable to find PSGI function entry point.\n"); // what to do here ? i hope no-one will use threads with dynamic apps... free(callables); @@ -430,18 +439,16 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe goto clear; } + if (!uperl.no_die_catch) { + perl_eval_pv("use Devel::StackTrace; $SIG{__DIE__} = sub { print Devel::StackTrace->new()->as_string() };", 0); + if(SvTRUE(ERRSV)) { + uwsgi_log("%s", SvPV_nolen(ERRSV)); + } + } + PERL_SET_CONTEXT(interpreters[0]); } - free(buf); - - if(SvTRUE(ERRSV)) { - uwsgi_log("%s", SvPV_nolen(ERRSV)); - free(callables); - uwsgi_perl_free_stashes(); - goto clear; - } - if (uwsgi_apps_cnt >= uwsgi.max_apps) { uwsgi_log("ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps); goto clear; @@ -504,6 +511,8 @@ void uwsgi_psgi_preinit_apps() { perl_parse(uperl.main[0], xs_init, 3, uperl.embedding, NULL); struct uwsgi_string_list *usl; uwsgi_foreach(usl, uperl.exec) { + SV *dollar_zero = get_sv("0", GV_ADD); + sv_setsv(dollar_zero, newSVpv(usl->value, usl->len)); uwsgi_perl_exec(usl->value); } } diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index d5ebe9f5..a764cb2c 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -44,6 +44,8 @@ struct uwsgi_option uwsgi_perl_options[] = { {"plshell", optional_argument, 0, "run a perl interactive shell", uwsgi_opt_plshell, NULL, 0}, {"plshell-oneshot", no_argument, 0, "run a perl interactive shell (one shot)", uwsgi_opt_plshell, NULL, 0}, + + {"perl-no-plack", no_argument, 0, "force the use of do instead of Plack::Util::load_psgi", uwsgi_opt_true, &uperl.no_plack, 0}, {0, 0, 0, 0, 0, 0, 0}, }; @@ -705,6 +707,8 @@ void uwsgi_perl_post_fork() { struct uwsgi_string_list *usl; uwsgi_foreach(usl, uperl.exec_post_fork) { + SV *dollar_zero = get_sv("0", GV_ADD); + sv_setsv(dollar_zero, newSVpv(usl->value, usl->len)); uwsgi_perl_exec(usl->value); } diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index f57a16de..7eb57b08 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -21,12 +21,13 @@ int uwsgi_python_send_body(struct wsgi_request *wsgi_req, PyObject *chunk) { char *content = NULL; size_t content_len = 0; - if (!up.wsgi_accept_buffer && !wsgi_req->is_raw) goto strict; #if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER) Py_buffer pbuf; int has_buffer = 0; #endif + if (!up.wsgi_accept_buffer && !wsgi_req->is_raw) goto strict; + #if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER) if (PyObject_CheckBuffer(chunk)) { if (!PyObject_GetBuffer(chunk, &pbuf, PyBUF_SIMPLE)) { diff --git a/plugins/syslog/syslog_plugin.c b/plugins/syslog/syslog_plugin.c index acebccf3..a1cfa5c7 100644 --- a/plugins/syslog/syslog_plugin.c +++ b/plugins/syslog/syslog_plugin.c @@ -93,7 +93,11 @@ ssize_t uwsgi_syslog_logger(struct uwsgi_logger *ul, char *message, size_t len) ul->configured = 1; } +#ifdef __APPLE__ + syslog(LOG_NOTICE, "%.*s", (int) len, message); +#else syslog(LOG_INFO, "%.*s", (int) len, message); +#endif return 0; } diff --git a/t/perl/test.psgi b/t/perl/test.psgi index 1e7a6bc6..2535e36a 100644 --- a/t/perl/test.psgi +++ b/t/perl/test.psgi @@ -1,5 +1,9 @@ use strict; use warnings; +BEGIN { + die "PANIC: We should only load this once" if ++$main::count_BEGIN > 1; +} +die "PANIC: We should only run this once" if ++$main::count_runs > 1; uwsgi::register_rpc('hello', sub { my ($one, $two, $three) = @_; diff --git a/uwsgi.gemspec b/uwsgi.gemspec index fcbb9b0a..cccfa6b8 100644 --- a/uwsgi.gemspec +++ b/uwsgi.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'uwsgi' s.license = 'GPL-2' s.version = `python -c "import uwsgiconfig as uc; print uc.uwsgi_version"`.sub(/-dev-.*/,'') - s.date = '2014-02-09' + s.date = '2014-02-26' s.summary = "uWSGI" s.description = "The uWSGI server for Ruby/Rack" s.authors = ["Unbit"] diff --git a/uwsgiconfig.py b/uwsgiconfig.py index c40835e9..cdde3aa8 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -367,6 +367,15 @@ def build_uwsgi(uc, print_only=False, gcll=None): if len(kv) > 1: p = kv[1] p = p.strip() + if p.startswith('http://') or p.startswith('https://') or p.startswith('git://') or p.startswith('ssh://'): + git_dir = p.split('/').pop() + if not os.path.isdir(git_dir): + if os.system('git clone %s' % p) != 0: + sys.exit(1) + else: + if os.system('cd %s ; git pull' % git_dir) != 0: + sys.exit(1) + p = git_dir path = os.path.abspath(p) else: p = kv[0]