Files
nginx-rtmp-module/ngx_rtmp_stat_module.c
2012-12-24 23:22:39 +04:00

898 lines
25 KiB
C

/*
* Copyright (c) 2012 Roman Arutyunyan
*/
#include <nginx.h>
#include <ngx_http.h>
#include "ngx_rtmp.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_play_module.h"
#include "ngx_rtmp_codec_module.h"
static ngx_int_t ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf);
static void * ngx_rtmp_stat_create_loc_conf(ngx_conf_t *cf);
static char * ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_rtmp_stat_init_process(ngx_cycle_t *cycle);
static void ngx_rtmp_stat_main(ngx_pool_t *pool, ngx_chain_t ***lll);
static time_t start_time;
static ngx_str_t shm_name = ngx_string("rtmp_stat");
static ngx_shm_zone_t *shm_zone;
static ngx_slab_pool_t *shm_pool;
static ngx_buf_t *shm_map;
static ngx_event_t shm_evt;
static ngx_connection_t shm_dummy_conn;
static size_t shm_size = 1024 * 1024; /*TODO:tune*/
#define shm_map_size (sizeof(ngx_buf_t) * NGX_MAX_PROCESSES)
#define shm_update_timeout 1000
#define NGX_RTMP_STAT_ALL 0xff
#define NGX_RTMP_STAT_GLOBAL 0x01
#define NGX_RTMP_STAT_LIVE 0x02
#define NGX_RTMP_STAT_CLIENTS 0x04
#define NGX_RTMP_STAT_PLAY 0x08
/*
* global: stat-{bufs-{total,free,used}, total bytes in/out, bw in/out} - cscf
*/
typedef struct {
ngx_uint_t stat;
ngx_str_t stylesheet;
} ngx_rtmp_stat_loc_conf_t;
static ngx_conf_bitmask_t ngx_rtmp_stat_masks[] = {
{ ngx_string("all"), NGX_RTMP_STAT_ALL },
{ ngx_string("global"), NGX_RTMP_STAT_GLOBAL },
{ ngx_string("live"), NGX_RTMP_STAT_LIVE },
{ ngx_string("clients"), NGX_RTMP_STAT_CLIENTS },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_rtmp_stat_commands[] = {
{ ngx_string("rtmp_stat"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
ngx_conf_set_bitmask_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_rtmp_stat_loc_conf_t, stat),
ngx_rtmp_stat_masks },
{ ngx_string("rtmp_stat_stylesheet"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_rtmp_stat_loc_conf_t, stylesheet),
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_rtmp_stat_module_ctx = {
NULL, /* preconfiguration */
ngx_rtmp_stat_postconfiguration, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_rtmp_stat_create_loc_conf, /* create location configuration */
ngx_rtmp_stat_merge_loc_conf, /* merge location configuration */
};
ngx_module_t ngx_rtmp_stat_module = {
NGX_MODULE_V1,
&ngx_rtmp_stat_module_ctx, /* module context */
ngx_rtmp_stat_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_rtmp_stat_init_process, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void
ngx_rtmp_stat_update(ngx_event_t *ev)
{
ngx_buf_t *b;
ngx_pool_t *pool;
ngx_chain_t *l, *cl, **ll, ***lll;
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ev->log, 0,
"stat: updating slot %d",
(ngx_int_t) ngx_process_slot);
b = &shm_map[ngx_process_slot];
b->pos = b->last = b->start;
pool = ngx_create_pool(4096, ev->log);
if (pool == NULL) {
return;
}
cl = NULL;
ll = &cl;
lll = &ll;
ngx_rtmp_stat_main(pool, lll);
for (l = cl; l; l = l->next) {
if (b->end - b->last < l->buf->last - l->buf->end) {
b->last = b->pos;
ngx_log_error(NGX_LOG_ERR, ev->log, 0,
"stat: not enough space for worker stat");
goto next;
}
b->last = ngx_cpymem(b->last, l->buf->pos,
(size_t) (l->buf->last - l->buf->pos));
}
next:
ngx_destroy_pool(pool);
ngx_add_timer(ev, shm_update_timeout);
}
static ngx_int_t
ngx_rtmp_stat_init_process(ngx_cycle_t *cycle)
{
ngx_buf_t *b;
size_t size;
ngx_core_conf_t *ccf;
start_time = ngx_cached_time->sec;
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
if (ccf->worker_processes == 1) {
return NGX_OK;
}
if (shm_map == NULL) {
ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
"stat: NULL shm_map");
return NGX_ERROR;
}
b = &shm_map[ngx_process_slot];
if (b->start) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
"stat: slot %d already allocated",
(ngx_int_t) ngx_process_slot);
goto schedule;
}
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, cycle->log, 0,
"stat: allocating slot %d",
(ngx_int_t) ngx_process_slot);
size = ((shm_size - shm_map_size) / ccf->worker_processes) &
~(ngx_pagesize - 1);
b->start= ngx_slab_alloc(shm_pool, size);
if (b->start == NULL) {
ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
"stat: error allocating %d slot",
ngx_process_slot);
return NGX_ERROR;
}
b->end = b->start + size;
schedule:
b->pos = b->last = b->start;
shm_evt.log = cycle->log;
shm_evt.data = &shm_dummy_conn;
shm_evt.handler = ngx_rtmp_stat_update;
ngx_add_timer(&shm_evt, shm_update_timeout);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_stat_shm_init(ngx_shm_zone_t *shm_zone, void *data)
{
shm_pool = (ngx_slab_pool_t *) shm_zone->shm.addr;
shm_map = ngx_slab_alloc(shm_pool, shm_map_size);
if (shm_map == NULL) {
return NGX_ERROR;
}
ngx_memzero(shm_map, shm_map_size);
return NGX_OK;
}
#define NGX_RTMP_STAT_BUFSIZE 256
/* ngx_escape_html does not escape characters out of ASCII range
* which are bad for xslt */
static void *
ngx_rtmp_stat_escape(ngx_pool_t *pool, void *data, size_t len)
{
u_char *p, *np;
void *new_data;
size_t n;
p = data;
for (n = 0; n < len; ++n, ++p) {
if (*p < 0x20 || *p >= 0x7f) {
break;
}
}
if (n == len) {
return data;
}
new_data = ngx_palloc(pool, len);
if (new_data == NULL) {
return NULL;
}
p = data;
np = new_data;
for (n = 0; n < len; ++n, ++p, ++np) {
*np = (*p < 0x20 || *p >= 0x7f) ? (u_char) ' ' : *p;
}
return new_data;
}
static void
ngx_rtmp_stat_output(ngx_pool_t *pool, ngx_chain_t ***lll,
void *data, size_t len, ngx_uint_t escape)
{
ngx_chain_t *cl;
ngx_buf_t *b;
size_t real_len;
if (len == 0) {
return;
}
if (escape) {
data = ngx_rtmp_stat_escape(pool, data, len);
if (data == NULL) {
return;
}
}
real_len = escape
? len + ngx_escape_html(NULL, data, len)
: len;
cl = **lll;
if (cl && cl->buf->last + real_len > cl->buf->end) {
*lll = &cl->next;
}
if (**lll == NULL) {
cl = ngx_alloc_chain_link(pool);
if (cl == NULL) {
return;
}
b = ngx_create_temp_buf(pool,
ngx_max(NGX_RTMP_STAT_BUFSIZE, real_len));
if (b == NULL || b->pos == NULL) {
return;
}
cl->next = NULL;
cl->buf = b;
**lll = cl;
}
b = (**lll)->buf;
if (escape) {
b->last = (u_char *)ngx_escape_html(b->last, data, len);
} else {
b->last = ngx_cpymem(b->last, data, len);
}
}
/* These shortcuts assume 2 variables exist in current context:
* ngx_pool_t *pool
* ngx_chain_t ***lll */
/* plain data */
#define NGX_RTMP_STAT(data, len) ngx_rtmp_stat_output(pool, lll, data, len,\
0)
/* escaped data */
#define NGX_RTMP_STAT_E(data, len) ngx_rtmp_stat_output(pool, lll, data, len,\
1)
/* literal */
#define NGX_RTMP_STAT_L(s) NGX_RTMP_STAT((s), sizeof(s) - 1)
/* ngx_str_t */
#define NGX_RTMP_STAT_S(s) NGX_RTMP_STAT((s)->data, (s)->len)
/* escaped ngx_str_t */
#define NGX_RTMP_STAT_ES(s) NGX_RTMP_STAT_E((s)->data, (s)->len)
/* C string */
#define NGX_RTMP_STAT_CS(s) NGX_RTMP_STAT((s), ngx_strlen(s))
/* escaped C string */
#define NGX_RTMP_STAT_ECS(s) NGX_RTMP_STAT_E((s), ngx_strlen(s))
static void
ngx_rtmp_stat_bw(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_bandwidth_t *bw_in, ngx_rtmp_bandwidth_t *bw_out)
{
u_char buf[NGX_OFF_T_LEN + 1];
ngx_rtmp_update_bandwidth(bw_in, 0);
ngx_rtmp_update_bandwidth(bw_out, 0);
NGX_RTMP_STAT_L("<in>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", bw_in->bytes) - buf);
NGX_RTMP_STAT_L("</in>\r\n");
NGX_RTMP_STAT_L("<out>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", bw_out->bytes) - buf);
NGX_RTMP_STAT_L("</out>\r\n");
NGX_RTMP_STAT_L("<bwin>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", bw_in->bandwidth * 8) - buf);
NGX_RTMP_STAT_L("</bwin>\r\n");
NGX_RTMP_STAT_L("<bwout>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", bw_out->bandwidth * 8) - buf);
NGX_RTMP_STAT_L("</bwout>\r\n");
}
#ifdef NGX_RTMP_POOL_DEBUG
static void
ngx_rtmp_stat_get_pool_size(ngx_pool_t *pool, ngx_uint_t *nlarge,
size_t *size)
{
ngx_pool_large_t *l;
ngx_pool_t *p, *n;
*nlarge = 0;
for (l = pool->large; l; l = l->next) {
++*nlarge;
}
*size = 0;
for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
*size += (p->d.last - (u_char *)p);
if (n == NULL) {
break;
}
}
}
static void
ngx_rtmp_stat_dump_pool(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_pool_t *p)
{
ngx_uint_t nlarge;
size_t size;
u_char buf[NGX_OFF_T_LEN + 1];
size = 0;
nlarge = 0;
ngx_rtmp_stat_get_pool_size(p, &nlarge, &size);
NGX_RTMP_STAT_L("<pool><nlarge>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", nlarge) - buf);
NGX_RTMP_STAT_L("</nlarge><size>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", size) - buf);
NGX_RTMP_STAT_L("</size></pool>\r\n");
}
#endif
static void
ngx_rtmp_stat_client(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_session_t *s)
{
u_char buf[NGX_OFF_T_LEN + 1];
#ifdef NGX_RTMP_POOL_DEBUG
ngx_rtmp_stat_dump_pool(pool, lll, s->connection->pool);
#endif
NGX_RTMP_STAT_L("<id>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%ui",
(ngx_uint_t) s->connection->number)
- buf);
NGX_RTMP_STAT_L("</id>");
NGX_RTMP_STAT_L("<address>");
NGX_RTMP_STAT_S(&s->connection->addr_text);
NGX_RTMP_STAT_L("</address>");
NGX_RTMP_STAT_L("<time>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%M",
ngx_current_msec - s->epoch) - buf);
NGX_RTMP_STAT_L("</time>");
if (s->flashver.len) {
NGX_RTMP_STAT_L("<flashver>");
NGX_RTMP_STAT_ES(&s->flashver);
NGX_RTMP_STAT_L("</flashver>");
}
if (s->page_url.len) {
NGX_RTMP_STAT_L("<pageurl>");
NGX_RTMP_STAT_ES(&s->page_url);
NGX_RTMP_STAT_L("</pageurl>");
}
if (s->swf_url.len) {
NGX_RTMP_STAT_L("<swfurl>");
NGX_RTMP_STAT_ES(&s->swf_url);
NGX_RTMP_STAT_L("</swfurl>");
}
}
static void
ngx_rtmp_stat_live(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_live_app_conf_t *lacf)
{
ngx_rtmp_live_stream_t *stream;
ngx_rtmp_codec_ctx_t *codec;
ngx_rtmp_live_ctx_t *ctx;
ngx_rtmp_session_t *s;
ngx_int_t n;
size_t nclients, total_nclients;
u_char buf[NGX_OFF_T_LEN + 1];
u_char *cname;
if (!lacf->live) {
return;
}
NGX_RTMP_STAT_L("<live>\r\n");
total_nclients = 0;
for (n = 0; n < lacf->nbuckets; ++n) {
for (stream = lacf->streams[n]; stream; stream = stream->next) {
NGX_RTMP_STAT_L("<stream>\r\n");
NGX_RTMP_STAT_L("<name>");
NGX_RTMP_STAT_ECS(stream->name);
NGX_RTMP_STAT_L("</name>\r\n");
NGX_RTMP_STAT_L("<time>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%M", ngx_current_msec - stream->epoch) - buf);
NGX_RTMP_STAT_L("</time>");
ngx_rtmp_stat_bw(pool, lll, &stream->bw_in, &stream->bw_out);
nclients = 0;
codec = NULL;
for (ctx = stream->ctx; ctx; ctx = ctx->next, ++nclients) {
s = ctx->session;
NGX_RTMP_STAT_L("<client>");
ngx_rtmp_stat_client(pool, lll, s);
NGX_RTMP_STAT_L("<dropped>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uD/%uD", ctx->cs[1].dropped,
ctx->cs[0].dropped) - buf);
NGX_RTMP_STAT_L("</dropped>");
NGX_RTMP_STAT_L("<avsync>");
if (!lacf->interleave) {
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%L", (int64_t) ctx->cs[1].timestamp -
(int64_t) ctx->cs[0].timestamp)
- buf);
}
NGX_RTMP_STAT_L("</avsync>");
if (ctx->publishing) {
NGX_RTMP_STAT_L("<publishing/>");
}
if (ctx->active) {
NGX_RTMP_STAT_L("<active/>");
}
NGX_RTMP_STAT_L("</client>\r\n");
if (ctx->publishing) {
codec = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
}
}
total_nclients += nclients;
if (codec) {
NGX_RTMP_STAT_L("<meta><width>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->width) - buf);
NGX_RTMP_STAT_L("</width><height>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->height) - buf);
NGX_RTMP_STAT_L("</height><framerate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->frame_rate) - buf);
NGX_RTMP_STAT_L("</framerate>");
cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id);
if (*cname) {
NGX_RTMP_STAT_L("<video>");
NGX_RTMP_STAT_ECS(cname);
NGX_RTMP_STAT_L("</video>");
}
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
if (*cname) {
NGX_RTMP_STAT_L("<audio>");
NGX_RTMP_STAT_ECS(cname);
NGX_RTMP_STAT_L("</audio>");
}
if (*codec->profile) {
NGX_RTMP_STAT_L("<profile>");
NGX_RTMP_STAT_ECS(codec->profile);
NGX_RTMP_STAT_L("</profile>");
}
if (*codec->level) {
NGX_RTMP_STAT_L("<level>");
NGX_RTMP_STAT_ECS(codec->level);
NGX_RTMP_STAT_L("</level>");
}
NGX_RTMP_STAT_L("</meta>\r\n");
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
if (stream->publishing) {
NGX_RTMP_STAT_L("<publishing/>\r\n");
}
if (stream->active) {
NGX_RTMP_STAT_L("<active/>\r\n");
}
NGX_RTMP_STAT_L("</stream>\r\n");
}
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", total_nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
NGX_RTMP_STAT_L("</live>\r\n");
}
static void
ngx_rtmp_stat_play(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_play_app_conf_t *pacf)
{
ngx_rtmp_play_ctx_t *ctx, *sctx;
ngx_rtmp_session_t *s;
ngx_uint_t n;
size_t nclients, total_nclients;
u_char buf[NGX_OFF_T_LEN + 1];
if (pacf->root.len == 0 && pacf->url == NULL) {
return;
}
NGX_RTMP_STAT_L("<play>\r\n");
total_nclients = 0;
for (n = 0; n < pacf->nbuckets; ++n) {
for (ctx = pacf->ctx[n]; ctx; ) {
NGX_RTMP_STAT_L("<stream>\r\n");
NGX_RTMP_STAT_L("<name>");
NGX_RTMP_STAT_ECS(ctx->name);
NGX_RTMP_STAT_L("</name>\r\n");
nclients = 0;
sctx = ctx;
for (; ctx; ctx = ctx->next) {
if (ngx_strcmp(ctx->name, sctx->name)) {
break;
}
++nclients;
s = ctx->session;
NGX_RTMP_STAT_L("<client>");
ngx_rtmp_stat_client(pool, lll, s);
NGX_RTMP_STAT_L("</client>\r\n");
}
total_nclients += nclients;
NGX_RTMP_STAT_L("<active/>");
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
NGX_RTMP_STAT_L("</stream>\r\n");
}
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", total_nclients) - buf);
NGX_RTMP_STAT_L("</nclients>\r\n");
NGX_RTMP_STAT_L("</play>\r\n");
}
static void
ngx_rtmp_stat_application(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_core_app_conf_t *cacf)
{
NGX_RTMP_STAT_L("<application>\r\n");
NGX_RTMP_STAT_L("<name>");
NGX_RTMP_STAT_ES(&cacf->name);
NGX_RTMP_STAT_L("</name>\r\n");
ngx_rtmp_stat_live(pool, lll,
cacf->app_conf[ngx_rtmp_live_module.ctx_index]);
ngx_rtmp_stat_play(pool, lll,
cacf->app_conf[ngx_rtmp_play_module.ctx_index]);
NGX_RTMP_STAT_L("</application>\r\n");
}
static void
ngx_rtmp_stat_server(ngx_pool_t *pool, ngx_chain_t ***lll,
ngx_rtmp_core_srv_conf_t *cscf)
{
ngx_rtmp_core_app_conf_t **cacf;
size_t n;
NGX_RTMP_STAT_L("<server>\r\n");
#ifdef NGX_RTMP_POOL_DEBUG
ngx_rtmp_stat_dump_pool(pool, lll, cscf->pool);
#endif
cacf = cscf->applications.elts;
for (n = 0; n < cscf->applications.nelts; ++n, ++cacf) {
ngx_rtmp_stat_application(pool, lll, *cacf);
}
NGX_RTMP_STAT_L("</server>\r\n");
}
static void
ngx_rtmp_stat_main(ngx_pool_t *pool, ngx_chain_t ***lll)
{
ngx_rtmp_core_main_conf_t *cmcf = ngx_rtmp_core_main_conf;
ngx_rtmp_core_srv_conf_t **cscf;
size_t n;
static u_char nbuf[NGX_OFF_T_LEN + 1];
static u_char tbuf[NGX_TIME_T_LEN + 1];
NGX_RTMP_STAT_L("<uptime>");
NGX_RTMP_STAT(tbuf, ngx_snprintf(tbuf, sizeof(tbuf),
"%T", ngx_cached_time->sec - start_time) - tbuf);
NGX_RTMP_STAT_L("</uptime>\r\n");
NGX_RTMP_STAT_L("<pid>");
NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf),
"%ui", (ngx_uint_t) ngx_getpid()) - nbuf);
NGX_RTMP_STAT_L("</pid>\r\n");
ngx_rtmp_stat_bw(pool, lll, &ngx_rtmp_bw_in, &ngx_rtmp_bw_out);
cscf = cmcf->servers.elts;
for (n = 0; n < cmcf->servers.nelts; ++n, ++cscf) {
ngx_rtmp_stat_server(pool, lll, *cscf);
}
}
static void
ngx_rtmp_stat_get(ngx_pool_t *pool, ngx_chain_t ***lll)
{
ngx_buf_t *b;
ngx_uint_t n;
static u_char nbuf[NGX_OFF_T_LEN + 1];
b = &shm_map[ngx_process_slot];
if (b->start == NULL) {
/* single-worker */
NGX_RTMP_STAT_L("<worker>\r\n");
ngx_rtmp_stat_main(pool, lll);
NGX_RTMP_STAT_L("</worker>\r\n");
return;
}
/* multi-worker */
b = &shm_map[0];
for (n = 0; n < NGX_MAX_PROCESSES; ++n, ++b) {
if (b->start == NULL) {
continue;
}
NGX_RTMP_STAT_L("<worker>\r\n");
NGX_RTMP_STAT_L("<n>");
NGX_RTMP_STAT(nbuf, ngx_snprintf(nbuf, sizeof(nbuf), "%ui", n + 1)
- nbuf);
NGX_RTMP_STAT_L("</n>\r\n");
NGX_RTMP_STAT(b->pos, b->last - b->pos);
NGX_RTMP_STAT_L("</worker>\r\n");
}
}
static ngx_int_t
ngx_rtmp_stat_handler(ngx_http_request_t *r)
{
ngx_rtmp_stat_loc_conf_t *slcf;
ngx_chain_t *cl, *l, **ll, ***lll;
ngx_pool_t *pool;
off_t len;
pool = r->pool;
r->keepalive = 0;
slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module);
if (slcf->stat == 0) {
return NGX_DECLINED;
}
if (ngx_rtmp_core_main_conf == NULL) {
goto error;
}
cl = NULL;
ll = &cl;
lll = &ll;
NGX_RTMP_STAT_L("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n");
if (slcf->stylesheet.len) {
NGX_RTMP_STAT_L("<?xml-stylesheet type=\"text/xsl\" href=\"");
NGX_RTMP_STAT_ES(&slcf->stylesheet);
NGX_RTMP_STAT_L("\" ?>\r\n");
}
NGX_RTMP_STAT_L("<rtmp>\r\n");
#ifdef NGINX_VERSION
NGX_RTMP_STAT_L("<version>" NGINX_VERSION "</version>\r\n");
#endif
#ifdef NGX_COMPILER
NGX_RTMP_STAT_L("<compiler>" NGX_COMPILER "</compiler>\r\n");
#endif
NGX_RTMP_STAT_L("<built>" __DATE__ " " __TIME__ "</built>\r\n");
ngx_rtmp_stat_get(r->pool, lll);
NGX_RTMP_STAT_L("</rtmp>\r\n");
len = 0;
for (l = cl; l; l = l->next) {
len += (l->buf->last - l->buf->pos);
}
ngx_str_set(&r->headers_out.content_type, "text/xml");
r->headers_out.content_length_n = len;
r->headers_out.status = NGX_HTTP_OK;
ngx_http_send_header(r);
(*ll)->buf->last_buf = 1;
return ngx_http_output_filter(r, cl);
error:
r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;
r->headers_out.content_length_n = 0;
return ngx_http_send_header(r);
}
static void *
ngx_rtmp_stat_create_loc_conf(ngx_conf_t *cf)
{
ngx_rtmp_stat_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_stat_loc_conf_t));
if (conf == NULL) {
return NULL;
}
conf->stat = 0;
return conf;
}
static char *
ngx_rtmp_stat_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_stat_loc_conf_t *prev = parent;
ngx_rtmp_stat_loc_conf_t *conf = child;
ngx_conf_merge_bitmask_value(conf->stat, prev->stat, 0);
ngx_conf_merge_str_value(conf->stylesheet, prev->stylesheet, "");
return NGX_CONF_OK;
}
static ngx_int_t
ngx_rtmp_stat_postconfiguration(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_rtmp_stat_handler;
if (shm_zone == NULL) {
shm_zone = ngx_shared_memory_add(cf, &shm_name, shm_size +
ngx_pagesize, &ngx_rtmp_stat_module);
if (shm_zone == NULL) {
return NGX_ERROR;
}
shm_zone->init = ngx_rtmp_stat_shm_init;
}
return NGX_OK;
}