stats.c 26.8 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
#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
51
#define STATS_EVENT_HIDDEN  5
52

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

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

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

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

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

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


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

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 123

/* 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
124 125
void stats_initialize()
{
126
    _event_listeners = NULL;
127

128 129 130
    /* 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
131

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

201 202
/* 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
203
{
204 205
    stats_event_t *event;

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

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
/* 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);
    }
}
227 228 229

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

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

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

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

256
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
257

258 259 260 261 262 263 264 265
    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
266

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

269
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
270

271
    return value;
Jack Moffitt's avatar
Jack Moffitt committed
272 273
}

274 275
char *stats_get_value(char *source, char *name)
{
276
    return(_get_stats(source, name));
277
}
278 279 280

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

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

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

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

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

365 366
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
367 368 369 370
}

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

385
    return copy;
Jack Moffitt's avatar
Jack Moffitt committed
386 387
}

388 389 390 391 392 393

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

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


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

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

                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;
    }
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527
    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;
    }
528 529 530 531 532 533 534 535
    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
536 537 538 539 540 541 542 543 544 545 546 547
void stats_event_time (const char *mount, const char *name)
{
    time_t now = time(NULL);
    struct tm local; 
    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
548 549
static void *_stats_thread(void *arg)
{
550 551 552 553
    stats_event_t *event;
    stats_event_t *copy;
    event_listener_t *listener;

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

    /* 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");
569
    stats_event (NULL, "listener_connections", "0");
570 571

    INFO0 ("stats thread started");
572 573 574
    while (_stats_running) {
        if (_global_event_queue != NULL) {
            /* grab the next event from the queue */
575 576
            thread_mutex_lock(&_global_event_mutex);
            event = (stats_event_t *)_global_event_queue;
577
            _global_event_queue = event->next;
578 579
            thread_mutex_unlock(&_global_event_mutex);

580 581 582
            event->next = NULL;

            thread_mutex_lock(&_stats_mutex);
583 584 585 586 587 588

            /* check if we are dealing with a global or source event */
            if (event->source == NULL)
                process_global_event (event);
            else
                process_source_event (event);
589 590 591
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
592
            listener = (event_listener_t *)_event_listeners;
593 594 595 596 597 598 599 600 601 602 603 604 605
            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);
606
            continue;
607 608
        }

609
        thread_sleep(300000);
610 611 612
    }

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
613 614
}

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
/* you must have the _stats_mutex locked here */
static void _unregister_listener(stats_event_t **queue)
{
    event_listener_t **prev = (event_listener_t **)&_event_listeners,
                     *current = *prev;
    while (current)
    {
        if (current->queue == queue)
        {
            *prev = current->next;
            free (current);
            break;
        }
        prev = &current->next;
        current = *prev;
    }
}


Jack Moffitt's avatar
Jack Moffitt committed
634 635 636
/* you must have the _stats_mutex locked here */
static void _register_listener(stats_event_t **queue, mutex_t *mutex)
{
637 638
    event_listener_t *node;
    event_listener_t *evli = (event_listener_t *)malloc(sizeof(event_listener_t));
Jack Moffitt's avatar
Jack Moffitt committed
639

640 641 642
    evli->queue = queue;
    evli->mutex = mutex;
    evli->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
643

644 645 646
    if (_event_listeners == NULL) {
        _event_listeners = evli;
    } else {
647
        node = (event_listener_t *)_event_listeners;
648 649 650
        while (node->next) node = node->next;
        node->next = evli;
    }
Jack Moffitt's avatar
Jack Moffitt committed
651 652 653 654
}

static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
655 656 657 658 659 660 661 662
    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);
663
    event->hidden = node->hidden;
664
    event->action = STATS_EVENT_SET;
665
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
666

667
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
668 669 670 671
}

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

674 675 676 677 678 679 680
    if (*queue == NULL) {
        *queue = event;
    } else {
        node = *queue;
        while (node->next) node = node->next;
        node->next = event;
    }
Jack Moffitt's avatar
Jack Moffitt committed
681 682 683 684
}

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

687
    if (*queue == NULL) return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
688

689 690 691
    event = *queue;
    *queue = (*queue)->next;
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
692

693
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
694 695
}

696
static int _send_event_to_client(stats_event_t *event, client_t *client)
Jack Moffitt's avatar
Jack Moffitt committed
697
{
698 699
    int ret = -1, len;
    char buf [200];
Jack Moffitt's avatar
Jack Moffitt committed
700

701
    /* send data to the client!!!! */
702
    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
703 704 705
            (event->source != NULL) ? event->source : "global",
            event->name ? event->name : "null",
            event->value ? event->value : "null");
706 707
    if (len > 0 && len < sizeof (buf))
        ret = client_send_bytes (client, buf, len);
Jack Moffitt's avatar
Jack Moffitt committed
708

709
    return (ret == -1) ? 0 : 1;
Jack Moffitt's avatar
Jack Moffitt committed
710 711 712 713
}

void _dump_stats_to_queue(stats_event_t **queue)
{
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
    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
745 746
}

747 748 749 750 751 752
/* 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
753
{
754 755 756 757
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
758

759
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
760

761 762 763 764 765 766 767
    /* 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
768

769 770
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
771

772 773 774 775 776 777 778 779 780 781 782 783 784 785
    /* 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
786

787 788
    /* now we register to receive future event notices */
    _register_listener(queue, mutex);
Jack Moffitt's avatar
Jack Moffitt committed
789

790
    thread_mutex_unlock(&_stats_mutex);
791 792 793 794
}

void *stats_connection(void *arg)
{
795
    client_t *client = (client_t *)arg;
796 797 798 799
    stats_event_t *local_event_queue = NULL;
    mutex_t local_event_mutex;
    stats_event_t *event;

800 801
    INFO0 ("stats client starting");

802 803 804
    /* increment the thread count */
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
805
    stats_event_args (NULL, "stats", "%d", _stats_threads);
806 807 808 809 810 811 812 813 814 815
    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) {
816
            if (!_send_event_to_client(event, client)) {
817 818 819 820 821 822 823
                _free_event(event);
                thread_mutex_unlock(&local_event_mutex);
                break;
            }
            _free_event(event);
        } else {
            thread_mutex_unlock(&local_event_mutex);
824
            thread_sleep (500000);
825 826 827 828 829 830 831
            continue;
        }
                   
        thread_mutex_unlock(&local_event_mutex);
    }

    thread_mutex_lock(&_stats_mutex);
832
    _unregister_listener (&local_event_queue);
833
    _stats_threads--;
834
    stats_event_args (NULL, "stats", "%d", _stats_threads);
835 836
    thread_mutex_unlock(&_stats_mutex);

837
    thread_mutex_destroy(&local_event_mutex);
838
    client_destroy (client);
839 840
    INFO0 ("stats client finished");

841
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
842 843 844
}

typedef struct _source_xml_tag {
845 846
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
847

848
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
849 850 851 852
} source_xml_t;

static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
{
853 854 855 856 857 858 859 860 861 862 863 864
    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
865

866
    if (found) return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
867

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

870 871 872 873 874 875
    /* 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
876

877 878 879 880 881 882 883 884
    /* 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
885

886
    return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
887 888
}

889 890 891 892
void stats_transform_xslt(client_t *client, char *xslpath)
{
    xmlDocPtr doc;

893
    stats_get_xml(&doc, 0);
894

895
    xslt_transform(doc, xslpath, client);
896 897 898 899

    xmlFreeDoc(doc);
}

900
void stats_get_xml(xmlDocPtr *doc, int show_hidden)
901
{
902 903 904 905 906
    stats_event_t *event;
    stats_event_t *queue;
    xmlNodePtr node, srcnode;
    source_xml_t *src_nodes = NULL;
    source_xml_t *next;
907

908 909
    queue = NULL;
    _dump_stats_to_queue(&queue);
910

911 912 913
    *doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(*doc, node);
914 915


916
    event = _get_event_from_queue(&queue);
917 918
    while (event)
    {
919 920 921 922 923 924 925 926 927 928 929 930
        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);
931
        }
932

933 934 935
        _free_event(event);
        event = _get_event_from_queue(&queue);
    }
936

937 938 939 940 941 942
    while (src_nodes) {
        next = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = next;
    }
943
}
Jack Moffitt's avatar
Jack Moffitt committed
944 945
void stats_sendxml(client_t *client)
{
946 947 948 949 950 951
    int bytes;
    stats_event_t *event;
    stats_event_t *queue;
    xmlDocPtr doc;
    xmlNodePtr node, srcnode;
    int len;
952
    xmlChar *buff = NULL;
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
    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);
    }

977
    xmlDocDumpMemory(doc, &buff, &len);
978 979 980 981 982 983 984 985 986 987
    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;

988
    bytes = client_send_bytes (client, buff, (unsigned)len);
Jack Moffitt's avatar
Jack Moffitt committed
989 990

 send_error:
991 992 993 994 995 996 997
    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
998 999 1000 1001
}

static int _compare_stats(void *arg, void *a, void *b)
{
1002 1003
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
1004

1005
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
1006 1007 1008 1009
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
1010 1011
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
1012

1013
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
1014 1015 1016 1017
}

static int _free_stats(void *key)
{
1018 1019 1020 1021 1022 1023
    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
1024 1025 1026 1027
}

static int _free_source_stats(void *key)
{
1028 1029 1030
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
1031
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
1032

1033
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
1034 1035 1036 1037
}

static void _free_event(stats_event_t *event)
{
1038 1039 1040 1041
    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
1042
}