stats.c 26.4 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
#include "util.h"
39 40
#define CATMODULE "stats"
#include "logging.h"
Jack Moffitt's avatar
Jack Moffitt committed
41

42
#ifdef _WIN32
43
#define vsnprintf _vsnprintf
44
#define snprintf _snprintf
45
#endif
Jack Moffitt's avatar
Jack Moffitt committed
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
52
#define STATS_EVENT_HIDDEN  5
53

54 55 56 57 58 59 60 61
typedef struct _event_queue_tag
{
    volatile stats_event_t *head;
    volatile stats_event_t **tail;
} event_queue_t;

#define event_queue_init(qp)    { (qp)->head = NULL; (qp)->tail = &(qp)->head; }

Jack Moffitt's avatar
Jack Moffitt committed
62 63
typedef struct _event_listener_tag
{
64 65
    event_queue_t queue;
    mutex_t mutex;
Jack Moffitt's avatar
Jack Moffitt committed
66

67
    struct _event_listener_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
68 69
} event_listener_t;

70
static volatile int _stats_running = 0;
71
static thread_type *_stats_thread_id;
72
static volatile int _stats_threads = 0;
Jack Moffitt's avatar
Jack Moffitt committed
73

74 75
static stats_t _stats;
static mutex_t _stats_mutex;
Jack Moffitt's avatar
Jack Moffitt committed
76

77
static event_queue_t _global_event_queue;
Jack Moffitt's avatar
Jack Moffitt committed
78 79
mutex_t _global_event_mutex;

80
static volatile event_listener_t *_event_listeners;
Jack Moffitt's avatar
Jack Moffitt committed
81 82 83 84 85 86 87


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);
88
static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
Jack Moffitt's avatar
Jack Moffitt committed
89 90 91
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);
92
static stats_event_t *_get_event_from_queue (event_queue_t *queue);
Jack Moffitt's avatar
Jack Moffitt committed
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

/* 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);
118
    _add_event_to_queue (event, &_global_event_queue);
119 120 121
    thread_mutex_unlock(&_global_event_mutex);
}

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

126 127 128
    /* 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
129

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

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

137 138 139
    /* 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
140 141 142 143
}

void stats_shutdown()
{
144
    int n;
Jack Moffitt's avatar
Jack Moffitt committed
145

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

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

153 154 155 156 157 158 159
    /* 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);
160
    INFO0("stats thread finished");
161

162
    /* free the queues */
Jack Moffitt's avatar
Jack Moffitt committed
163

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

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

171 172 173 174
    while (1)
    {
        stats_event_t *event = _get_event_from_queue (&_global_event_queue);
        if (event == NULL) break;
175 176 177 178
        if(event->source)
            free(event->source);
        if(event->value)
            free(event->value);
179 180
        if(event->name)
            free(event->name);
181 182
        free(event);
    }
Jack Moffitt's avatar
Jack Moffitt committed
183 184 185 186
}

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

191
     unlock global stats
Jack Moffitt's avatar
Jack Moffitt committed
192

193
     return copied stats */
Jack Moffitt's avatar
Jack Moffitt committed
194

195
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
196 197
}

198 199
/* 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
200
{
201 202
    stats_event_t *event;

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

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
/* make stat hidden (non-zero). name can be NULL if it applies to a whole
 * source stats tree. */
void stats_event_hidden (const char *source, const char *name, int hidden)
{
    stats_event_t *event;
    const char *str = NULL;

    if (hidden)
        str = "";
    event = build_event (source, name, str);
    if (event)
    {
        event->action = STATS_EVENT_HIDDEN;
        queue_global_event (event);
    }
}
224 225 226

/* 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
227
{
228 229
    char buf[1024];
    va_list val;
230 231 232 233
    int ret;

    if (name == NULL)
        return;
234
    va_start(val, format);
235
    ret = vsnprintf(buf, 1024, format, val);
236
    va_end(val);
Jack Moffitt's avatar
Jack Moffitt committed
237

238 239 240 241 242 243
    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
    {
        WARN2 ("problem with formatting %s stat %s",
                source==NULL ? "global" : source, name);
        return;
    }
244
    stats_event(source, name, buf);
Jack Moffitt's avatar
Jack Moffitt committed
245 246 247 248
}

static char *_get_stats(char *source, char *name)
{
249 250 251
    stats_node_t *stats = NULL;
    stats_source_t *src = NULL;
    char *value = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
252

253
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
254

255 256 257 258 259 260 261 262
    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
263

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

266
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
267

268
    return value;
Jack Moffitt's avatar
Jack Moffitt committed
269 270
}

271 272
char *stats_get_value(char *source, char *name)
{
273
    return(_get_stats(source, name));
274
}
275 276 277

/* 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
278
{
279 280 281 282 283 284
    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);
285
    }
Jack Moffitt's avatar
Jack Moffitt committed
286 287
}

288
void stats_event_add(const char *source, const char *name, unsigned long value)
Jack Moffitt's avatar
Jack Moffitt committed
289
{
290 291 292 293 294 295 296 297
    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);
298
    }
Jack Moffitt's avatar
Jack Moffitt committed
299 300
}

301 302
/* 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
303
{
304 305 306 307 308 309
    /* 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);
310
    }
Jack Moffitt's avatar
Jack Moffitt committed
311 312 313 314 315 316 317
}

/* 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)
{
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
    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
338 339 340 341 342 343 344
}

/* 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)
{
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    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
361

362 363
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
364 365 366 367
}

static stats_event_t *_copy_event(stats_event_t *event)
{
368
    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
369 370 371 372
    if (event->source) 
        copy->source = (char *)strdup(event->source);
    else
        copy->source = NULL;
373 374
    if (event->name)
        copy->name = (char *)strdup(event->name);
375 376 377 378
    if (event->value)
        copy->value = (char *)strdup(event->value);
    else
        copy->value = NULL;
379
    copy->hidden = event->hidden;
380
    copy->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
381

382
    return copy;
Jack Moffitt's avatar
Jack Moffitt committed
383 384
}

385 386 387 388 389 390

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

391 392 393 394 395 396 397 398
    if (event->action == STATS_EVENT_HIDDEN)
    {
        if (event->value)
            node->hidden = 1;
        else
            node->hidden = 0;
        return;
    }
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
    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);
419 420
        if (event->value == NULL)
            event->value = strdup (str);
421 422 423 424 425
    }
    else
        str = (char *)strdup (event->value);
    free (node->value);
    node->value = str;
426
    DEBUG2 ("update node %s (%s)", node->name, node->value);
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
}


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);
473 474 475 476
        if (event->action == STATS_EVENT_HIDDEN)
            snode->hidden = 1;
        else
            snode->hidden = 0;
477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

        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);
494
                node->hidden = snode->hidden;
495 496 497 498 499 500 501 502 503 504 505 506 507 508

                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;
    }
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
    if (event->action == STATS_EVENT_HIDDEN)
    {
        avl_node *node = avl_get_first (snode->stats_tree);

        if (event->value)
            snode->hidden = 1;
        else
            snode->hidden = 0;
        while (node)
        {
            stats_node_t *stats = (stats_node_t*)node->key;
            stats->hidden = snode->hidden;
            node = avl_get_next (node);
        }
        return;
    }
525 526 527 528 529 530 531 532
    if (event->action == STATS_EVENT_REMOVE)
    {
        DEBUG1 ("delete source node %s", event->source);
        avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
    }
}


Karl Heyes's avatar
Karl Heyes committed
533 534 535
void stats_event_time (const char *mount, const char *name)
{
    time_t now = time(NULL);
536
    struct tm local;
Karl Heyes's avatar
Karl Heyes committed
537 538 539 540 541 542 543 544
    char buffer[100];

    localtime_r (&now, &local);
    strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S %z", &local);
    stats_event (mount, name, buffer);
}


Jack Moffitt's avatar
Jack Moffitt committed
545 546
static void *_stats_thread(void *arg)
{
547 548 549 550
    stats_event_t *event;
    stats_event_t *copy;
    event_listener_t *listener;

551
    stats_event (NULL, "server", ICECAST_VERSION_STRING);
Karl Heyes's avatar
Karl Heyes committed
552
    stats_event_time (NULL, "server_start");
553 554 555 556 557 558 559 560 561 562 563 564 565

    /* 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");
566
    stats_event (NULL, "listener_connections", "0");
567 568

    INFO0 ("stats thread started");
569
    while (_stats_running) {
570
        if (_global_event_queue.head != NULL) {
571
            /* grab the next event from the queue */
572
            thread_mutex_lock(&_global_event_mutex);
573
            event = _get_event_from_queue (&_global_event_queue);
574 575
            thread_mutex_unlock(&_global_event_mutex);

576 577 578
            event->next = NULL;

            thread_mutex_lock(&_stats_mutex);
579 580 581 582 583 584

            /* check if we are dealing with a global or source event */
            if (event->source == NULL)
                process_global_event (event);
            else
                process_source_event (event);
585 586 587
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
588
            listener = (event_listener_t *)_event_listeners;
589 590
            while (listener) {
                copy = _copy_event(event);
591 592 593
                thread_mutex_lock (&listener->mutex);
                _add_event_to_queue (copy, &listener->queue);
                thread_mutex_unlock (&listener->mutex);
594 595 596 597 598 599 600 601

                listener = listener->next;
            }

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

            thread_mutex_unlock(&_stats_mutex);
602
            continue;
603 604
        }

605
        thread_sleep(300000);
606 607 608
    }

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
609 610
}

611
/* you must have the _stats_mutex locked here */
612
static void _unregister_listener(event_listener_t *listener)
613 614 615 616 617
{
    event_listener_t **prev = (event_listener_t **)&_event_listeners,
                     *current = *prev;
    while (current)
    {
618
        if (current == listener)
619 620 621 622 623 624 625 626 627 628
        {
            *prev = current->next;
            break;
        }
        prev = &current->next;
        current = *prev;
    }
}


Jack Moffitt's avatar
Jack Moffitt committed
629 630
static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
631 632 633 634 635 636 637 638
    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);
639
    event->hidden = node->hidden;
640
    event->action = STATS_EVENT_SET;
641
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
642

643
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
644 645 646
}


647 648 649 650
static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue)
{
    *queue->tail = event;
    queue->tail = (volatile stats_event_t **)&event->next;
Jack Moffitt's avatar
Jack Moffitt committed
651 652 653
}


654 655 656
static stats_event_t *_get_event_from_queue (event_queue_t *queue)
{
    stats_event_t *event = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
657

658 659 660 661 662 663 664
    if (queue && queue->head)
    {
        event = (stats_event_t *)queue->head;
        queue->head = event->next;
        if (queue->head == NULL)
            queue->tail = &queue->head;
    }
Jack Moffitt's avatar
Jack Moffitt committed
665

666
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
667 668
}

669
static int _send_event_to_client(stats_event_t *event, client_t *client)
Jack Moffitt's avatar
Jack Moffitt committed
670
{
671
    int len;
672
    char buf [200];
Jack Moffitt's avatar
Jack Moffitt committed
673

674
    /* send data to the client!!!! */
675
    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
676 677 678
            (event->source != NULL) ? event->source : "global",
            event->name ? event->name : "null",
            event->value ? event->value : "null");
679
    if (len > 0 && len < (int)sizeof (buf))
680 681 682 683 684 685
    {
        client_send_bytes (client, buf, len);
        if (client->con->error)
            return -1;
    }
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
686 687
}

688
void _dump_stats_to_queue (event_queue_t *queue)
Jack Moffitt's avatar
Jack Moffitt committed
689
{
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
    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
721 722
}

723 724 725 726 727
/* 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!
*/
728
static void _register_listener (event_listener_t *listener)
Jack Moffitt's avatar
Jack Moffitt committed
729
{
730 731 732 733
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
734

735
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
736

737 738 739 740 741 742
    /* 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);
743
        _add_event_to_queue (event, &listener->queue);
Jack Moffitt's avatar
Jack Moffitt committed
744

745 746
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
747

748 749 750 751 752 753 754
    /* 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);
755
            _add_event_to_queue (event, &listener->queue);
756 757 758 759 760 761

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

763
    /* now we register to receive future event notices */
764 765
    listener->next = (event_listener_t *)_event_listeners;
    _event_listeners = listener;
Jack Moffitt's avatar
Jack Moffitt committed
766

767
    thread_mutex_unlock(&_stats_mutex);
768 769 770 771
}

void *stats_connection(void *arg)
{
772
    client_t *client = (client_t *)arg;
773
    stats_event_t *event;
774
    event_listener_t listener;
775

776 777
    INFO0 ("stats client starting");

778
    event_queue_init (&listener.queue);
779 780 781
    /* increment the thread count */
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
782
    stats_event_args (NULL, "stats", "%d", _stats_threads);
783 784
    thread_mutex_unlock(&_stats_mutex);

785
    thread_mutex_create (&(listener.mutex));
786

787
    _register_listener (&listener);
788 789

    while (_stats_running) {
790 791 792
        thread_mutex_lock (&listener.mutex);
        event = _get_event_from_queue (&listener.queue);
        thread_mutex_unlock (&listener.mutex);
793
        if (event != NULL) {
794
            if (_send_event_to_client(event, client) < 0) {
795 796 797 798 799 800
                _free_event(event);
                break;
            }
            _free_event(event);
            continue;
        }
801
        thread_sleep (500000);
802 803 804
    }

    thread_mutex_lock(&_stats_mutex);
805
    _unregister_listener (&listener);
806
    _stats_threads--;
807
    stats_event_args (NULL, "stats", "%d", _stats_threads);
808 809
    thread_mutex_unlock(&_stats_mutex);

810
    thread_mutex_destroy (&listener.mutex);
811
    client_destroy (client);
812 813
    INFO0 ("stats client finished");

814
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
815 816
}

817 818 819 820 821 822 823 824 825 826 827 828 829

void stats_callback (client_t *client, void *notused)
{
    if (client->con->error)
    {
        client_destroy (client);
        return;
    }
    client_set_queue (client, NULL);
    thread_create("Stats Connection", stats_connection, (void *)client, THREAD_DETACHED);
}


Jack Moffitt's avatar
Jack Moffitt committed
830
typedef struct _source_xml_tag {
831 832
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
833

834
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
835 836 837 838
} source_xml_t;

static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
{
839 840 841 842 843 844 845 846 847 848 849 850
    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
851

852
    if (found) return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
853

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

856 857 858 859 860 861
    /* 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
862

863 864 865 866 867 868 869 870
    /* 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
871

872
    return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
873 874
}

875
void stats_transform_xslt(client_t *client, const char *uri)
876 877
{
    xmlDocPtr doc;
878
    char *xslpath = util_get_path_from_normalised_uri (uri);
879

880
    stats_get_xml(&doc, 0);
881

882
    xslt_transform(doc, xslpath, client);
883 884

    xmlFreeDoc(doc);
885
    free (xslpath);
886 887
}

888
void stats_get_xml(xmlDocPtr *doc, int show_hidden)
889
{
890
    stats_event_t *event;
891
    event_queue_t queue;
892 893 894
    xmlNodePtr node, srcnode;
    source_xml_t *src_nodes = NULL;
    source_xml_t *next;
895

896 897
    event_queue_init (&queue);
    _dump_stats_to_queue (&queue);
898

899 900 901
    *doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(*doc, node);
902

903
    event = _get_event_from_queue(&queue);
904 905
    while (event)
    {
906 907 908 909 910 911 912 913 914 915 916 917
        if (event->hidden <= show_hidden)
        {
            xmlChar *name, *value;
            name = xmlEncodeEntitiesReentrant (*doc, event->name);
            value = xmlEncodeEntitiesReentrant (*doc, event->value);
            srcnode = node;
            if (event->source) {
                srcnode = _find_xml_node(event->source, &src_nodes, node);
            }
            xmlNewChild(srcnode, NULL, name, value);
            xmlFree (value);
            xmlFree (name);
918
        }
919

920 921 922
        _free_event(event);
        event = _get_event_from_queue(&queue);
    }
923

924 925 926 927 928 929
    while (src_nodes) {
        next = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = next;
    }
930
}
Jack Moffitt's avatar
Jack Moffitt committed
931 932
void stats_sendxml(client_t *client)
{
933 934
    int bytes;
    stats_event_t *event;
935
    event_queue_t queue;
936 937 938
    xmlDocPtr doc;
    xmlNodePtr node, srcnode;
    int len;
939
    xmlChar *buff = NULL;
940 941 942
    source_xml_t *snd;
    source_xml_t *src_nodes = NULL;

943 944
    event_queue_init (&queue);
    _dump_stats_to_queue (&queue);
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963

    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);
    }

964
    xmlDocDumpMemory(doc, &buff, &len);
965 966 967 968 969 970 971 972 973 974
    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;

975
    bytes = client_send_bytes (client, buff, (unsigned)len);
Jack Moffitt's avatar
Jack Moffitt committed
976 977

 send_error:
978 979 980 981 982 983 984
    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
985 986 987 988
}

static int _compare_stats(void *arg, void *a, void *b)
{
989 990
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
991

992
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
993 994 995 996
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
997 998
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
999

1000
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
1001 1002 1003 1004
}

static int _free_stats(void *key)
{
1005 1006 1007 1008 1009 1010
    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
1011 1012 1013 1014
}

static int _free_source_stats(void *key)
{
1015 1016 1017
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
1018
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
1019

1020
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
1021 1022 1023 1024
}

static void _free_event(stats_event_t *event)
{
1025 1026 1027 1028
    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
1029
}