diff --git a/README b/README index 5816d789..6cf48d80 100644 --- a/README +++ b/README @@ -1,3 +1,5 @@ -surprise i am empty ! +The uWSGI project -Look in the online documentation on https://uwsgi-docs.readthedocs.org/en/latest/ +For official documentation check: https://uwsgi-docs.readthedocs.org/en/latest/ + +For commercial support check: http://unbit.com/ diff --git a/buildconf/base.ini b/buildconf/base.ini index 31b26814..6dbe8e33 100644 --- a/buildconf/base.ini +++ b/buildconf/base.ini @@ -2,7 +2,6 @@ xml = auto yaml = true json = auto -zeromq = auto ssl = auto pcre = auto routing = auto diff --git a/buildconf/minimal.ini b/buildconf/minimal.ini index 99be3ac2..82afe1e1 100644 --- a/buildconf/minimal.ini +++ b/buildconf/minimal.ini @@ -2,7 +2,6 @@ xml = false yaml = false json = false -zeromq = false ssl = false pcre = false routing = false diff --git a/buildconf/ppa.ini b/buildconf/ppa.ini index 9ce8e737..06c21aff 100644 --- a/buildconf/ppa.ini +++ b/buildconf/ppa.ini @@ -2,7 +2,6 @@ xml = true yaml = true json = true -zeromq = true ssl = true pcre = true routing = true diff --git a/buildconf/unbit.ini b/buildconf/unbit.ini index f695891b..051cf701 100644 --- a/buildconf/unbit.ini +++ b/buildconf/unbit.ini @@ -2,7 +2,6 @@ xml = true yaml = true json = false -zeromq = false ssl = true pcre = true routing = true diff --git a/core/async.c b/core/async.c index 87af14d2..af3122a6 100644 --- a/core/async.c +++ b/core/async.c @@ -4,18 +4,18 @@ extern struct uwsgi_server uwsgi; /* - This is a general purpose async loop engine (it expects a coroutine based approach) + This is a general-purpose async loop engine (it expects a coroutine-based approach) You can see it as an hub holding the following structures: - 1) the runqueue, cores ready to be run are appended in this list + 1) the runqueue, cores ready to be run are appended to this list 2) the fd list, this is a list of monitored file descriptors, a core can wait for all the file descriptors it needs - 3) the timeout value, if set the current core will timeout aftert he specified number of seconds (unless an event cancel it) + 3) the timeout value, if set, the current core will timeout after the specified number of seconds (unless an event cancels it) - IMPORTANT: this is not a callback based engine !!! + IMPORTANT: this is not a callback-based engine !!! */ @@ -28,7 +28,6 @@ void uwsgi_async_queue_is_full(time_t now) { } void uwsgi_async_init() { - int i; uwsgi.async_queue = event_queue_init(); @@ -40,16 +39,9 @@ void uwsgi_async_init() { uwsgi.rb_async_timeouts = uwsgi_init_rb_timer(); - // a stack of unused cores - uwsgi.async_queue_unused = uwsgi_malloc(sizeof(struct wsgi_request *) * uwsgi.async); - - // fill it with default values - for (i = 0; i < uwsgi.async; i++) { - uwsgi.async_queue_unused[i] = &uwsgi.workers[uwsgi.mywid].cores[i].req; - } - - // the first available core is the last one - uwsgi.async_queue_unused_ptr = uwsgi.async - 1; + // optimization, this array maps file descriptor to requests + uwsgi.async_waiting_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd); + uwsgi.async_proto_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd); } @@ -159,7 +151,7 @@ static void async_expire_timeouts(uint64_t now) { wsgi_req = (struct wsgi_request *) urbt->data; // timeout expired wsgi_req->async_timed_out = 1; - // reset teh request + // reset the request async_reset_request(wsgi_req); // push it in the runqueue runqueue_push(wsgi_req); @@ -173,6 +165,11 @@ static void async_expire_timeouts(uint64_t now) { int async_add_fd_read(struct wsgi_request *wsgi_req, int fd, int timeout) { + if (uwsgi.async < 2 || !uwsgi.async_waiting_fd_table){ + uwsgi_log_verbose("ASYNC call without async mode !!!\n"); + return -1; + } + struct uwsgi_async_fd *last_uad = NULL, *uad = wsgi_req->waiting_fds; if (fd < 0) @@ -224,8 +221,47 @@ static int async_wait_fd_read(int fd, int timeout) { return 1; } +static int async_wait_fd_read2(int fd0, int fd1, int timeout, int *fd) { + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + wsgi_req->async_ready_fd = 0; + + if (async_add_fd_read(wsgi_req, fd0, timeout)) { + return -1; + } + + if (async_add_fd_read(wsgi_req, fd1, timeout)) { + // reset already registered fd + async_reset_request(wsgi_req); + return -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; + } + + if (wsgi_req->async_ready_fd) { + *fd = wsgi_req->async_last_ready_fd; + return 1; + } + + return -1; +} + + void async_add_timeout(struct wsgi_request *wsgi_req, int timeout) { + if (uwsgi.async < 2 || !uwsgi.rb_async_timeouts) { + uwsgi_log_verbose("ASYNC call without async mode !!!\n"); + return; + } + wsgi_req->async_ready_fd = 0; if (timeout > 0 && wsgi_req->async_timeout == NULL) { @@ -236,6 +272,11 @@ void async_add_timeout(struct wsgi_request *wsgi_req, int timeout) { int async_add_fd_write(struct wsgi_request *wsgi_req, int fd, int timeout) { + if (uwsgi.async < 2 || !uwsgi.async_waiting_fd_table) { + uwsgi_log_verbose("ASYNC call without async mode !!!\n"); + return -1; + } + struct uwsgi_async_fd *last_uad = NULL, *uad = wsgi_req->waiting_fds; if (fd < 0) @@ -295,9 +336,8 @@ void async_schedule_to_req(void) { // a trick to avoid calling routes again uwsgi.wsgi_req->is_routing = 1; #endif - if (uwsgi.p[uwsgi.wsgi_req->uh->modifier1]->request(uwsgi.wsgi_req) <= UWSGI_OK) { - goto end; - } + uwsgi.wsgi_req->async_status = uwsgi.p[uwsgi.wsgi_req->uh->modifier1]->request(uwsgi.wsgi_req); + if (uwsgi.wsgi_req->async_status <= UWSGI_OK) goto end; if (uwsgi.schedule_to_main) { uwsgi.schedule_to_main(uwsgi.wsgi_req); @@ -366,10 +406,13 @@ void async_loop() { void *events = event_queue_alloc(64); struct uwsgi_socket *uwsgi_sock; + uwsgi_async_init(); + uwsgi.async_runqueue = NULL; uwsgi.wait_write_hook = async_wait_fd_write; uwsgi.wait_read_hook = async_wait_fd_read; + uwsgi.wait_read2_hook = async_wait_fd_read2; if (uwsgi.signal_socket > -1) { event_queue_add_fd_read(uwsgi.async_queue, uwsgi.signal_socket); @@ -478,7 +521,8 @@ void async_loop() { // remove fd from event poll and fd proto table uwsgi.async_proto_fd_table[interesting_fd] = NULL; event_queue_del_fd(uwsgi.async_queue, interesting_fd, event_queue_read()); - // put request in the runqueue + // put request in the runqueue (set it as UWSGI_OK to signal the first run) + uwsgi.wsgi_req->async_status = UWSGI_OK; runqueue_push(uwsgi.wsgi_req); continue; } diff --git a/core/cache.c b/core/cache.c index 186ad158..dbdb02d4 100644 --- a/core/cache.c +++ b/core/cache.c @@ -762,7 +762,7 @@ int uwsgi_cache_set2(struct uwsgi_cache *uc, char *key, uint16_t keylen, char *v } // mark used blocks; uint64_t needed_blocks = cache_mark_blocks(uc, uci->first_block, vallen); - // optimize teh scan + // optimize the scan if (uc->blocks_bitmap_pos + (needed_blocks+1) > uc->blocks) { uc->blocks_bitmap_pos = 0; } diff --git a/core/clock.c b/core/clock.c index 6fbf6c8b..74113c02 100644 --- a/core/clock.c +++ b/core/clock.c @@ -2,6 +2,11 @@ extern struct uwsgi_server uwsgi; +int uwsgi_simple_wait_milliseconds_hook(int timeout) { + return poll(NULL, 0, timeout); +} + + // in the future we will need to use the best clock source for each os/system time_t uwsgi_now() { return uwsgi.clock->seconds(); diff --git a/core/config.c b/core/config.c index 86873517..dd638ebf 100644 --- a/core/config.c +++ b/core/config.c @@ -720,18 +720,18 @@ char *uwsgi_manage_placeholder(char *key) { } else if (!strcmp(p, "++")) { if (current_value) { - int64_t tmp_value = strtoll(current_value, NULL, 10); + int64_t tmp_num = strtoll(current_value, NULL, 10); free(current_value); - current_value = uwsgi_64bit2str(tmp_value+1); + current_value = uwsgi_64bit2str(tmp_num+1); } state = concat; continue; } else if (!strcmp(p, "--")) { if (current_value) { - int64_t tmp_value = strtoll(current_value, NULL, 10); + int64_t tmp_num = strtoll(current_value, NULL, 10); free(current_value); - current_value = uwsgi_64bit2str(tmp_value-1); + current_value = uwsgi_64bit2str(tmp_num-1); } state = concat; continue; @@ -795,6 +795,7 @@ char *uwsgi_manage_placeholder(char *key) { // reset state to concat state = concat; } + free(tmp_value); return current_value; } diff --git a/core/daemons.c b/core/daemons.c index aa84c289..35fced08 100644 --- a/core/daemons.c +++ b/core/daemons.c @@ -6,7 +6,7 @@ extern struct uwsgi_server uwsgi; External uwsgi daemons - There are 3 kind of daemons (read: external applications) that can be managed + There are 3 kinds of daemons (read: external applications) that can be managed by uWSGI. 1) dumb daemons (attached with --attach-daemon) @@ -14,12 +14,12 @@ extern struct uwsgi_server uwsgi; and the process is respawned 2) smart daemons with daemonization you specify a pidfile and a command - - on startup - if the pidfile does not exists or contains a not-available pid (checked with kill(pid, 0)) - the daemon is respawned - - on master ckeck - if the pidfile does not exist or if it point to a non-existent pid - the daemon is respawned + - on startup - if the pidfile does not exist or contains a non-available pid (checked with kill(pid, 0)) + the daemon is respawned + - on master check - if the pidfile does not exist or if it points to a non-existent pid + the daemon is respawned 3) smart daemons without daemonization - same as 2, but the daemonization and pidfile creation is managed by uWSGI + same as 2, but the daemonization and pidfile creation are managed by uWSGI status: @@ -232,7 +232,7 @@ void uwsgi_detach_daemons() { if (ud->pid > 0 && !ud->pidfile) { #endif uwsgi_log("[uwsgi-daemons] stopping daemon (pid: %d): %s\n", (int) ud->pid, ud->command); - // try to gracefully stop daemon, kill it if it won't die + // try to stop daemon gracefully, kill it if it won't die // if mercy is not set then wait up to 3 seconds time_t timeout = uwsgi_now() + (uwsgi.reload_mercy ? uwsgi.reload_mercy : 3); int waitpid_status; diff --git a/core/emperor.c b/core/emperor.c index 2a64a00d..51769e87 100644 --- a/core/emperor.c +++ b/core/emperor.c @@ -59,12 +59,12 @@ void uwsgi_emperor_blacklist_add(char *id) { uebi->throttle_level += (uwsgi.emperor_throttle * 1000); } else { - uwsgi_log("[emperor] maximum throttle level for vassal %s reached !!!\n", id); + uwsgi_log_verbose("[emperor] maximum throttle level for vassal %s reached !!!\n", id); uebi->throttle_level = uebi->throttle_level / 2; } uebi->attempt++; if (uebi->attempt == 2) { - uwsgi_log("[emperor] unloyal bad behaving vassal found: %s throttling it...\n", id); + uwsgi_log_verbose("[emperor] unloyal bad behaving vassal found: %s throttling it...\n", id); } return; } @@ -603,7 +603,7 @@ void emperor_del(struct uwsgi_instance *c_ui) { uwsgi_log("[emperor] %s stop-hook returned %d\n", c_ui->name, stop_hook_ret); } - uwsgi_log("[emperor] removed uwsgi instance %s\n", c_ui->name); + uwsgi_log_verbose("[emperor] removed uwsgi instance %s\n", c_ui->name); // put the instance in the blacklist (or update its throttling value) if (!c_ui->loyal) { uwsgi_emperor_blacklist_add(c_ui->name); @@ -632,7 +632,7 @@ void emperor_stop(struct uwsgi_instance *c_ui) { c_ui->status = 1; c_ui->cursed_at = uwsgi_now(); - uwsgi_log("[emperor] stop the uwsgi instance %s\n", c_ui->name); + uwsgi_log_verbose("[emperor] stop the uwsgi instance %s\n", c_ui->name); } void emperor_curse(struct uwsgi_instance *c_ui) { @@ -642,7 +642,7 @@ void emperor_curse(struct uwsgi_instance *c_ui) { c_ui->status = 1; c_ui->cursed_at = uwsgi_now(); - uwsgi_log("[emperor] curse the uwsgi instance %s (pid: %d)\n", c_ui->name, (int) c_ui->pid); + uwsgi_log_verbose("[emperor] curse the uwsgi instance %s (pid: %d)\n", c_ui->name, (int) c_ui->pid); } @@ -675,8 +675,12 @@ void emperor_respawn(struct uwsgi_instance *c_ui, time_t mod) { c_ui->respawns++; c_ui->last_mod = mod; c_ui->last_run = uwsgi_now(); + // reset readyness + c_ui->ready = 0; + // reset accepting + c_ui->accepting = 0; - uwsgi_log("[emperor] reload the uwsgi instance %s\n", c_ui->name); + uwsgi_log_verbose("[emperor] reload the uwsgi instance %s\n", c_ui->name); } void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, char *config, uint32_t config_size, uid_t uid, gid_t gid, char *socket_name) { @@ -763,6 +767,9 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha n_ui->uid = uid; n_ui->gid = gid; n_ui->last_mod = born; + // start non-ready + n_ui->last_ready = 0; + n_ui->ready = 0; // start without loyalty n_ui->last_loyal = 0; n_ui->loyal = 0; @@ -1552,7 +1559,7 @@ void emperor_loop() { if (byte == 17) { ui_current->loyal = 1; ui_current->last_loyal = uwsgi_now(); - uwsgi_log("[emperor] vassal %s is now loyal\n", ui_current->name); + uwsgi_log_verbose("[emperor] vassal %s is now loyal\n", ui_current->name); // remove it from the blacklist uwsgi_emperor_blacklist_remove(ui_current->name); // TODO post-start hook @@ -1565,12 +1572,22 @@ void emperor_loop() { emperor_stop(ui_current); } else if (byte == 30 && uwsgi.emperor_broodlord > 0 && uwsgi.emperor_broodlord_count < uwsgi.emperor_broodlord) { - uwsgi_log("[emperor] going in broodlord mode: launching zergs for %s\n", ui_current->name); + uwsgi_log_verbose("[emperor] going in broodlord mode: launching zergs for %s\n", ui_current->name); char *zerg_name = uwsgi_concat3(ui_current->name, ":", "zerg"); // here we discard socket name as broodlord/zerg cannot be on demand emperor_add(ui_current->scanner, zerg_name, uwsgi_now(), NULL, 0, ui_current->uid, ui_current->gid, NULL); free(zerg_name); } + else if (byte == 5) { + ui_current->accepting = 1; + ui_current->last_accepting = uwsgi_now(); + uwsgi_log_verbose("[emperor] vassal %s is ready to accept requests\n", ui_current->name); + } + else if (byte == 1) { + ui_current->ready = 1; + ui_current->last_ready = uwsgi_now(); + uwsgi_log_verbose("[emperor] vassal %s has been spawned\n", ui_current->name); + } } } else { @@ -1773,8 +1790,16 @@ void emperor_send_stats(int fd) { goto end0; if (uwsgi_stats_keylong_comma(us, "loyal", (unsigned long long) c_ui->loyal)) goto end0; + if (uwsgi_stats_keylong_comma(us, "ready", (unsigned long long) c_ui->ready)) + goto end0; + if (uwsgi_stats_keylong_comma(us, "accepting", (unsigned long long) c_ui->accepting)) + goto end0; if (uwsgi_stats_keylong_comma(us, "last_loyal", (unsigned long long) c_ui->last_loyal)) goto end0; + if (uwsgi_stats_keylong_comma(us, "last_ready", (unsigned long long) c_ui->last_ready)) + goto end0; + if (uwsgi_stats_keylong_comma(us, "last_accepting", (unsigned long long) c_ui->last_accepting)) + goto end0; if (uwsgi_stats_keylong_comma(us, "first_run", (unsigned long long) c_ui->first_run)) goto end0; if (uwsgi_stats_keylong_comma(us, "last_run", (unsigned long long) c_ui->last_run)) @@ -2105,3 +2130,16 @@ void uwsgi_master_manage_emperor_proxy() { close(ep_client); } + +static void emperor_notify_ready() { + if (!uwsgi.has_emperor) return; + char byte = 1; + if (write(uwsgi.emperor_fd, &byte, 1) != 1) { + uwsgi_error("emperor_notify_ready()/write()"); + } +} + +void uwsgi_setup_emperor() { + if (!uwsgi.has_emperor) return; + uwsgi.notify_ready = emperor_notify_ready; +} diff --git a/core/errors.c b/core/errors.c index afd4c96c..8df221e7 100644 --- a/core/errors.c +++ b/core/errors.c @@ -1,9 +1,30 @@ #include +extern struct uwsgi_server uwsgi; + +static int error_page(struct wsgi_request *wsgi_req, struct uwsgi_string_list *l) { + struct uwsgi_string_list *usl = NULL; + uwsgi_foreach(usl, l) { + struct stat st; + if (!stat(usl->value, &st)) { + int fd = open(usl->value, O_RDONLY); + if (fd >= 0) { + if (uwsgi_response_add_header(wsgi_req, "Content-Type", 12, "text/html", 9)) { close(fd); return 0;} + if (uwsgi_response_add_content_length(wsgi_req, st.st_size)) { close(fd); return 0;} + uwsgi_response_sendfile_do(wsgi_req, fd, 0, st.st_size); + return -1; + } + } + } + return 0; +} + // generate internal server error message void uwsgi_500(struct wsgi_request *wsgi_req) { if (uwsgi_response_prepare_headers(wsgi_req, "500 Internal Server Error", 25)) return; if (uwsgi_response_add_connection_close(wsgi_req)) return; + if (error_page(wsgi_req, uwsgi.error_page_500)) return; + if (uwsgi_response_add_header(wsgi_req, "Content-Type", 12, "text/plain", 10)) return; uwsgi_response_write_body_do(wsgi_req, "Internal Server Error", 21); } @@ -11,6 +32,7 @@ void uwsgi_500(struct wsgi_request *wsgi_req) { void uwsgi_404(struct wsgi_request *wsgi_req) { if (uwsgi_response_prepare_headers(wsgi_req, "404 Not Found", 13)) return; if (uwsgi_response_add_connection_close(wsgi_req)) return; + if (error_page(wsgi_req, uwsgi.error_page_404)) return; if (uwsgi_response_add_header(wsgi_req, "Content-Type", 12, "text/plain", 10)) return; uwsgi_response_write_body_do(wsgi_req, "Not Found", 9); } @@ -18,6 +40,7 @@ void uwsgi_404(struct wsgi_request *wsgi_req) { void uwsgi_403(struct wsgi_request *wsgi_req) { if (uwsgi_response_prepare_headers(wsgi_req, "403 Forbidden", 13)) return; if (uwsgi_response_add_connection_close(wsgi_req)) return; + if (error_page(wsgi_req, uwsgi.error_page_403)) return; if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) return; uwsgi_response_write_body_do(wsgi_req, "Forbidden", 9); } diff --git a/core/event.c b/core/event.c index 3cd63119..cc98cdf1 100644 --- a/core/event.c +++ b/core/event.c @@ -1231,6 +1231,8 @@ static int timerfd_create(clockid_t __clock_id, int __flags) { return syscall(283, __clock_id, __flags); #elif defined(__i386__) return syscall(322, __clock_id, __flags); +#elif defined(__arm__) + return syscall(350, __clock_id, __flags); #else return -1; #endif @@ -1241,6 +1243,10 @@ static int timerfd_settime(int __ufd, int __flags, __const struct itimerspec *__ return syscall(286, __ufd, __flags, __utmr, __otmr); #elif defined(__i386__) return syscall(325, __ufd, __flags, __utmr, __otmr); +#elif defined(__arm__) + return syscall(353, __ufd, __flags, __utmr, __otmr); +#else + return -1; #endif } #endif diff --git a/core/fifo.c b/core/fifo.c index ddfa27aa..b6e4e748 100644 --- a/core/fifo.c +++ b/core/fifo.c @@ -24,16 +24,29 @@ static char *uwsgi_fifo_by_slot() { return uwsgi.master_fifo->value; } -static void uwsgi_fifo_set_slot_zero() { uwsgi.master_fifo_slot = 0; } -static void uwsgi_fifo_set_slot_one() { uwsgi.master_fifo_slot = 1; } -static void uwsgi_fifo_set_slot_two() { uwsgi.master_fifo_slot = 2; } -static void uwsgi_fifo_set_slot_three() { uwsgi.master_fifo_slot = 3; } -static void uwsgi_fifo_set_slot_four() { uwsgi.master_fifo_slot = 4; } -static void uwsgi_fifo_set_slot_five() { uwsgi.master_fifo_slot = 5; } -static void uwsgi_fifo_set_slot_six() { uwsgi.master_fifo_slot = 6; } -static void uwsgi_fifo_set_slot_seven() { uwsgi.master_fifo_slot = 7; } -static void uwsgi_fifo_set_slot_eight() { uwsgi.master_fifo_slot = 8; } -static void uwsgi_fifo_set_slot_nine() { uwsgi.master_fifo_slot = 9; } +#define announce_fifo uwsgi_log_verbose("active master fifo is now %s\n", uwsgi_fifo_by_slot()) + +static void uwsgi_fifo_set_slot_zero() { uwsgi.master_fifo_slot = 0; announce_fifo; } +static void uwsgi_fifo_set_slot_one() { uwsgi.master_fifo_slot = 1; announce_fifo; } +static void uwsgi_fifo_set_slot_two() { uwsgi.master_fifo_slot = 2; announce_fifo; } +static void uwsgi_fifo_set_slot_three() { uwsgi.master_fifo_slot = 3; announce_fifo; } +static void uwsgi_fifo_set_slot_four() { uwsgi.master_fifo_slot = 4; announce_fifo; } +static void uwsgi_fifo_set_slot_five() { uwsgi.master_fifo_slot = 5; announce_fifo; } +static void uwsgi_fifo_set_slot_six() { uwsgi.master_fifo_slot = 6; announce_fifo; } +static void uwsgi_fifo_set_slot_seven() { uwsgi.master_fifo_slot = 7; announce_fifo; } +static void uwsgi_fifo_set_slot_eight() { uwsgi.master_fifo_slot = 8; announce_fifo; } +static void uwsgi_fifo_set_slot_nine() { uwsgi.master_fifo_slot = 9; announce_fifo; } + +static void subscriptions_blocker() { + if (uwsgi.subscriptions_blocked) { + uwsgi_log_verbose("subscriptions re-enabled\n"); + uwsgi.subscriptions_blocked = 0; + } + else { + uwsgi.subscriptions_blocked = 1; + uwsgi_log_verbose("subscriptions blocked\n"); + } +} /* @@ -71,6 +84,7 @@ void uwsgi_master_fifo_prepare() { uwsgi_fifo_table['r'] = grace_them_all; uwsgi_fifo_table['R'] = reap_them_all; uwsgi_fifo_table['s'] = stats; + uwsgi_fifo_table['S'] = subscriptions_blocker; uwsgi_fifo_table['w'] = uwsgi_reload_workers; uwsgi_fifo_table['W'] = uwsgi_brutally_reload_workers; diff --git a/core/hooks.c b/core/hooks.c index 59b06f82..49dde6d2 100644 --- a/core/hooks.c +++ b/core/hooks.c @@ -78,6 +78,67 @@ static int uwsgi_hook_print(char *arg) { return 0; } +static int uwsgi_hook_unlink(char *arg) { + int ret = unlink(arg); + if (ret) { + uwsgi_error("uwsgi_hook_unlink()/unlink()"); + } + return ret; +} + +static int uwsgi_hook_writefifo(char *arg) { + char *space = strchr(arg, ' '); + if (!space) { + uwsgi_log("invalid hook writefifo syntax, must be: \n"); + return -1; + } + *space = 0; + int fd = open(arg, O_WRONLY|O_NONBLOCK); + if (fd < 0) { + uwsgi_error_open(arg); + *space = ' '; + if (errno == ENODEV) return 0; +#ifdef ENXIO + if (errno == ENXIO) return 0; +#endif + return -1; + } + *space = ' '; + size_t l = strlen(space+1); + if (write(fd, space+1, l) != (ssize_t) l) { + uwsgi_error("uwsgi_hook_writefifo()/write()"); + close(fd); + return -1; + } + close(fd); + return 0; +} + +static int uwsgi_hook_write(char *arg) { + char *space = strchr(arg, ' '); + if (!space) { + uwsgi_log("invalid hook write syntax, must be: \n"); + return -1; + } + *space = 0; + int fd = open(arg, O_WRONLY); + if (fd < 0) { + uwsgi_error_open(arg); + *space = ' '; + return -1; + } + *space = ' '; + size_t l = strlen(space+1); + if (write(fd, space+1, l) != (ssize_t) l) { + uwsgi_error("uwsgi_hook_write()/write()"); + close(fd); + return -1; + } + close(fd); + return 0; +} + + static int uwsgi_hook_callint(char *arg) { char *space = strchr(arg, ' '); if (space) { @@ -177,6 +238,10 @@ void uwsgi_register_base_hooks() { uwsgi_register_hook("cd", uwsgi_hook_chdir); uwsgi_register_hook("exec", uwsgi_hook_exec); + uwsgi_register_hook("write", uwsgi_hook_write); + uwsgi_register_hook("writefifo", uwsgi_hook_writefifo); + uwsgi_register_hook("unlink", uwsgi_hook_unlink); + uwsgi_register_hook("mount", uwsgi_mount_hook); uwsgi_register_hook("umount", uwsgi_umount_hook); diff --git a/core/init.c b/core/init.c index 3129cdc2..a81e9538 100644 --- a/core/init.c +++ b/core/init.c @@ -170,6 +170,8 @@ void uwsgi_init_default() { uwsgi.wait_read_hook = uwsgi_simple_wait_read_hook; uwsgi.wait_write_hook = uwsgi_simple_wait_write_hook; + uwsgi.wait_milliseconds_hook = uwsgi_simple_wait_milliseconds_hook; + uwsgi.wait_read2_hook = uwsgi_simple_wait_read2_hook; uwsgi_websockets_init(); diff --git a/core/io.c b/core/io.c index 1b06bc68..f5c98e07 100644 --- a/core/io.c +++ b/core/io.c @@ -756,11 +756,12 @@ int uwsgi_write_true_nb(int fd, char *buf, size_t remains, int timeout) { int ret; while(remains > 0) { + errno = 0; ssize_t len = write(fd, ptr, remains); if (len > 0) goto written; if (len == 0) return -1; if (len < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait; + if (uwsgi_is_again()) goto wait; return -1; } wait: @@ -911,7 +912,7 @@ ssize_t uwsgi_read_true_nb(int fd, char *buf, size_t len, int timeout) { } if (rlen == 0) return -1; if (rlen < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait; + if (uwsgi_is_again()) goto wait; } return -1; wait: @@ -961,7 +962,7 @@ int uwsgi_read_with_realloc(int fd, char **buffer, size_t *rlen, int timeout, ui if (len > 0) goto readok; if (len == 0) return -1; if (len < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait; + if (uwsgi_is_again()) goto wait; return -1; } wait: @@ -1003,7 +1004,7 @@ readok: if (len > 0) goto readok2; if (len == 0) return -1; if (len < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) goto wait2; + if (uwsgi_is_again()) goto wait2; return -1; } wait2: diff --git a/core/lock.c b/core/lock.c index fc9bd4da..d3681486 100644 --- a/core/lock.c +++ b/core/lock.c @@ -647,6 +647,7 @@ void *uwsgi_robust_mutexes_watchdog_loop(void *arg) { for(;;) { uwsgi_lock(uwsgi.the_thunder_lock); uwsgi_unlock(uwsgi.the_thunder_lock); + sleep(1); } return NULL; } diff --git a/core/logging.c b/core/logging.c index cc284c66..d0714e97 100644 --- a/core/logging.c +++ b/core/logging.c @@ -211,49 +211,6 @@ void create_logpipe(void) { } -#ifdef UWSGI_ZEROMQ -// the zeromq logger -ssize_t uwsgi_zeromq_logger(struct uwsgi_logger *ul, char *message, size_t len) { - - if (!ul->configured) { - - if (!ul->arg) { - uwsgi_log_safe("invalid zeromq syntax\n"); - exit(1); - } - - void *ctx = uwsgi_zeromq_init(); - - ul->data = zmq_socket(ctx, ZMQ_PUSH); - if (ul->data == NULL) { - uwsgi_error_safe("zmq_socket()"); - exit(1); - } - - if (zmq_connect(ul->data, ul->arg) < 0) { - uwsgi_error_safe("zmq_connect()"); - exit(1); - } - - ul->configured = 1; - } - - zmq_msg_t msg; - if (zmq_msg_init_size(&msg, len) == 0) { - memcpy(zmq_msg_data(&msg), message, len); -#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3,0,0) - zmq_sendmsg(ul->data, &msg, 0); -#else - zmq_send(ul->data, &msg, 0); -#endif - zmq_msg_close(&msg); - } - - return 0; -} -#endif - - // log to the specified file or udp address void logto(char *logfile) { @@ -618,6 +575,9 @@ void log_request(struct wsgi_request *wsgi_req) { if (uwsgi.shared->options[UWSGI_OPTION_LOG_SENDFILE] && wsgi_req->via == UWSGI_VIA_SENDFILE) { goto logit; } + if (uwsgi.shared->options[UWSGI_OPTION_LOG_IOERROR] && wsgi_req->read_errors > 0 && wsgi_req->write_errors > 0) { + goto logit; + } if (!log_it) return; @@ -1139,6 +1099,16 @@ static ssize_t uwsgi_lf_ftime(struct wsgi_request * wsgi_req, char **buf) { return ret; } +static ssize_t uwsgi_lf_tmsecs(struct wsgi_request * wsgi_req, char **buf) { + *buf = uwsgi_64bit2str(wsgi_req->start_of_request / (int64_t) 1000); + return strlen(*buf); +} + +static ssize_t uwsgi_lf_tmicros(struct wsgi_request * wsgi_req, char **buf) { + *buf = uwsgi_64bit2str(wsgi_req->start_of_request); + return strlen(*buf); +} + static ssize_t uwsgi_lf_micros(struct wsgi_request * wsgi_req, char **buf) { *buf = uwsgi_num2str(wsgi_req->end_of_request - wsgi_req->start_of_request); return strlen(*buf); @@ -1214,6 +1184,21 @@ static ssize_t uwsgi_lf_headers(struct wsgi_request * wsgi_req, char **buf) { return strlen(*buf); } +static ssize_t uwsgi_lf_werr(struct wsgi_request * wsgi_req, char **buf) { + *buf = uwsgi_num2str((int) wsgi_req->write_errors); + return strlen(*buf); +} + +static ssize_t uwsgi_lf_rerr(struct wsgi_request * wsgi_req, char **buf) { + *buf = uwsgi_num2str((int) wsgi_req->read_errors); + return strlen(*buf); +} + +static ssize_t uwsgi_lf_ioerr(struct wsgi_request * wsgi_req, char **buf) { + *buf = uwsgi_num2str((int) (wsgi_req->write_errors + wsgi_req->read_errors)); + return strlen(*buf); +} + void uwsgi_add_logchunk(int variable, int pos, char *ptr, size_t len) { struct uwsgi_logchunk *logchunk = uwsgi.logchunks; @@ -1315,6 +1300,16 @@ void uwsgi_add_logchunk(int variable, int pos, char *ptr, size_t len) { logchunk->func = uwsgi_lf_msecs; logchunk->free = 1; } + else if (!uwsgi_strncmp(ptr, len, "tmsecs", 6)) { + logchunk->type = 3; + logchunk->func = uwsgi_lf_tmsecs; + logchunk->free = 1; + } + else if (!uwsgi_strncmp(ptr, len, "tmicros", 7)) { + logchunk->type = 3; + logchunk->func = uwsgi_lf_tmicros; + logchunk->free = 1; + } else if (!uwsgi_strncmp(ptr, len, "time", 4)) { logchunk->type = 3; logchunk->func = uwsgi_lf_time; @@ -1405,6 +1400,21 @@ void uwsgi_add_logchunk(int variable, int pos, char *ptr, size_t len) { logchunk->func = uwsgi_lf_headers; logchunk->free = 1; } + else if (!uwsgi_strncmp(ptr, len, "werr", 4)) { + logchunk->type = 3; + logchunk->func = uwsgi_lf_werr; + logchunk->free = 1; + } + else if (!uwsgi_strncmp(ptr, len, "rerr", 4)) { + logchunk->type = 3; + logchunk->func = uwsgi_lf_rerr; + logchunk->free = 1; + } + else if (!uwsgi_strncmp(ptr, len, "ioerr", 5)) { + logchunk->type = 3; + logchunk->func = uwsgi_lf_ioerr; + logchunk->free = 1; + } else if (!uwsgi_starts_with(ptr, len, "metric.", 7)) { logchunk->type = 4; logchunk->ptr = uwsgi_concat2n(ptr+7, len - 7, "", 0); diff --git a/core/master_checks.c b/core/master_checks.c index 6f62b139..6b58522e 100644 --- a/core/master_checks.c +++ b/core/master_checks.c @@ -48,6 +48,8 @@ void uwsgi_master_check_chain() { } uwsgi_block_signal(SIGHUP); for(i=1;i<=uwsgi.numproc;i++) { + // do not curse a worker until the old one is ready + if (uwsgi.workers[i].accepting == 0) break; if (uwsgi.workers[i].pid > 0 && uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].cursed_at == 0 && i == uwsgi.status.chain_reloading) { uwsgi_curse(i, SIGHUP); break; diff --git a/core/master_utils.c b/core/master_utils.c index 9ffd499d..b046d81d 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -592,6 +592,8 @@ void uwsgi_fixup_fds(int wid, int muleid, struct uwsgi_gateway *ug) { int uwsgi_respawn_worker(int wid) { int respawns = uwsgi.workers[wid].respawn_count; + // the workers is not accepting (obviously) + uwsgi.workers[wid].accepting = 0; // we count the respawns before errors... uwsgi.workers[wid].respawn_count++; // ... same for update time @@ -990,6 +992,8 @@ struct uwsgi_stats *uwsgi_master_generate_stats() { goto end; if (uwsgi_stats_keylong_comma(us, "pid", (unsigned long long) uwsgi.workers[i + 1].pid)) goto end; + if (uwsgi_stats_keylong_comma(us, "accepting", (unsigned long long) uwsgi.workers[i + 1].accepting)) + goto end; if (uwsgi_stats_keylong_comma(us, "requests", (unsigned long long) uwsgi.workers[i + 1].requests)) goto end; if (uwsgi_stats_keylong_comma(us, "delta_requests", (unsigned long long) uwsgi.workers[i + 1].delta_requests)) @@ -1129,6 +1133,9 @@ struct uwsgi_stats *uwsgi_master_generate_stats() { if (uwsgi_stats_keylong_comma(us, "write_errors", (unsigned long long) uc->write_errors)) goto end; + if (uwsgi_stats_keylong_comma(us, "read_errors", (unsigned long long) uc->read_errors)) + goto end; + if (uwsgi_stats_keylong_comma(us, "in_request", (unsigned long long) uc->in_request)) goto end; diff --git a/core/metrics.c b/core/metrics.c index 47dc8ee0..0c8741cd 100644 --- a/core/metrics.c +++ b/core/metrics.c @@ -752,7 +752,7 @@ void uwsgi_setup_metrics() { uwsgi_metric_name("worker.%d.avg_response_time", i) ; uwsgi_metric_oid("3.%d.8", i); uwsgi_register_metric(buf, buf2, UWSGI_METRIC_GAUGE, "ptr", &uwsgi.workers[i].avg_response_time, 0, NULL); - uwsgi_metric_name("worker.%d.total_rx", i) ; uwsgi_metric_oid("3.%d.9", i); + uwsgi_metric_name("worker.%d.total_tx", i) ; uwsgi_metric_oid("3.%d.9", i); struct uwsgi_metric *tx = uwsgi_register_metric(buf, buf2, UWSGI_METRIC_COUNTER, "ptr", &uwsgi.workers[i].tx, 0, NULL); uwsgi_metric_add_child(total_tx, tx); @@ -784,6 +784,9 @@ void uwsgi_setup_metrics() { uwsgi_metric_name2("worker.%d.core.%d.exceptions", i, j) ; uwsgi_metric_oid2("3.%d.2.%d.7", i, j); uwsgi_register_metric(buf, buf2, UWSGI_METRIC_COUNTER, "ptr", &uwsgi.workers[i].cores[j].exceptions, 0, NULL); + uwsgi_metric_name2("worker.%d.core.%d.read_errors", i, j) ; uwsgi_metric_oid2("3.%d.2.%d.8", i, j); + uwsgi_register_metric(buf, buf2, UWSGI_METRIC_COUNTER, "ptr", &uwsgi.workers[i].cores[j].read_errors, 0, NULL); + } } @@ -970,6 +973,42 @@ static int64_t uwsgi_metric_collector_sum(struct uwsgi_metric *um) { return total; } +static int64_t uwsgi_metric_collector_accumulator(struct uwsgi_metric *um) { + int64_t total = *um->value; + struct uwsgi_metric_child *umc = um->children; + while(umc) { + struct uwsgi_metric *c = umc->um; + total += *c->value; + umc = umc->next; + } + + return total; +} + +static int64_t uwsgi_metric_collector_multiplier(struct uwsgi_metric *um) { + int64_t total = 0; + struct uwsgi_metric_child *umc = um->children; + while(umc) { + struct uwsgi_metric *c = umc->um; + total += *c->value; + umc = umc->next; + } + + return total * um->arg1n; +} + +static int64_t uwsgi_metric_collector_adder(struct uwsgi_metric *um) { + int64_t total = 0; + struct uwsgi_metric_child *umc = um->children; + while(umc) { + struct uwsgi_metric *c = umc->um; + total += *c->value; + umc = umc->next; + } + + return total + um->arg1n; +} + static int64_t uwsgi_metric_collector_avg(struct uwsgi_metric *um) { int64_t total = 0; int64_t count = 0; @@ -1001,6 +1040,9 @@ void uwsgi_metrics_collectors_setup() { uwsgi_register_metric_collector("ptr", uwsgi_metric_collector_ptr); uwsgi_register_metric_collector("file", uwsgi_metric_collector_file); uwsgi_register_metric_collector("sum", uwsgi_metric_collector_sum); + uwsgi_register_metric_collector("accumulator", uwsgi_metric_collector_accumulator); + uwsgi_register_metric_collector("adder", uwsgi_metric_collector_adder); + uwsgi_register_metric_collector("multiplier", uwsgi_metric_collector_multiplier); uwsgi_register_metric_collector("avg", uwsgi_metric_collector_avg); uwsgi_register_metric_collector("func", uwsgi_metric_collector_func); } diff --git a/core/mule.c b/core/mule.c index 11fba3b7..d2aa8498 100644 --- a/core/mule.c +++ b/core/mule.c @@ -77,6 +77,7 @@ void uwsgi_mule(int id) { } } + uwsgi_hooks_run(uwsgi.hook_as_mule, "as-mule", 1); uwsgi_mule_run(); } diff --git a/core/notify.c b/core/notify.c index 4a550aa4..4e47e735 100644 --- a/core/notify.c +++ b/core/notify.c @@ -1,4 +1,4 @@ -#include "uwsgi.h" +#include extern struct uwsgi_server uwsgi; diff --git a/core/plugins_builder.c b/core/plugins_builder.c new file mode 100644 index 00000000..f0fd38c9 --- /dev/null +++ b/core/plugins_builder.c @@ -0,0 +1,112 @@ +#include + +#define UWSGI_BUILD_DIR ".uwsgi_plugins_builder" + +/* + + steps: + + mkdir(.uwsgi_plugin_builder) + generate .uwsgi_plugin_builder/uwsgi.h + generate .uwsgi_plugin_builder/uwsgiconfig.py + setenv(UWSGI_PLUGINS_BUILDER_CFLAGS=uwsgi_cflags) + exec PYTHON .uwsgi_plugin_builder/uwsgiconfig.py --extra-plugin [name] + +*/ + +void uwsgi_build_plugin(char *directory) { + + if (!uwsgi_file_exists(UWSGI_BUILD_DIR)) { + if (mkdir(UWSGI_BUILD_DIR, S_IRWXU) < 0) { + uwsgi_error("uwsgi_build_plugin()/mkdir() " UWSGI_BUILD_DIR "/"); + _exit(1); + } + } + + char *dot_h = uwsgi_get_dot_h(); + if (!dot_h) { + uwsgi_log("unable to generate uwsgi.h"); + _exit(1); + } + + if (strlen(dot_h) == 0) { + free(dot_h); + uwsgi_log("invalid uwsgi.h"); + _exit(1); + } + + int dot_h_fd = open(UWSGI_BUILD_DIR "/uwsgi.h", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + if (dot_h_fd < 0) { + uwsgi_error_open(UWSGI_BUILD_DIR "/uwsgi.h"); + free(dot_h); + _exit(1); + } + + ssize_t dot_h_len = (ssize_t) strlen(dot_h); + if (write(dot_h_fd, dot_h, dot_h_len) != dot_h_len) { + uwsgi_error("uwsgi_build_plugin()/write()"); + _exit(1); + } + + char *config_py = uwsgi_get_config_py(); + if (!config_py) { + uwsgi_log("unable to generate uwsgiconfig.py"); + _exit(1); + } + + if (strlen(config_py) == 0) { + uwsgi_log("invalid uwsgiconfig.py"); + _exit(1); + } + + int config_py_fd = open(UWSGI_BUILD_DIR "/uwsgiconfig.py", O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR); + if (config_py_fd < 0) { + uwsgi_error_open(UWSGI_BUILD_DIR "/uwsgiconfig.py"); + _exit(1); + } + + ssize_t config_py_len = (ssize_t) strlen(config_py); + if (write(config_py_fd, config_py, config_py_len) != config_py_len) { + uwsgi_error("uwsgi_build_plugin()/write()"); + _exit(1); + } + + char *cflags = uwsgi_get_cflags(); + if (!cflags) { + uwsgi_log("unable to find cflags\n"); + _exit(1); + } + if (strlen(cflags) == 0) { + uwsgi_log("invalid cflags\n"); + _exit(1); + } + + if (setenv("UWSGI_PLUGINS_BUILDER_CFLAGS", cflags, 1)) { + uwsgi_error("uwsgi_build_plugin()/setenv()"); + _exit(1); + } + + // now run the python script + char *argv[6]; + + argv[0] = getenv("PYTHON"); + if (!argv[0]) argv[0] = "python"; + + argv[1] = UWSGI_BUILD_DIR "/uwsgiconfig.py"; + argv[2] = "--extra-plugin"; + char *space = strchr(directory, ' '); + if (space) { + *space = 0; + argv[3] = directory; + argv[4] = space+1; + argv[5] = NULL; + } + else { + argv[3] = directory; + argv[4] = NULL; + } + + execvp(argv[0], argv); + // never here... + _exit(1); +} diff --git a/core/reader.c b/core/reader.c index 1d402113..57359579 100644 --- a/core/reader.c +++ b/core/reader.c @@ -24,6 +24,39 @@ int uwsgi_simple_wait_read_hook(int fd, int timeout) { return ret; } +int uwsgi_simple_wait_read2_hook(int fd0, int fd1, int timeout, int *fd) { + struct pollfd upoll[2]; + timeout = timeout * 1000; + + upoll[0].fd = fd0; + upoll[0].events = POLLIN; + upoll[0].revents = 0; + + upoll[1].fd = fd1; + upoll[1].events = POLLIN; + upoll[1].revents = 0; + + int ret = poll(upoll, 2, timeout); + + if (ret > 0) { + if (upoll[0].revents & POLLIN) { + *fd = fd0; + return 1; + } + if (upoll[1].revents & POLLIN) { + *fd = fd1; + return 1; + } + return -1; + } + if (ret < 0) { + uwsgi_error("uwsgi_simple_wait_read_hook2()/poll()"); + } + + return ret; +} + + /* seek()/rewind() language-independent implementations. */ @@ -32,14 +65,16 @@ void uwsgi_request_body_seek(struct wsgi_request *wsgi_req, off_t pos) { if (wsgi_req->post_file) { if (pos < 0) { if (fseek(wsgi_req->post_file, pos, SEEK_CUR)) { - uwsgi_error("uwsgi_request_body_seek()/fseek()"); + uwsgi_req_error("uwsgi_request_body_seek()/fseek()"); + wsgi_req->read_errors++; } wsgi_req->post_pos = ftell(wsgi_req->post_file); return; } if (fseek(wsgi_req->post_file, pos, SEEK_SET)) { - uwsgi_error("uwsgi_request_body_seek()/fseek()"); + uwsgi_req_error("uwsgi_request_body_seek()/fseek()"); + wsgi_req->read_errors++; } wsgi_req->post_pos = ftell(wsgi_req->post_file); return; @@ -95,7 +130,7 @@ static int consume_body_for_readline(struct wsgi_request *wsgi_req) { if (wsgi_req->post_readline_size - wsgi_req->post_readline_watermark < remains) { char *tmp_buf = realloc(wsgi_req->post_readline_buf, wsgi_req->post_readline_size + remains); if (!tmp_buf) { - uwsgi_error("consume_body_for_readline()/realloc()"); + uwsgi_req_error("consume_body_for_readline()/realloc()"); return -1; } wsgi_req->post_readline_buf = tmp_buf; @@ -114,7 +149,7 @@ static int consume_body_for_readline(struct wsgi_request *wsgi_req) { if (wsgi_req->post_file) { size_t ret = fread(wsgi_req->post_readline_buf + wsgi_req->post_readline_watermark, remains, 1, wsgi_req->post_file); if (ret == 0) { - uwsgi_error("consume_body_for_readline()/fread()"); + uwsgi_req_error("consume_body_for_readline()/fread()"); return -1; } wsgi_req->post_pos += remains; @@ -140,6 +175,7 @@ static int consume_body_for_readline(struct wsgi_request *wsgi_req) { } if (len == 0) { uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } if (len < 0) { @@ -147,6 +183,7 @@ static int consume_body_for_readline(struct wsgi_request *wsgi_req) { goto wait; } uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } wait: @@ -159,6 +196,7 @@ wait: return 0; } uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } // 0 means timeout @@ -167,6 +205,7 @@ wait: return -1; } uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } @@ -204,7 +243,8 @@ char *uwsgi_request_body_readline(struct wsgi_request *wsgi_req, ssize_t hint, s size_t amount = UMIN(uwsgi.buffer_size, wsgi_req->post_cl); wsgi_req->post_readline_buf = malloc(amount); if (!wsgi_req->post_readline_buf) { - uwsgi_error("uwsgi_request_body_readline()/malloc()"); + uwsgi_req_error("uwsgi_request_body_readline()/malloc()"); + wsgi_req->read_errors++; *rlen = -1; return NULL; } @@ -217,6 +257,7 @@ char *uwsgi_request_body_readline(struct wsgi_request *wsgi_req, ssize_t hint, s if (wsgi_req->post_pos >= wsgi_req->post_cl) break; if (consume_body_for_readline(wsgi_req)) { + wsgi_req->read_errors++; *rlen = -1; return NULL; } @@ -272,8 +313,9 @@ char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize if (avail > wsgi_req->post_read_buf_size) { char *tmp_buf = realloc(wsgi_req->post_read_buf, avail); if (!tmp_buf) { - uwsgi_error("uwsgi_request_body_read()/realloc()"); + uwsgi_req_error("uwsgi_request_body_read()/realloc()"); *rlen = -1; + wsgi_req->read_errors++; return NULL; } wsgi_req->post_read_buf = tmp_buf; @@ -320,7 +362,8 @@ char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize if (!wsgi_req->post_read_buf) { wsgi_req->post_read_buf = malloc(remains); if (!wsgi_req->post_read_buf) { - uwsgi_error("uwsgi_request_body_read()/malloc()"); + uwsgi_req_error("uwsgi_request_body_read()/malloc()"); + wsgi_req->read_errors++; *rlen = -1; return NULL; } @@ -331,7 +374,8 @@ char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize if ((remains+*rlen) > wsgi_req->post_read_buf_size) { char *tmp_buf = realloc(wsgi_req->post_read_buf, (remains+*rlen)); if (!tmp_buf) { - uwsgi_error("uwsgi_request_body_read()/realloc()"); + uwsgi_req_error("uwsgi_request_body_read()/realloc()"); + wsgi_req->read_errors++; *rlen = -1; return NULL; } @@ -348,7 +392,8 @@ char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize if (wsgi_req->post_file) { if (fread(wsgi_req->post_read_buf + *rlen, remains, 1, wsgi_req->post_file) != 1) { *rlen = -1; - uwsgi_error("uwsgi_request_body_read()/fread()"); + uwsgi_req_error("uwsgi_request_body_read()/fread()"); + wsgi_req->read_errors++; return NULL; } *rlen += remains; @@ -378,6 +423,7 @@ char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize } *rlen = -1; uwsgi_read_error(remains); + wsgi_req->read_errors++; return NULL; } wait: @@ -398,6 +444,7 @@ wait: } else { uwsgi_read_error(remains); + wsgi_req->read_errors++; } return NULL; } @@ -409,6 +456,7 @@ wait: } *rlen = -1; uwsgi_read_error(remains); + wsgi_req->read_errors++; return NULL; } @@ -447,6 +495,7 @@ int uwsgi_postbuffer_do_in_mem(struct wsgi_request *wsgi_req) { goto wait; } uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } @@ -462,6 +511,7 @@ wait: } if (ret < 0) { uwsgi_read_error(remains); + wsgi_req->read_errors++; return -1; } uwsgi_read_timeout(remains); @@ -482,7 +532,8 @@ int uwsgi_postbuffer_do_in_disk(struct wsgi_request *wsgi_req) { wsgi_req->post_file = uwsgi_tmpfile(); if (!wsgi_req->post_file) { - uwsgi_error("uwsgi_postbuffer_do_in_disk()/uwsgi_tmpfile()"); + uwsgi_req_error("uwsgi_postbuffer_do_in_disk()/uwsgi_tmpfile()"); + wsgi_req->read_errors++; return -1; } @@ -518,6 +569,7 @@ int uwsgi_postbuffer_do_in_disk(struct wsgi_request *wsgi_req) { goto wait; } uwsgi_read_error(remains); + wsgi_req->read_errors++; goto end; } @@ -531,11 +583,13 @@ wait: } else { uwsgi_read_error(remains); + wsgi_req->read_errors++; } goto end; } if (ret < 0) { uwsgi_read_error(remains); + wsgi_req->read_errors++; goto end; } uwsgi_read_timeout(remains); @@ -543,7 +597,8 @@ wait: write: if (fwrite(wsgi_req->post_buffering_buf, rlen, 1, wsgi_req->post_file) != 1) { - uwsgi_error("uwsgi_postbuffer_do_in_disk()/fwrite()"); + uwsgi_req_error("uwsgi_postbuffer_do_in_disk()/fwrite()"); + wsgi_req->read_errors++; goto end; } diff --git a/core/routing.c b/core/routing.c index 2300f941..4513cf47 100644 --- a/core/routing.c +++ b/core/routing.c @@ -962,7 +962,7 @@ static int uwsgi_router_chdir_func(struct wsgi_request *wsgi_req, struct uwsgi_r struct uwsgi_buffer *ub = uwsgi_routing_translate(wsgi_req, ur, *subject, *subject_len, ur->data, ur->data_len); if (!ub) return UWSGI_ROUTE_BREAK; if (chdir(ub->buf)) { - uwsgi_error("uwsgi_router_chdir_func()/chdir()"); + uwsgi_req_error("uwsgi_router_chdir_func()/chdir()"); uwsgi_buffer_destroy(ub); return UWSGI_ROUTE_BREAK; } diff --git a/core/sharedarea.c b/core/sharedarea.c new file mode 100644 index 00000000..680ed0e4 --- /dev/null +++ b/core/sharedarea.c @@ -0,0 +1,295 @@ +#include + +extern struct uwsgi_server uwsgi; + +/* + +This is an high-performance memory area shared by all workers/cores/threads + +Contrary to the caching subsystem it is 1-copy (caching for non-c apps is 2-copy) + +Languages not allowing that kind of access should emulate it calling uwsgi_malloc and then copying it back to +the language object. + +The memory areas could be monitored for changes (read: cores can be suspended while waiting for values) + +You can configure multiple areas specifying multiple --sharedarea options + +This is a very low-level api, try to use it to build higher-level primitives or rely on the caching subsystem + +*/ + +struct uwsgi_sharedarea *uwsgi_sharedarea_get_by_id(int id, uint64_t pos) { + if (id > uwsgi.sharedareas_cnt-1) return NULL; + struct uwsgi_sharedarea *sa = uwsgi.sharedareas[id]; + if (pos > sa->max_pos) return NULL; + return sa; +} + +int64_t uwsgi_sharedarea_read(int id, uint64_t pos, char *blob, uint64_t len) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + len > sa->max_pos + 1) return -1; + if (len == 0) len = (sa->max_pos + 1) - pos; + if (sa->honour_used && sa->used-pos < len) len = sa->used-pos ; + uwsgi_rlock(sa->lock); + memcpy(blob, sa->area + pos, len); + sa->hits++; + uwsgi_rwunlock(sa->lock); + return len; +} + +int uwsgi_sharedarea_write(int id, uint64_t pos, char *blob, uint64_t len) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + len > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + memcpy(sa->area + pos, blob, len); + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_read64(int id, uint64_t pos, int64_t *value) { + return uwsgi_sharedarea_read(id, pos, (char *) value, 8); +} + +int uwsgi_sharedarea_write64(int id, uint64_t pos, int64_t *value) { + return uwsgi_sharedarea_write(id, pos, (char *) value, 8); +} + +int uwsgi_sharedarea_read8(int id, uint64_t pos, int8_t *value) { + return uwsgi_sharedarea_read(id, pos, (char *) value, 1); +} + +int uwsgi_sharedarea_write8(int id, uint64_t pos, int8_t *value) { + return uwsgi_sharedarea_write(id, pos, (char *) value, 1); +} + +int uwsgi_sharedarea_read16(int id, uint64_t pos, int16_t *value) { + return uwsgi_sharedarea_read(id, pos, (char *) value, 2); +} + +int uwsgi_sharedarea_write16(int id, uint64_t pos, int16_t *value) { + return uwsgi_sharedarea_write(id, pos, (char *) value, 2); +} + + +int uwsgi_sharedarea_read32(int id, uint64_t pos, int32_t *value) { + return uwsgi_sharedarea_read(id, pos, (char *) value, 4); +} + +int uwsgi_sharedarea_write32(int id, uint64_t pos, int32_t *value) { + return uwsgi_sharedarea_write(id, pos, (char *) value, 4); +} + +int uwsgi_sharedarea_inc8(int id, uint64_t pos, int8_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 1 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int8_t *n_ptr = (int8_t *) (sa->area + pos); + *n_ptr+=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_inc16(int id, uint64_t pos, int16_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 2 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int16_t *n_ptr = (int16_t *) (sa->area + pos); + *n_ptr+=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_inc32(int id, uint64_t pos, int32_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 4 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int32_t *n_ptr = (int32_t *) (sa->area + pos); + *n_ptr+=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_inc64(int id, uint64_t pos, int64_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 4 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int64_t *n_ptr = (int64_t *) (sa->area + pos); + *n_ptr+=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + + +int uwsgi_sharedarea_dec8(int id, uint64_t pos, int8_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 1 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int8_t *n_ptr = (int8_t *) (sa->area + pos); + *n_ptr-=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_dec16(int id, uint64_t pos, int16_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 2 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int16_t *n_ptr = (int16_t *) (sa->area + pos); + *n_ptr-=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_dec32(int id, uint64_t pos, int32_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 4 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int32_t *n_ptr = (int32_t *) (sa->area + pos); + *n_ptr-=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + +int uwsgi_sharedarea_dec64(int id, uint64_t pos, int64_t amount) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (pos + 4 > sa->max_pos + 1) return -1; + uwsgi_wlock(sa->lock); + int64_t *n_ptr = (int64_t *) (sa->area + pos); + *n_ptr-=amount; + sa->updates++; + uwsgi_rwunlock(sa->lock); + return 0; +} + + + +/* + returns: + 0 -> on updates + -1 -> on error + -2 -> on timeout +*/ +int uwsgi_sharedarea_wait(int id, int freq, int timeout) { + int waiting = 0; + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, 0); + if (!sa) return -1; + if (!freq) freq = 100; + uwsgi_rlock(sa->lock); + uint64_t updates = sa->updates; + uwsgi_rwunlock(sa->lock); + while(timeout == 0 || (timeout > 0 && (waiting/1000) >= timeout)) { + uwsgi.wait_milliseconds_hook(freq); + waiting += freq; + // lock sa + uwsgi_rlock(sa->lock); + if (sa->updates != updates) { + uwsgi_rwunlock(sa->lock); + return 0; + } + // unlock sa + uwsgi_rwunlock(sa->lock); + } + return -2; +} + +int uwsgi_sharedarea_new_id() { + int id = uwsgi.sharedareas_cnt; + uwsgi.sharedareas_cnt++; + if (!uwsgi.sharedareas) { + uwsgi.sharedareas = uwsgi_malloc(sizeof(struct uwsgi_sharedarea *)); + } + else { + struct uwsgi_sharedarea **usa = realloc(uwsgi.sharedareas, ((sizeof(struct uwsgi_sharedarea *)) * uwsgi.sharedareas_cnt)); + if (!usa) { + uwsgi_error("uwsgi_sharedarea_init()/realloc()"); + exit(1); + } + uwsgi.sharedareas = usa; + } + return id; +} + +static struct uwsgi_sharedarea *announce_sa(struct uwsgi_sharedarea *sa) { + uwsgi_log("sharedarea %d created at %p (%d pages, area at %p)\n", sa->id, sa, sa->pages, sa->area); + return sa; +} + +struct uwsgi_sharedarea *uwsgi_sharedarea_init(int pages) { + int id = uwsgi_sharedarea_new_id(); + uwsgi.sharedareas[id] = uwsgi_calloc_shared(uwsgi.page_size * (pages + 1)); + uwsgi.sharedareas[id]->area = ((char *) uwsgi.sharedareas[id]) + uwsgi.page_size; + uwsgi.sharedareas[id]->id = id; + uwsgi.sharedareas[id]->fd = -1; + uwsgi.sharedareas[id]->pages = pages; + uwsgi.sharedareas[id]->max_pos = (uwsgi.page_size * pages) -1; + char *id_str = uwsgi_num2str(id); + uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str)); + free(id_str); + return announce_sa(uwsgi.sharedareas[id]); +} + +struct uwsgi_sharedarea *uwsgi_sharedarea_init_ptr(char *area, uint64_t len) { + int id = uwsgi_sharedarea_new_id(); + uwsgi.sharedareas[id] = uwsgi_calloc_shared(sizeof(struct uwsgi_sharedarea)); + uwsgi.sharedareas[id]->area = area; + uwsgi.sharedareas[id]->id = id; + uwsgi.sharedareas[id]->fd = -1; + uwsgi.sharedareas[id]->pages = len / uwsgi.page_size; + if (len % uwsgi.page_size != 0) uwsgi.sharedareas[id]->pages++; + uwsgi.sharedareas[id]->max_pos = len-1; + char *id_str = uwsgi_num2str(id); + uwsgi.sharedareas[id]->lock = uwsgi_rwlock_init(uwsgi_concat2("sharedarea", id_str)); + free(id_str); + return announce_sa(uwsgi.sharedareas[id]); +} + +struct uwsgi_sharedarea *uwsgi_sharedarea_init_keyval(char *arg) { + char *s_pages = NULL; + char *s_file = NULL; + char *s_fd = NULL; + char *s_ptr = NULL; + char *s_size = NULL; + if (uwsgi_kvlist_parse(arg, strlen(arg), ',', '=', + "pages", &s_pages, + "file", &s_file, + "fd", &s_fd, + "ptr", &s_ptr, + "size", &s_size, + NULL)) { + } + + return NULL; +} + + +void uwsgi_sharedareas_init() { + struct uwsgi_string_list *usl = NULL; + uwsgi_foreach(usl, uwsgi.sharedareas_list) { + char *is_keyval = strchr(usl->value, '='); + if (!is_keyval) { + uwsgi_sharedarea_init(atoi(usl->value)); + } + else { + uwsgi_sharedarea_init_keyval(usl->value); + } + } +} diff --git a/core/socket.c b/core/socket.c index 8f35394d..bd0577fe 100644 --- a/core/socket.c +++ b/core/socket.c @@ -1931,17 +1931,18 @@ found: void uwsgi_protocols_register() { uwsgi_register_protocol("uwsgi", uwsgi_proto_uwsgi_setup); uwsgi_register_protocol("puwsgi", uwsgi_proto_puwsgi_setup); + uwsgi_register_protocol("http", uwsgi_proto_http_setup); + #ifdef UWSGI_SSL uwsgi_register_protocol("suwsgi", uwsgi_proto_suwsgi_setup); uwsgi_register_protocol("https", uwsgi_proto_https_setup); #endif uwsgi_register_protocol("fastcgi", uwsgi_proto_fastcgi_setup); uwsgi_register_protocol("fastcgi-nph", uwsgi_proto_fastcgi_nph_setup); + uwsgi_register_protocol("scgi", uwsgi_proto_scgi_setup); uwsgi_register_protocol("scgi-nph", uwsgi_proto_scgi_nph_setup); -#ifdef UWSGI_ZEROMQ - uwsgi_register_protocol("zmq", uwsgi_proto_zmq_setup); -#endif + uwsgi_register_protocol("raw", uwsgi_proto_raw_setup); } diff --git a/core/subscription.c b/core/subscription.c index 3cf9926c..bf07314a 100644 --- a/core/subscription.c +++ b/core/subscription.c @@ -812,6 +812,8 @@ end: } void uwsgi_subscribe_all(uint8_t cmd, int verbose) { + + if (!uwsgi.subscriptions_blocked) return; // -- subscribe struct uwsgi_string_list *subscriptions = uwsgi.subscriptions; while (subscriptions) { diff --git a/core/utils.c b/core/utils.c index 98f2cfc3..75307989 100644 --- a/core/utils.c +++ b/core/utils.c @@ -1038,6 +1038,7 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) { uwsgi.workers[uwsgi.mywid].requests++; uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests++; uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].write_errors += wsgi_req->write_errors; + uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].read_errors += wsgi_req->read_errors; // this is used for MAX_REQUESTS uwsgi.workers[uwsgi.mywid].delta_requests++; } @@ -1073,6 +1074,13 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) { if (!wsgi_req->is_raw && uwsgi.p[wsgi_req->uh->modifier1]->after_request) uwsgi.p[wsgi_req->uh->modifier1]->after_request(wsgi_req); + // after_request custom hooks + struct uwsgi_string_list *usl = NULL; + uwsgi_foreach(usl, uwsgi.after_request_hooks) { + void (*func)(struct wsgi_request *) = (void (*)(struct wsgi_request *)) usl->custom_ptr; + func(wsgi_req); + } + if (uwsgi.threads > 1) { // now the thread can die... pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &tmp_id); @@ -1137,6 +1145,9 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) { if (wsgi_req->websocket_buf) { uwsgi_buffer_destroy(wsgi_req->websocket_buf); } + if (wsgi_req->websocket_send_buf) { + uwsgi_buffer_destroy(wsgi_req->websocket_send_buf); + } // reset request @@ -1162,7 +1173,7 @@ void uwsgi_close_request(struct wsgi_request *wsgi_req) { } - // ready to accept request, if i am a vassal signal Emperor about my loyalty + // after the first request, if i am a vassal, signal Emperor about my loyalty if (uwsgi.has_emperor && !uwsgi.loyal) { uwsgi_log("announcing my loyalty to the Emperor...\n"); char byte = 17; @@ -3922,9 +3933,9 @@ char *uwsgi_strip(char *src) { void uwsgi_uuid(char *buf) { #ifdef UWSGI_UUID - uuid_t uuid_zmq; - uuid_generate(uuid_zmq); - uuid_unparse(uuid_zmq, buf); + uuid_t uuid_value; + uuid_generate(uuid_value); + uuid_unparse(uuid_value, buf); #else int i,r[11]; if (!uwsgi_file_exists("/dev/urandom")) goto fallback; diff --git a/core/uwsgi.c b/core/uwsgi.c index 6aa8aae0..c4196556 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -241,7 +241,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"worker-reload-mercy", required_argument, 0, "set the maximum time (in seconds) a worker can take to reload/shutdown (default is 60)", uwsgi_opt_set_int, &uwsgi.worker_reload_mercy, 0}, {"exit-on-reload", no_argument, 0, "force exit even if a reload is requested", uwsgi_opt_true, &uwsgi.exit_on_reload, 0}, {"die-on-term", no_argument, 0, "exit instead of brutal reload on SIGTERM", uwsgi_opt_true, &uwsgi.die_on_term, 0}, - {"force-gateway", no_argument, 0, "force teh spawn of the first registered gateway without a master", uwsgi_opt_true, &uwsgi.force_gateway, 0}, + {"force-gateway", no_argument, 0, "force the spawn of the first registered gateway without a master", uwsgi_opt_true, &uwsgi.force_gateway, 0}, {"help", no_argument, 'h', "show this help", uwsgi_help, NULL, UWSGI_OPT_IMMEDIATE}, {"usage", no_argument, 'h', "show this help", uwsgi_help, NULL, UWSGI_OPT_IMMEDIATE}, @@ -259,7 +259,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"lock-engine", required_argument, 0, "set the lock engine", uwsgi_opt_set_str, &uwsgi.lock_engine, 0}, {"ftok", required_argument, 0, "set the ipcsem key via ftok() for avoiding duplicates", uwsgi_opt_set_str, &uwsgi.ftok, 0}, {"persistent-ipcsem", no_argument, 0, "do not remove ipcsem's on shutdown", uwsgi_opt_true, &uwsgi.persistent_ipcsem, 0}, - {"sharedarea", required_argument, 'A', "create a raw shared memory area of specified pages", uwsgi_opt_set_int, &uwsgi.sharedareasize, 0}, + {"sharedarea", required_argument, 'A', "create a raw shared memory area of specified pages (note: it supports keyval too)", uwsgi_opt_add_string_list, &uwsgi.sharedareas_list, 0}, {"safe-fd", required_argument, 0, "do not close the specified file descriptor", uwsgi_opt_safe_fd, NULL, 0}, {"fd-safe", required_argument, 0, "do not close the specified file descriptor", uwsgi_opt_safe_fd, NULL, 0}, @@ -373,9 +373,18 @@ static struct uwsgi_option uwsgi_base_options[] = { {"hook-as-user-atexit", required_argument, 0, "run the specified hook before app exit and reload", uwsgi_opt_add_string_list, &uwsgi.hook_as_user_atexit, 0}, {"hook-pre-app", required_argument, 0, "run the specified hook before app loading", uwsgi_opt_add_string_list, &uwsgi.hook_pre_app, 0}, {"hook-post-app", required_argument, 0, "run the specified hook after app loading", uwsgi_opt_add_string_list, &uwsgi.hook_post_app, 0}, + {"hook-accepting", required_argument, 0, "run the specified hook after each worker enter the accepting phase", uwsgi_opt_add_string_list, &uwsgi.hook_accepting, 0}, + {"hook-accepting1", required_argument, 0, "run the specified hook after the first worker enters the accepting phase", uwsgi_opt_add_string_list, &uwsgi.hook_accepting1, 0}, + {"hook-accepting-once", required_argument, 0, "run the specified hook after each worker enter the accepting phase (once per-instance)", uwsgi_opt_add_string_list, &uwsgi.hook_accepting_once, 0}, + {"hook-accepting1-once", required_argument, 0, "run the specified hook after the first worker enters the accepting phase (once per instance)", uwsgi_opt_add_string_list, &uwsgi.hook_accepting1_once, 0}, - {"hook-as-vassal", required_argument, 0, "run the specified command before exec()ing the vassal", uwsgi_opt_add_string_list, &uwsgi.hook_as_vassal, 0}, - {"hook-as-emperor", required_argument, 0, "run the specified command in the emperor after the vassal has been started", uwsgi_opt_add_string_list, &uwsgi.hook_as_emperor, 0}, + {"hook-as-vassal", required_argument, 0, "run the specified hook before exec()ing the vassal", uwsgi_opt_add_string_list, &uwsgi.hook_as_vassal, 0}, + {"hook-as-emperor", required_argument, 0, "run the specified hook in the emperor after the vassal has been started", uwsgi_opt_add_string_list, &uwsgi.hook_as_emperor, 0}, + + {"hook-as-mule", required_argument, 0, "run the specified hook in each mule", uwsgi_opt_add_string_list, &uwsgi.hook_as_mule, 0}, + + {"after-request-hook", required_argument, 0, "run the specified function/symbol after each request", uwsgi_opt_add_string_list, &uwsgi.after_request_hooks, 0}, + {"after-request-call", required_argument, 0, "run the specified function/symbol after each request", uwsgi_opt_add_string_list, &uwsgi.after_request_hooks, 0}, {"exec-asap", required_argument, 0, "run the specified command as soon as possible", uwsgi_opt_add_string_list, &uwsgi.exec_asap, 0}, {"exec-pre-jail", required_argument, 0, "run the specified command before jailing", uwsgi_opt_add_string_list, &uwsgi.exec_pre_jail, 0}, @@ -449,12 +458,6 @@ static struct uwsgi_option uwsgi_base_options[] = { #ifdef UWSGI_JSON {"json", required_argument, 'j', "load config from json file", uwsgi_opt_load_json, NULL, UWSGI_OPT_IMMEDIATE}, {"js", required_argument, 'j', "load config from json file", uwsgi_opt_load_json, NULL, UWSGI_OPT_IMMEDIATE}, -#endif -#ifdef UWSGI_ZEROMQ - {"zeromq", required_argument, 0, "create a zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "zmq", 0}, - {"zmq", required_argument, 0, "create a zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "zmq", 0}, - {"zeromq-socket", required_argument, 0, "create a zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "zmq", 0}, - {"zmq-socket", required_argument, 0, "create a zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "zmq", 0}, #endif {"weight", required_argument, 0, "weight of the instance (used by clustering/lb/subscriptions)", uwsgi_opt_set_64bit, &uwsgi.weight, 0}, {"auto-weight", required_argument, 0, "set weight of the instance (used by clustering/lb/subscriptions) automatically", uwsgi_opt_true, &uwsgi.auto_weight, 0}, @@ -649,9 +652,6 @@ static struct uwsgi_option uwsgi_base_options[] = { {"alarm-list", no_argument, 0, "list enabled alarms", uwsgi_opt_true, &uwsgi.alarms_list, 0}, {"alarms-list", no_argument, 0, "list enabled alarms", uwsgi_opt_true, &uwsgi.alarms_list, 0}, {"alarm-msg-size", required_argument, 0, "set the max size of an alarm message (default 8192)", uwsgi_opt_set_64bit, &uwsgi.alarm_msg_size, 0}, -#ifdef UWSGI_ZEROMQ - {"log-zeromq", required_argument, 0, "send logs to a zeromq server", uwsgi_opt_set_logger, "zeromq", UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, -#endif {"log-master", no_argument, 0, "delegate logging to master process", uwsgi_opt_true, &uwsgi.log_master, UWSGI_OPT_MASTER}, {"log-master-bufsize", required_argument, 0, "set the buffer size for the master logger. bigger log messages will be truncated", uwsgi_opt_set_64bit, &uwsgi.log_master_bufsize, 0}, {"log-master-stream", no_argument, 0, "create the master logpipe as SOCK_STREAM", uwsgi_opt_true, &uwsgi.log_master_stream, 0}, @@ -671,6 +671,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"log-5xx", no_argument, 0, "log requests with a 5xx response", uwsgi_opt_dyn_true, (void *) UWSGI_OPTION_LOG_5xx, 0}, {"log-big", required_argument, 0, "log requestes bigger than the specified size", uwsgi_opt_set_dyn, (void *) UWSGI_OPTION_LOG_BIG, 0}, {"log-sendfile", required_argument, 0, "log sendfile requests", uwsgi_opt_dyn_true, (void *) UWSGI_OPTION_LOG_SENDFILE, 0}, + {"log-ioerror", required_argument, 0, "log requests with io errors", uwsgi_opt_dyn_true, (void *) UWSGI_OPTION_LOG_IOERROR, 0}, {"log-micros", no_argument, 0, "report response time in microseconds instead of milliseconds", uwsgi_opt_true, &uwsgi.log_micros, 0}, {"log-x-forwarded-for", no_argument, 0, "use the ip from X-Forwarded-For header instead of REMOTE_ADDR", uwsgi_opt_true, &uwsgi.log_x_forwarded_for, 0}, {"master-as-root", no_argument, 0, "leave master process running as root", uwsgi_opt_true, &uwsgi.master_as_root, 0}, @@ -770,6 +771,11 @@ static struct uwsgi_option uwsgi_base_options[] = { {"routers-list", no_argument, 0, "list enabled routers", uwsgi_opt_true, &uwsgi.router_list, 0}, #endif + + {"error-page-403", required_argument, 0, "add an error page (html) for managed 403 response", uwsgi_opt_add_string_list, &uwsgi.error_page_403, 0}, + {"error-page-404", required_argument, 0, "add an error page (html) for managed 404 response", uwsgi_opt_add_string_list, &uwsgi.error_page_404, 0}, + {"error-page-500", required_argument, 0, "add an error page (html) for managed 500 response", uwsgi_opt_add_string_list, &uwsgi.error_page_500, 0}, + {"websockets-ping-freq", required_argument, 0, "set the frequency (in seconds) of websockets automatic ping packets", uwsgi_opt_set_int, &uwsgi.websockets_ping_freq, 0}, {"websocket-ping-freq", required_argument, 0, "set the frequency (in seconds) of websockets automatic ping packets", uwsgi_opt_set_int, &uwsgi.websockets_ping_freq, 0}, @@ -886,7 +892,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"worker-exec", required_argument, 0, "run the specified command as worker", uwsgi_opt_set_str, &uwsgi.worker_exec, 0}, {"worker-exec2", required_argument, 0, "run the specified command as worker (after post_fork hook)", uwsgi_opt_set_str, &uwsgi.worker_exec2, 0}, {"attach-daemon", required_argument, 0, "attach a command/daemon to the master process (the command has to not go in background)", uwsgi_opt_add_daemon, NULL, UWSGI_OPT_MASTER}, - {"attach-control-daemon", required_argument, 0, "attach a command/daemon to the master process (the command has to not go in background), when teh daemon dies, the master dies too", uwsgi_opt_add_daemon, NULL, UWSGI_OPT_MASTER}, + {"attach-control-daemon", required_argument, 0, "attach a command/daemon to the master process (the command has to not go in background), when the daemon dies, the master dies too", uwsgi_opt_add_daemon, NULL, UWSGI_OPT_MASTER}, {"smart-attach-daemon", required_argument, 0, "attach a command/daemon to the master process managed by a pidfile (the command has to daemonize)", uwsgi_opt_add_daemon, NULL, UWSGI_OPT_MASTER}, {"smart-attach-daemon2", required_argument, 0, "attach a command/daemon to the master process managed by a pidfile (the command has to NOT daemonize)", uwsgi_opt_add_daemon, NULL, UWSGI_OPT_MASTER}, #ifdef UWSGI_SSL @@ -916,6 +922,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"exit", optional_argument, 0, "force exit() of the instance", uwsgi_opt_exit, NULL, UWSGI_OPT_IMMEDIATE}, {"cflags", no_argument, 0, "report uWSGI CFLAGS (useful for building external plugins)", uwsgi_opt_cflags, NULL, UWSGI_OPT_IMMEDIATE}, {"dot-h", no_argument, 0, "dump the uwsgi.h used for building the core (useful for building external plugins)", uwsgi_opt_dot_h, NULL, UWSGI_OPT_IMMEDIATE}, + {"config-py", no_argument, 0, "dump the uwsgiconfig.py used for building the core (useful for building external plugins)", uwsgi_opt_config_py, NULL, UWSGI_OPT_IMMEDIATE}, + {"build-plugin", required_argument, 0, "build a uWSGI plugin for the current binary", uwsgi_opt_build_plugin, NULL, UWSGI_OPT_IMMEDIATE}, {"version", no_argument, 0, "print uWSGI version", uwsgi_opt_print, UWSGI_VERSION, 0}, {0, 0, 0, 0, 0, 0, 0} }; @@ -1847,14 +1855,19 @@ void uwsgi_init_random() { #ifdef UWSGI_AS_SHARED_LIBRARY int uwsgi_init(int argc, char *argv[], char *envp[]) { +#else +int main(int argc, char *argv[], char *envp[]) { +#endif + uwsgi_setup(argc, argv, envp); + return uwsgi_run(); +} +void uwsgi_setup(int argc, char *argv[], char *envp[]) { +#ifdef UWSGI_AS_SHARED_LIBRARY #ifdef __APPLE__ char ***envPtr = _NSGetEnviron(); environ = *envPtr; #endif - -#else -int main(int argc, char *argv[], char *envp[]) { #endif int i; @@ -1990,11 +2003,6 @@ int main(int argc, char *argv[], char *envp[]) { } uwsgi.hostname_len = strlen(uwsgi.hostname); -#ifdef UWSGI_ZEROMQ - uwsgi_register_logger("zeromq", uwsgi_zeromq_logger); - uwsgi_register_logger("zmq", uwsgi_zeromq_logger); -#endif - #ifdef UWSGI_ROUTING uwsgi_register_embedded_routers(); #endif @@ -2318,7 +2326,6 @@ int main(int argc, char *argv[], char *envp[]) { } } - // TODO pluginize basic Linux namespace support #if defined(__linux__) && !defined(__ia64__) if (uwsgi.ns) { @@ -2331,10 +2338,6 @@ int main(int argc, char *argv[], char *envp[]) { #if defined(__linux__) && !defined(__ia64__) } #endif - - - // never here - return 0; } @@ -2526,9 +2529,6 @@ int uwsgi_start(void *v_argv) { } } uwsgi_log_initial("- async cores set to %d - fd table size: %d\n", uwsgi.async, (int) uwsgi.max_fd); - // optimization, this array maps file descriptor to requests - uwsgi.async_waiting_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd); - uwsgi.async_proto_fd_table = uwsgi_calloc(sizeof(struct wsgi_request *) * uwsgi.max_fd); } #ifdef UWSGI_DEBUG @@ -2551,12 +2551,8 @@ int uwsgi_start(void *v_argv) { // allocate rpc structures uwsgi_rpc_init(); - // setup sharedarea - if (uwsgi.sharedareasize > 0) { - uwsgi.sharedarea = uwsgi_calloc_shared(uwsgi.page_size * uwsgi.sharedareasize); - uwsgi_log("shared area mapped at %p, you can access it with uwsgi.sharedarea* functions.\n", uwsgi.sharedarea); - uwsgi.sa_lock = uwsgi_rwlock_init("sharedarea"); - } + // initialize sharedareas + uwsgi_sharedareas_init(); uwsgi.snmp_lock = uwsgi_lock_init("snmp"); @@ -2615,6 +2611,7 @@ int uwsgi_start(void *v_argv) { uwsgi_setup_systemd(); uwsgi_setup_upstart(); uwsgi_setup_zerg(); + uwsgi_setup_emperor(); } @@ -2907,6 +2904,15 @@ next: } } + // initialize after_request hooks + uwsgi_foreach(usl, uwsgi.after_request_hooks) { + usl->custom_ptr = dlsym(RTLD_DEFAULT, usl->value); + if (!usl->custom_ptr) { + uwsgi_log("unable to find symbol/function \"%s\"\n", usl->value); + exit(1); + } + uwsgi_log("added \"%s(struct wsgi_request *)\" to the after-request chain\n", usl->value); + } if (uwsgi.daemonize2) { masterpid = uwsgi_daemonize2(); @@ -3050,9 +3056,14 @@ next2: // END OF INITIALIZATION + return 0; + +} + +int uwsgi_run() { // !!! from now on, we could be in the master or in a worker !!! - + int i; if (getpid() == masterpid && uwsgi.master_process == 1) { #ifdef UWSGI_AS_SHARED_LIBRARY @@ -3166,18 +3177,17 @@ void uwsgi_worker_run() { // some apps could be mounted only on specific workers uwsgi_init_worker_mount_apps(); - -#ifdef UWSGI_ZEROMQ - // setup zeromq context (if required) one per-worker - if (uwsgi.zeromq) { - uwsgi_zeromq_init_sockets(); - } -#endif - - //postpone the queue initialization as kevent - //do not pass kfd after fork() if (uwsgi.async > 1) { - uwsgi_async_init(); + // a stack of unused cores + uwsgi.async_queue_unused = uwsgi_malloc(sizeof(struct wsgi_request *) * uwsgi.async); + + // fill it with default values + for (i = 0; i < uwsgi.async; i++) { + uwsgi.async_queue_unused[i] = &uwsgi.workers[uwsgi.mywid].cores[i].req; + } + + // the first available core is the last one + uwsgi.async_queue_unused_ptr = uwsgi.async - 1; } // setup UNIX signals for the worker @@ -3271,6 +3281,28 @@ void uwsgi_ignition() { } } + // mark the worker as "accepting" (this is a mark used by chain reloading) + uwsgi.workers[uwsgi.mywid].accepting = 1; + // ready to accept request, if i am a vassal signal Emperor about it + if (uwsgi.has_emperor && uwsgi.mywid == 1) { + char byte = 5; + if (write(uwsgi.emperor_fd, &byte, 1) != 1) { + uwsgi_error("write()"); + } + } + + // run accepting hooks + uwsgi_hooks_run(uwsgi.hook_accepting, "accepting", 1); + if (uwsgi.workers[uwsgi.mywid].respawn_count == 1) { + uwsgi_hooks_run(uwsgi.hook_accepting_once, "accepting-once", 1); + } + + if (uwsgi.mywid == 1) { + uwsgi_hooks_run(uwsgi.hook_accepting1, "accepting1", 1); + if (uwsgi.workers[uwsgi.mywid].respawn_count == 1) { + uwsgi_hooks_run(uwsgi.hook_accepting1_once, "accepting1-once", 1); + } + } if (uwsgi.loop) { void (*u_loop) (void) = uwsgi_get_loop(uwsgi.loop); @@ -3718,7 +3750,7 @@ void uwsgi_opt_set_16bit(char *opt, char *value, void *key) { void uwsgi_opt_set_megabytes(char *opt, char *value, void *key) { uint64_t *ptr = (uint64_t *) key; - *ptr = (strtoul(value, NULL, 10)) * 1024 * 1024; + *ptr = (uint64_t)strtoul(value, NULL, 10) * 1024 * 1024; } void uwsgi_opt_set_dyn(char *opt, char *value, void *key) { @@ -4518,6 +4550,45 @@ void uwsgi_opt_dot_h(char *opt, char *filename, void *foobar) { exit(0); } +extern char *uwsgi_config_py; +char *uwsgi_get_config_py() { + char *src = uwsgi_config_py; + size_t len = strlen(src); + char *ptr = uwsgi_malloc(len / 2); + char *base = ptr; + size_t i; + unsigned int u; + for (i = 0; i < len; i += 2) { + sscanf(src + i, "%2x", &u); + *ptr++ = (char) u; + } +#ifdef UWSGI_ZLIB + struct uwsgi_buffer *ub = uwsgi_zlib_decompress(base, ptr-base); + if (!ub) { + free(base); + return ""; + } + // add final null byte + uwsgi_buffer_append(ub, "\0", 1); + free(base); + // base is the final blob + base = ub->buf; + ub->buf = NULL; + uwsgi_buffer_destroy(ub); +#endif + return base; +} + +void uwsgi_opt_config_py(char *opt, char *filename, void *foobar) { + fprintf(stdout, "%s\n", uwsgi_get_config_py()); + exit(0); +} + + +void uwsgi_opt_build_plugin(char *opt, char *directory, void *foobar) { + uwsgi_build_plugin(directory); + exit(1); +} void uwsgi_opt_connect_and_read(char *opt, char *address, void *foobar) { diff --git a/core/websockets.c b/core/websockets.c index aea5ac04..e03df308 100644 --- a/core/websockets.c +++ b/core/websockets.c @@ -10,9 +10,17 @@ extern struct uwsgi_server uwsgi; -static struct uwsgi_buffer *uwsgi_websocket_message(char *msg, size_t len) { - struct uwsgi_buffer *ub = uwsgi_buffer_new(10 + len); - if (uwsgi_buffer_u8(ub, 0x81)) goto error; +static struct uwsgi_buffer *uwsgi_websocket_message(struct wsgi_request *wsgi_req, char *msg, size_t len, uint8_t opcode) { + struct uwsgi_buffer *ub = wsgi_req->websocket_send_buf; + if (!ub) { + wsgi_req->websocket_send_buf = uwsgi_buffer_new(10 + len); + ub = wsgi_req->websocket_send_buf; + } + else { + // reset the buffer + ub->pos = 0; + } + if (uwsgi_buffer_u8(ub, opcode)) goto error; if (len < 126) { if (uwsgi_buffer_u8(ub, len)) goto error; } @@ -29,7 +37,6 @@ static struct uwsgi_buffer *uwsgi_websocket_message(char *msg, size_t len) { return ub; error: - uwsgi_buffer_destroy(ub); return NULL; } @@ -66,27 +73,72 @@ static int uwsgi_websockets_check_pingpong(struct wsgi_request *wsgi_req) { return 0; } -static int uwsgi_websocket_send_do(struct wsgi_request *wsgi_req, char *msg, size_t len) { - struct uwsgi_buffer *ub = uwsgi_websocket_message(msg, len); +static int uwsgi_websocket_send_do(struct wsgi_request *wsgi_req, char *msg, size_t len, uint8_t opcode) { + struct uwsgi_buffer *ub = uwsgi_websocket_message(wsgi_req, msg, len, opcode); if (!ub) return -1; - ssize_t ret = uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); - uwsgi_buffer_destroy(ub); - return ret; - + return uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); +} + +static int uwsgi_websocket_send_from_sharedarea_do(struct wsgi_request *wsgi_req, int id, uint64_t pos, uint64_t len, uint8_t opcode) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) return -1; + if (!len) { + len = sa->honour_used ? sa->used-pos : ((sa->max_pos+1)-pos); + } + uwsgi_rlock(sa->lock); + sa->hits++; + struct uwsgi_buffer *ub = uwsgi_websocket_message(wsgi_req, sa->area, len, opcode); + uwsgi_rwunlock(sa->lock); + if (!ub) return -1; + + return uwsgi_response_write_body_do(wsgi_req, ub->buf, ub->pos); } int uwsgi_websocket_send(struct wsgi_request *wsgi_req, char *msg, size_t len) { if (wsgi_req->websocket_closed) { return -1; } - ssize_t ret = uwsgi_websocket_send_do(wsgi_req, msg, len); + ssize_t ret = uwsgi_websocket_send_do(wsgi_req, msg, len, 0x81); if (ret < 0) { wsgi_req->websocket_closed = 1; } return ret; } +int uwsgi_websocket_send_from_sharedarea(struct wsgi_request *wsgi_req, int id, uint64_t pos, uint64_t len) { + if (wsgi_req->websocket_closed) { + return -1; + } + ssize_t ret = uwsgi_websocket_send_from_sharedarea_do(wsgi_req, id, pos, len, 0x81); + if (ret < 0) { + wsgi_req->websocket_closed = 1; + } + return ret; +} + +int uwsgi_websocket_send_binary(struct wsgi_request *wsgi_req, char *msg, size_t len) { + if (wsgi_req->websocket_closed) { + return -1; + } + ssize_t ret = uwsgi_websocket_send_do(wsgi_req, msg, len, 0x82); + if (ret < 0) { + wsgi_req->websocket_closed = 1; + } + return ret; +} + +int uwsgi_websocket_send_binary_from_sharedarea(struct wsgi_request *wsgi_req, int id, uint64_t pos, uint64_t len) { + if (wsgi_req->websocket_closed) { + return -1; + } + ssize_t ret = uwsgi_websocket_send_from_sharedarea_do(wsgi_req, id, pos, len, 0x82); + if (ret < 0) { + wsgi_req->websocket_closed = 1; + } + return ret; +} + static void uwsgi_websocket_parse_header(struct wsgi_request *wsgi_req) { uint8_t byte1 = wsgi_req->websocket_buf->buf[0]; uint8_t byte2 = wsgi_req->websocket_buf->buf[1]; diff --git a/core/writer.c b/core/writer.c index 4bbd58d4..3a10d9ba 100644 --- a/core/writer.c +++ b/core/writer.c @@ -179,7 +179,7 @@ int uwsgi_response_add_header_force(struct wsgi_request *wsgi_req, char *key, ui return uwsgi_response_add_header_do(wsgi_req, key, key_len, value, value_len); } -int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { +static int uwsgi_response_write_headers_do0(struct wsgi_request *wsgi_req) { if (wsgi_req->headers_sent || !wsgi_req->headers || wsgi_req->response_size || wsgi_req->write_errors) { return UWSGI_OK; } @@ -209,11 +209,20 @@ int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { if (wsgi_req->socket->proto_fix_headers(wsgi_req)) { wsgi_req->write_errors++ ; return -1;} + return UWSGI_AGAIN; +} + +int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { + + int ret = uwsgi_response_write_headers_do0(wsgi_req); + if (ret != UWSGI_AGAIN) return ret; + for(;;) { + errno = 0; int ret = wsgi_req->socket->proto_write_headers(wsgi_req, wsgi_req->headers->buf, wsgi_req->headers->pos); if (ret < 0) { if (!uwsgi.ignore_write_errors) { - uwsgi_error("uwsgi_response_write_headers_do()"); + uwsgi_req_error("uwsgi_response_write_headers_do()"); } wsgi_req->write_errors++; return -1; @@ -221,6 +230,7 @@ int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { if (ret == UWSGI_OK) { break; } + if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { @@ -238,6 +248,100 @@ int uwsgi_response_write_headers_do(struct wsgi_request *wsgi_req) { return UWSGI_OK; } +/* + private function for highly optimized writes (1 single syscall for headers and body) +*/ +static int uwsgi_response_writev_headers_and_body_do(struct wsgi_request *wsgi_req, char *buf, size_t len) { + + struct iovec iov[2]; + + int ret = uwsgi_response_write_headers_do0(wsgi_req); + if (ret != UWSGI_AGAIN) return ret; + + iov[0].iov_base = wsgi_req->headers->buf; + iov[0].iov_len = wsgi_req->headers->pos; + iov[1].iov_base = buf; + iov[1].iov_len = len; + + size_t iov_len = 2; + for(;;) { + errno = 0; + // no need to use writev if a single iovec remains + if (iov_len == 1) { + buf = iov[0].iov_base; + len = iov[0].iov_len; + // update counters + wsgi_req->headers_size += wsgi_req->headers->pos; + wsgi_req->headers_sent = 1; + wsgi_req->response_size += wsgi_req->write_pos - wsgi_req->headers_size; + wsgi_req->write_pos = 0; + goto fallback; + } + int ret = wsgi_req->socket->proto_writev(wsgi_req, iov, &iov_len); + if (ret < 0) { + if (!uwsgi.ignore_write_errors) { + uwsgi_req_error("uwsgi_response_writev_headers_and_body_do()"); + } + wsgi_req->write_errors++; + return -1; + } + if (ret == UWSGI_OK) { + break; + } + if (!uwsgi_is_again()) continue; + ret = uwsgi_wait_write_req(wsgi_req); + if (ret < 0) { wsgi_req->write_errors++; return -1;} + if (ret == 0) { + uwsgi_log("uwsgi_response_writev_headers_and_body_do() TIMEOUT !!!\n"); + wsgi_req->write_errors++; + return -1; + } + } + + wsgi_req->headers_size += wsgi_req->headers->pos; + wsgi_req->response_size += len; + wsgi_req->headers_sent = 1; + + // reset for the next write + wsgi_req->write_pos = 0; + + return UWSGI_OK; + +fallback: + + for(;;) { + errno = 0; + int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); + if (ret < 0) { + if (!uwsgi.ignore_write_errors) { + // here we use the parent name + uwsgi_req_error("uwsgi_response_write_body_do()"); + } + wsgi_req->write_errors++; + return -1; + } + if (ret == UWSGI_OK) { + break; + } + if (!uwsgi_is_again()) continue; + ret = uwsgi_wait_write_req(wsgi_req); + if (ret < 0) { wsgi_req->write_errors++; return -1;} + if (ret == 0) { + // here we use the parent name + uwsgi_log("uwsgi_response_write_body_do() TIMEOUT !!!\n"); + wsgi_req->write_errors++; + return -1; + } + } + + wsgi_req->response_size += wsgi_req->write_pos; + // reset for the next write + wsgi_req->write_pos = 0; + + return UWSGI_OK; + +} + // this is the function called by all request plugins to send chunks to the client int uwsgi_response_write_body_do(struct wsgi_request *wsgi_req, char *buf, size_t len) { @@ -278,6 +382,9 @@ int uwsgi_response_write_body_do(struct wsgi_request *wsgi_req, char *buf, size_ write: // send headers if not already sent if (!wsgi_req->headers_sent) { + if (wsgi_req->socket->proto_writev && len > 0 && wsgi_req->headers) { + return uwsgi_response_writev_headers_and_body_do(wsgi_req, buf, len); + } int ret = uwsgi_response_write_headers_do(wsgi_req); if (ret == UWSGI_OK) goto sendbody; if (ret == UWSGI_AGAIN) return UWSGI_AGAIN; @@ -290,10 +397,11 @@ sendbody: if (len == 0) return UWSGI_OK; for(;;) { + errno = 0; int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { - uwsgi_error("uwsgi_response_write_body_do()"); + uwsgi_req_error("uwsgi_response_write_body_do()"); } wsgi_req->write_errors++; return -1; @@ -301,6 +409,7 @@ sendbody: if (ret == UWSGI_OK) { break; } + if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { @@ -317,6 +426,130 @@ sendbody: return UWSGI_OK; } +int uwsgi_response_writev_body_do(struct wsgi_request *wsgi_req, struct iovec *iov, size_t len) { + + if (wsgi_req->write_errors) return -1; + if (wsgi_req->ignore_body) return UWSGI_OK; + +#ifdef UWSGI_ROUTING + // special case here, we could need to set transformations before + if (!wsgi_req->headers_sent) { + // apply response routes + if (uwsgi_apply_response_routes(wsgi_req) == UWSGI_ROUTE_BREAK) { + // from now on ignore write body requests... + wsgi_req->ignore_body = 1; + return -1; + } + wsgi_req->is_response_routing = 0; + } +#endif + + size_t i; + int buffering = 0; + // transformations apply to every vector + for(i=0;itransformed_chunk_len == 0 && wsgi_req->transformations) { + int t_ret = uwsgi_apply_transformations(wsgi_req, iov[i].iov_base, iov[i].iov_len); + if (t_ret == 0) { + iov[i].iov_base = wsgi_req->transformed_chunk; + iov[i].iov_len = wsgi_req->transformed_chunk_len; + // reset transformation + wsgi_req->transformed_chunk = NULL; + wsgi_req->transformed_chunk_len = 0; + goto write; + } + if (t_ret == 1) { + buffering = 1; + continue; + } + wsgi_req->write_errors++; + return -1; + } + } + + if (buffering) return UWSGI_OK; + +write: + // send headers if not already sent + if (!wsgi_req->headers_sent) { + int ret = uwsgi_response_write_headers_do(wsgi_req); + if (ret == UWSGI_OK) goto sendbody; + if (ret == UWSGI_AGAIN) return UWSGI_AGAIN; + wsgi_req->write_errors++; + return -1; + } + +sendbody: + + if (len == 0) return UWSGI_OK; + // unfortunately vector based I/O cannot be accomplished on all protocols + if (!wsgi_req->socket->proto_writev) goto fallback; + + // we use a copy to avoid mess + size_t iov_len = len; + for(;;) { + errno = 0; + int ret = wsgi_req->socket->proto_writev(wsgi_req, iov, &iov_len); + if (ret < 0) { + if (!uwsgi.ignore_write_errors) { + uwsgi_req_error("uwsgi_response_writev_body_do()"); + } + wsgi_req->write_errors++; + return -1; + } + if (ret == UWSGI_OK) { + break; + } + if (!uwsgi_is_again()) continue; + ret = uwsgi_wait_write_req(wsgi_req); + if (ret < 0) { wsgi_req->write_errors++; return -1;} + if (ret == 0) { + uwsgi_log("uwsgi_response_writev_body_do() TIMEOUT !!!\n"); + wsgi_req->write_errors++; + return -1; + } + } + + goto done; + +fallback: + + for(i=0;isocket->proto_write(wsgi_req, iov[i].iov_base, iov[i].iov_len); + if (ret < 0) { + if (!uwsgi.ignore_write_errors) { + uwsgi_req_error("uwsgi_response_writev_body_do()"); + } + wsgi_req->write_errors++; + return -1; + } + if (ret == UWSGI_OK) { + break; + } + if (!uwsgi_is_again()) continue; + ret = uwsgi_wait_write_req(wsgi_req); + if (ret < 0) { wsgi_req->write_errors++; return -1;} + if (ret == 0) { + uwsgi_log("uwsgi_response_writev_body_do() TIMEOUT !!!\n"); + wsgi_req->write_errors++; + return -1; + } + } + } + +done: + + wsgi_req->response_size += wsgi_req->write_pos; + // reset for the next write + wsgi_req->write_pos = 0; + + return UWSGI_OK; +} + + int uwsgi_response_sendfile_do(struct wsgi_request *wsgi_req, int fd, size_t pos, size_t len) { return uwsgi_response_sendfile_do_can_close(wsgi_req, fd, pos, len, 1); } @@ -349,7 +582,7 @@ sendfile: if (len == 0) { struct stat st; if (fstat(fd, &st)) { - uwsgi_error("uwsgi_response_sendfile_do()/fstat()"); + uwsgi_req_error("uwsgi_response_sendfile_do()/fstat()"); wsgi_req->write_errors++; if (can_close) close(fd); return -1; @@ -367,7 +600,7 @@ sendfile: if (!can_close) { int tmp_fd = dup(fd); if (tmp_fd < 0) { - uwsgi_error("uwsgi_response_sendfile_do()/dup()"); + uwsgi_req_error("uwsgi_response_sendfile_do()/dup()"); wsgi_req->write_errors++; return -1; } @@ -388,10 +621,11 @@ sendfile: wsgi_req->via = UWSGI_VIA_SENDFILE; for(;;) { + errno = 0; int ret = wsgi_req->socket->proto_sendfile(wsgi_req, fd, pos, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { - uwsgi_error("uwsgi_response_sendfile_do()"); + uwsgi_req_error("uwsgi_response_sendfile_do()"); } wsgi_req->write_errors++; if (can_close) close(fd); @@ -400,6 +634,7 @@ sendfile: if (ret == UWSGI_OK) { break; } + if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; @@ -454,10 +689,11 @@ int uwsgi_simple_write(struct wsgi_request *wsgi_req, char *buf, size_t len) { wsgi_req->write_pos = 0; for(;;) { + errno = 0; int ret = wsgi_req->socket->proto_write(wsgi_req, buf, len); if (ret < 0) { if (!uwsgi.ignore_write_errors) { - uwsgi_error("uwsgi_simple_write()"); + uwsgi_req_error("uwsgi_simple_write()"); } wsgi_req->write_errors++; return -1; @@ -465,6 +701,7 @@ int uwsgi_simple_write(struct wsgi_request *wsgi_req, char *buf, size_t len) { if (ret == UWSGI_OK) { break; } + if (!uwsgi_is_again()) continue; ret = uwsgi_wait_write_req(wsgi_req); if (ret < 0) { wsgi_req->write_errors++; return -1;} if (ret == 0) { diff --git a/examples/mjpeg_stream.py b/examples/mjpeg_stream.py index 20c33438..ae9e5474 100644 --- a/examples/mjpeg_stream.py +++ b/examples/mjpeg_stream.py @@ -1,5 +1,4 @@ import uwsgi - import os def application(env, start_response): @@ -15,12 +14,12 @@ def application(env, start_response): ] ) - - yield "--%s\n" % boundary + yield "--%s\r\n" % boundary while 1: yield "Content-Type: image/jpeg\r\n\r\n" - uwsgi.sendfile('screenshot.jpg') - yield "--%s\n" % boundary + print os.system('screencapture -t jpg -m -T 1 screenshot.jpg') + f = open('screenshot.jpg') + yield env['wsgi.file_wrapper'](f) + yield "\r\n--%s\r\n" % boundary #os.system('./isightcapture -w 640 -h 480 screenshot.jpg') - #os.system('screencapture -m -T 1 screenshot.jpg') diff --git a/lib/linux_ns.c b/lib/linux_ns.c index 474c2529..65ee105e 100644 --- a/lib/linux_ns.c +++ b/lib/linux_ns.c @@ -52,6 +52,11 @@ int uwsgi_is_a_keep_mount(char *mp) { } +static int uwsgi_ns_start(void *v_argv) { + uwsgi_start(v_argv); + return uwsgi_run(); +} + void linux_namespace_start(void *argv) { for (;;) { char stack[PTHREAD_STACK_MIN]; @@ -61,7 +66,7 @@ void linux_namespace_start(void *argv) { if (uwsgi.ns_net) { clone_flags |= CLONE_NEWNET; } - pid_t pid = clone(uwsgi_start, stack + PTHREAD_STACK_MIN, clone_flags, (void *) argv); + pid_t pid = clone(uwsgi_ns_start, stack + PTHREAD_STACK_MIN, clone_flags, (void *) argv); if (pid == -1) { uwsgi_error("clone()"); exit(1); diff --git a/plugins/coroae/coroae.c b/plugins/coroae/coroae.c index 1e05e6e5..e3616a42 100644 --- a/plugins/coroae/coroae.c +++ b/plugins/coroae/coroae.c @@ -65,6 +65,33 @@ SV * coroae_coro_new(CV *block) { return newobj; } +static int coroae_wait_milliseconds(int timeout) { + int ret = -1; + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs(newSVnv(((double)timeout)/1000.0)); + PUTBACK; + call_pv("Coro::AnyEvent::sleep", G_SCALAR); + SPAGAIN; + if(SvTRUE(ERRSV)) { + uwsgi_log("[uwsgi-perl error] %s", SvPV_nolen(ERRSV)); + } + else { + SV *p_ret = POPs; + if (SvTRUE(p_ret)) { + ret = 0; + } + } + PUTBACK; + FREETMPS; + LEAVE; + + return ret; +} + + static int coroae_wait_fd_read(int fd, int timeout) { int ret = 0; dSP; @@ -377,6 +404,7 @@ static void coroae_loop() { uwsgi.current_wsgi_req = coroae_current_wsgi_req; uwsgi.wait_write_hook = coroae_wait_fd_write; uwsgi.wait_read_hook = coroae_wait_fd_read; + uwsgi.wait_milliseconds_hook = coroae_wait_milliseconds; I_CORO_API("uwsgi::coroae"); diff --git a/plugins/coroae/uwsgiplugin.py b/plugins/coroae/uwsgiplugin.py index 28ecfac4..6d35564e 100644 --- a/plugins/coroae/uwsgiplugin.py +++ b/plugins/coroae/uwsgiplugin.py @@ -14,7 +14,7 @@ if not coroapi: NAME='coroae' CFLAGS = os.popen('perl -MExtUtils::Embed -e ccopts').read().rstrip().split() -CFLAGS += ['-Wno-int-to-pointer-cast', '-Wno-error=int-to-pointer-cast', '-I%s/Coro' % coroapi] +CFLAGS += ['-Wno-int-to-pointer-cast', '-Wno-error=format', '-Wno-error=int-to-pointer-cast', '-I%s/Coro' % coroapi] LDFLAGS = [] LIBS = [] diff --git a/plugins/emperor_zeromq/emperor_zeromq.c b/plugins/emperor_zeromq/emperor_zeromq.c index 319db68b..eb88ecf7 100644 --- a/plugins/emperor_zeromq/emperor_zeromq.c +++ b/plugins/emperor_zeromq/emperor_zeromq.c @@ -52,9 +52,7 @@ s.send_multipart(['touch','foo.ini',"[uwsgi]\nsocket=:4142"]) */ -#ifdef UWSGI_ZEROMQ - -#include "../../uwsgi.h" +#include #include extern struct uwsgi_server uwsgi; @@ -215,7 +213,11 @@ static void uwsgi_imperial_monitor_zeromq_event(struct uwsgi_emperor_scanner *ue // initialize the zmq PULL socket static void uwsgi_imperial_monitor_zeromq_init(struct uwsgi_emperor_scanner *ues) { - void *context = uwsgi_zeromq_init(); + void *context = zmq_init(1); + if (!context) { + uwsgi_error("uwsgi_imperial_monitor_zeromq_init()/zmq_init()"); + exit(1); + } ues->data = zmq_socket(context, ZMQ_PULL); if (!ues->data) { @@ -256,7 +258,3 @@ struct uwsgi_plugin emperor_zeromq_plugin = { .name = "emperor_zeromq", .on_load = emperor_zeromq_init, }; - -#else -#error the uWSGI ZeroMQ imperial monitor requires uWSGI zeromq support -#endif diff --git a/plugins/gevent/gevent.c b/plugins/gevent/gevent.c index e50a8f9a..d4df77ef 100644 --- a/plugins/gevent/gevent.c +++ b/plugins/gevent/gevent.c @@ -361,6 +361,7 @@ static void gevent_loop() { uwsgi.wait_write_hook = uwsgi_gevent_wait_write_hook; uwsgi.wait_read_hook = uwsgi_gevent_wait_read_hook; + uwsgi.wait_milliseconds_hook = uwsgi_gevent_wait_milliseconds_hook; struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; diff --git a/plugins/gevent/gevent.h b/plugins/gevent/gevent.h index 8f794c0c..f7687d88 100644 --- a/plugins/gevent/gevent.h +++ b/plugins/gevent/gevent.h @@ -2,6 +2,7 @@ int uwsgi_gevent_wait_write_hook(int, int); int uwsgi_gevent_wait_read_hook(int, int); +int uwsgi_gevent_wait_milliseconds_hook(int); #define GEVENT_SWITCH PyObject *gswitch = python_call(ugevent.greenlet_switch, ugevent.greenlet_switch_args, 0, NULL); Py_DECREF(gswitch) #define GET_CURRENT_GREENLET python_call(ugevent.get_current, ugevent.get_current_args, 0, NULL) diff --git a/plugins/gevent/hooks.c b/plugins/gevent/hooks.c index 1c213905..e96d0fe4 100644 --- a/plugins/gevent/hooks.c +++ b/plugins/gevent/hooks.c @@ -98,3 +98,42 @@ int uwsgi_gevent_wait_read_hook(int fd, int timeout) { return 1; } +int uwsgi_gevent_wait_milliseconds_hook(int timeout) { + + PyObject *ret = NULL; + + PyObject *timer = PyObject_CallMethod(ugevent.hub_loop, "timer", "f", ((double) timeout)/1000.0); + if (!timer) return -1; + + PyObject *current_greenlet = GET_CURRENT_GREENLET; + PyObject *current = PyObject_GetAttrString(current_greenlet, "switch"); + + ret = PyObject_CallMethod(timer, "start", "OO", current, timer); + if (!ret) { + Py_DECREF(current); Py_DECREF(current_greenlet); + Py_DECREF(timer); + return -1; + } + Py_DECREF(ret); + + ret = PyObject_CallMethod(ugevent.hub, "switch", NULL); + if (!ret) { + ret = PyObject_CallMethod(timer, "stop", NULL); + if (ret) { Py_DECREF(ret); } + Py_DECREF(current); Py_DECREF(current_greenlet); + Py_DECREF(timer); + return -1; + } + Py_DECREF(ret); + + if (ret == timer) { + ret = PyObject_CallMethod(timer, "stop", NULL); + if (ret) { Py_DECREF(ret); } + Py_DECREF(current); Py_DECREF(current_greenlet); + Py_DECREF(timer); + return 0; + } + + return -1; +} + diff --git a/plugins/logzmq/plugin.c b/plugins/logzmq/plugin.c new file mode 100644 index 00000000..502d607b --- /dev/null +++ b/plugins/logzmq/plugin.c @@ -0,0 +1,61 @@ +#include +#include + +extern struct uwsgi_server uwsgi; + +static struct uwsgi_option uwsgi_zmq_logger_options[] = { + {"log-zeromq", required_argument, 0, "send logs to a zeromq server", uwsgi_opt_set_logger, "zeromq", UWSGI_OPT_MASTER | UWSGI_OPT_LOG_MASTER}, + {NULL, 0, 0, NULL, NULL, NULL, 0}, +}; + +// the zeromq logger +static ssize_t uwsgi_zeromq_logger(struct uwsgi_logger *ul, char *message, size_t len) { + + if (!ul->configured) { + + if (!ul->arg) { + uwsgi_log_safe("invalid zeromq syntax\n"); + exit(1); + } + + void *ctx = zmq_init(1); + if (!ctx) exit(1); + + ul->data = zmq_socket(ctx, ZMQ_PUSH); + if (ul->data == NULL) { + uwsgi_error_safe("zmq_socket()"); + exit(1); + } + + if (zmq_connect(ul->data, ul->arg) < 0) { + uwsgi_error_safe("zmq_connect()"); + exit(1); + } + + ul->configured = 1; + } + + zmq_msg_t msg; + if (zmq_msg_init_size(&msg, len) == 0) { + memcpy(zmq_msg_data(&msg), message, len); +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3,0,0) + zmq_sendmsg(ul->data, &msg, 0); +#else + zmq_send(ul->data, &msg, 0); +#endif + zmq_msg_close(&msg); + } + + return 0; +} + +static void uwsgi_zmq_logger_register() { + uwsgi_register_logger("zmq", uwsgi_zeromq_logger); + uwsgi_register_logger("zeromq", uwsgi_zeromq_logger); +} + +struct uwsgi_plugin logzmq_plugin = { + .name = "logzmq", + .options = uwsgi_zmq_logger_options, + .on_load = uwsgi_zmq_logger_register, +}; diff --git a/plugins/logzmq/uwsgiplugin.py b/plugins/logzmq/uwsgiplugin.py new file mode 100644 index 00000000..0799e325 --- /dev/null +++ b/plugins/logzmq/uwsgiplugin.py @@ -0,0 +1,7 @@ + +NAME='logzmq' +CFLAGS = [] +LDFLAGS = [] +LIBS = ['-lzmq'] + +GCC_LIST = ['plugin'] diff --git a/proto/zeromq.c b/plugins/mongrel2/mongrel2.c similarity index 82% rename from proto/zeromq.c rename to plugins/mongrel2/mongrel2.c index b666ce4f..bdb4dbb6 100644 --- a/proto/zeromq.c +++ b/plugins/mongrel2/mongrel2.c @@ -1,39 +1,21 @@ /* - generic ZeroMQ functions + Mongrel2 protocol parser + Mongrel2 protocol parser */ + #include +#include extern struct uwsgi_server uwsgi; -void *uwsgi_zeromq_init() { - if (!uwsgi.zmq_context) { - uwsgi.zmq_context = zmq_init(1); - if (uwsgi.zmq_context == NULL) { - uwsgi_error("zmq_init()"); - exit(1); - } - } - return uwsgi.zmq_context; -} - -void uwsgi_zeromq_init_sockets() { - - uwsgi_zeromq_init(); - - struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; - while (uwsgi_sock) { - if (!uwsgi_sock->proto_name || strcmp(uwsgi_sock->proto_name, "zmq")) { - goto zmq_next; - } - uwsgi_proto_zeromq_setup(uwsgi_sock); -zmq_next: - uwsgi_sock = uwsgi_sock->next; - - } - -} +static struct uwsgi_option mongrel2_options[] = { + {"zeromq", required_argument, 0, "create a mongrel2/zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "mongrel2", 0}, + {"zmq", required_argument, 0, "create a mongrel2/zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "mongrel2", 0}, + {"zeromq-socket", required_argument, 0, "create a mongrel2/zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "mongrel2", 0}, + {"zmq-socket", required_argument, 0, "create a mongrel2/zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "mongrel2", 0}, + {"mongrel2", required_argument, 0, "create a mongrel2/zeromq pub/sub pair", uwsgi_opt_add_lazy_socket, "mongrel2", 0}, +}; #ifdef UWSGI_JSON #include @@ -290,7 +272,7 @@ int uwsgi_proto_zeromq_parser(struct wsgi_request *wsgi_req) { void uwsgi_proto_zeromq_thread_fixup(struct uwsgi_socket *uwsgi_sock, int async_id) { - void *tmp_zmq_pull = zmq_socket(uwsgi.zmq_context, ZMQ_PULL); + void *tmp_zmq_pull = zmq_socket(uwsgi_sock->ctx, ZMQ_PULL); if (tmp_zmq_pull == NULL) { uwsgi_error("zmq_socket()"); exit(1); @@ -315,7 +297,7 @@ void uwsgi_proto_zeromq_thread_fixup(struct uwsgi_socket *uwsgi_sock, int async_ #endif } -// fake function, the bosy is i na file or completely in memory +// fake function, the body is in a file or completely in memory ssize_t uwsgi_proto_zeromq_read_body(struct wsgi_request *wsgi_req, char *buf, size_t len) { return 0; } @@ -565,38 +547,6 @@ int uwsgi_proto_zeromq_sendfile(struct wsgi_request *wsgi_req, int fd, size_t po void uwsgi_proto_zeromq_setup(struct uwsgi_socket *uwsgi_sock) { - char *responder = strchr(uwsgi_sock->name, ','); - if (!responder) { - uwsgi_log("invalid zeromq address\n"); - exit(1); - } - uwsgi_sock->receiver = uwsgi_concat2n(uwsgi_sock->name, responder - uwsgi_sock->name, "", 0); - responder++; - - uwsgi_sock->pub = zmq_socket(uwsgi.zmq_context, ZMQ_PUB); - if (uwsgi_sock->pub == NULL) { - uwsgi_error("zmq_socket()"); - exit(1); - } - - - // generate uuid - uuid_t uuid_zmq; - uuid_generate(uuid_zmq); - uuid_unparse(uuid_zmq, uwsgi_sock->uuid); - - if (zmq_setsockopt(uwsgi_sock->pub, ZMQ_IDENTITY, uwsgi_sock->uuid, 36) < 0) { - uwsgi_error("zmq_setsockopt()"); - exit(1); - } - - if (zmq_connect(uwsgi_sock->pub, responder) < 0) { - uwsgi_error("zmq_connect()"); - exit(1); - } - - uwsgi_log("zeromq UUID for responder %s on worker %d: %.*s\n", responder, uwsgi.mywid, 36, uwsgi_sock->uuid); - uwsgi_sock->proto = uwsgi_proto_zeromq_parser; uwsgi_sock->proto_accept = uwsgi_proto_zeromq_accept; uwsgi_sock->proto_prepare_headers = uwsgi_proto_base_prepare_headers; @@ -614,55 +564,105 @@ void uwsgi_proto_zeromq_setup(struct uwsgi_socket *uwsgi_sock) { uwsgi_sock->retry = uwsgi_malloc(sizeof(int) * uwsgi.threads); uwsgi_sock->retry[0] = 1; - // inform loop engine about edge trigger status - uwsgi.is_et = 1; + uwsgi_sock->fd = -1; +} + +static void uwsgi_proto_mongrel2_setup(struct uwsgi_socket *uwsgi_sock) { +}; + +static void mongrel2_register_proto() { + uwsgi_register_protocol("mongrel2", uwsgi_proto_mongrel2_setup); +} + +static void mongrel2_connect() { + struct uwsgi_socket *uwsgi_sock = uwsgi.sockets; + while(uwsgi_sock) { + uwsgi_sock->ctx = zmq_init(1); + if (!uwsgi_sock->ctx) { + uwsgi_error("mongrel2_connect()/zmq_init()"); + exit(1); + } + char *responder = strchr(uwsgi_sock->name, ','); + if (!responder) { + uwsgi_log("invalid zeromq address\n"); + exit(1); + } + uwsgi_sock->receiver = uwsgi_concat2n(uwsgi_sock->name, responder - uwsgi_sock->name, "", 0); + responder++; + + uwsgi_sock->pub = zmq_socket(uwsgi_sock->ctx, ZMQ_PUB); + if (uwsgi_sock->pub == NULL) { + uwsgi_error("mongrel2_connect()/zmq_socket()"); + exit(1); + } + + // generate uuid + uwsgi_uuid(uwsgi_sock->uuid); + + if (zmq_setsockopt(uwsgi_sock->pub, ZMQ_IDENTITY, uwsgi_sock->uuid, 36) < 0) { + uwsgi_error("mongrel2_connect()/zmq_setsockopt()"); + exit(1); + } + + if (zmq_connect(uwsgi_sock->pub, responder) < 0) { + uwsgi_error("mongrel2_connect()/zmq_connect()"); + exit(1); + } + + uwsgi_log("zeromq UUID for responder %s on worker %d: %.*s\n", responder, uwsgi.mywid, 36, uwsgi_sock->uuid); + + // inform loop engine about edge trigger status + uwsgi.is_et = 1; - // initialize a lock for multithread usage - if (uwsgi.threads > 1) { - pthread_mutex_init(&uwsgi_sock->lock, NULL); - } + // initialize a lock for multithread usage + if (uwsgi.threads > 1) { + pthread_mutex_init(&uwsgi_sock->lock, NULL); + } - // one pull per-thread - if (pthread_key_create(&uwsgi_sock->key, NULL)) { - uwsgi_error("pthread_key_create()"); - exit(1); - } + // one pull per-thread + if (pthread_key_create(&uwsgi_sock->key, NULL)) { + uwsgi_error("mongrel2_connect()/pthread_key_create()"); + exit(1); + } - void *tmp_zmq_pull = zmq_socket(uwsgi.zmq_context, ZMQ_PULL); - if (tmp_zmq_pull == NULL) { - uwsgi_error("zmq_socket()"); - exit(1); - } - if (zmq_connect(tmp_zmq_pull, uwsgi_sock->receiver) < 0) { - uwsgi_error("zmq_connect()"); - exit(1); - } + void *tmp_zmq_pull = zmq_socket(uwsgi_sock->ctx, ZMQ_PULL); + if (tmp_zmq_pull == NULL) { + uwsgi_error("mongrel2_connect()/zmq_socket()"); + exit(1); + } + if (zmq_connect(tmp_zmq_pull, uwsgi_sock->receiver) < 0) { + uwsgi_error("mongrel2_connect()/zmq_connect()"); + exit(1); + } - pthread_setspecific(uwsgi_sock->key, tmp_zmq_pull); + pthread_setspecific(uwsgi_sock->key, tmp_zmq_pull); #ifdef ZMQ_FD - size_t zmq_socket_len = sizeof(int); - if (zmq_getsockopt(pthread_getspecific(uwsgi_sock->key), ZMQ_FD, &uwsgi_sock->fd, &zmq_socket_len) < 0) { - uwsgi_error("zmq_getsockopt()"); - exit(1); - } - if (uwsgi.threads > 1) { - uwsgi_sock->fd_threads = uwsgi_malloc(sizeof(int) * uwsgi.threads); - uwsgi_sock->fd_threads[0] = uwsgi_sock->fd; - } -#else - uwsgi_sock->fd = -1; + size_t zmq_socket_len = sizeof(int); + if (zmq_getsockopt(pthread_getspecific(uwsgi_sock->key), ZMQ_FD, &uwsgi_sock->fd, &zmq_socket_len) < 0) { + uwsgi_error("mongrel2_connect()/zmq_getsockopt()"); + exit(1); + } + if (uwsgi.threads > 1) { + uwsgi_sock->fd_threads = uwsgi_malloc(sizeof(int) * uwsgi.threads); + uwsgi_sock->fd_threads[0] = uwsgi_sock->fd; + } #endif - uwsgi_sock->bound = 1; + uwsgi_sock->bound = 1; #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3,0,0) - uwsgi_sock->recv_flag = ZMQ_DONTWAIT; + uwsgi_sock->recv_flag = ZMQ_DONTWAIT; #else - uwsgi_sock->recv_flag = ZMQ_NOBLOCK; + uwsgi_sock->recv_flag = ZMQ_NOBLOCK; #endif + uwsgi_sock = uwsgi_sock->next; + } } -void uwsgi_proto_zmq_setup(struct uwsgi_socket *uwsgi_sock) { - uwsgi.zeromq = 1; -} +struct uwsgi_plugin mongrel2_plugin = { + .name = "mongrel2", + .options = mongrel2_options, + .post_fork = mongrel2_connect, + .on_load = mongrel2_register_proto, +}; diff --git a/plugins/mongrel2/uwsgiplugin.py b/plugins/mongrel2/uwsgiplugin.py new file mode 100644 index 00000000..723885cf --- /dev/null +++ b/plugins/mongrel2/uwsgiplugin.py @@ -0,0 +1,7 @@ + +NAME='mongrel2' +CFLAGS = [] +LDFLAGS = [] +LIBS = ['-lzmq'] + +GCC_LIST = ['mongrel2'] diff --git a/plugins/psgi/psgi_loader.c b/plugins/psgi/psgi_loader.c index ead460c7..420b71d3 100644 --- a/plugins/psgi/psgi_loader.c +++ b/plugins/psgi/psgi_loader.c @@ -149,7 +149,7 @@ XS(XS_streaming_write) { uwsgi_response_write_body_do(wsgi_req, body, blen); uwsgi_pl_check_write_errors { - // noop + croak("error while streaming PSGI response"); } XSRETURN(0); diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index a89cf842..5db391be 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -194,9 +194,12 @@ int uwsgi_perl_obj_can(SV *obj, char *method, size_t len) { XPUSHs(sv_2mortal(newSVpv(method, len))); PUTBACK; - call_method( "can", G_SCALAR); + call_method( "can", G_SCALAR|G_EVAL); SPAGAIN; + if(SvTRUE(ERRSV)) { + uwsgi_log("%s", SvPV_nolen(ERRSV)); + } ret = SvROK(POPs); PUTBACK; @@ -253,7 +256,7 @@ SV *uwsgi_perl_obj_call(SV *obj, char *method) { SPAGAIN; if(SvTRUE(ERRSV)) { - uwsgi_log("%s\n", SvPV_nolen(ERRSV)); + uwsgi_log("%s", SvPV_nolen(ERRSV)); } else { ret = SvREFCNT_inc(POPs); diff --git a/plugins/psgi/psgi_response.c b/plugins/psgi/psgi_response.c index e1faaadd..e20cfc4d 100644 --- a/plugins/psgi/psgi_response.c +++ b/plugins/psgi/psgi_response.c @@ -22,15 +22,13 @@ int psgi_response(struct wsgi_request *wsgi_req, AV *response) { return UWSGI_OK; } - if (wsgi_req->async_force_again) { - SvREFCNT_dec(chunk); - return UWSGI_AGAIN; - } - chitem = SvPV( chunk, hlen); if (hlen <= 0) { SvREFCNT_dec(chunk); + if (wsgi_req->async_force_again) { + return UWSGI_AGAIN; + } SV *closed = uwsgi_perl_obj_call(wsgi_req->async_placeholder, "close"); if (closed) { SvREFCNT_dec(closed); @@ -102,41 +100,40 @@ int psgi_response(struct wsgi_request *wsgi_req, AV *response) { } if (!SvRV(*hitem)) { uwsgi_log("invalid PSGI response body\n") ; return UWSGI_OK; } + + if (!SvROK(*hitem)) goto unsupported; if (SvTYPE(SvRV(*hitem)) == SVt_PVGV || SvTYPE(SvRV(*hitem)) == SVt_PVHV || SvTYPE(SvRV(*hitem)) == SVt_PVMG) { - // respond to fileno ? - if (uwsgi.async < 2) { - // check for fileno() method, IO class or GvIO - if (uwsgi_perl_obj_can(*hitem, "fileno", 6) || uwsgi_perl_obj_isa(*hitem, "IO") || (uwsgi_perl_obj_isa(*hitem, "GLOB") && GvIO(SvRV(*hitem))) ) { - SV *fn = uwsgi_perl_obj_call(*hitem, "fileno"); - if (fn) { - if (SvTYPE(fn) == SVt_IV && SvIV(fn) >= 0) { - wsgi_req->sendfile_fd = SvIV(fn); - SvREFCNT_dec(fn); - uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0); - // no need to close here as perl GC will do the close() - uwsgi_pl_check_write_errors { - // noop - } - return UWSGI_OK; - } + // check for fileno() method, IO class or GvIO + if (uwsgi_perl_obj_can(*hitem, "fileno", 6) || uwsgi_perl_obj_isa(*hitem, "IO") || (uwsgi_perl_obj_isa(*hitem, "GLOB") && GvIO(SvRV(*hitem))) ) { + SV *fn = uwsgi_perl_obj_call(*hitem, "fileno"); + if (fn) { + if (SvTYPE(fn) == SVt_IV && SvIV(fn) >= 0) { + wsgi_req->sendfile_fd = SvIV(fn); SvREFCNT_dec(fn); + uwsgi_response_sendfile_do(wsgi_req, wsgi_req->sendfile_fd, 0, 0); + // no need to close here as perl GC will do the close() + uwsgi_pl_check_write_errors { + // noop + } + return UWSGI_OK; } + SvREFCNT_dec(fn); } + } - // check for path method - if (uwsgi_perl_obj_can(*hitem, "path", 4)) { - SV *p = uwsgi_perl_obj_call(*hitem, "path"); - int fd = open(SvPV_nolen(p), O_RDONLY); - SvREFCNT_dec(p); - // the following function will close fd - uwsgi_response_sendfile_do(wsgi_req, fd, 0, 0); - uwsgi_pl_check_write_errors { - // noop - } - return UWSGI_OK; + // check for path method + if (uwsgi_perl_obj_can(*hitem, "path", 4)) { + SV *p = uwsgi_perl_obj_call(*hitem, "path"); + int fd = open(SvPV_nolen(p), O_RDONLY); + SvREFCNT_dec(p); + // the following function will close fd + uwsgi_response_sendfile_do(wsgi_req, fd, 0, 0); + uwsgi_pl_check_write_errors { + // noop } + return UWSGI_OK; } for(;;) { @@ -149,13 +146,12 @@ int psgi_response(struct wsgi_request *wsgi_req, AV *response) { } chitem = SvPV( chunk, hlen); - if (uwsgi.async > 1 && wsgi_req->async_force_again) { - SvREFCNT_dec(chunk); - wsgi_req->async_placeholder = (SV *) *hitem; - return UWSGI_AGAIN; - } if (hlen <= 0) { SvREFCNT_dec(chunk); + if (uwsgi.async > 1 && wsgi_req->async_force_again) { + wsgi_req->async_placeholder = (SV *) *hitem; + return UWSGI_AGAIN; + } break; } @@ -194,6 +190,7 @@ int psgi_response(struct wsgi_request *wsgi_req, AV *response) { } else { +unsupported: uwsgi_log("unsupported response body type: %d\n", SvTYPE(SvRV(*hitem))); } diff --git a/plugins/psgi/uwsgi_plmodule.c b/plugins/psgi/uwsgi_plmodule.c index 6d25f4c9..1ad10647 100644 --- a/plugins/psgi/uwsgi_plmodule.c +++ b/plugins/psgi/uwsgi_plmodule.c @@ -498,6 +498,70 @@ XS(XS_websocket_send) { XSRETURN_UNDEF; } +XS(XS_websocket_send_from_sharedarea) { + dXSARGS; + + psgi_check_args(2); + int id = SvIV(ST(0)); + uint64_t pos = SvIV(ST(1)); + uint64_t len = 0; + + if (items > 2) { + len = SvIV(ST(2)); + } + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (uwsgi_websocket_send_from_sharedarea(wsgi_req, id, pos, len)) { + croak("unable to send websocket message from sharedarea"); + } + + XSRETURN_UNDEF; +} + + +XS(XS_websocket_send_binary) { + dXSARGS; + + char *message = NULL; + STRLEN message_len = 0; + + psgi_check_args(1); + + message = SvPV(ST(0), message_len); + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (uwsgi_websocket_send_binary(wsgi_req, message, message_len)) { + croak("unable to send websocket binary message"); + } + + XSRETURN_UNDEF; +} + +XS(XS_websocket_send_binary_from_sharedarea) { + dXSARGS; + + psgi_check_args(2); + int id = SvIV(ST(0)); + uint64_t pos = SvIV(ST(1)); + uint64_t len = 0; + + if (items > 2) { + len = SvIV(ST(2)); + } + + struct wsgi_request *wsgi_req = current_wsgi_req(); + + if (uwsgi_websocket_send_binary_from_sharedarea(wsgi_req, id, pos, len)) { + croak("unable to send websocket binary message from sharedarea"); + } + + XSRETURN_UNDEF; +} + + + XS(XS_websocket_recv) { dXSARGS; @@ -668,6 +732,106 @@ XS(XS_metric_get) { XSRETURN(1); } +XS(XS_sharedarea_wait) { + dXSARGS; + int id; + int freq = 0; + int timeout = 0; + + psgi_check_args(1); + + id = SvIV(ST(0)); + if (items > 1) { + freq = SvIV(ST(1)); + } + + if (uwsgi_sharedarea_wait(id, freq, timeout)) { + croak("unable to wait for sharedarea %d", id); + XSRETURN_UNDEF; + } + XSRETURN_YES; +} + +XS(XS_sharedarea_read) { + dXSARGS; + int id; + uint64_t pos; + uint64_t len = 0; + psgi_check_args(2); + + id = SvIV(ST(0)); + pos = SvIV(ST(1)); + + if (items > 2) { + len = SvIV(ST(2)); + } + else { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) { + croak("unable to read from sharedarea %d", id); + XSRETURN_UNDEF; + } + len = (sa->max_pos+1)-pos; + } + + char *buf = uwsgi_malloc(len); + int64_t rlen = uwsgi_sharedarea_read(id, pos, buf, len); + if (rlen < 0) { + free(buf); + croak("unable to read from sharedarea %d", id); + XSRETURN_UNDEF; + } + + ST(0) = sv_newmortal(); + sv_usepvn(ST(0), buf, rlen); + XSRETURN(1); +} + +XS(XS_sharedarea_readfast) { + dXSARGS; + int id; + uint64_t pos; + uint64_t len = 0; + psgi_check_args(3); + + id = SvIV(ST(0)); + pos = SvIV(ST(1)); + char *buf = SvPV_nolen(ST(2)); + + if (items > 3) { + len = SvIV(ST(3)); + } + + if (uwsgi_sharedarea_read(id, pos, buf, len)) { + croak("unable to (fast) read from sharedarea %d", id); + XSRETURN_UNDEF; + } + + XSRETURN_YES; +} + + +XS(XS_sharedarea_write) { + dXSARGS; + int id; + uint64_t pos; + STRLEN vallen; + + psgi_check_args(3); + + id = SvIV(ST(0)); + pos = SvIV(ST(1)); + char *value = SvPV(ST(2), vallen); + + if (uwsgi_sharedarea_write(id, pos, value, vallen)) { + croak("unable to write to sharedarea %d", id); + XSRETURN_UNDEF; + } + + XSRETURN_YES; +} + + XS(XS_chunked_read) { dXSARGS; int timeout = 0; @@ -740,6 +904,9 @@ void init_perl_embedded_module() { psgi_xs(websocket_recv); psgi_xs(websocket_recv_nb); psgi_xs(websocket_send); + psgi_xs(websocket_send_from_sharedarea); + psgi_xs(websocket_send_binary); + psgi_xs(websocket_send_binary_from_sharedarea); psgi_xs(postfork); psgi_xs(atexit); @@ -757,5 +924,9 @@ void init_perl_embedded_module() { psgi_xs(chunked_read); psgi_xs(chunked_read_nb); -} + psgi_xs(sharedarea_read); + psgi_xs(sharedarea_readfast); + psgi_xs(sharedarea_write); + psgi_xs(sharedarea_wait); +} diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 95cbc726..b88c8295 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -833,7 +833,7 @@ void init_uwsgi_embedded_module() { init_uwsgi_module_spooler(new_uwsgi_module); } - if (uwsgi.sharedareasize > 0 && uwsgi.sharedarea) { + if (uwsgi.sharedareas) { init_uwsgi_module_sharedarea(new_uwsgi_module); } diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index c336b4e8..a717532d 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -1066,6 +1066,27 @@ PyObject *py_uwsgi_websocket_send(PyObject * self, PyObject * args) { return Py_None; } +PyObject *py_uwsgi_websocket_send_binary(PyObject * self, PyObject * args) { + char *message = NULL; + Py_ssize_t message_len = 0; + + if (!PyArg_ParseTuple(args, "s#:websocket_send_binary", &message, &message_len)) { + return NULL; + } + + struct wsgi_request *wsgi_req = py_current_wsgi_req(); + + UWSGI_RELEASE_GIL + int ret = uwsgi_websocket_send_binary(wsgi_req, message, message_len); + UWSGI_GET_GIL + if (ret < 0) { + return PyErr_Format(PyExc_IOError, "unable to send websocket binary message"); + } + Py_INCREF(Py_None); + return Py_None; +} + + PyObject *py_uwsgi_chunked_read(PyObject * self, PyObject * args) { int timeout = 0; if (!PyArg_ParseTuple(args, "|i:chunked_read", &timeout)) { @@ -1426,222 +1447,149 @@ PyObject *py_uwsgi_extract(PyObject * self, PyObject * args) { } -PyObject *py_uwsgi_sharedarea_inclong(PyObject * self, PyObject * args) { +PyObject *py_uwsgi_sharedarea_inc64(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; - uint64_t value = 1; - uint64_t current_value = 0; + int64_t value = 1; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "l|l:sharedarea_inclong", &pos, &value)) { + if (!PyArg_ParseTuple(args, "il|l:sharedarea_inc64", &id, &pos, &value)) { return NULL; } - if (pos + 8 >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; - } - UWSGI_RELEASE_GIL - - uwsgi_wlock(uwsgi.sa_lock); - - memcpy(¤t_value, uwsgi.sharedarea + pos, 8); - value = current_value + value; - memcpy(uwsgi.sharedarea + pos, &value, 8); - - uwsgi_rwunlock(uwsgi.sa_lock); + int ret = uwsgi_sharedarea_inc64(id, pos, value); UWSGI_GET_GIL + + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_inc64()"); + } - return PyInt_FromLong(value); + Py_INCREF(Py_None); + return Py_None; } -PyObject *py_uwsgi_sharedarea_writelong(PyObject * self, PyObject * args) { +PyObject *py_uwsgi_sharedarea_write64(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; - uint64_t value = 0; + int64_t value = 0; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "ll:sharedarea_writelong", &pos, &value)) { + if (!PyArg_ParseTuple(args, "ill:sharedarea_write64", &id, &pos, &value)) { return NULL; } - if (pos + 8 >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; - } - UWSGI_RELEASE_GIL + int ret = uwsgi_sharedarea_write64(id, pos, &value); + UWSGI_GET_GIL - uwsgi_wlock(uwsgi.sa_lock); - - memcpy(uwsgi.sharedarea + pos, &value, 8); - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL - - return PyInt_FromLong(value); + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_write64()"); + } + Py_INCREF(Py_None); + return Py_None; } PyObject *py_uwsgi_sharedarea_write(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; char *value; Py_ssize_t value_len = 0; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "ls#:sharedarea_write", &pos, &value, &value_len)) { + if (!PyArg_ParseTuple(args, "ils#:sharedarea_write", &id, &pos, &value, &value_len)) { return NULL; } - if (pos + value_len >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; + UWSGI_RELEASE_GIL + int ret = uwsgi_sharedarea_write(id, pos, value, value_len); + UWSGI_GET_GIL + + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_write()"); + } + + Py_INCREF(Py_None); + return Py_None; +} + +PyObject *py_uwsgi_sharedarea_write8(PyObject * self, PyObject * args) { + int id; + uint64_t pos = 0; + int8_t value; + + if (!PyArg_ParseTuple(args, "ilb:sharedarea_write8", &id, &pos, &value)) { + return NULL; } UWSGI_RELEASE_GIL + int ret = uwsgi_sharedarea_write8(id, pos, &value); + UWSGI_GET_GIL - uwsgi_wlock(uwsgi.sa_lock); + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_write8()"); + } - memcpy(uwsgi.sharedarea + pos, value, value_len); - - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL - - return PyInt_FromLong(value_len); - + Py_INCREF(Py_None); + return Py_None; } -PyObject *py_uwsgi_sharedarea_writebyte(PyObject * self, PyObject * args) { +PyObject *py_uwsgi_sharedarea_read64(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; - char value; + int64_t value; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - - if (!PyArg_ParseTuple(args, "lb:sharedarea_writebyte", &pos, &value)) { + if (!PyArg_ParseTuple(args, "il:sharedarea_read64", &id, &pos)) { return NULL; } - if (pos >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; - } - UWSGI_RELEASE_GIL + int ret = uwsgi_sharedarea_read64(id, pos, &value); + UWSGI_GET_GIL - uwsgi_wlock(uwsgi.sa_lock); - - uwsgi.sharedarea[pos] = value; - - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL - - return PyInt_FromLong(value); - -} - -PyObject *py_uwsgi_sharedarea_readlong(PyObject * self, PyObject * args) { - uint64_t pos = 0; - uint64_t value; - - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "l:sharedarea_readlong", &pos)) { - return NULL; - } - - if (pos + 8 >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; - } - - UWSGI_RELEASE_GIL - - uwsgi_wlock(uwsgi.sa_lock); - - memcpy(&value, uwsgi.sharedarea + pos, 8); - - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_read64()"); + } return PyLong_FromLong(value); } - -PyObject *py_uwsgi_sharedarea_readbyte(PyObject * self, PyObject * args) { +PyObject *py_uwsgi_sharedarea_read8(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; + int8_t byte; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "l:sharedarea_readbyte", &pos)) { + if (!PyArg_ParseTuple(args, "il:sharedarea_read8", &id, &pos)) { return NULL; } - if (pos >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; - } - UWSGI_RELEASE_GIL + int ret = uwsgi_sharedarea_read8(id, pos, &byte); + UWSGI_GET_GIL - uwsgi_wlock(uwsgi.sa_lock); - - char value = uwsgi.sharedarea[pos]; - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL - - return PyInt_FromLong(value); + if (ret) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_read8()"); + } + return PyInt_FromLong(byte); } PyObject *py_uwsgi_sharedarea_read(PyObject * self, PyObject * args) { + int id; uint64_t pos = 0; - uint64_t len = 1; + uint64_t len = 0; - if (uwsgi.sharedareasize <= 0) { - Py_INCREF(Py_None); - return Py_None; - } - - if (!PyArg_ParseTuple(args, "l|l:sharedarea_read", &pos, &len)) { + if (!PyArg_ParseTuple(args, "il|l:sharedarea_read", &id, &pos, &len)) { return NULL; } - if (pos + len >= uwsgi.page_size * uwsgi.sharedareasize) { - Py_INCREF(Py_None); - return Py_None; + if (!len) { + struct uwsgi_sharedarea *sa = uwsgi_sharedarea_get_by_id(id, pos); + if (!sa) { + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_read()"); + } + len = (sa->max_pos+1)-pos; } PyObject *ret = PyString_FromStringAndSize(NULL, len); @@ -1652,14 +1600,16 @@ PyObject *py_uwsgi_sharedarea_read(PyObject * self, PyObject * args) { #endif UWSGI_RELEASE_GIL + int64_t rlen = uwsgi_sharedarea_read(id, pos, storage, len); + UWSGI_GET_GIL - uwsgi_wlock(uwsgi.sa_lock); + if (rlen < 0) { + Py_DECREF(ret); + return PyErr_Format(PyExc_ValueError, "error calling uwsgi_sharedarea_read()"); + } - memcpy(storage, uwsgi.sharedarea + pos, len); - - uwsgi_rwunlock(uwsgi.sa_lock); - - UWSGI_GET_GIL + // HACK: we are safe as rlen can only be lower or equal to len + Py_SIZE(ret) = rlen; return ret; } @@ -2519,11 +2469,11 @@ static PyMethodDef uwsgi_advanced_methods[] = { {"ready", py_uwsgi_ready, METH_VARARGS, ""}, {"set_user_harakiri", py_uwsgi_set_user_harakiri, METH_VARARGS, ""}, - //{"call_hook", py_uwsgi_call_hook, METH_VARARGS, ""}, {"websocket_recv", py_uwsgi_websocket_recv, METH_VARARGS, ""}, {"websocket_recv_nb", py_uwsgi_websocket_recv_nb, METH_VARARGS, ""}, {"websocket_send", py_uwsgi_websocket_send, METH_VARARGS, ""}, + {"websocket_send_binary", py_uwsgi_websocket_send_binary, METH_VARARGS, ""}, {"websocket_handshake", py_uwsgi_websocket_handshake, METH_VARARGS, ""}, {"chunked_read", py_uwsgi_chunked_read, METH_VARARGS, ""}, @@ -2538,11 +2488,16 @@ static PyMethodDef uwsgi_advanced_methods[] = { static PyMethodDef uwsgi_sa_methods[] = { {"sharedarea_read", py_uwsgi_sharedarea_read, METH_VARARGS, ""}, {"sharedarea_write", py_uwsgi_sharedarea_write, METH_VARARGS, ""}, - {"sharedarea_readbyte", py_uwsgi_sharedarea_readbyte, METH_VARARGS, ""}, - {"sharedarea_writebyte", py_uwsgi_sharedarea_writebyte, METH_VARARGS, ""}, - {"sharedarea_readlong", py_uwsgi_sharedarea_readlong, METH_VARARGS, ""}, - {"sharedarea_writelong", py_uwsgi_sharedarea_writelong, METH_VARARGS, ""}, - {"sharedarea_inclong", py_uwsgi_sharedarea_inclong, METH_VARARGS, ""}, + {"sharedarea_readbyte", py_uwsgi_sharedarea_read8, METH_VARARGS, ""}, + {"sharedarea_writebyte", py_uwsgi_sharedarea_write8, METH_VARARGS, ""}, + {"sharedarea_read8", py_uwsgi_sharedarea_read8, METH_VARARGS, ""}, + {"sharedarea_write8", py_uwsgi_sharedarea_write8, METH_VARARGS, ""}, + {"sharedarea_readlong", py_uwsgi_sharedarea_read64, METH_VARARGS, ""}, + {"sharedarea_writelong", py_uwsgi_sharedarea_write64, METH_VARARGS, ""}, + {"sharedarea_read64", py_uwsgi_sharedarea_read64, METH_VARARGS, ""}, + {"sharedarea_write64", py_uwsgi_sharedarea_write64, METH_VARARGS, ""}, + {"sharedarea_inclong", py_uwsgi_sharedarea_inc64, METH_VARARGS, ""}, + {"sharedarea_inc64", py_uwsgi_sharedarea_inc64, METH_VARARGS, ""}, {NULL, NULL}, }; diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index fdf8e97e..ce4a54ba 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -30,6 +30,12 @@ #define PYTHREE #endif +#if (PY_VERSION_HEX < 0x02060000) +#ifndef Py_SIZE +#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size) +#endif +#endif + #define UWSGI_GET_GIL up.gil_get(); #define UWSGI_RELEASE_GIL up.gil_release(); diff --git a/plugins/rbthreads/rbthreads.c b/plugins/rbthreads/rbthreads.c index 8cf65b4f..6ec15f51 100644 --- a/plugins/rbthreads/rbthreads.c +++ b/plugins/rbthreads/rbthreads.c @@ -37,6 +37,7 @@ struct uwsgi_rbthreads { int rbthreads; int (*orig_wait_write_hook) (int, int); int (*orig_wait_read_hook) (int, int); + int (*orig_wait_milliseconds_hook) (int); } urbts; static struct uwsgi_option rbthreads_options[] = { @@ -142,6 +143,20 @@ static int rbthreads_wait_fd_read(int fd, int timeout) { return urbt.ret; } +static void *rbthreads_wait_milliseconds_do(void *arg) { + struct uwsgi_rbthread *urbt = (struct uwsgi_rbthread *) arg; + urbt->ret = urbts.orig_wait_milliseconds_hook(urbt->timeout); + return NULL; +} + +static int rbthreads_wait_milliseconds(int timeout) { + struct uwsgi_rbthread urbt; + urbt.timeout = timeout; + rb_thread_call_without_gvl(rbthreads_wait_milliseconds_do, &urbt, NULL, NULL); + return urbt.ret; +} + + static void rbthreads_loop() { struct uwsgi_plugin *rup = uwsgi_plugin_get("rack"); // disable init_thread warning @@ -152,8 +167,10 @@ static void rbthreads_loop() { // override read/write nb hooks urbts.orig_wait_write_hook = uwsgi.wait_write_hook; urbts.orig_wait_read_hook = uwsgi.wait_read_hook; + urbts.orig_wait_milliseconds_hook = uwsgi.wait_milliseconds_hook; uwsgi.wait_write_hook = rbthreads_wait_fd_write; uwsgi.wait_read_hook = rbthreads_wait_fd_read; + uwsgi.wait_milliseconds_hook = rbthreads_wait_milliseconds; int i; for(i=1;iitems[ilen-1] == ';') items--; + if (items < 1) return UWSGI_ROUTE_BREAK; + uint32_t hashed_result = h % items; uint32_t found = 0; char *value = urhc->items; diff --git a/plugins/rpc/rpc_plugin.c b/plugins/rpc/rpc_plugin.c index d8ecec8b..134d8864 100644 --- a/plugins/rpc/rpc_plugin.c +++ b/plugins/rpc/rpc_plugin.c @@ -112,7 +112,11 @@ static int uwsgi_rpc_xmlrpc(struct wsgi_request *wsgi_req, xmlDoc *doc, char **a xmlChar *xmlbuf; int xlen = 0; xmlDocDumpFormatMemory(rdoc, &xmlbuf, &xlen, 1); - uwsgi_response_prepare_headers(wsgi_req,"200 OK", 6); + if (uwsgi_response_prepare_headers(wsgi_req,"200 OK", 6)) { + xmlFreeDoc(rdoc); + xmlFree(xmlbuf); + return -1; + } uwsgi_response_add_content_length(wsgi_req, xlen); uwsgi_response_add_content_type(wsgi_req, "application/xml", 15); uwsgi_response_write_body_do(wsgi_req, (char *) xmlbuf, xlen); diff --git a/plugins/symcall/symcall_plugin.c b/plugins/symcall/symcall_plugin.c index 7bb57886..49750a50 100644 --- a/plugins/symcall/symcall_plugin.c +++ b/plugins/symcall/symcall_plugin.c @@ -3,9 +3,8 @@ extern struct uwsgi_server uwsgi; struct uwsgi_symcall { - char *symcall_function_name; + struct uwsgi_string_list *symcall_function_name; int (*symcall_function)(struct wsgi_request *); - struct uwsgi_string_list *rpc; struct uwsgi_string_list *post_fork; } usym; @@ -13,24 +12,40 @@ struct uwsgi_symcall { struct uwsgi_plugin symcall_plugin; static struct uwsgi_option uwsgi_symcall_options[] = { - {"symcall", required_argument, 0, "load the specified C symbol as the symcall request handler", uwsgi_opt_set_str, &usym.symcall_function_name, 0}, + {"symcall", required_argument, 0, "load the specified C symbol as the symcall request handler (supports too)", uwsgi_opt_add_string_list, &usym.symcall_function_name, 0}, {"symcall-register-rpc", required_argument, 0, "load the specified C symbol as an RPC function (syntax: name function)", uwsgi_opt_add_string_list, &usym.rpc, 0}, {"symcall-post-fork", required_argument, 0, "call the specified C symbol after each fork()", uwsgi_opt_add_string_list, &usym.post_fork, 0}, {0, 0, 0, 0}, }; static void uwsgi_symcall_init(){ - if (usym.symcall_function_name) { - usym.symcall_function = dlsym(RTLD_DEFAULT, usym.symcall_function_name); - if (!usym.symcall_function) { - uwsgi_log("unable to find symbol \"%s\" in process address space\n", usym.symcall_function_name); + struct uwsgi_string_list *usl = NULL; + int has_mountpoints = 0; + uwsgi_foreach(usl, usym.symcall_function_name) { + char *func = usl->value, *mountpoint = ""; + char *equal = strchr(usl->value, '='); + if (equal) { + *equal = 0; + func = equal+1; + mountpoint = usl->value; + has_mountpoints = 1; + } + usl->custom_ptr = dlsym(RTLD_DEFAULT, func); + if (!usl->custom_ptr) { + uwsgi_log("unable to find symbol \"%s\" in process address space\n", func); exit(1); } - uwsgi_log("symcall function ptr: %p\n", usym.symcall_function); + int id = uwsgi_apps_cnt; + struct uwsgi_app *ua = uwsgi_add_app(id, symcall_plugin.modifier1, mountpoint, strlen(mountpoint), usl->custom_ptr, NULL); + uwsgi_log("symcall app %d (mountpoint: \"%.*s\") mapped to function ptr: %p\n", id, ua->mountpoint_len, ua->mountpoint, usl->custom_ptr); + if (equal) *equal = '='; } - struct uwsgi_string_list *usl = usym.rpc; - while(usl) { + if (!has_mountpoints && usym.symcall_function_name) { + usym.symcall_function = usym.symcall_function_name->custom_ptr; + } + + uwsgi_foreach(usl, usym.rpc) { char *space = strchr(usl->value, ' '); if (!space) { uwsgi_log("invalid symcall RPC syntax, must be: rpcname symbol\n"); @@ -55,6 +70,26 @@ static int uwsgi_symcall_request(struct wsgi_request *wsgi_req) { if (usym.symcall_function) { return usym.symcall_function(wsgi_req); } + + if (uwsgi_parse_vars(wsgi_req)) return -1; + + wsgi_req->app_id = uwsgi_get_app_id(wsgi_req, wsgi_req->appid, wsgi_req->appid_len, symcall_plugin.modifier1); + if (wsgi_req->app_id == -1 && !uwsgi.no_default_app && uwsgi.default_app > -1) { + if (uwsgi_apps[uwsgi.default_app].modifier1 == symcall_plugin.modifier1) { + wsgi_req->app_id = uwsgi.default_app; + } + } + + if (wsgi_req->app_id == -1) { + uwsgi_404(wsgi_req); + return UWSGI_OK; + } + + struct uwsgi_app *ua = &uwsgi_apps[wsgi_req->app_id]; + if (ua->interpreter) { + int (*func)(struct wsgi_request *) = (int (*)(struct wsgi_request *)) ua->interpreter; + return func(wsgi_req); + } return UWSGI_OK; } @@ -82,6 +117,21 @@ static void uwsgi_symcall_post_fork() { } } +static int uwsgi_symcall_mule(char *opt) { + if (uwsgi_endswith(opt, "()")) { + char *func_name = uwsgi_concat2n(opt, strlen(opt)-2, "", 0); + void (*func)() = dlsym(RTLD_DEFAULT, func_name); + if (!func) { + uwsgi_log("unable to find symbol \"%s\" in process address space\n", func_name); + exit(1); + } + free(func_name); + func(); + return 1; + } + return 0; +} + struct uwsgi_plugin symcall_plugin = { .name = "symcall", @@ -92,6 +142,6 @@ struct uwsgi_plugin symcall_plugin = { .after_request = uwsgi_symcall_after_request, .rpc = uwsgi_symcall_rpc, .post_fork = uwsgi_symcall_post_fork, - + .mule = uwsgi_symcall_mule, }; diff --git a/plugins/transformation_offload/offload.c b/plugins/transformation_offload/offload.c index 52b753d6..c599f56d 100644 --- a/plugins/transformation_offload/offload.c +++ b/plugins/transformation_offload/offload.c @@ -66,7 +66,7 @@ static int transform_offload(struct wsgi_request *wsgi_req, struct uwsgi_transfo if (ut->ub) { ssize_t wlen = write(ut->fd, ut->ub->buf, ut->ub->pos); if (wlen != (ssize_t) ut->ub->pos) { - uwsgi_error("transform_offload/write()"); + uwsgi_req_error("transform_offload/write()"); return -1; } } @@ -76,7 +76,7 @@ static int transform_offload(struct wsgi_request *wsgi_req, struct uwsgi_transfo if (ut->fd > -1) { ssize_t wlen = write(ut->fd, ut->chunk->buf, ut->chunk->pos); if (wlen != (ssize_t) ut->chunk->pos) { - uwsgi_error("transform_offload/write()"); + uwsgi_req_error("transform_offload/write()"); return -1; } ut->len += wlen; diff --git a/plugins/transformation_tofile/tofile.c b/plugins/transformation_tofile/tofile.c index 72b795ac..bb6671e9 100644 --- a/plugins/transformation_tofile/tofile.c +++ b/plugins/transformation_tofile/tofile.c @@ -43,7 +43,7 @@ static int transform_tofile(struct wsgi_request *wsgi_req, struct uwsgi_transfor while(remains) { ssize_t rlen = write(fd, ub->buf + (ub->pos - remains), remains); if (rlen <= 0) { - uwsgi_error("transform_tofile()/write()"); + uwsgi_req_error("transform_tofile()/write()"); unlink(uttc->filename->buf); break; } diff --git a/plugins/v8/v8_uwsgi.cc b/plugins/v8/v8_uwsgi.cc index 91a8cfdc..14167f18 100644 --- a/plugins/v8/v8_uwsgi.cc +++ b/plugins/v8/v8_uwsgi.cc @@ -285,7 +285,7 @@ extern "C" uint64_t uwsgi_v8_rpc(void * func, uint8_t argc, char **argv, uint16_ if (!*robj) { return 0; } - uint16_t rlen = robj->Length(); + uint64_t rlen = (uint64_t) robj->Length(); if (rlen > 0) { *buffer = (char *)uwsgi_malloc(rlen); memcpy(*buffer, *r_value, rlen); diff --git a/proto/base.c b/proto/base.c index 5ea3643b..a8d85059 100644 --- a/proto/base.c +++ b/proto/base.c @@ -208,6 +208,59 @@ int uwsgi_proto_base_write(struct wsgi_request * wsgi_req, char *buf, size_t len return -1; } +/* + NOTE: len is a pointer as it could be changed on the fly +*/ +int uwsgi_proto_base_writev(struct wsgi_request * wsgi_req, struct iovec *iov, size_t *len) { + size_t i,needed = 0; + // count the number of bytes to write + for(i=0;i<*len;i++) needed += iov[i].iov_len; + ssize_t wlen = writev(wsgi_req->fd, iov, *len); + if (wlen > 0) { + wsgi_req->write_pos += wlen; + if ((size_t)wlen == needed) { + return UWSGI_OK; + } + // now the complex part, we need to rebuild iovec and len... + size_t orig_len = *len; + size_t new_len = orig_len; + // first remove the consumed items + size_t first_iov = 0; + size_t skip_bytes = 0; + for(i=0;iproto_fix_headers = uwsgi_proto_base_fix_headers; uwsgi_sock->proto_read_body = uwsgi_proto_base_read_body; uwsgi_sock->proto_write = uwsgi_proto_base_write; + uwsgi_sock->proto_writev = uwsgi_proto_base_writev; uwsgi_sock->proto_write_headers = uwsgi_proto_base_write; uwsgi_sock->proto_sendfile = uwsgi_proto_base_sendfile; uwsgi_sock->proto_close = uwsgi_proto_base_close; diff --git a/proto/puwsgi.c b/proto/puwsgi.c index 83729bed..72840bea 100644 --- a/proto/puwsgi.c +++ b/proto/puwsgi.c @@ -98,6 +98,7 @@ void uwsgi_proto_puwsgi_setup(struct uwsgi_socket *uwsgi_sock) { uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers; uwsgi_sock->proto_read_body = uwsgi_proto_noop_read_body; uwsgi_sock->proto_write = uwsgi_proto_base_write; + uwsgi_sock->proto_writev = uwsgi_proto_base_writev; uwsgi_sock->proto_write_headers = uwsgi_proto_base_write; uwsgi_sock->proto_sendfile = uwsgi_proto_base_sendfile; uwsgi_sock->proto_close = uwsgi_proto_puwsgi_close; diff --git a/proto/scgi.c b/proto/scgi.c index a94428be..a5afec4a 100644 --- a/proto/scgi.c +++ b/proto/scgi.c @@ -115,6 +115,7 @@ void uwsgi_proto_scgi_setup(struct uwsgi_socket *uwsgi_sock) { uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers; uwsgi_sock->proto_read_body = uwsgi_proto_base_read_body; uwsgi_sock->proto_write = uwsgi_proto_base_write; + uwsgi_sock->proto_writev = uwsgi_proto_base_writev; uwsgi_sock->proto_write_headers = uwsgi_proto_base_write; uwsgi_sock->proto_sendfile = uwsgi_proto_base_sendfile; uwsgi_sock->proto_close = uwsgi_proto_base_close; @@ -128,6 +129,7 @@ void uwsgi_proto_scgi_nph_setup(struct uwsgi_socket *uwsgi_sock) { uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers; uwsgi_sock->proto_read_body = uwsgi_proto_base_read_body; uwsgi_sock->proto_write = uwsgi_proto_base_write; + uwsgi_sock->proto_writev = uwsgi_proto_base_writev; uwsgi_sock->proto_write_headers = uwsgi_proto_base_write; uwsgi_sock->proto_sendfile = uwsgi_proto_base_sendfile; uwsgi_sock->proto_close = uwsgi_proto_base_close; diff --git a/proto/uwsgi.c b/proto/uwsgi.c index 42dc10a0..07f3aaa1 100644 --- a/proto/uwsgi.c +++ b/proto/uwsgi.c @@ -219,6 +219,7 @@ void uwsgi_proto_uwsgi_setup(struct uwsgi_socket *uwsgi_sock) { uwsgi_sock->proto_fix_headers = uwsgi_proto_base_fix_headers; uwsgi_sock->proto_read_body = uwsgi_proto_base_read_body; uwsgi_sock->proto_write = uwsgi_proto_base_write; + uwsgi_sock->proto_writev = uwsgi_proto_base_writev; uwsgi_sock->proto_write_headers = uwsgi_proto_base_write; uwsgi_sock->proto_sendfile = uwsgi_proto_base_sendfile; uwsgi_sock->proto_close = uwsgi_proto_base_close; diff --git a/t/core/apps/read_body_and_send.pl b/t/core/apps/read_body_and_send.pl new file mode 100644 index 00000000..bd088ec5 --- /dev/null +++ b/t/core/apps/read_body_and_send.pl @@ -0,0 +1,5 @@ +my $app = sub { + my $env = shift; + $env->{'psgi.input'}->read(my $body, $env->{CONTENT_LENGTH}); + return [200, ['Content-Type' => 'x-application/binary'], [$body]]; +}; diff --git a/t/core/read_body_and_send.pl b/t/core/read_body_and_send.pl new file mode 100644 index 00000000..36cdbaf0 --- /dev/null +++ b/t/core/read_body_and_send.pl @@ -0,0 +1,35 @@ +#!/usr/bin/perl + +#uwsgi --http-socket :9090 --psgi t/core/apps/read_body_and_send.pl + +use IO::Socket::INET; + +my @chars = ("A".."Z", "a".."z"); + +foreach(0..100) { + $size = int(rand(8*1024*1024)); + print "testing: round ".$_." body size ".$size."\n"; + my $req = "POST /foobar HTTP/1.0\r\n"; + $req .= 'Content-Length: '.$size."\r\n\r\n"; + my $body = ''; + $body .= $chars[rand @chars] for 1..($size); + $req .= $body; + + my $s = IO::Socket::INET->new(PeerAddr => $ARGV[0]); + $s->send($req); + + my $response = ''; + while(1) { + $s->recv(my $buf, 4096); + last unless length($buf); + $response .= $buf; + } + $s->close; + + if ($response ne "HTTP/1.0 200 OK\r\nContent-Type: x-application/binary\r\n\r\n".$body) { + print "TEST FOR ROUND ".$_." FAILED\n"; + exit; + } +} + +print "test result: SUCCESS\n"; diff --git a/uwsgi.gemspec b/uwsgi.gemspec index 1fbbde05..0b607685 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 = '2013-11-09' + s.date = '2013-11-17' s.summary = "uWSGI" s.description = "The uWSGI server for Ruby/Rack" s.authors = ["Unbit"] @@ -12,4 +12,5 @@ Gem::Specification.new do |s| s.require_paths = ['.'] s.executables << 'uwsgi' s.homepage = 'http://projects.unbit.it/uwsgi' + s.add_runtime_dependency 'rack' end diff --git a/uwsgi.h b/uwsgi.h index 45e7201a..25e4f6b8 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -337,10 +337,6 @@ extern int pivot_root(const char *new_root, const char *put_old); #undef __EXTENSIONS__ #endif -#ifdef UWSGI_ZEROMQ -#include -#endif - #define UWSGI_CACHE_FLAG_UNGETTABLE 0x01 #define UWSGI_CACHE_FLAG_UPDATE 1 << 1 #define UWSGI_CACHE_FLAG_LOCAL 1 << 2 @@ -448,13 +444,14 @@ struct uwsgi_dyn_dict { uint64_t hits; int status; + struct uwsgi_dyn_dict *prev; + struct uwsgi_dyn_dict *next; + #ifdef UWSGI_PCRE pcre *pattern; pcre_extra *pattern_extra; #endif - struct uwsgi_dyn_dict *prev; - struct uwsgi_dyn_dict *next; }; struct uwsgi_hook { @@ -583,12 +580,13 @@ struct uwsgi_daemon { // frequency of pidfile checks (default 10 secs) int freq; + int control; + struct uwsgi_daemon *next; + #ifdef UWSGI_SSL char *legion; #endif - int control; - struct uwsgi_daemon *next; }; struct uwsgi_logger { @@ -713,6 +711,19 @@ struct uwsgi_hash_algo *uwsgi_hash_algo_get(char *); void uwsgi_hash_algo_register(char *, uint32_t(*)(char *, uint64_t)); void uwsgi_hash_algo_register_all(void); +struct uwsgi_sharedarea { + int id; + int pages; + int fd; + struct uwsgi_lock_item *lock; + char *area; + uint64_t max_pos; + uint64_t updates; + uint64_t hits; + uint8_t honour_used; + uint64_t used; +}; + // maintain alignment here !!! struct uwsgi_cache_item { // item specific flags @@ -848,6 +859,7 @@ struct uwsgi_opt { #define UWSGI_OPTION_MULE_HARAKIRI 18 #define UWSGI_OPTION_MAX_WORKER_LIFETIME 19 #define UWSGI_OPTION_MIN_WORKER_LIFETIME 20 +#define UWSGI_OPTION_LOG_IOERROR 21 #define UWSGI_SPOOLER_EXTERNAL 1 @@ -928,6 +940,8 @@ struct uwsgi_socket { void (*proto_close) (struct wsgi_request *); // special hook to call (if needed) in multithread mode void (*proto_thread_fixup) (struct uwsgi_socket *, int); + // optimization for vectors + int (*proto_writev) (struct wsgi_request *, struct iovec *, size_t *); int edge_trigger; int *retry; @@ -937,11 +951,8 @@ struct uwsgi_socket { // this is a special map for having socket->thread mapping int *fd_threads; -#ifdef UWSGI_UUID + // generally used by zeromq handlers char uuid[37]; -#endif - - // currently used by zeromq handlers void *pub; void *pull; pthread_key_t key; @@ -958,12 +969,13 @@ struct uwsgi_socket { int shared; int from_shared; + // used for avoiding vacuum mess + ino_t inode; + #ifdef UWSGI_SSL SSL_CTX *ssl_ctx; #endif - // used for avoiding vacuum mess - ino_t inode; }; struct uwsgi_protocol { @@ -1440,6 +1452,7 @@ struct wsgi_request { int suspended; uint64_t write_errors; + uint64_t read_errors; int *ovector; size_t post_cl; @@ -1486,6 +1499,7 @@ struct wsgi_request { struct uwsgi_string_list *remove_headers; struct uwsgi_buffer *websocket_buf; + struct uwsgi_buffer *websocket_send_buf; size_t websocket_need; int websocket_phase; uint8_t websocket_opcode; @@ -1534,15 +1548,6 @@ struct wsgi_request { #ifdef UWSGI_SSL SSL *ssl; #endif - - struct msghdr msg; - union { - struct cmsghdr cmsg; - // should be enough... - char control[64]; - } msg_control; - - }; @@ -2003,9 +2008,14 @@ struct uwsgi_server { struct uwsgi_string_list *hook_as_user_atexit; struct uwsgi_string_list *hook_pre_app; struct uwsgi_string_list *hook_post_app; + struct uwsgi_string_list *hook_accepting; + struct uwsgi_string_list *hook_accepting1; + struct uwsgi_string_list *hook_accepting_once; + struct uwsgi_string_list *hook_accepting1_once; struct uwsgi_string_list *hook_as_vassal; struct uwsgi_string_list *hook_as_emperor; + struct uwsgi_string_list *hook_as_mule; struct uwsgi_string_list *exec_asap; @@ -2058,6 +2068,8 @@ struct uwsgi_server { struct uwsgi_string_list *umount_as_vassal; struct uwsgi_string_list *umount_as_emperor; + struct uwsgi_string_list *after_request_hooks; + struct uwsgi_string_list *wait_for_interface; int wait_for_interface_timeout; @@ -2310,8 +2322,9 @@ struct uwsgi_server { int vec_size; // shared area - char *sharedarea; - uint64_t sharedareasize; + struct uwsgi_string_list *sharedareas_list; + int sharedareas_cnt; + struct uwsgi_sharedarea **sharedareas; // avoid thundering herd in threaded modes pthread_mutex_t thunder_mutex; @@ -2367,6 +2380,10 @@ struct uwsgi_server { struct uwsgi_route_var *route_vars; #endif + struct uwsgi_string_list *error_page_403; + struct uwsgi_string_list *error_page_404; + struct uwsgi_string_list *error_page_500; + int single_interpreter; struct uwsgi_shared *shared; @@ -2410,10 +2427,6 @@ struct uwsgi_server { int signal_socket; int my_signal_socket; -#ifdef UWSGI_ZEROMQ - int zeromq; - void *zmq_context; -#endif struct uwsgi_protocol *protocols; struct uwsgi_socket *sockets; struct uwsgi_socket *shared_sockets; @@ -2526,6 +2539,7 @@ struct uwsgi_server { struct uwsgi_rpc *rpc_table; // subscription client + int subscriptions_blocked; int subscribe_freq; int subscription_tolerance; int unsubscribe_on_graceful_reload; @@ -2593,6 +2607,8 @@ struct uwsgi_server { int (*wait_write_hook) (int, int); int (*wait_read_hook) (int, int); + int (*wait_milliseconds_hook) (int); + int (*wait_read2_hook) (int, int, int, int *); struct uwsgi_string_list *schemes; @@ -2644,9 +2660,6 @@ struct uwsgi_cron { uint8_t sig; char *command; -#ifdef UWSGI_SSL - char *legion; -#endif void (*func)(struct uwsgi_cron *, time_t); time_t started_at; @@ -2660,6 +2673,10 @@ struct uwsgi_cron { pid_t pid; struct uwsgi_cron *next; + +#ifdef UWSGI_SSL + char *legion; +#endif }; struct uwsgi_shared { @@ -2733,6 +2750,7 @@ struct uwsgi_core { uint64_t offloaded_requests; uint64_t write_errors; + uint64_t read_errors; uint64_t exceptions; pthread_t thread_id; @@ -2802,6 +2820,8 @@ struct uwsgi_worker { struct uwsgi_core *cores; + int accepting; + char name[0xff]; }; @@ -3201,16 +3221,9 @@ int uwsgi_str4_num(char *); #ifdef __linux__ #if !defined(__ia64__) - void linux_namespace_start(void *); - void linux_namespace_jail(void); +void linux_namespace_start(void *); +void linux_namespace_jail(void); #endif - int uwsgi_netlink_veth(char *, char *); - int uwsgi_netlink_veth_attach(char *, pid_t); - int uwsgi_netlink_ifup(char *); - int uwsgi_netlink_ip(char *, char *, int); - int uwsgi_netlink_gw(char *, char *); - int uwsgi_netlink_rt(char *, char *, int, char *); - int uwsgi_netlink_del(char *); #endif @@ -3229,6 +3242,7 @@ size_t uwsgi_str_num(char *, int); size_t uwsgi_str_occurence(char *, size_t, char); int uwsgi_proto_base_write(struct wsgi_request *, char *, size_t); +int uwsgi_proto_base_writev(struct wsgi_request *, struct iovec *, size_t *); #ifdef UWSGI_SSL int uwsgi_proto_ssl_write(struct wsgi_request *, char *, size_t); #endif @@ -3249,13 +3263,6 @@ void uwsgi_proto_ssl_close(struct wsgi_request *); uint16_t proto_base_add_uwsgi_header(struct wsgi_request *, char *, uint16_t, char *, uint16_t); uint16_t proto_base_add_uwsgi_var(struct wsgi_request *, char *, uint16_t, char *, uint16_t); -#ifdef UWSGI_ZEROMQ -void uwsgi_proto_zeromq_setup(struct uwsgi_socket *); -ssize_t uwsgi_zeromq_logger(struct uwsgi_logger *, char *, size_t len); -void *uwsgi_zeromq_init(void); -void uwsgi_zeromq_init_sockets(void); -#endif - // protocols void uwsgi_proto_uwsgi_setup(struct uwsgi_socket *); void uwsgi_proto_puwsgi_setup(struct uwsgi_socket *); @@ -3443,16 +3450,16 @@ struct uwsgi_subscribe_slot { uint64_t hits; + struct uwsgi_subscribe_node *nodes; + + struct uwsgi_subscribe_slot *prev; + struct uwsgi_subscribe_slot *next; + #ifdef UWSGI_SSL EVP_PKEY *sign_public_key; EVP_MD_CTX *sign_ctx; #endif - - struct uwsgi_subscribe_node *nodes; - - struct uwsgi_subscribe_slot *prev; - struct uwsgi_subscribe_slot *next; }; void mule_send_msg(int, char *, size_t); @@ -3792,11 +3799,14 @@ char *uwsgi_substitute(char *, char *, char *); void uwsgi_opt_add_custom_option(char *, char *, void *); void uwsgi_opt_cflags(char *, char *, void *); +void uwsgi_opt_build_plugin(char *, char *, void *); void uwsgi_opt_dot_h(char *, char *, void *); +void uwsgi_opt_config_py(char *, char *, void *); void uwsgi_opt_connect_and_read(char *, char *, void *); void uwsgi_opt_extract(char *, char *, void *); char *uwsgi_get_dot_h(); +char *uwsgi_get_config_py(); char *uwsgi_get_cflags(); struct uwsgi_string_list *uwsgi_string_list_has_item(struct uwsgi_string_list *, char *, size_t); @@ -3807,6 +3817,7 @@ void uwsgi_setup_systemd(); void uwsgi_setup_upstart(); void uwsgi_setup_zerg(); void uwsgi_setup_inherited_sockets(); +void uwsgi_setup_emperor(); #ifdef UWSGI_SSL void uwsgi_ssl_init(void); @@ -3877,6 +3888,8 @@ struct uwsgi_instance { time_t born; time_t last_mod; time_t last_loyal; + time_t last_accepting; + time_t last_ready; time_t last_run; time_t first_run; @@ -3896,6 +3909,9 @@ struct uwsgi_instance { int zerg; + int ready; + int accepting; + struct uwsgi_emperor_scanner *scanner; uid_t uid; @@ -4161,6 +4177,7 @@ void uwsgi_subscribe_all(uint8_t, int); void uwsgi_websockets_init(void); int uwsgi_websocket_send(struct wsgi_request *, char *, size_t); +int uwsgi_websocket_send_binary(struct wsgi_request *, char *, size_t); struct uwsgi_buffer *uwsgi_websocket_recv(struct wsgi_request *); struct uwsgi_buffer *uwsgi_websocket_recv_nb(struct wsgi_request *); @@ -4184,6 +4201,8 @@ struct uwsgi_buffer *uwsgi_proto_base_add_header(struct wsgi_request *, char *, int uwsgi_simple_wait_write_hook(int, int); int uwsgi_simple_wait_read_hook(int, int); +int uwsgi_simple_wait_read2_hook(int, int, int, int *); +int uwsgi_simple_wait_milliseconds_hook(int); int uwsgi_response_write_headers_do(struct wsgi_request *); char *uwsgi_request_body_read(struct wsgi_request *, ssize_t , ssize_t *); char *uwsgi_request_body_readline(struct wsgi_request *, ssize_t, ssize_t *); @@ -4192,6 +4211,7 @@ void uwsgi_request_body_seek(struct wsgi_request *, off_t); struct uwsgi_buffer *uwsgi_proto_base_prepare_headers(struct wsgi_request *, char *, uint16_t); struct uwsgi_buffer *uwsgi_proto_base_cgi_prepare_headers(struct wsgi_request *, char *, uint16_t); int uwsgi_response_write_body_do(struct wsgi_request *, char *, size_t); +int uwsgi_response_writev_body_do(struct wsgi_request *, struct iovec *, size_t); int uwsgi_proto_base_sendfile(struct wsgi_request *, int, size_t, size_t); #ifdef UWSGI_SSL @@ -4553,6 +4573,40 @@ struct uwsgi_protocol *uwsgi_register_protocol(char *, void (*)(struct uwsgi_soc void uwsgi_protocols_register(void); +void uwsgi_build_plugin(char *dir); + +void uwsgi_sharedareas_init(); + +struct uwsgi_sharedarea *uwsgi_sharedarea_init(int); +struct uwsgi_sharedarea *uwsgi_sharedarea_init_ptr(char *, uint64_t); + +int64_t uwsgi_sharedarea_read(int, uint64_t, char *, uint64_t); +int uwsgi_sharedarea_write(int, uint64_t, char *, uint64_t); +int uwsgi_sharedarea_read64(int, uint64_t, int64_t *); +int uwsgi_sharedarea_write64(int, uint64_t, int64_t *); +int uwsgi_sharedarea_read8(int, uint64_t, int8_t *); +int uwsgi_sharedarea_write8(int, uint64_t, int8_t *); +int uwsgi_sharedarea_read16(int, uint64_t, int16_t *); +int uwsgi_sharedarea_write16(int, uint64_t, int16_t *); +int uwsgi_sharedarea_read32(int, uint64_t, int32_t *); +int uwsgi_sharedarea_write32(int, uint64_t, int32_t *); +int uwsgi_sharedarea_inc8(int, uint64_t, int8_t); +int uwsgi_sharedarea_inc16(int, uint64_t, int16_t); +int uwsgi_sharedarea_inc32(int, uint64_t, int32_t); +int uwsgi_sharedarea_inc64(int, uint64_t, int64_t); +int uwsgi_sharedarea_dec8(int, uint64_t, int8_t); +int uwsgi_sharedarea_dec16(int, uint64_t, int16_t); +int uwsgi_sharedarea_dec32(int, uint64_t, int32_t); +int uwsgi_sharedarea_dec64(int, uint64_t, int64_t); +int uwsgi_sharedarea_wait(int, int, int); + +struct uwsgi_sharedarea *uwsgi_sharedarea_get_by_id(int, uint64_t); +int uwsgi_websocket_send_from_sharedarea(struct wsgi_request *, int, uint64_t, uint64_t); +int uwsgi_websocket_send_binary_from_sharedarea(struct wsgi_request *, int, uint64_t, uint64_t); + +void uwsgi_setup(int, char **, char **); +int uwsgi_run(void); + #ifdef __cplusplus } #endif diff --git a/uwsgiconfig.py b/uwsgiconfig.py index a18e10e7..ada0e28a 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1,6 +1,6 @@ # uWSGI build system -uwsgi_version = '1.9.20' +uwsgi_version = '1.9.21' import os import re @@ -55,6 +55,8 @@ if CPUCOUNT < 1: binary_list = [] +started_at = time.time() + # this is used for reporting (at the end of the build) # the server configuration report = { @@ -71,7 +73,6 @@ report = { 'yaml': False, 'json': False, 'ssl': False, - 'zeromq': False, 'xml': False, 'debug': False, 'plugin_dir': False, @@ -306,6 +307,24 @@ def build_uwsgi(uc, print_only=False, gcll=None): uwsgi_dot_h = uwsgi_dot_h_content.encode('hex') open('core/dot_h.c', 'w').write('char *uwsgi_dot_h = "%s";\n' % uwsgi_dot_h); gcc_list.append('core/dot_h') + + # embed uwsgiconfig.py in the server binary. It increases the binary size, but will be very useful + # if possibile, the blob is compressed + if sys.version_info[0] >= 3: + uwsgi_config_py_content = open('uwsgiconfig.py', 'rb').read() + else: + uwsgi_config_py_content = open('uwsgiconfig.py').read() + if report['zlib']: + import zlib + # maximum level of compression + uwsgi_config_py_content = zlib.compress(uwsgi_config_py_content, 9) + if sys.version_info[0] >= 3: + import binascii + uwsgi_config_py = binascii.b2a_hex(uwsgi_config_py_content).decode('ascii') + else: + uwsgi_config_py = uwsgi_config_py_content.encode('hex') + open('core/config_py.c', 'w').write('char *uwsgi_config_py = "%s";\n' % uwsgi_config_py); + gcc_list.append('core/config_py') cflags.append('-DUWSGI_CFLAGS=\\"%s\\"' % uwsgi_cflags) cflags.append('-DUWSGI_BUILD_DATE="\\"%s\\""' % time.strftime("%d %B %Y %H:%M:%S")) @@ -360,7 +379,10 @@ def build_uwsgi(uc, print_only=False, gcll=None): f.close() p_cflags = cflags[:] - p_cflags += up['CFLAGS'] + try: + p_cflags += up['CFLAGS'] + except: + pass if uwsgi_os.startswith('CYGWIN'): try: @@ -431,7 +453,13 @@ def build_uwsgi(uc, print_only=False, gcll=None): except: pass - libs += up['LIBS'] + try: + libs += up['LIBS'] + except: + pass + + if not 'LDFLAGS' in up: + up['LDFLAGS'] = [] if uwsgi_os == 'Darwin': found_arch = False @@ -495,6 +523,8 @@ def build_uwsgi(uc, print_only=False, gcll=None): print("") print("############## end of uWSGI configuration #############") + print("total build time: %d seconds" % (time.time() - started_at)) + if bin_name.find("/") < 0: bin_name = './' + bin_name if uc.get('as_shared_library'): @@ -547,7 +577,7 @@ class uConf(object): 'core/setup_utils', 'core/clock', 'core/init', 'core/buffer', 'core/reader', 'core/writer', 'core/alarm', 'core/cron', 'core/hooks', 'core/plugins', 'core/lock', 'core/cache', 'core/daemons', 'core/errors', 'core/hash', 'core/master_events', 'core/chunked', 'core/queue', 'core/event', 'core/signal', 'core/strings', 'core/progress', 'core/timebomb', 'core/ini', 'core/fsmon', 'core/mount', - 'core/metrics', + 'core/metrics', 'core/plugins_builder', 'core/sharedarea', 'core/rpc', 'core/gateway', 'core/loop', 'core/cookie', 'core/querystring', 'core/rb_timers', 'core/transformations', 'core/uwsgi'] # add protocols self.gcc_list.append('proto/base') @@ -1127,20 +1157,6 @@ class uConf(object): self.gcc_list.append('core/legion') report['ssl'] = True - - if has_uuid and self.get('zeromq'): - if self.get('zeromq') == 'auto': - if self.has_include('zmq.h'): - self.cflags.append("-DUWSGI_ZEROMQ") - self.gcc_list.append('proto/zeromq') - self.libs.append('-lzmq') - report['zeromq'] = True - else: - self.cflags.append("-DUWSGI_ZEROMQ") - self.gcc_list.append('proto/zeromq') - self.libs.append('-lzmq') - report['zeromq'] = True - if self.get('xml'): if self.get('xml') == 'auto': xmlconf = spcall('xml2-config --libs') @@ -1195,26 +1211,47 @@ class uConf(object): def build_plugin(path, uc, cflags, ldflags, libs, name = None): path = path.rstrip('/') - if not os.path.isdir(path): - print("Error: unable to find directory '%s'" % path) - sys.exit(1) + plugin_started_at = time.time() up = {} - try: - execfile('%s/uwsgiplugin.py' % path, up) - except: - f = open('%s/uwsgiplugin.py' % path) - exec(f.read(), up) - f.close() + + if os.path.isfile(path): + bname = os.path.basename(path) + # override path + path = os.path.dirname(path) + up['GCC_LIST'] = [bname] + up['NAME'] = bname.split('.')[0] + if not path: path = '.' + elif os.path.isdir(path): + try: + execfile('%s/uwsgiplugin.py' % path, up) + except: + f = open('%s/uwsgiplugin.py' % path) + exec(f.read(), up) + f.close() + else: + print("Error: unable to find directory '%s'" % path) + sys.exit(1) requires = [] p_cflags = cflags[:] p_ldflags = ldflags[:] - p_cflags += up['CFLAGS'] - p_ldflags += up['LDFLAGS'] - p_libs = up['LIBS'] + try: + p_cflags += up['CFLAGS'] + except: + pass + + try: + p_ldflags += up['LDFLAGS'] + except: + pass + + try: + p_libs = up['LIBS'] + except: + p_libs = [] post_build = None @@ -1355,6 +1392,7 @@ def build_plugin(path, uc, cflags, ldflags, libs, name = None): if post_build: post_build(uc) + print("build time: %d seconds" % (time.time() - plugin_started_at)) print("*** %s plugin built and available in %s ***" % (name, plugin_dest + '.so')) def vararg_callback(option, opt_str, value, parser): @@ -1378,7 +1416,7 @@ if __name__ == "__main__": parser.add_option("-f", "--cflags", action="callback", callback=vararg_callback, dest="cflags", help="same as --build but less verbose", metavar="PROFILE") parser.add_option("-u", "--unbit", action="store_true", dest="unbit", help="build unbit profile") parser.add_option("-p", "--plugin", action="callback", callback=vararg_callback, dest="plugin", help="build a plugin as shared library, optionally takes a build profile name", metavar="PLUGIN [PROFILE]") - parser.add_option("-x", "--extra-plugin", action="callback", callback=vararg_callback, dest="extra_plugin", help="build an external plugin as shared library, takes an optional include dir", metavar="PLUGIN [INCLUDE_DIR]") + parser.add_option("-x", "--extra-plugin", action="callback", callback=vararg_callback, dest="extra_plugin", help="build an external plugin as shared library, takes an optional include dir", metavar="PLUGIN [NAME]") parser.add_option("-c", "--clean", action="store_true", dest="clean", help="clean the build") parser.add_option("-e", "--check", action="store_true", dest="check", help="run cppcheck") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="more verbose build") @@ -1446,13 +1484,16 @@ if __name__ == "__main__": print("*** uWSGI building and linking plugin %s ***" % options.plugin[0] ) build_plugin(options.plugin[0], uc, cflags, ldflags, libs, name) elif options.extra_plugin: - print("*** uWSGI building and linking plugin ***") - cflags = spcall("%s --cflags" % options.extra_plugin[0]).split() + print("*** uWSGI building and linking plugin from %s ***" % options.extra_plugin[0]) + cflags = os.environ['UWSGI_PLUGINS_BUILDER_CFLAGS'].split() + os.environ.get("CFLAGS", "").split() + cflags.append('-I.uwsgi_plugins_builder/') + ldflags = os.environ.get("LDFLAGS", "").split() + name = None try: - cflags.append('-I%s' % options.extra_plugin[1]) + name = options.extra_plugin[1] except: pass - build_plugin('.', None, cflags, [], [], None) + build_plugin(options.extra_plugin[0], None, cflags, ldflags, None, name) elif options.clean: os.system("rm -f core/*.o") os.system("rm -f proto/*.o") @@ -1460,6 +1501,7 @@ if __name__ == "__main__": os.system("rm -f plugins/*/*.o") os.system("rm -f build/*.o") os.system("rm -f core/dot_h.c") + os.system("rm -f core/config_py.c") elif options.check: os.system("cppcheck --max-configs=1000 --enable=all -q core/ plugins/ proto/ lib/ apache2/") else: