// this is the monitor for glob patterns void uwsgi_imperial_monitor_glob(struct uwsgi_emperor_scanner *ues) { glob_t g; int i; struct stat st; struct uwsgi_instance *ui_current; if (glob(ues->arg, GLOB_MARK | GLOB_NOCHECK, NULL, &g)) { uwsgi_error("glob()"); return; } for (i = 0; i < (int) g.gl_pathc; i++) { if (!uwsgi_emperor_is_valid(g.gl_pathv[i])) continue; if (stat(g.gl_pathv[i], &st)) continue; if (!S_ISREG(st.st_mode)) continue; ui_current = emperor_get(g.gl_pathv[i]); if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (st.st_uid != ui_current->uid || st.st_gid != ui_current->gid) { uwsgi_log("!!! permissions of file %s changed. stopping the instance... !!!\n", g.gl_pathv[i]); emperor_stop(ui_current); continue; } } // check if mtime is changed and the uWSGI instance must be reloaded if (st.st_mtime > ui_current->last_mod) { emperor_respawn(ui_current, st.st_mtime); } } else { emperor_add(ues, g.gl_pathv[i], st.st_mtime, NULL, 0, st.st_uid, st.st_gid); } } globfree(&g); // now check for removed instances struct uwsgi_instance *c_ui = ui->ui_next; while (c_ui) { if (c_ui->scanner == ues) { if (stat(c_ui->name, &st)) { emperor_stop(c_ui); } } c_ui = c_ui->ui_next; } }
void uwsgi_emperor_simple_do(struct uwsgi_emperor_scanner *ues, char *name, char *config, time_t ts, uid_t uid, gid_t gid) { if (!uwsgi_emperor_is_valid(name)) return; struct uwsgi_instance *ui_current = emperor_get(name); if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (uid != ui_current->uid || gid != ui_current->gid) { uwsgi_log("[emperor-tyrant] !!! permissions of vassal %s changed. stopping the instance... !!!\n", name); emperor_stop(ui_current); return; } } // check if mtime is changed and the uWSGI instance must be reloaded if (ts > ui_current->last_mod) { // make a new config (free the old one) free(ui_current->config); ui_current->config = config; ui_current->config_len = strlen(config); // always respawn (no need for amqp-style rules) emperor_respawn(ui_current, ts); } } else { // make a copy of the config as it will be freed emperor_add(ues, name, ts, uwsgi_str(config), strlen((const char *) config), uid, gid); } }
// this is the monitor for non-glob directories void uwsgi_imperial_monitor_directory(struct uwsgi_emperor_scanner *ues) { struct uwsgi_instance *ui_current; struct dirent *de; struct stat st; if (chdir(ues->arg)) { uwsgi_error("chdir()"); return; } DIR *dir = opendir("."); while ((de = readdir(dir)) != NULL) { if (!uwsgi_emperor_is_valid(de->d_name)) continue; if (stat(de->d_name, &st)) continue; if (!S_ISREG(st.st_mode)) continue; ui_current = emperor_get(de->d_name); if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (st.st_uid != ui_current->uid || st.st_gid != ui_current->gid) { uwsgi_log("!!! permissions of file %s changed. stopping the instance... !!!\n", de->d_name); emperor_stop(ui_current); continue; } } // check if mtime is changed and the uWSGI instance must be reloaded if (st.st_mtime > ui_current->last_mod) { emperor_respawn(ui_current, st.st_mtime); } } else { emperor_add(ues, de->d_name, st.st_mtime, NULL, 0, st.st_uid, st.st_gid); } } closedir(dir); // now check for removed instances struct uwsgi_instance *c_ui = ui->ui_next; while (c_ui) { if (c_ui->scanner == ues) { if (stat(c_ui->name, &st)) { emperor_stop(c_ui); } } c_ui = c_ui->ui_next; } }
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)) return; struct uwsgi_instance *ui_current = emperor_get(name); if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (uid != ui_current->uid || gid != ui_current->gid) { uwsgi_log("[emperor-tyrant] !!! permissions of vassal %s changed. stopping the instance... !!!\n", name); emperor_stop(ui_current); return; } } // check if mtime is changed and the uWSGI instance must be reloaded if (ts > ui_current->last_mod) { // make a new config (free the old one) if needed if (config) { if (ui_current->config) free(ui_current->config); ui_current->config = uwsgi_str(config); ui_current->config_len = strlen(ui_current->config); } // reload the instance emperor_respawn(ui_current, ts); } } else { // make a copy of the config as it will be freed char *new_config = NULL; size_t new_config_len = 0; if (config) { new_config = uwsgi_str(config); new_config_len = strlen(new_config); } emperor_add(ues, name, ts, new_config, new_config_len, uid, gid, socket_name); } }
void uwsgi_imperial_monitor_amqp_event(struct uwsgi_emperor_scanner *ues) { uint64_t msgsize; char *amqp_routing_key = NULL; struct uwsgi_instance *ui_current; struct stat st; char *config = uwsgi_amqp_consume(ues->fd, &msgsize, &amqp_routing_key); if (!config) { uwsgi_log("problem with RabbitMQ server, trying reconnection...\n"); close(ues->fd); ues->fd = -1; return; } // having a routing key means the body will be mapped to a config chunk if (amqp_routing_key) { uwsgi_log("AMQP routing_key = %s\n", amqp_routing_key); ui_current = emperor_get(amqp_routing_key); if (ui_current) { // make a new config free(ui_current->config); ui_current->config = config; ui_current->config_len = msgsize; if (!msgsize) { emperor_stop(ui_current); } else { emperor_respawn(ui_current, uwsgi_now()); } goto end0; } if (msgsize > 0) { emperor_add(ues, amqp_routing_key, uwsgi_now(), config, msgsize, 0, 0); } end0: free(config); free(amqp_routing_key); return; } // no routing key means the body contains a path to a config file if (msgsize >= 0xff || !msgsize) goto end; char *config_file = uwsgi_concat2n(config, msgsize, "", 0); ui_current = emperor_get(config_file); // if non-http check for file existance if (strncmp(config_file, "http://", 7)) { if (stat(config_file, &st)) { free(config_file); if (ui_current) emperor_stop(ui_current); goto end; } if (!S_ISREG(st.st_mode)) { free(config_file); if (ui_current) emperor_stop(ui_current); goto end; } } if (ui_current) { emperor_respawn(ui_current, uwsgi_now()); } else { emperor_add(ues, config_file, uwsgi_now(), NULL, 0, 0, 0); } free(config_file); end: free(config); }
void emperor_loop() { // monitor a directory struct uwsgi_instance ui_base; struct uwsgi_instance *ui_current; pid_t diedpid; int waitpid_status; int has_children = 0; int i_am_alone = 0; int i; void *events; int nevents; int interesting_fd; char notification_message[64]; struct rlimit rl; uwsgi.emperor_stats_fd = -1; if (uwsgi.emperor_pidfile) { uwsgi_write_pidfile(uwsgi.emperor_pidfile); } signal(SIGPIPE, SIG_IGN); uwsgi_unix_signal(SIGINT, royal_death); uwsgi_unix_signal(SIGTERM, royal_death); uwsgi_unix_signal(SIGQUIT, royal_death); uwsgi_unix_signal(SIGUSR1, emperor_stats); uwsgi_unix_signal(SIGHUP, emperor_massive_reload); memset(&ui_base, 0, sizeof(struct uwsgi_instance)); if (getrlimit(RLIMIT_NOFILE, &rl)) { uwsgi_error("getrlimit()"); exit(1); } uwsgi.max_fd = rl.rlim_cur; emperor_throttle_level = uwsgi.emperor_throttle; // the queue must be initialized before adding scanners uwsgi.emperor_queue = event_queue_init(); emperor_build_scanners(); events = event_queue_alloc(64); if (uwsgi.has_emperor) { uwsgi_log("*** starting uWSGI sub-Emperor ***\n"); } else { uwsgi_log("*** starting uWSGI Emperor ***\n"); } if (uwsgi.emperor_stats) { char *tcp_port = strchr(uwsgi.emperor_stats, ':'); if (tcp_port) { // disable deferred accept for this socket int current_defer_accept = uwsgi.no_defer_accept; uwsgi.no_defer_accept = 1; uwsgi.emperor_stats_fd = bind_to_tcp(uwsgi.emperor_stats, uwsgi.listen_queue, tcp_port); uwsgi.no_defer_accept = current_defer_accept; } else { uwsgi.emperor_stats_fd = bind_to_unix(uwsgi.emperor_stats, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); } event_queue_add_fd_read(uwsgi.emperor_queue, uwsgi.emperor_stats_fd); uwsgi_log("*** Emperor stats server enabled on %s fd: %d ***\n", uwsgi.emperor_stats, uwsgi.emperor_stats_fd); } ui = &ui_base; int freq = 0; for (;;) { if (!i_am_alone) { diedpid = waitpid(uwsgi.emperor_pid, &waitpid_status, WNOHANG); if (diedpid < 0 || diedpid > 0) { i_am_alone = 1; } } nevents = event_queue_wait_multi(uwsgi.emperor_queue, freq, events, 64); freq = uwsgi.emperor_freq; for (i = 0; i < nevents; i++) { interesting_fd = event_queue_interesting_fd(events, i); if (uwsgi.emperor_stats && uwsgi.emperor_stats_fd > -1 && interesting_fd == uwsgi.emperor_stats_fd) { emperor_send_stats(uwsgi.emperor_stats_fd); continue; } // check if a monitor is mapped to that file descriptor if (uwsgi_emperor_scanner_event(interesting_fd)) continue; ui_current = emperor_get_by_fd(interesting_fd); if (ui_current) { char byte; ssize_t rlen = read(interesting_fd, &byte, 1); if (rlen <= 0) { // SAFE if (!ui_current->config_len) { emperor_del(ui_current); } } else { 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); // remove it from the blacklist uwsgi_emperor_blacklist_remove(ui_current->name); // TODO post-start hook } // heartbeat can be used for spotting blocked instances else if (byte == 26) { ui_current->last_heartbeat = uwsgi_now(); } else if (byte == 22) { 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); char *zerg_name = uwsgi_concat3(ui_current->name, ":", "zerg"); emperor_add(NULL, zerg_name, uwsgi_now(), NULL, 0, ui_current->uid, ui_current->gid); free(zerg_name); } } } else { uwsgi_log("[emperor] unrecognized vassal event on fd %d\n", interesting_fd); close(interesting_fd); } } uwsgi_emperor_run_scanners(); // check for heartbeat (if required) ui_current = ui->ui_next; while (ui_current) { if (ui_current->last_heartbeat > 0) { if ((ui_current->last_heartbeat + uwsgi.emperor_heartbeat) < uwsgi_now()) { uwsgi_log("[emperor] vassal %s sent no heartbeat in last %d seconds, respawning it...\n", ui_current->name, uwsgi.emperor_heartbeat); // set last_heartbeat to 0 avoiding races ui_current->last_heartbeat = 0; emperor_respawn(ui_current, uwsgi_now()); } } ui_current = ui_current->ui_next; } // check for removed instances ui_current = ui; has_children = 0; while (ui_current->ui_next) { ui_current = ui_current->ui_next; has_children++; } if (uwsgi.notify) { if (snprintf(notification_message, 64, "The Emperor is governing %d vassals", has_children) >= 34) { uwsgi_notify(notification_message); } } if (has_children) { diedpid = waitpid(WAIT_ANY, &waitpid_status, WNOHANG); } else { // vacuum waitpid(WAIT_ANY, &waitpid_status, WNOHANG); diedpid = 0; } if (diedpid < 0) { // it looks like it happens when OOM is triggered to Linux cgroup, but it could be a uWSGI bug :P // by the way, fallback to a clean situation... if (errno == ECHILD) { uwsgi_log("--- MUTINY DETECTED !!! IMPALING VASSALS... ---\n"); ui_current = ui->ui_next; while (ui_current) { struct uwsgi_instance *rebel_vassal = ui_current; ui_current = ui_current->ui_next; emperor_del(rebel_vassal); } } else { uwsgi_error("waitpid()"); } } ui_current = ui; while (ui_current->ui_next) { ui_current = ui_current->ui_next; if (ui_current->status == 1) { if (ui_current->config) free(ui_current->config); // SAFE emperor_del(ui_current); break; } else if (ui_current->pid == diedpid) { if (ui_current->status == 0) { // respawn an accidentally dead instance if its exit code is not UWSGI_EXILE_CODE if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == UWSGI_EXILE_CODE) { // SAFE emperor_del(ui_current); } else { // UNSAFE emperor_add(ui_current->scanner, ui_current->name, ui_current->last_mod, ui_current->config, ui_current->config_len, ui_current->uid, ui_current->gid); emperor_del(ui_current); } break; } else if (ui_current->status == 1) { // remove 'marked for dead' instance if (ui_current->config) free(ui_current->config); // SAFE emperor_del(ui_current); break; } } } } }
// this is the monitor for glob patterns void uwsgi_imperial_monitor_glob(struct uwsgi_emperor_scanner *ues) { glob_t g; int i; struct stat st; struct uwsgi_instance *ui_current; if (glob(ues->arg, GLOB_MARK | GLOB_NOCHECK, NULL, &g)) { uwsgi_error("glob()"); return; } for (i = 0; i < (int) g.gl_pathc; i++) { if (!uwsgi_emperor_is_valid(g.gl_pathv[i])) continue; if (stat(g.gl_pathv[i], &st)) continue; if (!S_ISREG(st.st_mode)) continue; ui_current = emperor_get(g.gl_pathv[i]); uid_t t_uid = st.st_uid; gid_t t_gid = st.st_gid; if (uwsgi.emperor_tyrant && uwsgi.emperor_tyrant_nofollow) { struct stat lst; if (lstat(g.gl_pathv[i], &lst)) { uwsgi_error("[emperor-tyrant]/lstat()"); if (ui_current) { uwsgi_log("!!! availability of file %s changed. stopping the instance... !!!\n", g.gl_pathv[i]); emperor_stop(ui_current); } continue; } t_uid = lst.st_uid; t_gid = lst.st_gid; } if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (t_uid != ui_current->uid || t_gid != ui_current->gid) { uwsgi_log("!!! permissions of file %s changed. stopping the instance... !!!\n", g.gl_pathv[i]); emperor_stop(ui_current); continue; } } // check if mtime is changed and the uWSGI instance must be reloaded if (st.st_mtime > ui_current->last_mod) { emperor_respawn(ui_current, st.st_mtime); } } 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); } } globfree(&g); // now check for removed instances struct uwsgi_instance *c_ui = ui->ui_next; while (c_ui) { if (c_ui->scanner == ues) { if (c_ui->zerg) { char *colon = strrchr(c_ui->name, ':'); if (!colon) { emperor_stop(c_ui); } else { char *filename = uwsgi_calloc(0xff); memcpy(filename, c_ui->name, colon - c_ui->name); if (stat(filename, &st)) { emperor_stop(c_ui); } free(filename); } } else { if (stat(c_ui->name, &st)) { emperor_stop(c_ui); } } } c_ui = c_ui->ui_next; } }
// this is the monitor for non-glob directories void uwsgi_imperial_monitor_directory(struct uwsgi_emperor_scanner *ues) { struct uwsgi_instance *ui_current; struct dirent *de; struct stat st; if (chdir(ues->arg)) { uwsgi_error("chdir()"); return; } DIR *dir = opendir("."); while ((de = readdir(dir)) != NULL) { if (!uwsgi_emperor_is_valid(de->d_name)) continue; if (stat(de->d_name, &st)) continue; if (!S_ISREG(st.st_mode)) continue; ui_current = emperor_get(de->d_name); uid_t t_uid = st.st_uid; gid_t t_gid = st.st_gid; if (uwsgi.emperor_tyrant && uwsgi.emperor_tyrant_nofollow) { struct stat lst; if (lstat(de->d_name, &lst)) { uwsgi_error("[emperor-tyrant]/lstat()"); if (ui_current) { uwsgi_log("!!! availability of file %s changed. stopping the instance... !!!\n", de->d_name); emperor_stop(ui_current); } continue; } t_uid = lst.st_uid; t_gid = lst.st_gid; } if (ui_current) { // check if uid or gid are changed, in such case, stop the instance if (uwsgi.emperor_tyrant) { if (t_uid != ui_current->uid || t_gid != ui_current->gid) { uwsgi_log("!!! permissions of file %s changed. stopping the instance... !!!\n", de->d_name); emperor_stop(ui_current); continue; } } // check if mtime is changed and the uWSGI instance must be reloaded if (st.st_mtime > ui_current->last_mod) { emperor_respawn(ui_current, st.st_mtime); } } 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); if (socket_name) free(socket_name); } } closedir(dir); // now check for removed instances struct uwsgi_instance *c_ui = ui->ui_next; while (c_ui) { if (c_ui->scanner == ues) { if (c_ui->zerg) { char *colon = strrchr(c_ui->name, ':'); if (!colon) { emperor_stop(c_ui); } else { char *filename = uwsgi_calloc(0xff); memcpy(filename, c_ui->name, colon - c_ui->name); if (stat(filename, &st)) { emperor_stop(c_ui); } free(filename); } } else { if (stat(c_ui->name, &st)) { emperor_stop(c_ui); } } } c_ui = c_ui->ui_next; } }