stats.c 28.7 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

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

#include "connection.h"

33
#include "source.h"
Jack Moffitt's avatar
Jack Moffitt committed
34 35 36 37
#include "global.h"
#include "refbuf.h"
#include "client.h"
#include "stats.h"
38
#include "xslt.h"
39
#include "util.h"
40 41
#define CATMODULE "stats"
#include "logging.h"
Jack Moffitt's avatar
Jack Moffitt committed
42

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

48 49 50 51 52
#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
53
#define STATS_EVENT_HIDDEN  5
54

55 56 57 58 59 60 61 62
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
63 64
typedef struct _event_listener_tag
{
65 66
    event_queue_t queue;
    mutex_t mutex;
Jack Moffitt's avatar
Jack Moffitt committed
67

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

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

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

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

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


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

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

123
void stats_initialize(void)
Jack Moffitt's avatar
Jack Moffitt committed
124
{
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
    /* set up stats queues */
135
    event_queue_init (&_global_event_queue);
136
    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
void stats_shutdown(void)
Jack Moffitt's avatar
Jack Moffitt committed
144
{
145
    int n;
Jack Moffitt's avatar
Jack Moffitt committed
146

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

386 387 388 389 390 391

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

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


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

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

                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;
    }
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
    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;
    }
526 527 528 529 530 531 532 533
    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
534 535 536
void stats_event_time (const char *mount, const char *name)
{
    time_t now = time(NULL);
537
    struct tm local;
Karl Heyes's avatar
Karl Heyes committed
538 539 540 541 542 543 544 545
    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);
}


546 547 548 549 550 551 552 553
void stats_global (ice_config_t *config)
{
    stats_event (NULL, "host", config->hostname);
    stats_event (NULL, "location", config->location);
    stats_event (NULL, "admin", config->admin);
}


Jack Moffitt's avatar
Jack Moffitt committed
554 555
static void *_stats_thread(void *arg)
{
556 557 558 559
    stats_event_t *event;
    stats_event_t *copy;
    event_listener_t *listener;

560
    stats_event (NULL, "server", ICECAST_VERSION_STRING);
Karl Heyes's avatar
Karl Heyes committed
561
    stats_event_time (NULL, "server_start");
562 563 564 565 566 567 568 569 570 571 572 573 574

    /* 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");
575
    stats_event (NULL, "listener_connections", "0");
576 577

    INFO0 ("stats thread started");
578
    while (_stats_running) {
579
        if (_global_event_queue.head != NULL) {
580
            /* grab the next event from the queue */
581
            thread_mutex_lock(&_global_event_mutex);
582
            event = _get_event_from_queue (&_global_event_queue);
583 584
            thread_mutex_unlock(&_global_event_mutex);

585 586 587
            event->next = NULL;

            thread_mutex_lock(&_stats_mutex);
588 589 590 591 592 593

            /* check if we are dealing with a global or source event */
            if (event->source == NULL)
                process_global_event (event);
            else
                process_source_event (event);
594 595 596
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
597
            listener = (event_listener_t *)_event_listeners;
598 599
            while (listener) {
                copy = _copy_event(event);
600 601 602
                thread_mutex_lock (&listener->mutex);
                _add_event_to_queue (copy, &listener->queue);
                thread_mutex_unlock (&listener->mutex);
603 604 605 606 607 608 609 610

                listener = listener->next;
            }

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

            thread_mutex_unlock(&_stats_mutex);
611
            continue;
612 613
        }

614
        thread_sleep(300000);
615 616 617
    }

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
618 619
}

620
/* you must have the _stats_mutex locked here */
621
static void _unregister_listener(event_listener_t *listener)
622 623 624 625 626
{
    event_listener_t **prev = (event_listener_t **)&_event_listeners,
                     *current = *prev;
    while (current)
    {
627
        if (current == listener)
628 629 630 631 632 633 634 635 636 637
        {
            *prev = current->next;
            break;
        }
        prev = &current->next;
        current = *prev;
    }
}


Jack Moffitt's avatar
Jack Moffitt committed
638 639
static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
640 641 642 643 644 645 646 647
    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);
648
    event->hidden = node->hidden;
649
    event->action = STATS_EVENT_SET;
650
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
651

652
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
653 654 655
}


656 657 658 659
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
660 661 662
}


663 664 665
static stats_event_t *_get_event_from_queue (event_queue_t *queue)
{
    stats_event_t *event = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
666

667 668 669 670 671 672 673
    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
674

675
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
676 677
}

678
static int _send_event_to_client(stats_event_t *event, client_t *client)
Jack Moffitt's avatar
Jack Moffitt committed
679
{
680
    int len;
681
    char buf [200];
Jack Moffitt's avatar
Jack Moffitt committed
682

683
    /* send data to the client!!!! */
684
    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
685 686 687
            (event->source != NULL) ? event->source : "global",
            event->name ? event->name : "null",
            event->value ? event->value : "null");
688
    if (len > 0 && len < (int)sizeof (buf))
689 690 691 692 693 694
    {
        client_send_bytes (client, buf, len);
        if (client->con->error)
            return -1;
    }
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
695 696
}

697
void _dump_stats_to_queue (event_queue_t *queue)
Jack Moffitt's avatar
Jack Moffitt committed
698
{
699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729
    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
730 731
}

732 733 734 735 736
/* 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!
*/
737
static void _register_listener (event_listener_t *listener)
Jack Moffitt's avatar
Jack Moffitt committed
738
{
739 740 741 742
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
743

744
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
745

746 747 748 749 750 751
    /* 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);
752
        _add_event_to_queue (event, &listener->queue);
Jack Moffitt's avatar
Jack Moffitt committed
753

754 755
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
756

757 758 759 760 761 762 763
    /* 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);
764
            _add_event_to_queue (event, &listener->queue);
765 766 767 768 769 770

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

772
    /* now we register to receive future event notices */
773 774
    listener->next = (event_listener_t *)_event_listeners;
    _event_listeners = listener;
Jack Moffitt's avatar
Jack Moffitt committed
775

776
    thread_mutex_unlock(&_stats_mutex);
777 778 779 780
}

void *stats_connection(void *arg)
{
781
    client_t *client = (client_t *)arg;
782
    stats_event_t *event;
783
    event_listener_t listener;
784

785 786
    INFO0 ("stats client starting");

787
    event_queue_init (&listener.queue);
788 789 790
    /* increment the thread count */
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
791
    stats_event_args (NULL, "stats", "%d", _stats_threads);
792 793
    thread_mutex_unlock(&_stats_mutex);

794
    thread_mutex_create (&(listener.mutex));
795

796
    _register_listener (&listener);
797 798

    while (_stats_running) {
799 800 801
        thread_mutex_lock (&listener.mutex);
        event = _get_event_from_queue (&listener.queue);
        thread_mutex_unlock (&listener.mutex);
802
        if (event != NULL) {
803
            if (_send_event_to_client(event, client) < 0) {
804 805 806 807 808 809
                _free_event(event);
                break;
            }
            _free_event(event);
            continue;
        }
810
        thread_sleep (500000);
811 812 813
    }

    thread_mutex_lock(&_stats_mutex);
814
    _unregister_listener (&listener);
815
    _stats_threads--;
816
    stats_event_args (NULL, "stats", "%d", _stats_threads);
817 818
    thread_mutex_unlock(&_stats_mutex);

819
    thread_mutex_destroy (&listener.mutex);
820
    client_destroy (client);
821 822
    INFO0 ("stats client finished");

823
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
824 825
}

826 827 828 829 830 831 832 833 834 835 836 837 838

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
839
typedef struct _source_xml_tag {
840 841
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
842

843
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
844 845 846 847
} source_xml_t;

static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
{
848 849 850 851 852 853 854 855 856 857 858 859
    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
860

861
    if (found) return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
862

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

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

872 873 874 875 876 877 878 879
    /* 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
880

881
    return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
882 883
}

884
void stats_transform_xslt(client_t *client, const char *uri)
885 886
{
    xmlDocPtr doc;
887
    char *xslpath = util_get_path_from_normalised_uri (uri);
888

889
    stats_get_xml(&doc, 0, NULL);
890

891
    xslt_transform(doc, xslpath, client);
892 893

    xmlFreeDoc(doc);
894
    free (xslpath);
895 896
}

897
void stats_get_xml(xmlDocPtr *doc, int show_hidden, const char *show_mount)
898
{
899
    stats_event_t *event;
900
    event_queue_t queue;
901 902 903
    xmlNodePtr node, srcnode;
    source_xml_t *src_nodes = NULL;
    source_xml_t *next;
904

905 906
    event_queue_init (&queue);
    _dump_stats_to_queue (&queue);
907

908 909 910
    *doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(*doc, node);
911

912
    event = _get_event_from_queue(&queue);
913 914
    while (event)
    {
915 916
        if (event->hidden <= show_hidden)
        {
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
            do
            {
                xmlChar *name, *value;
                name = xmlEncodeEntitiesReentrant (*doc, event->name);
                value = xmlEncodeEntitiesReentrant (*doc, event->value);
                srcnode = node;
                if (event->source)
                {
                    if (show_mount && strcmp (event->source, show_mount) != 0)
                        break;
                    srcnode = _find_xml_node(event->source, &src_nodes, node);
                }
                else
                    srcnode = node;
                xmlNewChild(srcnode, NULL, name, value);
                xmlFree (value);
                xmlFree (name);
            } while (0);
935
        }
936

937 938 939
        _free_event(event);
        event = _get_event_from_queue(&queue);
    }
940

941 942 943 944 945 946
    while (src_nodes) {
        next = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = next;
    }
947
}
Jack Moffitt's avatar
Jack Moffitt committed
948 949
void stats_sendxml(client_t *client)
{
950 951
    int bytes;
    stats_event_t *event;
952
    event_queue_t queue;
953 954 955
    xmlDocPtr doc;
    xmlNodePtr node, srcnode;
    int len;
956
    xmlChar *buff = NULL;
957 958 959
    source_xml_t *snd;
    source_xml_t *src_nodes = NULL;

960 961
    event_queue_init (&queue);
    _dump_stats_to_queue (&queue);
962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980

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

981
    xmlDocDumpMemory(doc, &buff, &len);
982 983 984 985 986 987 988 989 990 991
    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;

992
    bytes = client_send_bytes (client, buff, (unsigned)len);
Jack Moffitt's avatar
Jack Moffitt committed
993 994

 send_error:
995 996 997 998 999 1000 1001
    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
1002 1003 1004 1005
}

static int _compare_stats(void *arg, void *a, void *b)
{
1006 1007
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
1008

1009
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
1010 1011 1012 1013
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
1014 1015
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
1016

1017
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
1018 1019 1020 1021
}

static int _free_stats(void *key)
{
1022 1023 1024 1025 1026 1027
    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
1028 1029 1030 1031
}

static int _free_source_stats(void *key)
{
1032 1033 1034
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
1035
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
1036

1037
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
1038 1039 1040 1041
}

static void _free_event(stats_event_t *event)
{
1042 1043 1044 1045
    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
1046
}
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081


/* get a list of mountpoints that are in the stats but are not marked as hidden */
void stats_get_streamlist (char *buffer, size_t remaining)
{
    avl_node *node;

    /* now the stats for each source */
    thread_mutex_lock (&_stats_mutex);
    node = avl_get_first(_stats.source_tree);
    while (node)
    {
        int ret;
        stats_source_t *source = (stats_source_t *)node->key;

        if (source->hidden == 0)
        {
            if (remaining <= strlen (source->source)+2)
            {
                WARN0 ("streamlist was truncated");
                break;
            }
            ret = snprintf (buffer, remaining, "%s\r\n", source->source);
            if (ret > 0)
            {
                buffer += ret;
                remaining -= ret;
            }
        }

        node = avl_get_next(node);
    }
    thread_mutex_unlock (&_stats_mutex);
}

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110
/* This removes any source stats from virtual mountpoints, ie mountpoints
 * where no source_t exists. This function requires the global sources lock
 * to be held before calling.
 */
void stats_clear_virtual_mounts (void)
{
    avl_node *snode;

    thread_mutex_lock (&_stats_mutex);
    snode = avl_get_first(_stats.source_tree);
    while (snode)
    {
        stats_source_t *src = (stats_source_t *)snode->key;
        source_t *source = source_find_mount_raw (src->source);

        if (source == NULL)
        {
            /* no source_t is reserved so remove them now */
            snode = avl_get_next (snode);
            DEBUG1 ("releasing %s stats", src->source);
            avl_delete (_stats.source_tree, src, _free_source_stats);
            continue;
        }

        snode = avl_get_next (snode);
    }
    thread_mutex_unlock (&_stats_mutex);
}