stats.c 24.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
 */

13 14 15 16
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
17 18 19 20 21
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

Michael Smith's avatar
Michael Smith committed
22 23 24
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
Jack Moffitt's avatar
Jack Moffitt committed
25 26 27 28 29 30 31 32 33 34 35 36

#include <thread/thread.h>
#include <avl/avl.h>
#include <httpp/httpp.h>
#include <net/sock.h>

#include "connection.h"

#include "global.h"
#include "refbuf.h"
#include "client.h"
#include "stats.h"
37
#include "xslt.h"
38 39
#define CATMODULE "stats"
#include "logging.h"
Jack Moffitt's avatar
Jack Moffitt committed
40

41
#ifdef _WIN32
42
#define vsnprintf _vsnprintf
43
#define snprintf _snprintf
44
#endif
Jack Moffitt's avatar
Jack Moffitt committed
45

46 47 48 49 50 51
#define STATS_EVENT_SET     0
#define STATS_EVENT_INC     1
#define STATS_EVENT_DEC     2
#define STATS_EVENT_ADD     3
#define STATS_EVENT_REMOVE  4

Jack Moffitt's avatar
Jack Moffitt committed
52 53
typedef struct _event_listener_tag
{
54 55
    stats_event_t **queue;
    mutex_t *mutex;
Jack Moffitt's avatar
Jack Moffitt committed
56

57
    struct _event_listener_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
58 59
} event_listener_t;

60
volatile static int _stats_running = 0;
61
static thread_type *_stats_thread_id;
62
volatile static int _stats_threads = 0;
Jack Moffitt's avatar
Jack Moffitt committed
63

64 65
static stats_t _stats;
static mutex_t _stats_mutex;
Jack Moffitt's avatar
Jack Moffitt committed
66

67
volatile static stats_event_t *_global_event_queue;
Jack Moffitt's avatar
Jack Moffitt committed
68 69
mutex_t _global_event_mutex;

70
volatile static event_listener_t *_event_listeners;
Jack Moffitt's avatar
Jack Moffitt committed
71 72 73 74 75 76 77 78 79 80 81 82


static void *_stats_thread(void *arg);
static int _compare_stats(void *a, void *b, void *arg);
static int _compare_source_stats(void *a, void *b, void *arg);
static int _free_stats(void *key);
static int _free_source_stats(void *key);
static void _add_event_to_queue(stats_event_t *event, stats_event_t **queue);
static stats_node_t *_find_node(avl_tree *tree, char *name);
static stats_source_t *_find_source(avl_tree *tree, char *source);
static void _free_event(stats_event_t *event);

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

/* simple helper function for creating an event */
static stats_event_t *build_event (const char *source, const char *name, const char *value)
{
    stats_event_t *event;

    event = (stats_event_t *)calloc(1, sizeof(stats_event_t));
    if (event)
    {
        if (source)
            event->source = (char *)strdup(source);
        if (name)
            event->name = (char *)strdup(name);
        if (value)
            event->value = (char *)strdup(value);
        else
            event->action = STATS_EVENT_REMOVE;
    }
    return event;
}

static void queue_global_event (stats_event_t *event)
{
    thread_mutex_lock(&_global_event_mutex);
    if (_global_event_queue == NULL)
    {
        _global_event_queue = event;
    }
    else
    {
        stats_event_t *node = (stats_event_t *)_global_event_queue;
        while (node->next)
            node = node->next;
        node->next = event;
    }
    /* DEBUG3("event added (%s, %s, %s)", event->source,
         event->name, event->value); */
    thread_mutex_unlock(&_global_event_mutex);
}

Jack Moffitt's avatar
Jack Moffitt committed
123 124
void stats_initialize()
{
125
    _event_listeners = NULL;
126

127 128 129
    /* set up global struct */
    _stats.global_tree = avl_tree_new(_compare_stats, NULL);
    _stats.source_tree = avl_tree_new(_compare_source_stats, NULL);
Jack Moffitt's avatar
Jack Moffitt committed
130

131 132
    /* set up global mutex */
    thread_mutex_create(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
133

134 135 136
    /* set up stats queues */
    _global_event_queue = NULL;
    thread_mutex_create(&_global_event_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
137

138 139 140
    /* fire off the stats thread */
    _stats_running = 1;
    _stats_thread_id = thread_create("Stats Thread", _stats_thread, NULL, THREAD_ATTACHED);
Jack Moffitt's avatar
Jack Moffitt committed
141 142 143 144
}

void stats_shutdown()
{
145
    int n;
146
    stats_event_t *event, *next;
Jack Moffitt's avatar
Jack Moffitt committed
147

148 149 150
    if(!_stats_running) /* We can't shutdown if we're not running. */
        return;

151 152 153
    /* wait for thread to exit */
    _stats_running = 0;
    thread_join(_stats_thread_id);
Jack Moffitt's avatar
Jack Moffitt committed
154

155 156 157 158 159 160 161
    /* wait for other threads to shut down */
    do {
        thread_sleep(300000);
        thread_mutex_lock(&_stats_mutex);
        n = _stats_threads;
        thread_mutex_unlock(&_stats_mutex);
    } while (n > 0);
162
    INFO0("stats thread finished");
163

164
    /* free the queues */
Jack Moffitt's avatar
Jack Moffitt committed
165

166 167
    /* destroy the queue mutexes */
    thread_mutex_destroy(&_global_event_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
168

169 170 171
    thread_mutex_destroy(&_stats_mutex);
    avl_tree_free(_stats.source_tree, _free_source_stats);
    avl_tree_free(_stats.global_tree, _free_stats);
172

173
    event = (stats_event_t *)_global_event_queue;
174 175 176 177 178
    while(event) {
        if(event->source)
            free(event->source);
        if(event->value)
            free(event->value);
179 180
        if(event->name)
            free(event->name);
181 182 183 184
        next = event->next;
        free(event);
        event = next;
    }
Jack Moffitt's avatar
Jack Moffitt committed
185 186 187 188
}

stats_t *stats_get_stats()
{
189 190 191
    /* lock global stats
    
     copy stats
Jack Moffitt's avatar
Jack Moffitt committed
192

193
     unlock global stats
Jack Moffitt's avatar
Jack Moffitt committed
194

195
     return copied stats */
Jack Moffitt's avatar
Jack Moffitt committed
196

197
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
198 199
}

200 201
/* simple name=tag stat create/update */
void stats_event(const char *source, const char *name, const char *value)
Jack Moffitt's avatar
Jack Moffitt committed
202
{
203 204
    stats_event_t *event;

205 206 207
    event = build_event (source, name, value);
    if (event)
        queue_global_event (event);
Jack Moffitt's avatar
Jack Moffitt committed
208 209
}

210 211 212

/* printf style formatting for stat create/update */
void stats_event_args(const char *source, char *name, char *format, ...)
Jack Moffitt's avatar
Jack Moffitt committed
213
{
214 215
    char buf[1024];
    va_list val;
216 217 218 219
    int ret;

    if (name == NULL)
        return;
220
    va_start(val, format);
221
    ret = vsnprintf(buf, 1024, format, val);
222
    va_end(val);
Jack Moffitt's avatar
Jack Moffitt committed
223

224 225 226 227 228 229
    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
    {
        WARN2 ("problem with formatting %s stat %s",
                source==NULL ? "global" : source, name);
        return;
    }
230
    stats_event(source, name, buf);
Jack Moffitt's avatar
Jack Moffitt committed
231 232 233 234
}

static char *_get_stats(char *source, char *name)
{
235 236 237
    stats_node_t *stats = NULL;
    stats_source_t *src = NULL;
    char *value = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
238

239
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
240

241 242 243 244 245 246 247 248
    if (source == NULL) {
        stats = _find_node(_stats.global_tree, name);
    } else {
        src = _find_source(_stats.source_tree, source);
        if (src) {
            stats = _find_node(src->stats_tree, name);
        }
    }
Jack Moffitt's avatar
Jack Moffitt committed
249

250
    if (stats) value = (char *)strdup(stats->value);
Jack Moffitt's avatar
Jack Moffitt committed
251

252
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
253

254
    return value;
Jack Moffitt's avatar
Jack Moffitt committed
255 256
}

257 258
char *stats_get_value(char *source, char *name)
{
259
    return(_get_stats(source, name));
260
}
261 262 263

/* increase the value in the provided stat by 1 */
void stats_event_inc(const char *source, const char *name)
Jack Moffitt's avatar
Jack Moffitt committed
264
{
265 266 267 268 269 270
    stats_event_t *event = build_event (source, name, NULL);
    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
    if (event)
    {
        event->action = STATS_EVENT_INC;
        queue_global_event (event);
271
    }
Jack Moffitt's avatar
Jack Moffitt committed
272 273
}

274
void stats_event_add(const char *source, const char *name, unsigned long value)
Jack Moffitt's avatar
Jack Moffitt committed
275
{
276 277 278 279 280 281 282 283
    stats_event_t *event = build_event (source, name, NULL);
    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
    if (event)
    {
        event->value = malloc (16);
        snprintf (event->value, 16, "%ld", value);
        event->action = STATS_EVENT_ADD;
        queue_global_event (event);
284
    }
Jack Moffitt's avatar
Jack Moffitt committed
285 286
}

287 288
/* decrease the value in the provided stat by 1 */
void stats_event_dec(const char *source, const char *name)
Jack Moffitt's avatar
Jack Moffitt committed
289
{
290 291 292 293 294 295
    /* DEBUG2("%s on %s", name, source==NULL?"global":source); */
    stats_event_t *event = build_event (source, name, NULL);
    if (event)
    {
        event->action = STATS_EVENT_DEC;
        queue_global_event (event);
296
    }
Jack Moffitt's avatar
Jack Moffitt committed
297 298 299 300 301 302 303
}

/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
static stats_node_t *_find_node(avl_tree *stats_tree, char *name)
{
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    stats_node_t *stats;
    avl_node *node;
    int cmp;

    /* get the root node */
    node = stats_tree->root->right;
    
    while (node) {
        stats = (stats_node_t *)node->key;
        cmp = strcmp(name, stats->name);
        if (cmp < 0) 
            node = node->left;
        else if (cmp > 0)
            node = node->right;
        else
            return stats;
    }
    
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
324 325 326 327 328 329 330
}

/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
static stats_source_t *_find_source(avl_tree *source_tree, char *source)
{
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
    stats_source_t *stats;
    avl_node *node;
    int cmp;

    /* get the root node */
    node = source_tree->root->right;
    while (node) {
        stats = (stats_source_t *)node->key;
        cmp = strcmp(source, stats->source);
        if (cmp < 0)
            node = node->left;
        else if (cmp > 0)
            node = node->right;
        else
            return stats;
    }
Jack Moffitt's avatar
Jack Moffitt committed
347

348 349
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
350 351 352 353
}

static stats_event_t *_copy_event(stats_event_t *event)
{
354
    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
355 356 357 358
    if (event->source) 
        copy->source = (char *)strdup(event->source);
    else
        copy->source = NULL;
359 360
    if (event->name)
        copy->name = (char *)strdup(event->name);
361 362 363 364 365
    if (event->value)
        copy->value = (char *)strdup(event->value);
    else
        copy->value = NULL;
    copy->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
366

367
    return copy;
Jack Moffitt's avatar
Jack Moffitt committed
368 369
}

370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400

/* helper to apply specialised changes to a stats node */
static void modify_node_event (stats_node_t *node, stats_event_t *event)
{
    char *str;

    if (event->action != STATS_EVENT_SET)
    {
        int value = 0;

        switch (event->action)
        {
            case STATS_EVENT_INC:
                value = atoi (node->value)+1;
                break;
            case STATS_EVENT_DEC:
                value = atoi (node->value)-1;
                break;
            case STATS_EVENT_ADD:
                value = atoi (node->value)+atoi (event->value);
                break;
            default:
                break;
        }
        str = malloc (16);
        snprintf (str, 16, "%d", value);
    }
    else
        str = (char *)strdup (event->value);
    free (node->value);
    node->value = str;
401
    DEBUG2 ("update node %s (%s)", node->name, node->value);
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
}


static void process_global_event (stats_event_t *event)
{
    stats_node_t *node;

    /* DEBUG3("global event %s %s %d", event->name, event->value, event->action); */
    if (event->action == STATS_EVENT_REMOVE)
    {
        /* we're deleting */
        node = _find_node(_stats.global_tree, event->name);
        if (node != NULL)
            avl_delete(_stats.global_tree, (void *)node, _free_stats);
        return;
    }
    node = _find_node(_stats.global_tree, event->name);
    if (node)
    {
        modify_node_event (node, event);
    }
    else
    {
        /* add node */
        node = (stats_node_t *)calloc(1, sizeof(stats_node_t));
        node->name = (char *)strdup(event->name);
        node->value = (char *)strdup(event->value);

        avl_insert(_stats.global_tree, (void *)node);
    }
}


static void process_source_event (stats_event_t *event)
{
    stats_source_t *snode = _find_source(_stats.source_tree, event->source);
    if (snode == NULL)
    {
        if (event->action == STATS_EVENT_REMOVE)
            return;
        snode = (stats_source_t *)calloc(1,sizeof(stats_source_t));
        if (snode == NULL)
            return;
        DEBUG1 ("new source stat %s", event->source);
        snode->source = (char *)strdup(event->source);
        snode->stats_tree = avl_tree_new(_compare_stats, NULL);

        avl_insert(_stats.source_tree, (void *)snode);
    }
    if (event->name)
    {
        stats_node_t *node = _find_node(snode->stats_tree, event->name);
        if (node == NULL)
        {
            if (event->action == STATS_EVENT_REMOVE)
                return;
            /* adding node */
            if (event->value)
            {
                DEBUG2 ("new node %s (%s)", event->name, event->value);
                node = (stats_node_t *)calloc(1,sizeof(stats_node_t));
                node->name = (char *)strdup(event->name);
                node->value = (char *)strdup(event->value);

                avl_insert(snode->stats_tree, (void *)node);
            }
            return;
        }
        if (event->action == STATS_EVENT_REMOVE)
        {
            DEBUG1 ("delete node %s", event->name);
            avl_delete(snode->stats_tree, (void *)node, _free_stats);
            return;
        }
        modify_node_event (node, event);
        return;
    }
    if (event->action == STATS_EVENT_REMOVE)
    {
        DEBUG1 ("delete source node %s", event->source);
        avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
    }
}


Jack Moffitt's avatar
Jack Moffitt committed
487 488
static void *_stats_thread(void *arg)
{
489 490 491 492
    stats_event_t *event;
    stats_event_t *copy;
    event_listener_t *listener;

493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
    stats_event (NULL, "server", ICECAST_VERSION_STRING);

    /* global currently active stats */
    stats_event (NULL, "clients", "0");
    stats_event (NULL, "connections", "0");
    stats_event (NULL, "sources", "0");
    stats_event (NULL, "stats", "0");

    /* global accumulating stats */
    stats_event (NULL, "client_connections", "0");
    stats_event (NULL, "source_client_connections", "0");
    stats_event (NULL, "source_relay_connections", "0");
    stats_event (NULL, "source_total_connections", "0");
    stats_event (NULL, "stats_connections", "0");

    INFO0 ("stats thread started");
509 510 511
    while (_stats_running) {
        if (_global_event_queue != NULL) {
            /* grab the next event from the queue */
512 513
            thread_mutex_lock(&_global_event_mutex);
            event = (stats_event_t *)_global_event_queue;
514
            _global_event_queue = event->next;
515 516
            thread_mutex_unlock(&_global_event_mutex);

517 518 519 520
            event->next = NULL;
            thread_mutex_unlock(&_global_event_mutex);

            thread_mutex_lock(&_stats_mutex);
521 522 523 524 525 526

            /* check if we are dealing with a global or source event */
            if (event->source == NULL)
                process_global_event (event);
            else
                process_source_event (event);
527 528 529
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
530
            listener = (event_listener_t *)_event_listeners;
531 532 533 534 535 536 537 538 539 540 541 542 543
            while (listener) {
                copy = _copy_event(event);
                thread_mutex_lock(listener->mutex);
                _add_event_to_queue(copy, listener->queue);
                thread_mutex_unlock(listener->mutex);

                listener = listener->next;
            }

            /* now we need to destroy the event */
            _free_event(event);

            thread_mutex_unlock(&_stats_mutex);
544
            continue;
545 546
        }

547
        thread_sleep(300000);
548 549 550
    }

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
551 552 553 554 555
}

/* you must have the _stats_mutex locked here */
static void _register_listener(stats_event_t **queue, mutex_t *mutex)
{
556 557
    event_listener_t *node;
    event_listener_t *evli = (event_listener_t *)malloc(sizeof(event_listener_t));
Jack Moffitt's avatar
Jack Moffitt committed
558

559 560 561
    evli->queue = queue;
    evli->mutex = mutex;
    evli->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
562

563 564 565
    if (_event_listeners == NULL) {
        _event_listeners = evli;
    } else {
566
        node = (event_listener_t *)_event_listeners;
567 568 569
        while (node->next) node = node->next;
        node->next = evli;
    }
Jack Moffitt's avatar
Jack Moffitt committed
570 571 572 573
}

static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
574 575 576 577 578 579 580 581
    stats_event_t *event = (stats_event_t *)malloc(sizeof(stats_event_t));
    
    if (source != NULL)
        event->source = (char *)strdup(source);
    else
        event->source = NULL;
    event->name = (char *)strdup(node->name);
    event->value = (char *)strdup(node->value);
582
    event->action = STATS_EVENT_SET;
583
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
584

585
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
586 587 588 589
}

static void _add_event_to_queue(stats_event_t *event, stats_event_t **queue)
{
590
    stats_event_t *node;
Jack Moffitt's avatar
Jack Moffitt committed
591

592 593 594 595 596 597 598
    if (*queue == NULL) {
        *queue = event;
    } else {
        node = *queue;
        while (node->next) node = node->next;
        node->next = event;
    }
Jack Moffitt's avatar
Jack Moffitt committed
599 600 601 602
}

static stats_event_t *_get_event_from_queue(stats_event_t **queue)
{
603
    stats_event_t *event;
Jack Moffitt's avatar
Jack Moffitt committed
604

605
    if (*queue == NULL) return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
606

607 608 609
    event = *queue;
    *queue = (*queue)->next;
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
610

611
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
612 613 614 615
}

static int _send_event_to_client(stats_event_t *event, connection_t *con)
{
616
    int ret;
Jack Moffitt's avatar
Jack Moffitt committed
617

618
    /* send data to the client!!!! */
619 620 621 622
    ret = sock_write(con->sock, "EVENT %s %s %s\n",
            (event->source != NULL) ? event->source : "global",
            event->name ? event->name : "null",
            event->value ? event->value : "null");
Jack Moffitt's avatar
Jack Moffitt committed
623

624
    return (ret == -1) ? 0 : 1;
Jack Moffitt's avatar
Jack Moffitt committed
625 626 627 628
}

void _dump_stats_to_queue(stats_event_t **queue)
{
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;

    thread_mutex_lock(&_stats_mutex);
    /* first we fill our queue with the current stats */
    /* start with the global stats */
    node = avl_get_first(_stats.global_tree);
    while (node) {
        event = _make_event_from_node((stats_node_t *)node->key, NULL);
        _add_event_to_queue(event, queue);

        node = avl_get_next(node);
    }

    /* now the stats for each source */
    node = avl_get_first(_stats.source_tree);
    while (node) {
        source = (stats_source_t *)node->key;
        node2 = avl_get_first(source->stats_tree);
        while (node2) {
            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
            _add_event_to_queue(event, queue);

            node2 = avl_get_next(node2);
        }
        
        node = avl_get_next(node);
    }
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
660 661
}

662 663 664 665 666 667
/* factoring out code for stats loops
** this function copies all stats to queue, and registers 
** the queue for all new events atomically.
** note: mutex must already be created!
*/
static void _atomic_get_and_register(stats_event_t **queue, mutex_t *mutex)
Jack Moffitt's avatar
Jack Moffitt committed
668
{
669 670 671 672
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
673

674
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
675

676 677 678 679 680 681 682
    /* first we fill our queue with the current stats */
    
    /* start with the global stats */
    node = avl_get_first(_stats.global_tree);
    while (node) {
        event = _make_event_from_node((stats_node_t *)node->key, NULL);
        _add_event_to_queue(event, queue);
Jack Moffitt's avatar
Jack Moffitt committed
683

684 685
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
686

687 688 689 690 691 692 693 694 695 696 697 698 699 700
    /* now the stats for each source */
    node = avl_get_first(_stats.source_tree);
    while (node) {
        source = (stats_source_t *)node->key;
        node2 = avl_get_first(source->stats_tree);
        while (node2) {
            event = _make_event_from_node((stats_node_t *)node2->key, source->source);
            _add_event_to_queue(event, queue);

            node2 = avl_get_next(node2);
        }
        
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
701

702 703
    /* now we register to receive future event notices */
    _register_listener(queue, mutex);
Jack Moffitt's avatar
Jack Moffitt committed
704

705
    thread_mutex_unlock(&_stats_mutex);
706 707 708 709
}

void *stats_connection(void *arg)
{
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
    stats_connection_t *statcon = (stats_connection_t *)arg;
    stats_event_t *local_event_queue = NULL;
    mutex_t local_event_mutex;
    stats_event_t *event;

    /* increment the thread count */
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
    thread_mutex_unlock(&_stats_mutex);

    thread_mutex_create(&local_event_mutex);

    _atomic_get_and_register(&local_event_queue, &local_event_mutex);

    while (_stats_running) {
        thread_mutex_lock(&local_event_mutex);
        event = _get_event_from_queue(&local_event_queue);
        if (event != NULL) {
            if (!_send_event_to_client(event, statcon->con)) {
                _free_event(event);
                thread_mutex_unlock(&local_event_mutex);
                break;
            }
            _free_event(event);
        } else {
            thread_mutex_unlock(&local_event_mutex);
736
            thread_sleep (500000);
737 738 739 740 741 742 743 744 745 746 747 748 749
            continue;
        }
                   
        thread_mutex_unlock(&local_event_mutex);
    }

    thread_mutex_destroy(&local_event_mutex);

    thread_mutex_lock(&_stats_mutex);
    _stats_threads--;
    thread_mutex_unlock(&_stats_mutex);

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
750 751 752
}

typedef struct _source_xml_tag {
753 754
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
755

756
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
757 758 759 760
} source_xml_t;

static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
{
761 762 763 764 765 766 767 768 769 770 771 772
    source_xml_t *node, *node2;
    int found = 0;

    /* search for existing node */
    node = *list;
    while (node) {
        if (strcmp(node->mount, mount) == 0) {
            found = 1;
            break;
        }
        node = node->next;
    }
Jack Moffitt's avatar
Jack Moffitt committed
773

774
    if (found) return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
775

776
    /* if we didn't find it, we must build it and add it to the list */
Jack Moffitt's avatar
Jack Moffitt committed
777

778 779 780 781 782 783
    /* build node */
    node = (source_xml_t *)malloc(sizeof(source_xml_t));
    node->mount = strdup(mount);
    node->node = xmlNewChild(root, NULL, "source", NULL);
    xmlSetProp(node->node, "mount", mount);
    node->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
784

785 786 787 788 789 790 791 792
    /* add node */
    if (*list == NULL) {
        *list = node;
    } else {
        node2 = *list;
        while (node2->next) node2 = node2->next;
        node2->next = node;
    }
Jack Moffitt's avatar
Jack Moffitt committed
793

794
    return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
795 796
}

797 798 799 800 801 802
void stats_transform_xslt(client_t *client, char *xslpath)
{
    xmlDocPtr doc;

    stats_get_xml(&doc);

803
    xslt_transform(doc, xslpath, client);
804 805 806 807 808 809

    xmlFreeDoc(doc);
}

void stats_get_xml(xmlDocPtr *doc)
{
810 811 812 813 814
    stats_event_t *event;
    stats_event_t *queue;
    xmlNodePtr node, srcnode;
    source_xml_t *src_nodes = NULL;
    source_xml_t *next;
815

816 817
    queue = NULL;
    _dump_stats_to_queue(&queue);
818

819 820 821
    *doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(*doc, node);
822 823


824
    event = _get_event_from_queue(&queue);
825 826 827 828 829 830 831
    while (event)
    {
        xmlChar *name, *value;
        name = xmlEncodeEntitiesReentrant (*doc, event->name);
        value = xmlEncodeEntitiesReentrant (*doc, event->value);
        srcnode = node;
        if (event->source) {
832 833
            srcnode = _find_xml_node(event->source, &src_nodes, node);
        }
834 835 836
        xmlNewChild(srcnode, NULL, name, value);
        xmlFree (value);
        xmlFree (name);
837

838 839 840
        _free_event(event);
        event = _get_event_from_queue(&queue);
    }
841

842 843 844 845 846 847
    while (src_nodes) {
        next = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = next;
    }
848
}
Jack Moffitt's avatar
Jack Moffitt committed
849 850
void stats_sendxml(client_t *client)
{
851 852 853 854 855 856
    int bytes;
    stats_event_t *event;
    stats_event_t *queue;
    xmlDocPtr doc;
    xmlNodePtr node, srcnode;
    int len;
857
    xmlChar *buff = NULL;
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    source_xml_t *snd;
    source_xml_t *src_nodes = NULL;

    queue = NULL;
    _dump_stats_to_queue(&queue);

    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(doc, node);


    event = _get_event_from_queue(&queue);
    while (event) {
        if (event->source == NULL) {
            xmlNewChild(node, NULL, event->name, event->value);
        } else {
            srcnode = _find_xml_node(event->source, &src_nodes, node);
            xmlNewChild(srcnode, NULL, event->name, event->value);
        }

        _free_event(event);
        event = _get_event_from_queue(&queue);
    }

882
    xmlDocDumpMemory(doc, &buff, &len);
883 884 885 886 887 888 889 890 891 892
    xmlFreeDoc(doc);
    
    client->respcode = 200;
    bytes = sock_write(client->con->sock, "HTTP/1.0 200 OK\r\n"
               "Content-Length: %d\r\n"
               "Content-Type: text/xml\r\n"
               "\r\n", len);
    if (bytes > 0) client->con->sent_bytes += bytes;
    else goto send_error;

893
    bytes = client_send_bytes (client, buff, (unsigned)len);
Jack Moffitt's avatar
Jack Moffitt committed
894 895

 send_error:
896 897 898 899 900 901 902
    while (src_nodes) {
        snd = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = snd;
    }
    if (buff) xmlFree(buff);
Jack Moffitt's avatar
Jack Moffitt committed
903 904 905 906
}

static int _compare_stats(void *arg, void *a, void *b)
{
907 908
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
909

910
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
911 912 913 914
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
915 916
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
917

918
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
919 920 921 922
}

static int _free_stats(void *key)
{
923 924 925 926 927 928
    stats_node_t *node = (stats_node_t *)key;
    free(node->value);
    free(node->name);
    free(node);
    
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
929 930 931 932
}

static int _free_source_stats(void *key)
{
933 934 935
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
936
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
937

938
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
939 940 941 942
}

static void _free_event(stats_event_t *event)
{
943 944 945 946
    if (event->source) free(event->source);
    if (event->name) free(event->name);
    if (event->value) free(event->value);
    free(event);
Jack Moffitt's avatar
Jack Moffitt committed
947
}