diff --git a/.gitignore b/.gitignore index d021ce9b..df2b62e4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ /t/ring/target core/dot_h.c + +/build/ +/dist/ +/uWSGI.egg-info/ diff --git a/core/emperor.c b/core/emperor.c index 6d9cecd4..c004d09d 100644 --- a/core/emperor.c +++ b/core/emperor.c @@ -5,7 +5,6 @@ The uWSGI Emperor */ #include - extern struct uwsgi_server uwsgi; extern char **environ; @@ -37,6 +36,128 @@ struct uwsgi_emperor_blacklist_item { struct uwsgi_emperor_blacklist_item *emperor_blacklist; +static char *vassal_attr_get(struct uwsgi_instance *c_ui, char *attr) { + if (!attr) return NULL; + struct uwsgi_dyn_dict *attrs = c_ui->attrs; + while(attrs) { + if (!strcmp(attrs->key, attr)) { + return attrs->value; + } + attrs = attrs->next; + } + return NULL; +} + +// this generates the argv for the new vassal +static char **vassal_new_argv(struct uwsgi_instance *n_ui, int *slot_to_free) { + + int counter = 4; + struct uwsgi_string_list *uct; + uwsgi_foreach(uct, uwsgi.vassals_templates_before) counter += 2; + uwsgi_foreach(uct, uwsgi.vassals_includes_before) counter += 2; + uwsgi_foreach(uct, uwsgi.vassals_set) counter += 2; + uwsgi_foreach(uct, uwsgi.vassals_templates) counter += 2; + uwsgi_foreach(uct, uwsgi.vassals_includes) counter += 2; + + char **vassal_argv = uwsgi_malloc(sizeof(char *) * counter); + // set args + vassal_argv[0] = uwsgi.emperor_wrapper ? uwsgi.emperor_wrapper : uwsgi.binary_path; + char *wrapper_attr = vassal_attr_get(n_ui, uwsgi.emperor_wrapper_attr); + if (wrapper_attr) vassal_argv[0] = wrapper_attr; + + // reset counter + counter = 1; + + uwsgi_foreach(uct, uwsgi.vassals_templates_before) { + vassal_argv[counter] = "--inherit"; + vassal_argv[counter + 1] = uct->value; + counter += 2; + } + + uwsgi_foreach(uct, uwsgi.vassals_includes_before) { + vassal_argv[counter] = "--include"; + vassal_argv[counter + 1] = uct->value; + counter += 2; + } + + uwsgi_foreach(uct, uwsgi.vassals_set) { + vassal_argv[counter] = "--set"; + vassal_argv[counter + 1] = uct->value; + counter += 2; + } + + char *colon = NULL; + if (uwsgi.emperor_broodlord) { + colon = strchr(n_ui->name, ':'); + if (colon) { + colon[0] = 0; + } + } + // initialize to a default value + vassal_argv[counter] = "--inherit"; + + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".xml")) + vassal_argv[counter] = "--xml"; + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".ini")) + vassal_argv[counter] = "--ini"; + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".yml")) + vassal_argv[counter] = "--yaml"; + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 5), ".yaml")) + vassal_argv[counter] = "--yaml"; + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 3), ".js")) + vassal_argv[counter] = "--json"; + if (!strcmp(n_ui->name + (strlen(n_ui->name) - 5), ".json")) + vassal_argv[counter] = "--json"; + struct uwsgi_string_list *usl = uwsgi.emperor_extra_extension; + while (usl) { + if (uwsgi_endswith(n_ui->name, usl->value)) { + vassal_argv[counter] = "--config"; + break; + } + usl = usl->next; + } + if (colon) + colon[0] = ':'; + + // start config filename... + counter++; + + vassal_argv[counter] = n_ui->name; + if (uwsgi.emperor_magic_exec) { + if (!access(n_ui->name, R_OK | X_OK)) { + vassal_argv[counter] = uwsgi_concat2("exec://", n_ui->name); + if (slot_to_free) + *slot_to_free = counter; + } + + } + else if (n_ui->use_config) { + vassal_argv[counter] = uwsgi_concat2("emperor://", n_ui->name); + if (slot_to_free) + *slot_to_free = counter; + } + + // start templates,includes,inherit... + counter++; + + uwsgi_foreach(uct, uwsgi.vassals_templates) { + vassal_argv[counter] = "--inherit"; + vassal_argv[counter + 1] = uct->value; + counter += 2; + } + + uwsgi_foreach(uct, uwsgi.vassals_includes) { + vassal_argv[counter] = "--include"; + vassal_argv[counter + 1] = uct->value; + counter += 2; + } + + vassal_argv[counter] = NULL; + + return vassal_argv; +} + + /* this should be placed in core/socket.c but we realized it was needed only after 2.0 so we cannot change uwsgi.h @@ -211,7 +332,7 @@ int uwsgi_emperor_is_valid(char *name) { return 0; } -static char *emperor_check_on_demand_socket(char *filename) { +static char *emperor_check_on_demand_socket(char *filename, struct uwsgi_dyn_dict *attrs) { size_t len = 0; if (uwsgi.emperor_on_demand_extension) { char *tmp = uwsgi_concat2(filename, uwsgi.emperor_on_demand_extension); @@ -348,8 +469,14 @@ void uwsgi_imperial_monitor_directory(struct uwsgi_emperor_scanner *ues) { } } else { - char *socket_name = emperor_check_on_demand_socket(de->d_name); - emperor_add(ues, de->d_name, st.st_mtime, NULL, 0, t_uid, t_gid, socket_name); + struct uwsgi_dyn_dict *attrs = NULL; + if (uwsgi.emperor_collect_attributes) { + if (uwsgi_endswith(de->d_name, ".ini")) { + uwsgi_emperor_ini_attrs(de->d_name, NULL, &attrs); + } + } + char *socket_name = emperor_check_on_demand_socket(de->d_name, attrs); + emperor_add_with_attrs(ues, de->d_name, st.st_mtime, NULL, 0, t_uid, t_gid, socket_name, attrs); if (socket_name) free(socket_name); } @@ -469,10 +596,16 @@ void uwsgi_imperial_monitor_glob(struct uwsgi_emperor_scanner *ues) { } } else { - char *socket_name = emperor_check_on_demand_socket(g.gl_pathv[i]); - emperor_add(ues, g.gl_pathv[i], st.st_mtime, NULL, 0, t_uid, t_gid, socket_name); - if (socket_name) - free(socket_name); + struct uwsgi_dyn_dict *attrs = NULL; + if (uwsgi.emperor_collect_attributes) { + if (uwsgi_endswith(g.gl_pathv[i], ".ini")) { + uwsgi_emperor_ini_attrs(g.gl_pathv[i], NULL, &attrs); + } + } + char *socket_name = emperor_check_on_demand_socket(g.gl_pathv[i], attrs); + emperor_add_with_attrs(ues, g.gl_pathv[i], st.st_mtime, NULL, 0, t_uid, t_gid, socket_name, attrs); + if (socket_name) + free(socket_name); } } @@ -666,16 +799,12 @@ void emperor_del(struct uwsgi_instance *c_ui) { } // this will destroy the whole uWSGI instance (and workers) - if (c_ui->pipe[0] != -1) - close(c_ui->pipe[0]); - if (c_ui->pipe[1] != -1) - close(c_ui->pipe[1]); + if (c_ui->pipe[0] != -1) close(c_ui->pipe[0]); + if (c_ui->pipe[1] != -1) close(c_ui->pipe[1]); if (c_ui->use_config) { - if (c_ui->pipe_config[0] != -1) - close(c_ui->pipe_config[0]); - if (c_ui->pipe_config[1] != -1) - close(c_ui->pipe_config[1]); + if (c_ui->pipe_config[0] != -1) close(c_ui->pipe_config[0]); + if (c_ui->pipe_config[1] != -1) close(c_ui->pipe_config[1]); } if (uwsgi.vassals_stop_hook) { @@ -703,15 +832,20 @@ void emperor_del(struct uwsgi_instance *c_ui) { free(c_ui->socket_name); } - if (c_ui->config) - free(c_ui->config); - if (c_ui->on_demand_fd > -1) { close(c_ui->on_demand_fd); } + if (c_ui->config) free(c_ui->config); + + struct uwsgi_dyn_dict *attr = c_ui->attrs; + while(attr) { + struct uwsgi_dyn_dict *tmp = attr; + attr = attr->next; + if (tmp->value) free(tmp->value); + free(tmp); + } free(c_ui); - } void emperor_back_to_ondemand(struct uwsgi_instance *c_ui) { @@ -822,6 +956,10 @@ void emperor_respawn(struct uwsgi_instance *c_ui, time_t mod) { } 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) { + emperor_add_with_attrs(ues, name, born, config, config_size, uid, gid, socket_name, NULL); +} + +void emperor_add_with_attrs(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, struct uwsgi_dyn_dict *attrs) { struct uwsgi_instance *c_ui = ui; struct uwsgi_instance *n_ui = NULL; @@ -912,6 +1050,8 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha n_ui->last_loyal = 0; n_ui->loyal = 0; + n_ui->attrs = attrs; + n_ui->first_run = uwsgi_now(); n_ui->last_run = n_ui->first_run; n_ui->on_demand_fd = -1; @@ -937,6 +1077,9 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha event_queue_add_fd_read(uwsgi.emperor_queue, n_ui->on_demand_fd); uwsgi_log("[uwsgi-emperor] %s -> \"on demand\" instance detected, waiting for connections on socket \"%s\" ...\n", name, socket_name); + if (uwsgi_hooks_run_and_return(uwsgi.hook_as_on_demand_vassal, "as-on-demand-vassal", name, 0)) { + emperor_del(n_ui); + } return; } @@ -948,6 +1091,117 @@ void emperor_add(struct uwsgi_emperor_scanner *ues, char *name, time_t born, cha static void uwsgi_emperor_spawn_vassal(struct uwsgi_instance *); +static void vassal_fork_server_parser_hook(char *key, uint16_t key_len, char *value, uint16_t value_len, void *data) { + pid_t *pid = (pid_t *) data; + + if (!uwsgi_strncmp(key, key_len, "pid", 3)) { + // ignore negative values + if (value_len > 0 && value[0] == '-') return; + *pid = uwsgi_str_num(value, value_len); + } +} + + +/* + there are max 3 file descriptors we need to pass to the fork server: + + n_ui->pipe[1] + n_ui->pipe_config[1] + n_ui->on_demand_fd + +*/ +static pid_t emperor_connect_to_fork_server(char *socket, struct uwsgi_instance *n_ui) { + int fd = uwsgi_connect(socket, uwsgi.socket_timeout, 0); + if (fd < 0) { + uwsgi_error("emperor_connect_to_fork_server()/uwsgi_connect()"); + return -1; + } + + int slot_to_free = -1; + char **vassal_argv = vassal_new_argv(n_ui, &slot_to_free); + + struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); + // leave space for uwsgi header + ub->pos = 4; + int error = 0, counter = 0; + while (vassal_argv[counter]) { + if (!error && uwsgi_buffer_u16le(ub, strlen(vassal_argv[counter]))) + error = 1; + if (!error && uwsgi_buffer_append(ub, vassal_argv[counter], strlen(vassal_argv[counter]))) + error = 1; + if (counter == slot_to_free) + free(vassal_argv[counter]); + counter++; + } + + free(vassal_argv); + if (error) { + uwsgi_log_verbose("[uwsgi-emperor] %s: unable to complete fork-server session\n", n_ui->name); + goto end; + } + + // bit 0 -> pipe (0x01) + // bit 1 -> config_pipe (0x02) + // bit 2 -> on_demand (0x04) + uint8_t modifier2_mask = 0x01; + int fds[8]; + int fds_count = 1; + fds[0] = n_ui->pipe[1]; + + // add pipe config ? + if (n_ui->use_config) { + modifier2_mask |= 0x02; + fds[fds_count] = n_ui->pipe_config[1]; + fds_count++; + } + + // add ondemand ? + if (n_ui->on_demand_fd > -1) { + modifier2_mask |= 0x04; + fds[fds_count] = n_ui->on_demand_fd; + fds_count++; + } + + // fix uwsgi header + if (uwsgi_buffer_set_uh(ub, 35, modifier2_mask)) goto end; + + if (uwsgi_send_fds_and_body(fd, fds, fds_count, ub->buf, ub->pos)) { + uwsgi_log_verbose("[uwsgi-emperor] %s: unable to complete fork-server session\n", n_ui->name); + goto end; + } + + uwsgi_buffer_destroy(ub); + + // now wait for the response (the pid number) + // the response could contain various info, currently we only need the "pid" attribute + size_t buf_len = uwsgi.page_size; + char *buf = uwsgi_malloc(buf_len); + uint8_t modifier1 = 0; + uint8_t modifier2 = 0; + int ret = uwsgi_read_with_realloc(fd, &buf, &buf_len, uwsgi.socket_timeout, &modifier1, &modifier2); + if (ret) { + free(buf); + uwsgi_log_verbose("[uwsgi-emperor] %s: unable to complete fork-server session\n", n_ui->name); + goto end2; + } + + pid_t pid = -1; + uwsgi_hooked_parse(buf, buf_len, vassal_fork_server_parser_hook, &pid); + free(buf); + + // close the connection + close(fd); + + // return the pid to the Emperor + return pid; + +end: + uwsgi_buffer_destroy(ub); +end2: + close(fd); + return -1; +} + int uwsgi_emperor_vassal_start(struct uwsgi_instance *n_ui) { pid_t pid; @@ -974,18 +1228,27 @@ int uwsgi_emperor_vassal_start(struct uwsgi_instance *n_ui) { // TODO pre-start hook + + // check for fork server + char *fork_server = uwsgi.emperor_use_fork_server; + char *fork_server_attr = vassal_attr_get(n_ui, uwsgi.emperor_fork_server_attr); + if (fork_server_attr) fork_server = fork_server_attr; // a new uWSGI instance will start + if (fork_server && !uwsgi_string_list_has_item(uwsgi.vassal_fork_base, n_ui->name, strlen(n_ui->name))) { + // pid can only be > 0 or -1 + n_ui->adopted = 1; + pid = emperor_connect_to_fork_server(fork_server, n_ui); + } #if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL) && !defined(__ia64__) - if (uwsgi.emperor_clone) { + else if (uwsgi.emperor_clone) { char stack[PTHREAD_STACK_MIN]; pid = clone((int (*)(void *)) uwsgi_emperor_spawn_vassal, stack + PTHREAD_STACK_MIN, SIGCHLD | uwsgi.emperor_clone, (void *) n_ui); } +#endif else { -#endif pid = fork(); -#if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL) && !defined(__ia64__) } -#endif + if (pid < 0) { uwsgi_error("uwsgi_emperor_spawn_vassal()/fork()") } @@ -1237,103 +1500,8 @@ static void uwsgi_emperor_spawn_vassal(struct uwsgi_instance *n_ui) { close(n_ui->pipe_config[0]); } - int counter = 4; - struct uwsgi_string_list *uct; - uwsgi_foreach(uct, uwsgi.vassals_templates_before) counter += 2; - uwsgi_foreach(uct, uwsgi.vassals_includes_before) counter += 2; - uwsgi_foreach(uct, uwsgi.vassals_set) counter += 2; - uwsgi_foreach(uct, uwsgi.vassals_templates) counter += 2; - uwsgi_foreach(uct, uwsgi.vassals_includes) counter += 2; + char **vassal_argv = vassal_new_argv(n_ui, NULL); - char **vassal_argv = uwsgi_malloc(sizeof(char *) * counter); - // set args - vassal_argv[0] = uwsgi.emperor_wrapper ? uwsgi.emperor_wrapper : uwsgi.binary_path; - - // reset counter - counter = 1; - - uwsgi_foreach(uct, uwsgi.vassals_templates_before) { - vassal_argv[counter] = "--inherit"; - vassal_argv[counter + 1] = uct->value; - counter += 2; - } - - uwsgi_foreach(uct, uwsgi.vassals_includes_before) { - vassal_argv[counter] = "--include"; - vassal_argv[counter + 1] = uct->value; - counter += 2; - } - - uwsgi_foreach(uct, uwsgi.vassals_set) { - vassal_argv[counter] = "--set"; - vassal_argv[counter + 1] = uct->value; - counter += 2; - } - - char *colon = NULL; - if (uwsgi.emperor_broodlord) { - colon = strchr(n_ui->name, ':'); - if (colon) { - colon[0] = 0; - } - } - // initialize to a default value - vassal_argv[counter] = "--inherit"; - - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".xml")) - vassal_argv[counter] = "--xml"; - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".ini")) - vassal_argv[counter] = "--ini"; - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 4), ".yml")) - vassal_argv[counter] = "--yaml"; - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 5), ".yaml")) - vassal_argv[counter] = "--yaml"; - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 3), ".js")) - vassal_argv[counter] = "--json"; - if (!strcmp(n_ui->name + (strlen(n_ui->name) - 5), ".json")) - vassal_argv[counter] = "--json"; - struct uwsgi_string_list *usl = uwsgi.emperor_extra_extension; - while (usl) { - if (uwsgi_endswith(n_ui->name, usl->value)) { - vassal_argv[counter] = "--config"; - break; - } - usl = usl->next; - } - if (colon) - colon[0] = ':'; - - // start config filename... - counter++; - - vassal_argv[counter] = n_ui->name; - if (uwsgi.emperor_magic_exec) { - if (!access(n_ui->name, R_OK | X_OK)) { - vassal_argv[counter] = uwsgi_concat2("exec://", n_ui->name); - } - - } - - if (n_ui->use_config) { - vassal_argv[counter] = uwsgi_concat2("emperor://", n_ui->name); - } - - // start templates,includes,inherit... - counter++; - - uwsgi_foreach(uct, uwsgi.vassals_templates) { - vassal_argv[counter] = "--inherit"; - vassal_argv[counter + 1] = uct->value; - counter += 2; - } - - uwsgi_foreach(uct, uwsgi.vassals_includes) { - vassal_argv[counter] = "--include"; - vassal_argv[counter + 1] = uct->value; - counter += 2; - } - - vassal_argv[counter] = NULL; // disable stdin OR map it to the "on demand" socket if (n_ui->on_demand_fd > -1) { @@ -1377,6 +1545,7 @@ static void uwsgi_emperor_spawn_vassal(struct uwsgi_instance *n_ui) { uwsgi_hooks_run(uwsgi.hook_as_vassal, "as-vassal", 1); + struct uwsgi_string_list *usl = NULL; uwsgi_foreach(usl, uwsgi.mount_as_vassal) { uwsgi_log("mounting \"%s\" (as-vassal)...\n", usl->value); if (uwsgi_mount_hook(usl->value)) { @@ -1588,6 +1757,15 @@ static void emperor_cleanup() { void emperor_loop() { +#if defined(__linux__) && defined(PR_SET_CHILD_SUBREAPER) + if (uwsgi.emperor_use_fork_server || uwsgi.emperor_subreaper) { + if (prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0)) { + uwsgi_error("uwsgi_fork_server()/fork()"); + exit(1); + } + } +#endif + // monitor a directory struct uwsgi_instance ui_base; @@ -1874,6 +2052,7 @@ recheck: uwsgi_error("waitpid()"); } } + ui_current = ui; while (ui_current->ui_next) { ui_current = ui_current->ui_next; @@ -1896,17 +2075,17 @@ recheck: socket_name = uwsgi_str(ui_current->socket_name); } emperor_add(ui_current->scanner, ui_current->name, ui_current->last_mod, config, ui_current->config_len, ui_current->uid, ui_current->gid, socket_name); - // temporarily set frequency to 0, so we can eventually fast-restart the instance emperor_del(ui_current); - freq = 0; + // temporarily set frequency to 1, so we can eventually fast-restart the instance + freq = 1; } break; } else if (ui_current->status == 1) { // remove 'marked for dead' instance emperor_del(ui_current); - // temporarily set frequency to 0, so we can eventually fast-restart the instance - freq = 0; + // temporarily set frequency to 1, so we can eventually fast-restart the instance + freq = 1; break; } // back to on_demand mode ... @@ -1924,14 +2103,18 @@ recheck: ui_current->ready = 0; ui_current->accepting = 0; uwsgi_log("[uwsgi-emperor] %s -> back to \"on demand\" mode, waiting for connections on socket \"%s\" ...\n", ui_current->name, ui_current->socket_name); + if (uwsgi_hooks_run_and_return(uwsgi.hook_as_on_demand_vassal, "as-on-demand-vassal", ui_current->name, 0)) { + emperor_del(ui_current); + freq = 1; + } break; } } else if (ui_current->cursed_at > 0) { if (ui_current->pid == -1) { emperor_del(ui_current); - // temporarily set frequency to 0, so we can eventually fast-restart the instance - freq = 0; + // temporarily set frequency to 1, so we can eventually fast-restart the instance + freq = 1; break; } else if (now - ui_current->cursed_at >= uwsgi.emperor_curse_tolerance) { @@ -2065,6 +2248,9 @@ void emperor_send_stats(int fd) { if (uwsgi_stats_keyval_comma(us, "on_demand", c_ui->socket_name ? c_ui->socket_name : "")) goto end0; + if (uwsgi_stats_keylong_comma(us, "adopted", (unsigned long long) c_ui->adopted)) + goto end0; + if (uwsgi_stats_keylong_comma(us, "uid", (unsigned long long) c_ui->uid)) goto end0; if (uwsgi_stats_keylong_comma(us, "gid", (unsigned long long) c_ui->gid)) @@ -2073,6 +2259,31 @@ void emperor_send_stats(int fd) { if (uwsgi_stats_keyval_comma(us, "monitor", c_ui->scanner->arg)) goto end0; + if (uwsgi_stats_key(us, "attrs")) + goto end0; + + if (uwsgi_stats_list_open(us)) + goto end0; + + struct uwsgi_dyn_dict *attrs = c_ui->attrs; + while(attrs) { + if (attrs->next) { + if (uwsgi_stats_keyval_comma(us, attrs->key, attrs->value)) + goto end0; + } + else { + if (uwsgi_stats_keyval(us, attrs->key, attrs->value)) + goto end0; + } + attrs = attrs->next; + } + + if (uwsgi_stats_list_close(us)) + goto end0; + + if (uwsgi_stats_comma(us)) + goto end0; + if (uwsgi_stats_keylong(us, "respawns", (unsigned long long) c_ui->respawns)) goto end0; @@ -2261,6 +2472,34 @@ next: } +void uwsgi_emperor_simple_do_with_attrs(struct uwsgi_emperor_scanner *ues, char *name, char *config, time_t ts, uid_t uid, gid_t gid, char *socket_name, struct uwsgi_dyn_dict *attrs) { + uwsgi_emperor_simple_do(ues, name, config, ts, uid, gid, socket_name); + struct uwsgi_instance *ui_current = emperor_get(name); + // free attrs ? + if (!ui_current) { + struct uwsgi_dyn_dict *attr = attrs; + while(attr) { + struct uwsgi_dyn_dict *tmp = attr; + attr = attr->next; + if (tmp->value) free(tmp->value); + free(tmp); + } + return; + } + + // if the instance has attrs mapped, let's free them + if (ui_current->attrs) { + struct uwsgi_dyn_dict *attr = ui_current->attrs; + while(attr) { + struct uwsgi_dyn_dict *tmp = attr; + attr = attr->next; + if (tmp->value) free(tmp->value); + free(tmp); + } + } + ui_current->attrs = attrs; +} + void uwsgi_emperor_simple_do(struct uwsgi_emperor_scanner *ues, char *name, char *config, time_t ts, uid_t uid, gid_t gid, char *socket_name) { if (!uwsgi_emperor_is_valid(name)) diff --git a/core/fork_server.c b/core/fork_server.c new file mode 100644 index 00000000..03caec58 --- /dev/null +++ b/core/fork_server.c @@ -0,0 +1,226 @@ +#include + +extern struct uwsgi_server uwsgi; + +/* + +on connection retrieve the uid,gid and pid of the connecting process, in addition to up to 3 +file descriptors (emperor pipe, emperor pipe_config, on_demand socket dup()'ed to 0) + +if authorized, double fork, get the pid of the second child and exit() +its parent (this will force the Emperor to became its subreaper). + +from now on, we can consider the new child as a full-featured vassal + +*/ + +#define VASSAL_HAS_CONFIG 0x02 +#define VASSAL_HAS_ON_DEMAND 0x04 + +static void parse_argv_hook(uint16_t item, char *value, uint16_t vlen, void *data) { + struct uwsgi_string_list **usl = (struct uwsgi_string_list **) data; + uwsgi_string_new_list(usl, uwsgi_concat2n(value, vlen, "", 0)); +} + + +void uwsgi_fork_server(char *socket) { + // map fd 0 to /dev/null to avoid mess + uwsgi_remap_fd(0, "/dev/null"); + + int fd = bind_to_unix(socket, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); + if (fd < 0) exit(1); + + // automatically receive credentials (TODO make something useful with them, like checking the pid is from the Emperor) + if (uwsgi_socket_passcred(fd)) exit(1); + + // initialize the event queue + int eq = event_queue_init(); + if (uwsgi.has_emperor) { + event_queue_add_fd_read(eq, uwsgi.emperor_fd); + } + event_queue_add_fd_read(eq, fd); + + // now start waiting for connections + for(;;) { + int interesting_fd = -1; + int rlen = event_queue_wait(eq, -1, &interesting_fd); + if (rlen <= 0) continue; + if (uwsgi.has_emperor && interesting_fd == uwsgi.emperor_fd) { + char byte; + ssize_t rlen = read(uwsgi.emperor_fd, &byte, 1); + if (rlen > 0) { + uwsgi_log_verbose("received message %d from emperor\n", byte); + } + exit(0); + } + if (interesting_fd != fd) continue; + struct sockaddr_un client_src; + socklen_t client_src_len = 0; + int client_fd = accept(fd, (struct sockaddr *) &client_src, &client_src_len); + if (client_fd < 0) { + uwsgi_error("uwsgi_fork_server()/accept()"); + continue; + } + char hbuf[4]; + pid_t ppid = -1; + uid_t uid = -1; + gid_t gid = -1; + int fds_count = 8; + size_t remains = 4; + // we can receive upto 8 fds (generally from 1 to 3) + int fds[8]; + // we only read 4 bytes header + ssize_t len = uwsgi_recv_cred_and_fds(client_fd, hbuf, remains, &ppid, &uid, &gid, fds, &fds_count); + uwsgi_log_verbose("[uwsgi-fork-server] connection from pid: %d uid: %d gid:%d fds:%d\n", ppid, uid, gid, fds_count); + if (len <= 0 || fds_count < 1) { + uwsgi_error("uwsgi_fork_server()/recvmsg()"); + goto end; + } + remains -= len; + + if (uwsgi_read_nb(client_fd, hbuf + (4-remains), remains, uwsgi.socket_timeout)) { + uwsgi_error("uwsgi_fork_server()/uwsgi_read_nb()"); + goto end; + } + + struct uwsgi_header *uh = (struct uwsgi_header *) hbuf; + // this memory area must be freed in the right place !!! + char *body_argv = uwsgi_malloc(uh->pktsize); + if (uwsgi_read_nb(client_fd, body_argv, uh->pktsize, uwsgi.socket_timeout)) { + free(body_argv); + uwsgi_error("uwsgi_fork_server()/uwsgi_read_nb()"); + goto end; + } + + pid_t pid = fork(); + if (pid < 0) { + free(body_argv); + int i; + for(i=0;i 0) { + free(body_argv); + // close inherited decriptors + int i; + for(i=0;i -1) close(uwsgi.emperor_fd_config); + } + + // set EMPEROR_FD and FD_CONFIG env vars + char *uef = uwsgi_num2str(fds[0]); + if (setenv("UWSGI_EMPEROR_FD", uef, 1)) { + uwsgi_error("uwsgi_fork_server()/setenv()"); + exit(1); + } + free(uef); + + int pipe_config = -1; + int on_demand = -1; + + if (uh->modifier2 & VASSAL_HAS_CONFIG && fds_count > 1) { + pipe_config = fds[1]; + char *uef = uwsgi_num2str(pipe_config); + if (setenv("UWSGI_EMPEROR_FD_CONFIG", uef, 1)) { + uwsgi_error("uwsgi_fork_server()/setenv()"); + exit(1); + } + free(uef); + } + + if (uh->modifier2 & VASSAL_HAS_ON_DEMAND && fds_count > 1) { + if (pipe_config > -1) { + if (fds_count > 2) { + on_demand = fds[2]; + } + } + else { + on_demand = fds[1]; + } + } + // dup the on_demand socket to 0 and close it + if (on_demand > -1) { + if (dup2(on_demand, 0) < 0) { + uwsgi_error("uwsgi_fork_server()/dup2()"); + exit(1); + } + close(on_demand); + } + + // now fork again and die + pid_t new_pid = fork(); + if (new_pid < 0) { + uwsgi_error("uwsgi_fork_server()/fork()"); + exit(1); + } + else if (new_pid > 0) { + exit(0); + } + else { + // send the pid to the client_fd and close it + struct uwsgi_buffer *ub = uwsgi_buffer_new(uwsgi.page_size); + // leave space for header + ub->pos = 4; + if (uwsgi_buffer_append_keynum(ub, "pid", 3, getpid())) exit(1); + // fix uwsgi header + if (uwsgi_buffer_set_uh(ub, 35, 0)) goto end; + // send_pid() + if (uwsgi_write_nb(client_fd, ub->buf, ub->pos, uwsgi.socket_timeout)) exit(1); + close(client_fd); + uwsgi_log("double fork() and reparenting successfull (new pid: %d)\n", getpid()); + + + // now parse the uwsgi packet array and build the argv + struct uwsgi_string_list *usl = NULL, *usl_argv = NULL; + uwsgi_hooked_parse_array(body_argv, uh->pktsize, parse_argv_hook, &usl_argv); + free(body_argv); + + // build new argc/argv + uwsgi.new_argc = 0; + size_t procname_len = 1; + uwsgi_foreach(usl, usl_argv) { + uwsgi.new_argc++; + procname_len += usl->len + 1; + } + + char *new_procname = uwsgi_calloc(procname_len); + + uwsgi.new_argv = uwsgi_calloc(sizeof(char *) * (uwsgi.new_argc + 1)); + int counter = 0; + uwsgi_foreach(usl, usl_argv) { + uwsgi.new_argv[counter] = usl->value; + strcat(new_procname, usl->value); + strcat(new_procname, " "); + counter++; + } + // fix process name + uwsgi_set_processname(new_procname); + free(new_procname); + // this is the only step required to have a consistent environment + uwsgi.fork_socket = NULL; + // this avoids the process to re-exec itself + uwsgi.exit_on_reload = 1; + // fixup the Emperor communication + uwsgi_check_emperor(); + // continue with uWSGI startup + return; + } + } + +end: + close(client_fd); + + } +} diff --git a/core/hooks.c b/core/hooks.c index 4f79fab0..12536dc5 100644 --- a/core/hooks.c +++ b/core/hooks.c @@ -586,55 +586,82 @@ void uwsgi_register_base_hooks() { uwsgi_register_hook("log", uwsgi_hook_print); } -void uwsgi_hooks_run(struct uwsgi_string_list *l, char *phase, int fatal) { +int uwsgi_hooks_run_and_return(struct uwsgi_string_list *l, char *phase, char *context, int fatal) { + int final_ret = 0; struct uwsgi_string_list *usl = NULL; - uwsgi_foreach(usl, l) { - char *colon = strchr(usl->value, ':'); - if (!colon) { - uwsgi_log("invalid hook syntax, must be hook:args\n"); - exit(1); - } - *colon = 0; - int private = 0; - char *action = usl->value; - // private hook ? - if (action[0] == '!') { - action++; - private = 1; - } - struct uwsgi_hook *uh = uwsgi_hook_by_name(action); - if (!uh) { - uwsgi_log("hook action not found: %s\n", action); - exit(1); - } - *colon = ':'; - - if (private) { - uwsgi_log("running --- PRIVATE HOOK --- (%s)...\n", phase); - } - else { - uwsgi_log("running \"%s\" (%s)...\n", usl->value, phase); - } - - int ret = uh->func(colon+1); - if (fatal && ret != 0) { - uwsgi_log_verbose("FATAL hook failed, destroying instance\n"); - if (uwsgi.master_process) { - if (uwsgi.workers) { - if (uwsgi.workers[0].pid == getpid()) { - kill_them_all(0); - return; - } - else { - if (kill(uwsgi.workers[0].pid, SIGINT)) { - uwsgi_error("uwsgi_hooks_run()/kill()"); - exit(1); - } - return; - } - } - } - exit(1); + if (context) { + if (setenv("UWSGI_HOOK_CONTEXT", context, 1)) { + uwsgi_error("uwsgi_hooks_run_and_return()/setenv()"); + return -1; } } + uwsgi_foreach(usl, l) { + char *colon = strchr(usl->value, ':'); + if (!colon) { + uwsgi_log("invalid hook syntax, must be hook:args\n"); + exit(1); + } + *colon = 0; + int private = 0; + char *action = usl->value; + // private hook ? + if (action[0] == '!') { + action++; + private = 1; + } + struct uwsgi_hook *uh = uwsgi_hook_by_name(action); + if (!uh) { + uwsgi_log("hook action not found: %s\n", action); + exit(1); + } + *colon = ':'; + + if (private) { + uwsgi_log("running --- PRIVATE HOOK --- (%s)...\n", phase); + } + else { + uwsgi_log("running \"%s\" (%s)...\n", usl->value, phase); + } + + int ret = uh->func(colon+1); + if (ret != 0) { + if (fatal) { + if (context) { + unsetenv("UWSGI_HOOK_CONTEXT"); + } + return ret; + } + final_ret = ret; + } + } + + if (context) { + unsetenv("UWSGI_HOOK_CONTEXT"); + } + + return final_ret; } + +void uwsgi_hooks_run(struct uwsgi_string_list *l, char *phase, int fatal) { + int ret = uwsgi_hooks_run_and_return(l, phase, NULL, fatal); + if (fatal && ret != 0) { + uwsgi_log_verbose("FATAL hook failed, destroying instance\n"); + if (uwsgi.master_process) { + if (uwsgi.workers) { + if (uwsgi.workers[0].pid == getpid()) { + kill_them_all(0); + return; + } + else { + if (kill(uwsgi.workers[0].pid, SIGINT)) { + uwsgi_error("uwsgi_hooks_run()/kill()"); + exit(1); + } + return; + } + } + } + exit(1); + } +} + diff --git a/core/ini.c b/core/ini.c index 9069fcba..61a3c26a 100644 --- a/core/ini.c +++ b/core/ini.c @@ -9,7 +9,7 @@ extern struct uwsgi_server uwsgi; static char *last_file = NULL; -void ini_rstrip(char *line) { +static void ini_rstrip(char *line) { off_t i; @@ -22,7 +22,7 @@ void ini_rstrip(char *line) { } } -char *ini_lstrip(char *line) { +static char *ini_lstrip(char *line) { off_t i; char *ptr = line; @@ -38,7 +38,7 @@ char *ini_lstrip(char *line) { return ptr; } -char *ini_get_key(char *key) { +static char *ini_get_key(char *key) { off_t i; char *ptr = key; @@ -54,7 +54,7 @@ char *ini_get_key(char *key) { return ptr; } -char *ini_get_line(char *ini, size_t size) { +static char *ini_get_line(char *ini, size_t size) { size_t i; char *ptr = ini; @@ -171,5 +171,58 @@ void uwsgi_ini_config(char *file, char *magic_table[]) { colon[0] = ':'; } +} +void uwsgi_emperor_ini_attrs(char *filename, char *section_asked, struct uwsgi_dyn_dict **attrs) { + if (!section_asked) section_asked = "emperor"; + + char *ini = uwsgi_simple_file_read(filename); + if (!ini) return; + + char *orig_ini = ini; + + size_t len = strlen(ini); + char *section = ""; + char *key, *val, *ini_line; + + while (len) { + ini_line = ini_get_line(ini, len); + if (ini_line == NULL) { + break; + } + // skip empty line + key = ini_lstrip(ini); + ini_rstrip(key); + if (key[0] != 0) { + if (key[0] == '[') { + section = key + 1; + section[strlen(section) - 1] = 0; + } + else if (key[0] == ';' || key[0] == '#') { + // this is a comment + } + else { + // val is always valid, but (obviously) can be ignored + val = ini_get_key(key); + + if (!strcmp(section, section_asked)) { + ini_rstrip(key); + struct uwsgi_string_list *usl = uwsgi_string_list_has_item(uwsgi.emperor_collect_attributes, key, strlen(key)); + if (usl) { + val = ini_lstrip(val); + ini_rstrip(val); + char *value = uwsgi_str(val); + uwsgi_dyn_dict_new(attrs, usl->value, usl->len, value, strlen(value)); + } + } + } + } + + + len -= (ini_line - ini); + ini += (ini_line - ini); + + } + + free(orig_ini); } diff --git a/core/init.c b/core/init.c index c4ce7147..48532b28 100644 --- a/core/init.c +++ b/core/init.c @@ -61,6 +61,7 @@ struct http_status_codes hsc[] = { void uwsgi_init_default() { uwsgi.cpus = 1; + uwsgi.new_argc = -1; uwsgi.backtrace_depth = 64; uwsgi.max_apps = 64; @@ -252,9 +253,20 @@ void uwsgi_commandline_config() { int i; uwsgi.option_index = -1; + // required in case we want to call getopt_long from the beginning + optind = 0; + + int argc = uwsgi.argc; + char **argv = uwsgi.argv; + + if (uwsgi.new_argc > -1 && uwsgi.new_argv) { + argc = uwsgi.new_argc; + argv = uwsgi.new_argv; + } + char *optname; - while ((i = getopt_long(uwsgi.argc, uwsgi.argv, uwsgi.short_options, uwsgi.long_options, &uwsgi.option_index)) != -1) { + while ((i = getopt_long(argc, argv, uwsgi.short_options, uwsgi.long_options, &uwsgi.option_index)) != -1) { if (i == '?') { uwsgi_log("getopt_long() error\n"); @@ -280,9 +292,9 @@ void uwsgi_commandline_config() { uwsgi_log("optind:%d argc:%d\n", optind, uwsgi.argc); #endif - if (optind < uwsgi.argc) { - for (i = optind; i < uwsgi.argc; i++) { - char *lazy = uwsgi.argv[i]; + if (optind < argc) { + for (i = optind; i < argc; i++) { + char *lazy = argv[i]; if (lazy[0] != '[') { uwsgi_opt_load(NULL, lazy, NULL); // manage magic mountpoint diff --git a/core/io.c b/core/io.c index 30334dce..a5194774 100644 --- a/core/io.c +++ b/core/io.c @@ -90,7 +90,7 @@ char *uwsgi_simple_file_read(char *filename) { } if (fstat(fd, &sb)) { - uwsgi_error("fstat()"); + uwsgi_error("uwsgi_simple_file_read()/fstat()"); close(fd); goto end; } @@ -99,7 +99,7 @@ char *uwsgi_simple_file_read(char *filename) { len = read(fd, buffer, sb.st_size); if (len != sb.st_size) { - uwsgi_error("read()"); + uwsgi_error("uwsgi_simple_file_read()/read()"); free(buffer); close(fd); goto end; @@ -112,7 +112,7 @@ char *uwsgi_simple_file_read(char *filename) { buffer[sb.st_size] = 0; return buffer; end: - return (char *) ""; + return NULL; } @@ -1481,3 +1481,129 @@ clear: #endif } +ssize_t uwsgi_recv_cred_and_fds(int fd, char *buf, size_t buf_len, pid_t *pid, uid_t *uid, gid_t *gid, int *fds, int *fds_count) { +#if defined(SCM_CREDENTIALS) && defined(SCM_RIGHTS) + ssize_t ret = -1; + + size_t msg_len = CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int) * (*fds_count)); + + // allocate space for credentials and file descriptors + void *msg_control = uwsgi_calloc(msg_len); + + // read into buf + struct iovec iov; + iov.iov_base = buf; + iov.iov_len = buf_len; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // set cmsg + msg.msg_control = msg_control; + msg.msg_controllen = msg_len; + + ssize_t len = recvmsg(fd, &msg, 0); + if (len <= 0) { + uwsgi_error("uwsgi_recv_cred_and_fds()/recvmsg()"); + goto clear; + } + + // reset the number of fds + *fds_count = 0; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + while(cmsg) { + if (cmsg->cmsg_level != SOL_SOCKET) goto next; + if (cmsg->cmsg_type == SCM_RIGHTS) { + size_t fds_len = cmsg->cmsg_len - ((char *) CMSG_DATA(cmsg) - (char *) cmsg); + memcpy(fds, CMSG_DATA(cmsg), fds_len); + *fds_count = fds_len/sizeof(int); + } + else if (cmsg->cmsg_type == SCM_CREDENTIALS) { + struct ucred *u = (struct ucred *) CMSG_DATA(cmsg); + *pid = u->pid; + *uid = u->uid; + *gid = u->gid; + } +next: + cmsg=CMSG_NXTHDR(&msg,cmsg); + } + + ret = len; + +clear: + free(msg_control); + return ret; +#else + return -1; +#endif +} + + +int uwsgi_send_fds_and_body(int fd, int *fds, int fds_count, char *body, size_t len) { + + int ret = -1; + + struct msghdr msg; + void *msg_control = uwsgi_malloc(CMSG_SPACE(sizeof(int) * fds_count)); + struct iovec iov; + struct cmsghdr *cmsg; + + iov.iov_base = body; + iov.iov_len = len; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_flags = 0; + msg.msg_control = msg_control; + msg.msg_controllen = CMSG_SPACE(sizeof(int) * fds_count); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int) * fds_count); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + + unsigned char *fd_ptr = CMSG_DATA(cmsg); + + memcpy(fd_ptr, fds, sizeof(int) * fds_count); + + ssize_t rlen = sendmsg(fd, &msg, 0); + if (rlen <= 0) { + uwsgi_error("uwsgi_send_fds_and_body()/sendmsg()"); + goto end; + } + else { + size_t remains = len - rlen; + while(remains > 0) { + char *buf = body + rlen; + ssize_t wlen = write(fd, buf, remains); + if (wlen == 0) goto end; + if (wlen < 0) { + if (uwsgi_is_again()) { + // wait for write + continue; + } + uwsgi_error("uwsgi_send_fds_and_body()/write()"); + goto end; + } + rlen += wlen; + remains -= wlen; + } + } + ret = 0; + +end: + free(msg_control); + return ret; +} + diff --git a/core/master_utils.c b/core/master_utils.c index 159e2a74..d93b32c2 100644 --- a/core/master_utils.c +++ b/core/master_utils.c @@ -388,6 +388,8 @@ void uwsgi_reload(char **argv) { int i; int waitpid_status; + if (uwsgi.new_argv) argv = uwsgi.new_argv; + if (!uwsgi.master_is_reforked) { // call a series of waitpid to ensure all processes (gateways, mules and daemons) are dead diff --git a/core/rpc.c b/core/rpc.c index 8f51b0f6..26e1773c 100644 --- a/core/rpc.c +++ b/core/rpc.c @@ -7,6 +7,11 @@ int uwsgi_register_rpc(char *name, struct uwsgi_plugin *plugin, uint8_t args, vo struct uwsgi_rpc *urpc; int ret = -1; + if (!uwsgi.workers || !uwsgi.shared || !uwsgi.rpc_table_lock) { + uwsgi_log("RPC subsystem still not initialized\n"); + return -1; + } + if (uwsgi.mywid == 0 && uwsgi.workers[0].pid != uwsgi.mypid) { uwsgi_log("only the master and the workers can register RPC functions\n"); return -1; diff --git a/core/utils.c b/core/utils.c index c4a5a823..27536ad6 100644 --- a/core/utils.c +++ b/core/utils.c @@ -3102,6 +3102,8 @@ pid_t uwsgi_fork(char *name) { #if defined(__linux__) || defined(__sun__) int i; for (i = 0; i < uwsgi.argc; i++) { + // stop fixing original argv if the new one is bigger + if (!uwsgi.orig_argv[i]) break; strcpy(uwsgi.orig_argv[i], uwsgi.argv[i]); } #endif diff --git a/core/uwsgi.c b/core/uwsgi.c index 0c4dfa53..55226547 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -225,11 +225,18 @@ static struct uwsgi_option uwsgi_base_options[] = { #if defined(__linux__) && !defined(OBSOLETE_LINUX_KERNEL) {"emperor-use-clone", required_argument, 0, "use clone() instead of fork() passing the specified unshare() flags", uwsgi_opt_set_unshare, &uwsgi.emperor_clone, 0}, #endif + {"emperor-use-fork-server", required_argument, 0, "connect to the specified fork server instead of using plain fork() for new vassals", uwsgi_opt_set_str, &uwsgi.emperor_use_fork_server, 0}, + {"vassal-fork-base", required_argument, 0, "use plain fork() for the specified vassal (instead of a fork-server)", uwsgi_opt_add_string_list, &uwsgi.vassal_fork_base, 0}, + {"emperor-subreaper", no_argument, 0, "force the Emperor to be a sub-reaper (if supported)", uwsgi_opt_true, &uwsgi.emperor_subreaper, 0}, #ifdef UWSGI_CAP {"emperor-cap", required_argument, 0, "set vassals capability", uwsgi_opt_set_emperor_cap, NULL, 0}, {"vassals-cap", required_argument, 0, "set vassals capability", uwsgi_opt_set_emperor_cap, NULL, 0}, {"vassal-cap", required_argument, 0, "set vassals capability", uwsgi_opt_set_emperor_cap, NULL, 0}, #endif + {"emperor-collect-attribute", required_argument, 0, "collect the specified vassal attribute from imperial monitors", uwsgi_opt_add_string_list, &uwsgi.emperor_collect_attributes, 0}, + {"emperor-collect-attr", required_argument, 0, "collect the specified vassal attribute from imperial monitors", uwsgi_opt_add_string_list, &uwsgi.emperor_collect_attributes, 0}, + {"emperor-fork-server-attr", required_argument, 0, "set teh vassal's attribute to get when checking for fork-server", uwsgi_opt_set_str, &uwsgi.emperor_fork_server_attr, 0}, + {"emperor-wrapper-attr", required_argument, 0, "set teh vassal's attribute to get when checking for fork-wrapper", uwsgi_opt_set_str, &uwsgi.emperor_wrapper_attr, 0}, {"imperial-monitor-list", no_argument, 0, "list enabled imperial monitors", uwsgi_opt_true, &uwsgi.imperial_monitor_list, 0}, {"imperial-monitors-list", no_argument, 0, "list enabled imperial monitors", uwsgi_opt_true, &uwsgi.imperial_monitor_list, 0}, {"vassals-inherit", required_argument, 0, "add config templates to vassals config (uses --inherit)", uwsgi_opt_add_string_list, &uwsgi.vassals_templates, 0}, @@ -359,6 +366,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"setns-skip", required_argument, 0, "skip the specified entry when sending setns file descriptors", uwsgi_opt_add_string_list, &uwsgi.setns_socket_skip, 0}, {"setns", required_argument, 0, "join a namespace created by an external uWSGI instance", uwsgi_opt_set_str, &uwsgi.setns, 0}, {"setns-preopen", no_argument, 0, "open /proc/self/ns as soon as possible and cache fds", uwsgi_opt_true, &uwsgi.setns_preopen, 0}, + {"fork-socket", required_argument, 0, "suspend the execution after early initialization and fork() at every unix socket connection", uwsgi_opt_set_str, &uwsgi.fork_socket, 0}, + {"fork-server", required_argument, 0, "suspend the execution after early initialization and fork() at every unix socket connection", uwsgi_opt_set_str, &uwsgi.fork_socket, 0}, #endif {"jailed", no_argument, 0, "mark the instance as jailed (force the execution of post_jail hooks)", uwsgi_opt_true, &uwsgi.jailed, 0}, #if defined(__FreeBSD__) || defined(__GNU_kFreeBSD__) @@ -406,6 +415,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"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-on-demand-vassal", required_argument, 0, "run the specified hook whenever a vassal enters on-demand mode", uwsgi_opt_add_string_list, &uwsgi.hook_as_on_demand_vassal, 0}, + {"hook-as-mule", required_argument, 0, "run the specified hook in each mule", uwsgi_opt_add_string_list, &uwsgi.hook_as_mule, 0}, {"hook-as-gateway", required_argument, 0, "run the specified hook in each gateway", uwsgi_opt_add_string_list, &uwsgi.hook_as_gateway, 0}, @@ -899,6 +910,7 @@ static struct uwsgi_option uwsgi_base_options[] = { {"close-on-exec2", no_argument, 0, "set close-on-exec on server sockets (could be required for spawning processes in requests)", uwsgi_opt_true, &uwsgi.close_on_exec2, 0}, {"mode", required_argument, 0, "set uWSGI custom mode", uwsgi_opt_set_str, &uwsgi.mode, 0}, {"env", required_argument, 0, "set environment variable", uwsgi_opt_set_env, NULL, 0}, + {"ienv", required_argument, 0, "set environment variable (IMMEDIATE version)", uwsgi_opt_set_env, NULL, UWSGI_OPT_IMMEDIATE}, {"envdir", required_argument, 0, "load a daemontools compatible envdir", uwsgi_opt_add_string_list, &uwsgi.envdirs, 0}, {"early-envdir", required_argument, 0, "load a daemontools compatible envdir ASAP", uwsgi_opt_envdir, NULL, UWSGI_OPT_IMMEDIATE}, {"unenv", required_argument, 0, "unset environment variable", uwsgi_opt_unset_env, NULL, 0}, @@ -1961,6 +1973,7 @@ static char *uwsgi_at_file_read(char *filename) { } void uwsgi_setup(int argc, char *argv[], char *envp[]) { + #ifdef UWSGI_AS_SHARED_LIBRARY #ifdef __APPLE__ char ***envPtr = _NSGetEnviron(); @@ -2141,6 +2154,8 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) { struct group *gr = getgrgid(getgid()); uwsgi.magic_table['G'] = gr ? gr->gr_name : uwsgi.magic_table['g']; +configure: + // you can embed a ini file in the uWSGi binary with default options #ifdef UWSGI_EMBED_CONFIG uwsgi_ini_config("", uwsgi.magic_table); @@ -2165,6 +2180,14 @@ void uwsgi_setup(int argc, char *argv[], char *envp[]) { // ok, the options dictionary is available, lets manage it uwsgi_configure(); + // stop the execution until a connection arrives on the fork socket + if (uwsgi.fork_socket) { + uwsgi_log_verbose("waiting for fork-socket connections...\n"); + uwsgi_fork_server(uwsgi.fork_socket); + // if we are here a new process has been spawned + goto configure; + } + // fixup cwd if (uwsgi.force_cwd) uwsgi.cwd = uwsgi.force_cwd; diff --git a/plugins/emperor_mongodb/emperor_mongodb.cc b/plugins/emperor_mongodb/emperor_mongodb.cc index 449f5d34..bb41fc36 100644 --- a/plugins/emperor_mongodb/emperor_mongodb.cc +++ b/plugins/emperor_mongodb/emperor_mongodb.cc @@ -24,7 +24,14 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues try { // requested fields - mongo::BSONObj p = BSON( "name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 << "socket" << 1 ); + mongo::BSONObjBuilder builder; + builder.appendElements(BSON("name" << 1 << "config" << 1 << "ts" << 1 << "uid" << 1 << "gid" << 1 << "socket" << 1 )); + struct uwsgi_string_list *e_attrs = uwsgi.emperor_collect_attributes; + while(e_attrs) { + builder.appendElements(BSON(e_attrs->value << 1)); + e_attrs = e_attrs->next; + } + mongo::BSONObj p = builder.obj(); mongo::BSONObj q = mongo::fromjson(uems->json); // the connection object (will be automatically destroyed at each cycle) mongo::DBClientConnection c; @@ -76,7 +83,26 @@ extern "C" void uwsgi_imperial_monitor_mongodb(struct uwsgi_emperor_scanner *ues const char *socket_name = p.getStringField("socket"); if (strlen(socket_name) == 0) socket_name = NULL; - uwsgi_emperor_simple_do(ues, (char *) name, (char *) config, vassal_ts/1000, vassal_uid, vassal_gid, (char *) socket_name); + struct uwsgi_dyn_dict *attrs = NULL; + struct uwsgi_string_list *e_attrs = uwsgi.emperor_collect_attributes; + while(e_attrs) { + const char *attr_value = p.getStringField(e_attrs->value); + if (strlen(attr_value) == 0) attr_value = NULL; + if (attr_value) { + // the value memory is always reallocated + char *value = uwsgi_str((char *)attr_value); + uwsgi_dyn_dict_new(&attrs, e_attrs->value, e_attrs->len, value, strlen(value)); + } + e_attrs = e_attrs->next; + } + + if (attrs) { + // attrs will be freed in case of error + uwsgi_emperor_simple_do_with_attrs(ues, (char *) name, (char *) config, vassal_ts/1000, vassal_uid, vassal_gid, (char *) socket_name, attrs); + } + else { + uwsgi_emperor_simple_do(ues, (char *) name, (char *) config, vassal_ts/1000, vassal_uid, vassal_gid, (char *) socket_name); + } } diff --git a/plugins/emperor_mongodb/uwsgiplugin.py b/plugins/emperor_mongodb/uwsgiplugin.py index 5d7810de..58b56757 100644 --- a/plugins/emperor_mongodb/uwsgiplugin.py +++ b/plugins/emperor_mongodb/uwsgiplugin.py @@ -5,7 +5,7 @@ NAME='emperor_mongodb' CFLAGS = ['-I/usr/include/mongo','-I/usr/local/include/mongo'] LDFLAGS = [] -LIBS = [] +LIBS = ['-lstdc++'] if not 'UWSGI_MONGODB_NOLIB' in os.environ: LIBS.append('-lmongoclient') LIBS.append('-lboost_thread') diff --git a/plugins/psgi/psgi.h b/plugins/psgi/psgi.h index 92e6b588..f204b80a 100644 --- a/plugins/psgi/psgi.h +++ b/plugins/psgi/psgi.h @@ -67,6 +67,11 @@ struct uwsgi_perl { CV *spooler; int no_plack; + + SV **early_psgi_callable; + char *early_psgi_app_name; + + PerlInterpreter *early_interpreter; }; void init_perl_embedded_module(void); @@ -87,3 +92,5 @@ void uwsgi_perl_exec(char *); void uwsgi_perl_check_auto_reload(void); void uwsgi_psgi_preinit_apps(void); + +int uwsgi_perl_add_app(struct wsgi_request *, char *, PerlInterpreter **, SV **, time_t); diff --git a/plugins/psgi/psgi_loader.c b/plugins/psgi/psgi_loader.c index c798408e..27b0280e 100644 --- a/plugins/psgi/psgi_loader.c +++ b/plugins/psgi/psgi_loader.c @@ -337,11 +337,13 @@ int init_psgi_app(struct wsgi_request *wsgi_req, char *app, uint16_t app_len, Pe if (!interpreters) goto clear2; callables = uwsgi_calloc(sizeof(SV *) * uwsgi.threads); - uperl.tmp_streaming_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); - uperl.tmp_input_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); - uperl.tmp_error_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); - uperl.tmp_stream_responder = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); - uperl.tmp_psgix_logger = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); + if (!uperl.early_interpreter) { + uperl.tmp_streaming_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_input_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_error_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_stream_responder = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); + uperl.tmp_psgix_logger = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); + } for(i=0;i= uwsgi.max_apps) { uwsgi_log("ERROR: you cannot load more than %d apps in a worker\n", uwsgi.max_apps); goto clear; } - int id = uwsgi_apps_cnt; - struct uwsgi_app *wi = NULL; - - if (wsgi_req) { - // we need a copy of app_id - wi = uwsgi_add_app(id, psgi_plugin.modifier1, uwsgi_concat2n(wsgi_req->appid, wsgi_req->appid_len, "", 0), wsgi_req->appid_len, interpreters, callables); - } - else { - wi = uwsgi_add_app(id, psgi_plugin.modifier1, "", 0, interpreters, callables); - } - - wi->started_at = now; - wi->startup_time = uwsgi_now() - now; - - uwsgi_log("PSGI app %d (%s) loaded in %d seconds at %p (interpreter %p)\n", id, app_name, (int) wi->startup_time, callables[0], interpreters[0]); - free(app_name); - - // copy global data to app-specific areas - wi->stream = uperl.tmp_streaming_stash; - wi->input = uperl.tmp_input_stash; - wi->error = uperl.tmp_error_stash; - wi->responder0 = uperl.tmp_stream_responder; - wi->responder1 = uperl.tmp_psgix_logger; - - uwsgi_emulate_cow_for_apps(id); - - - // restore context if required - if (interpreters != uperl.main) { - PERL_SET_CONTEXT(uperl.main[0]); - } - - uperl.loaded = 1; - - return id; + return uwsgi_perl_add_app(wsgi_req, app_name, interpreters, callables, now); clear: if (interpreters != uperl.main) { @@ -509,6 +484,44 @@ clear2: return -1; } +int uwsgi_perl_add_app(struct wsgi_request *wsgi_req, char *app_name, PerlInterpreter **interpreters, SV **callables, time_t now) { + int id = uwsgi_apps_cnt; + struct uwsgi_app *wi = NULL; + + if (wsgi_req) { + // we need a copy of app_id + wi = uwsgi_add_app(id, psgi_plugin.modifier1, uwsgi_concat2n(wsgi_req->appid, wsgi_req->appid_len, "", 0), wsgi_req->appid_len, interpreters, callables); + } + else { + wi = uwsgi_add_app(id, psgi_plugin.modifier1, "", 0, interpreters, callables); + } + + wi->started_at = now; + wi->startup_time = uwsgi_now() - now; + + uwsgi_log("PSGI app %d (%s) loaded in %d seconds at %p (interpreter %p)\n", id, app_name, (int) wi->startup_time, callables[0], interpreters[0]); + free(app_name); + + // copy global data to app-specific areas + wi->stream = uperl.tmp_streaming_stash; + wi->input = uperl.tmp_input_stash; + wi->error = uperl.tmp_error_stash; + wi->responder0 = uperl.tmp_stream_responder; + wi->responder1 = uperl.tmp_psgix_logger; + + uwsgi_emulate_cow_for_apps(id); + + + // restore context if required + if (interpreters != uperl.main) { + PERL_SET_CONTEXT(uperl.main[0]); + } + + uperl.loaded = 1; + + return id; +} + void uwsgi_psgi_preinit_apps() { if (uperl.exec) { PERL_SET_CONTEXT(uperl.main[0]); @@ -524,6 +537,10 @@ void uwsgi_psgi_preinit_apps() { void uwsgi_psgi_app() { + if (uperl.early_psgi_callable) { + uwsgi_perl_add_app(NULL, uperl.early_psgi_app_name, uperl.main, uperl.early_psgi_callable, uwsgi_now()); + } + if (uperl.psgi) { //load app in the main interpreter list init_psgi_app(NULL, uperl.psgi, strlen(uperl.psgi), uperl.main); diff --git a/plugins/psgi/psgi_plugin.c b/plugins/psgi/psgi_plugin.c index a764cb2c..a7c46026 100644 --- a/plugins/psgi/psgi_plugin.c +++ b/plugins/psgi/psgi_plugin.c @@ -26,6 +26,45 @@ static void uwsgi_opt_plshell(char *opt, char *value, void *foobar) { } } +EXTERN_C void xs_init (pTHX); +int uwsgi_perl_init(void); + +static void uwsgi_opt_early_perl(char *opt, char *value, void *foobar) { + // avoid duplicates + if (uperl.early_interpreter) return; + uwsgi_perl_init(); + uperl.early_interpreter = uperl.main[0]; + + // HACK the following allocations ensure correct xs initialization + uperl.tmp_streaming_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_input_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_error_stash = uwsgi_calloc(sizeof(HV *) * uwsgi.threads); + uperl.tmp_stream_responder = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); + uperl.tmp_psgix_logger = uwsgi_calloc(sizeof(CV *) * uwsgi.threads); + + char *perl_e_arg = uwsgi_concat2("#line 0 ", value); + char *perl_init_arg[] = { "", "-e", perl_e_arg }; + perl_parse(uperl.early_interpreter, xs_init, 3, perl_init_arg, NULL); + free(perl_e_arg); +} + +static void uwsgi_opt_early_psgi(char *opt, char *value, void *foobar) { + uwsgi_perl_init(); + init_psgi_app(NULL, value, strlen(value), uperl.main); + if (!uperl.early_psgi_callable) exit(1); +} + +static void uwsgi_opt_early_exec(char *opt, char *value, void *foobar) { + uwsgi_perl_init(); + if (!uperl.early_interpreter) { + perl_parse(uperl.main[0], xs_init, 3, uperl.embedding, NULL); + } + SV *dollar_zero = get_sv("0", GV_ADD); + sv_setsv(dollar_zero, newSVpv(value, strlen(value))); + uwsgi_perl_exec(value); +} + + struct uwsgi_option uwsgi_perl_options[] = { {"psgi", required_argument, 0, "load a psgi app", uwsgi_opt_set_str, &uperl.psgi, 0}, @@ -46,6 +85,9 @@ struct uwsgi_option uwsgi_perl_options[] = { {"plshell-oneshot", no_argument, 0, "run a perl interactive shell (one shot)", uwsgi_opt_plshell, NULL, 0}, {"perl-no-plack", no_argument, 0, "force the use of do instead of Plack::Util::load_psgi", uwsgi_opt_true, &uperl.no_plack, 0}, + {"early-perl", required_argument, 0, "initialize an early perl interpreter shared by all loaders", uwsgi_opt_early_perl, NULL, UWSGI_OPT_IMMEDIATE}, + {"early-psgi", required_argument, 0, "load a psgi app soon after uWSGI initialization", uwsgi_opt_early_psgi, NULL, UWSGI_OPT_IMMEDIATE}, + {"early-perl-exec", required_argument, 0, "load a perl script soon after uWSGI initialization", uwsgi_opt_early_exec, NULL, UWSGI_OPT_IMMEDIATE}, {0, 0, 0, 0, 0, 0, 0}, }; @@ -437,6 +479,10 @@ int uwsgi_perl_init(){ int argc; int i; + if (uperl.main) { + goto already_initialized; + } + uperl.embedding[0] = ""; uperl.embedding[1] = "-e"; uperl.embedding[2] = "0"; @@ -477,6 +523,7 @@ int uwsgi_perl_init(){ PERL_SET_CONTEXT(uperl.main[0]); +already_initialized: #ifdef PERL_VERSION_STRING uwsgi_log_initial("initialized Perl %s main interpreter at %p\n", PERL_VERSION_STRING, uperl.main[0]); #else @@ -945,7 +992,15 @@ static int uwsgi_perl_spooler(char *filename, char *buf, uint16_t len, char *bod return ret; } +static int uwsgi_perl_hook_perl(char *arg) { + SV *ret = perl_eval_pv(arg, 0); + if (!ret) return -1; + return 0; +} +static void uwsgi_perl_register_features() { + uwsgi_register_hook("perl", uwsgi_perl_hook_perl); +} struct uwsgi_plugin psgi_plugin = { @@ -976,4 +1031,5 @@ struct uwsgi_plugin psgi_plugin = { .magic = uwsgi_perl_magic, .spooler = uwsgi_perl_spooler, + .on_load = uwsgi_perl_register_features, }; diff --git a/plugins/python/uwsgi_pymodule.c b/plugins/python/uwsgi_pymodule.c index 1c6f2649..473f39e9 100644 --- a/plugins/python/uwsgi_pymodule.c +++ b/plugins/python/uwsgi_pymodule.c @@ -1649,7 +1649,6 @@ PyObject *py_uwsgi_sharedarea_read64(PyObject * self, PyObject * args) { } return PyLong_FromLongLong(value); - } PyObject *py_uwsgi_sharedarea_read32(PyObject * self, PyObject * args) { diff --git a/plugins/pyuwsgi/pyuwsgi.c b/plugins/pyuwsgi/pyuwsgi.c index 02a03329..2f248f4a 100644 --- a/plugins/pyuwsgi/pyuwsgi.c +++ b/plugins/pyuwsgi/pyuwsgi.c @@ -1,75 +1,325 @@ #include "../python/uwsgi_python.h" +//FIXME: [upstream:python] needs PyAPI_FUNC(void) +extern void Py_GetArgcArgv(int *, char ***); + extern struct uwsgi_server uwsgi; extern struct uwsgi_python up; extern char **environ; -PyObject *u_run(PyObject *self, PyObject *args) { +static int new_argc = -1; +static int orig_argc = -1; +static char **new_argv = NULL; +static char **orig_argv = NULL; +static char *new_argv_buf = NULL; - char **argv; - size_t size = 2; - int i; - if (PyTuple_Size(args) < 1) { - return PyErr_Format(PyExc_ValueError, "you have to specify at least one uWSGI option to run() it"); - } +PyObject * +pyuwsgi_setup(PyObject *self, PyObject *args, PyObject *kwds) +{ + if (new_argv) { + PyErr_SetString( + PyExc_RuntimeError, + "uWSGI already setup" + ); + return NULL; + } - PyObject *the_arg = PyTuple_GetItem(args, 0); + if (uwsgi.mywid) { + PyErr_SetString( + PyExc_RuntimeError, + "uWSGI must be setup by master" + ); + return NULL; + } - if (PyList_Check(the_arg)) { - size = PyList_Size(the_arg) + 2; - } - else if (PyTuple_Check(the_arg)) { - size = PyTuple_Size(the_arg) + 2; - } - else if (PyString_Check(the_arg)) { - size = 3; - } + PyObject *iterator; - argv = uwsgi_malloc(sizeof(char *) * size); - memset(argv, 0, sizeof(char *) * size); + if (args == NULL || PyObject_Size(args) == 0) { + PyObject *argv = PySys_GetObject("argv"); + if (argv == NULL) + return NULL; - // will be overwritten - argv[0] = "uwsgi"; + // during site.py maybe + if (argv == Py_None) { + argv = PyTuple_New(0); + iterator = PyObject_GetIter(argv); + Py_DECREF(argv); + } + else { + iterator = PyObject_GetIter(argv); + if (PyObject_Size(argv) > 0) { + // forward past argv0 + PyObject *item = PyIter_Next(iterator); + Py_DECREF(item); + } + } + } + else if ( + PyObject_Size(args) == 1 + && !PyString_Check(PyTuple_GetItem(args, 0)) + ) { + iterator = PyObject_GetIter(PyTuple_GetItem(args, 0)); + } + else { + iterator = PyObject_GetIter(args); + } - if (PyList_Check(the_arg)) { - for(i=0;i>> 0" + "\n" + "\n * Call setup(...) if not configured" + "\n * Begin uWSGI mainloop" + "\n NOTE: will not return" + "\n" + }, + {"init", + (PyCFunction) pyuwsgi_init, + METH_VARARGS | METH_KEYWORDS, + "init(...)" + "\n>>> 0" + "\n" + "\n * Call setup(...)" + "\n * Begin uWSGI mainloop" + "\n NOTE: will not return" + "\n" + }, + {"setup", + (PyCFunction) pyuwsgi_setup, + METH_VARARGS | METH_KEYWORDS, + "setup('--master', ...)" + "\n>>> " + "\n" + "\n * Initialize uWSGI core with (...)" + "\n MUST only call once [RuntimeException]" + "\n MUST only call from master [RuntimeException]" + "\n" + }, {NULL, NULL, 0, NULL} }; + +static void +pyuwsgi_set_orig_argv(PyObject *self) +{ + + // ask python for the original argc/argv saved in Py_Main() + Py_GetArgcArgv(&orig_argc, &orig_argv); + + // [re?]export to uwsgi.orig_argv + PyObject *m_orig_argv; + m_orig_argv = PyTuple_New(orig_argc); + + int i = 0; + int i_cm = -1; + + for(i=0; i < orig_argc; i++) { + char *arg = orig_argv[i]; + //XXX: _PyOS_optarg != 0 also indicates python quit early... + //FIXME: [upstream:python] orig_argv could be mangled; reset + // rel: http://bugs.python.org/issue8202 + orig_argv[i + 1] = arg + strlen(arg) + 1; + + // look for -c or -m and record the offset + if (i_cm < 0) { + if (strcmp(arg, "-c") || strcmp(arg, "-m")) { + // python's getopt would've failed had + 1 not exist + i_cm = i + 1; + } + else if (!uwsgi_startswith(arg, "-c", 2) || + !uwsgi_startswith(arg, "-m", 2)) { + //FIXME: ARGS prior to and including -c/-m are REQUIRED, + // but NOT a part of the uWSGI argv! Needed to make + // exec*() self-referential: exec*(...) -> uwsgi + // + // want: uwsgi.binary_argv[:] + uwsgi.argv[:]! + // binary_argv = [binary_path] + args + i_cm = i; + } + } + + PyTuple_SetItem(m_orig_argv, i, PyString_FromString(arg)); + } + + //TODO: howto properly detect uwsgi already running... + // orig_argv == uwsgi.orig_argv (?) + // ^^^ but if Py_Main not called, python/main.c:orig_argv unset + // howto interact/detect things in general + PyObject *m_new_argv = PyTuple_New(0); + PyObject_SetAttrString(self, "NEW_ARGV", m_new_argv); + PyObject_SetAttrString(self, "ORIG_ARGV", m_orig_argv); + Py_DECREF(m_new_argv); + Py_DECREF(m_orig_argv); +} + + +static PyObject * +pyuwsgi_init_as(char *mod_name) +{ + + PyObject *m; + + m = PyImport_GetModuleDict(); + if (m == NULL) { + return NULL; + } + + m = PyDict_GetItemString(m, mod_name); + if (!m) { + m = Py_InitModule(mod_name, NULL); + } + + if (orig_argc < 0) { + pyuwsgi_set_orig_argv(m); + } + + int i; + for (i=0; methods[i].ml_name != NULL; i++) { + PyObject *fun = PyObject_GetAttrString(m, methods[i].ml_name); + if (fun != NULL) { + // already exists + Py_DECREF(fun); + continue; + } + + PyErr_Clear(); + + // rel: Python/modsupport.c:Py_InitModule4 + PyObject* name = PyString_FromString(methods[i].ml_name); + // fun(self, ...) + fun = PyCFunction_NewEx(&methods[i], m, name); + Py_DECREF(name); + // module.fun + PyObject_SetAttrString(m, methods[i].ml_name, fun); + Py_DECREF(fun); + } + + return m; +} + + PyMODINIT_FUNC initpyuwsgi() { - (void) Py_InitModule("pyuwsgi", methods); + (void) pyuwsgi_init_as("pyuwsgi"); } -int pyuwsgi_init() { return 0; } +// allow the module to be called `uwsgi` +PyMODINIT_FUNC +inituwsgi() +{ + (void) pyuwsgi_init_as("uwsgi"); +} + + +void pyuwsgi_load() +{ + if (new_argc > -1) { + uwsgi.new_argc = new_argc; + uwsgi.new_argv = new_argv; + } +} + struct uwsgi_plugin pyuwsgi_plugin = { .name = "pyuwsgi", - .init = pyuwsgi_init, + .on_load = pyuwsgi_load, }; diff --git a/setup.cpyext.py b/setup.cpyext.py new file mode 100644 index 00000000..ee89dbc5 --- /dev/null +++ b/setup.cpyext.py @@ -0,0 +1,131 @@ +# encoding: utf-8 + +""" +This is a hack allowing you installing +uWSGI and uwsgidecorators via pip and easy_install +since 1.9.11 it automatically detects pypy +""" + +import os +import sys +import errno +import shlex +import shutil +import uwsgiconfig + +from setuptools import setup +from setuptools.dist import Distribution +from setuptools.command.install import install +from setuptools.command.install_lib import install_lib +from setuptools.command.build_ext import build_ext +from distutils.core import Extension + + +class uWSGIBuildExt(build_ext): + + UWSGI_NAME = 'uwsgi' + UWSGI_PLUGIN = 'pyuwsgi' + + def build_extensions(self): + self.uwsgi_setup() + #XXX: needs uwsgiconfig fix + self.uwsgi_build() + if 'UWSGI_USE_DISTUTILS' not in os.environ: + #XXX: needs uwsgiconfig fix + #uwsgiconfig.build_uwsgi(self.uwsgi_config) + return + + else: + #XXX: needs uwsgiconfig fix + os.unlink(self.uwsgi_config.get('bin_name')) + + #FIXME: else build fails :( + for baddie in set(self.compiler.compiler_so) & set(( + '-Wstrict-prototypes', + )): + self.compiler.compiler_so.remove(baddie) + + build_ext.build_extensions(self) + + def uwsgi_setup(self): + default = ( + '__pypy__' in sys.builtin_module_names + and 'pypy' + or 'default' + ) + profile = ( + os.environ.get('UWSGI_PROFILE') + or 'buildconf/%s.ini' % default + ) + + if not profile.endswith('.ini'): + profile = profile + '.ini' + if not '/' in profile: + profile = 'buildconf/' + profile + + #FIXME: update uwsgiconfig to properly set _EVERYTHING_! + config = uwsgiconfig.uConf(profile) + # insert in the beginning so UWSGI_PYTHON_NOLIB is exported + # before the python plugin compiles + ep = config.get('embedded_plugins').split(',') + if self.UWSGI_PLUGIN in ep: + ep.remove(self.UWSGI_PLUGIN) + ep.insert(0, self.UWSGI_PLUGIN) + config.set('embedded_plugins', ','.join(ep)) + config.set('as_shared_library', 'true') + config.set('bin_name', self.get_ext_fullpath(self.UWSGI_NAME)) + try: + os.makedirs(os.path.dirname(config.get('bin_name'))) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + self.uwsgi_profile = profile + self.uwsgi_config = config + + def uwsgi_build(self): + uwsgiconfig.build_uwsgi(self.uwsgi_config) + + #XXX: merge uwsgi_setup (see other comments) + for ext in self.extensions: + if ext.name == self.UWSGI_NAME: + ext.sources = [s + '.c' for s in self.uwsgi_config.gcc_list] + ext.library_dirs = self.uwsgi_config.include_path[:] + ext.libraries = list() + ext.extra_compile_args = list() + + for x in uwsgiconfig.uniq_warnings( + self.uwsgi_config.ldflags + self.uwsgi_config.libs, + ): + for y in shlex.split(x): + if y.startswith('-l'): + ext.libraries.append(y[2:]) + elif y.startswith('-L'): + ext.library_dirs.append(y[2:]) + + for x in self.uwsgi_config.cflags: + for y in shlex.split(x): + if y: + ext.extra_compile_args.append(y) + + +setup( + name='uWSGI', + license='GPL2', + version=uwsgiconfig.uwsgi_version, + author='Unbit', + author_email='info@unbit.it', + description='The uWSGI server', + cmdclass={ + 'build_ext': uWSGIBuildExt, + }, + py_modules=[ + 'uwsgidecorators', + ], + ext_modules=[ + Extension(uWSGIBuildExt.UWSGI_NAME, sources=[]), + ], + entry_points={ + 'console_scripts': ['uwsgi=%s:run' % uWSGIBuildExt.UWSGI_NAME], + }, + ) diff --git a/uwsgi.h b/uwsgi.h index 1c763830..7221ebfd 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -2717,12 +2717,22 @@ struct uwsgi_server { int mule_reload_mercy; int alarm_cheap; - int emperor_no_blacklist; int metrics_no_cores; int stats_no_cores; int stats_no_metrics; + // uWSGI 2.1 + char *fork_socket; + int new_argc; + char **new_argv; + char *emperor_use_fork_server; + struct uwsgi_string_list *vassal_fork_base; + struct uwsgi_string_list *emperor_collect_attributes; + char *emperor_fork_server_attr; + char *emperor_wrapper_attr; + int emperor_subreaper; + struct uwsgi_string_list *hook_as_on_demand_vassal; }; struct uwsgi_rpc { @@ -4070,6 +4080,11 @@ struct uwsgi_instance { int on_demand_fd; char *socket_name; time_t cursed_at; + + int adopted; + + // uWSGI 2.1 (vassal's attributes) + struct uwsgi_dyn_dict *attrs; }; struct uwsgi_instance *emperor_get_by_fd(int); @@ -4078,6 +4093,7 @@ void emperor_stop(struct uwsgi_instance *); void emperor_curse(struct uwsgi_instance *); void emperor_respawn(struct uwsgi_instance *, time_t); void emperor_add(struct uwsgi_emperor_scanner *, char *, time_t, char *, uint32_t, uid_t, gid_t, char *); +void emperor_add_with_attrs(struct uwsgi_emperor_scanner *, char *, time_t, char *, uint32_t, uid_t, gid_t, char *, struct uwsgi_dyn_dict *); void emperor_back_to_ondemand(struct uwsgi_instance *); void uwsgi_exec_command_with_args(char *); @@ -4158,6 +4174,7 @@ void uwsgi_master_cleanup_hooks(void); pid_t uwsgi_daemonize2(); void uwsgi_emperor_simple_do(struct uwsgi_emperor_scanner *, char *, char *, time_t, uid_t, gid_t, char *); +void uwsgi_emperor_simple_do_with_attrs(struct uwsgi_emperor_scanner *, char *, char *, time_t, uid_t, gid_t, char *, struct uwsgi_dyn_dict *); #if defined(__linux__) #define UWSGI_ELF @@ -4568,6 +4585,7 @@ int uwsgi_umount(char *, char *); int uwsgi_mount_hook(char *); int uwsgi_umount_hook(char *); +int uwsgi_hooks_run_and_return(struct uwsgi_string_list *, char *, char *, int); void uwsgi_hooks_run(struct uwsgi_string_list *, char *, int); void uwsgi_register_hook(char *, int (*)(char *)); struct uwsgi_hook *uwsgi_hook_by_name(char *); @@ -4784,6 +4802,12 @@ mode_t uwsgi_mode_t(char *, int *); int uwsgi_notify_socket_manage(int); int uwsgi_notify_msg(char *, char *, size_t); +int uwsgi_send_fds_and_body(int, int *, int, char *, size_t); +ssize_t uwsgi_recv_cred_and_fds(int, char *, size_t buf_len, pid_t *, uid_t *, gid_t *, int *, int *); +void uwsgi_fork_server(char *); + +void uwsgi_emperor_ini_attrs(char *, char *, struct uwsgi_dyn_dict **); + #ifdef __cplusplus } #endif diff --git a/uwsgiconfig.py b/uwsgiconfig.py index 24b1977d..f0792d44 100644 --- a/uwsgiconfig.py +++ b/uwsgiconfig.py @@ -1,6 +1,6 @@ # uWSGI build system -uwsgi_version = '2.0.5' +uwsgi_version = '2.1' import os import re @@ -602,7 +602,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/plugins_builder', 'core/sharedarea', + 'core/metrics', 'core/plugins_builder', 'core/sharedarea', 'core/fork_server', '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')