#include "uwsgi.h" /* utility functions for fast generating json output for the stats subsystem */ extern struct uwsgi_server uwsgi; struct uwsgi_stats *uwsgi_stats_new(size_t chunk_size) { struct uwsgi_stats *us = uwsgi_malloc(sizeof(struct uwsgi_stats)); us->base = uwsgi_malloc(chunk_size); us->base[0] = '{'; us->pos = 1; us->chunk = chunk_size; us->size = chunk_size; us->tabs = 1; us->dirty = 0; us->minified = uwsgi.stats_minified; if (!us->minified) { us->base[1] = '\n'; us->pos++; } return us; } int uwsgi_stats_symbol(struct uwsgi_stats *us, char sym) { char *ptr = us->base + us->pos; char *watermark = us->base + us->size; if (ptr + 1 > watermark) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; } *ptr = sym; us->pos++; return 0; } int uwsgi_stats_symbol_nl(struct uwsgi_stats *us, char sym) { if (uwsgi_stats_symbol(us, sym)) { return -1; } if (us->minified) return 0; return uwsgi_stats_symbol(us, '\n'); } int uwsgi_stats_comma(struct uwsgi_stats *us) { return uwsgi_stats_symbol_nl(us, ','); } int uwsgi_stats_apply_tabs(struct uwsgi_stats *us) { if (us->minified) return 0; size_t i; for (i = 0; i < us->tabs; i++) { if (uwsgi_stats_symbol(us, '\t')) return -1; }; return 0; } int uwsgi_stats_object_open(struct uwsgi_stats *us) { if (uwsgi_stats_apply_tabs(us)) return -1; if (!us->minified) us->tabs++; return uwsgi_stats_symbol_nl(us, '{'); } int uwsgi_stats_object_close(struct uwsgi_stats *us) { if (!us->minified) { if (uwsgi_stats_symbol(us, '\n')) return -1; us->tabs--; if (uwsgi_stats_apply_tabs(us)) return -1; } return uwsgi_stats_symbol(us, '}'); } int uwsgi_stats_list_open(struct uwsgi_stats *us) { us->tabs++; return uwsgi_stats_symbol_nl(us, '['); } int uwsgi_stats_list_close(struct uwsgi_stats *us) { if (!us->minified) { if (uwsgi_stats_symbol(us, '\n')) return -1; us->tabs--; if (uwsgi_stats_apply_tabs(us)) return -1; } return uwsgi_stats_symbol(us, ']'); } int uwsgi_stats_keyval(struct uwsgi_stats *us, char *key, char *value) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":\"%s\"", key, value); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":\"%s\"", key, value); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keyval_comma(struct uwsgi_stats *us, char *key, char *value) { int ret = uwsgi_stats_keyval(us, key, value); if (ret) return -1; return uwsgi_stats_comma(us); } int uwsgi_stats_keyvalnum(struct uwsgi_stats *us, char *key, char *value, unsigned long long num) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":\"%s%llu\"", key, value, num); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":\"%s%llu\"", key, value, num); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keyvalnum_comma(struct uwsgi_stats *us, char *key, char *value, unsigned long long num) { int ret = uwsgi_stats_keyvalnum(us, key, value, num); if (ret) return -1; return uwsgi_stats_comma(us); } int uwsgi_stats_keyvaln(struct uwsgi_stats *us, char *key, char *value, int vallen) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":\"%.*s\"", key, vallen, value); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":\"%.*s\"", key, vallen, value); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keyvaln_comma(struct uwsgi_stats *us, char *key, char *value, int vallen) { int ret = uwsgi_stats_keyvaln(us, key, value, vallen); if (ret) return -1; return uwsgi_stats_comma(us); } int uwsgi_stats_key(struct uwsgi_stats *us, char *key) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":", key); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":", key); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_str(struct uwsgi_stats *us, char *str) { char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\"", str); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\"", str); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keylong(struct uwsgi_stats *us, char *key, unsigned long long num) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":%llu", key, num); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":%llu", key, num); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keylong_comma(struct uwsgi_stats *us, char *key, unsigned long long num) { int ret = uwsgi_stats_keylong(us, key, num); if (ret) return -1; return uwsgi_stats_comma(us); } int uwsgi_stats_keyslong(struct uwsgi_stats *us, char *key, long long num) { if (uwsgi_stats_apply_tabs(us)) return -1; char *ptr = us->base + us->pos; char *watermark = us->base + us->size; size_t available = watermark - ptr; int ret = snprintf(ptr, available, "\"%s\":%lld", key, num); if (ret < 0) return -1; while (ret >= (int) available) { char *new_base = realloc(us->base, us->size + us->chunk); if (!new_base) return -1; us->base = new_base; us->size += us->chunk; ptr = us->base + us->pos; watermark = us->base + us->size; available = watermark - ptr; ret = snprintf(ptr, available, "\"%s\":%lld", key, num); if (ret < 0) return -1; } us->pos += ret; return 0; } int uwsgi_stats_keyslong_comma(struct uwsgi_stats *us, char *key, long long num) { int ret = uwsgi_stats_keyslong(us, key, num); if (ret) return -1; return uwsgi_stats_comma(us); } void uwsgi_send_stats(int fd, struct uwsgi_stats *(*func) (void)) { 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("accept()"); return; } if (uwsgi.stats_http) { if (uwsgi_send_http_stats(client_fd)) { close(client_fd); return; } } struct uwsgi_stats *us = func(); if (!us) goto end; size_t remains = us->pos; off_t pos = 0; while (remains > 0) { int ret = uwsgi_waitfd_write(client_fd, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]); if (ret <= 0) { goto end0; } ssize_t res = write(client_fd, us->base + pos, remains); if (res <= 0) { if (res < 0) { uwsgi_error("write()"); } goto end0; } pos += res; remains -= res; } end0: free(us->base); free(us); end: close(client_fd); } struct uwsgi_stats_pusher *uwsgi_stats_pusher_get(char *name) { struct uwsgi_stats_pusher *usp = uwsgi.stats_pushers; while (usp) { if (!strcmp(usp->name, name)) { return usp; } usp = usp->next; } return usp; } struct uwsgi_stats_pusher_instance *uwsgi_stats_pusher_add(struct uwsgi_stats_pusher *pusher, char *arg) { struct uwsgi_stats_pusher_instance *old_uspi = NULL, *uspi = uwsgi.stats_pusher_instances; while (uspi) { old_uspi = uspi; uspi = uspi->next; } uspi = uwsgi_calloc(sizeof(struct uwsgi_stats_pusher_instance)); uspi->pusher = pusher; if (arg) { uspi->arg = uwsgi_str(arg); } uspi->raw = pusher->raw; if (old_uspi) { old_uspi->next = uspi; } else { uwsgi.stats_pusher_instances = uspi; } return uspi; } void uwsgi_stats_pusher_loop(struct uwsgi_thread *ut) { void *events = event_queue_alloc(1); for (;;) { int nevents = event_queue_wait_multi(ut->queue, 1, events, 1); if (nevents > 0) { int interesting_fd = event_queue_interesting_fd(events, 0); char buf[4096]; ssize_t len = read(interesting_fd, buf, 4096); if (len <= 0) { uwsgi_log("[uwsgi-stats-pusher] goodbye...\n"); return; } uwsgi_log("[uwsgi-stats-pusher] message received from master: %.*s\n", (int) len, buf); continue; } time_t now = uwsgi_now(); struct uwsgi_stats_pusher_instance *uspi = uwsgi.stats_pusher_instances; struct uwsgi_stats *us = NULL; while (uspi) { int delta = uspi->freq ? uspi->freq : uwsgi.stats_pusher_default_freq; if ((uspi->last_run + delta) <= now) { if (uspi->raw) { uspi->pusher->func(uspi, now, NULL, 0); } else { if (!us) { us = uwsgi_master_generate_stats(); if (!us) goto next; } uspi->pusher->func(uspi, now, us->base, us->pos); } uspi->last_run = now; } next: uspi = uspi->next; } if (us) { free(us->base); free(us); } } } void uwsgi_stats_pusher_setup() { struct uwsgi_string_list *usl = uwsgi.requested_stats_pushers; while (usl) { char *ssp = uwsgi_str(usl->value); struct uwsgi_stats_pusher *pusher = NULL; char *colon = strchr(ssp, ':'); if (colon) { *colon = 0; } pusher = uwsgi_stats_pusher_get(ssp); if (!pusher) { uwsgi_log("unable to find \"%s\" stats_pusher\n", ssp); free(ssp); exit(1); } char *arg = NULL; if (colon) { arg = colon + 1; *colon = ':'; } uwsgi_stats_pusher_add(pusher, arg); usl = usl->next; free(ssp); } } struct uwsgi_stats_pusher *uwsgi_register_stats_pusher(char *name, void (*func) (struct uwsgi_stats_pusher_instance *, time_t, char *, size_t)) { struct uwsgi_stats_pusher *pusher = uwsgi.stats_pushers, *old_pusher = NULL; while (pusher) { old_pusher = pusher; pusher = pusher->next; } pusher = uwsgi_calloc(sizeof(struct uwsgi_stats_pusher)); pusher->name = name; pusher->func = func; if (old_pusher) { old_pusher->next = pusher; } else { uwsgi.stats_pushers = pusher; } return pusher; } struct uwsgi_stats_pusher_file_conf { char *path; char *freq; char *separator; }; void uwsgi_stats_pusher_file(struct uwsgi_stats_pusher_instance *uspi, time_t now, char *json, size_t json_len) { struct uwsgi_stats_pusher_file_conf *uspic = (struct uwsgi_stats_pusher_file_conf *) uspi->data; if (!uspi->configured) { uspic = uwsgi_calloc(sizeof(struct uwsgi_stats_pusher_file_conf)); if (uspi->arg) { if (uwsgi_kvlist_parse(uspi->arg, strlen(uspi->arg), ',', '=', "path", &uspic->path, "separator", &uspic->separator, "freq", &uspic->freq, NULL)) { free(uspi); return; } } if (!uspic->path) uspic->path = "uwsgi.stats"; if (!uspic->separator) uspic->separator = "\n\n"; if (uspic->freq) uspi->freq = atoi(uspic->freq); uspi->configured = 1; uspi->data = uspic; } int fd = open(uspic->path, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP); if (fd < 0) { uwsgi_error_open(uspic->path); return; } ssize_t rlen = write(fd, json, json_len); if (rlen != (ssize_t) json_len) { uwsgi_error("uwsgi_stats_pusher_file() -> write()\n"); } rlen = write(fd, uspic->separator, strlen(uspic->separator)); if (rlen != (ssize_t) strlen(uspic->separator)) { uwsgi_error("uwsgi_stats_pusher_file() -> write()\n"); } close(fd); } static void stats_dump_var(char *k, uint16_t kl, char *v, uint16_t vl, void *data) { struct uwsgi_stats *us = (struct uwsgi_stats *) data; if (us->dirty) return; uint16_t i; // replace " with ' for(i=0;i