stats.c 27.9 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
#include "thread/thread.h"
#include "avl/avl.h"
#include "httpp/httpp.h"
#include "net/sock.h"
Jack Moffitt's avatar
Jack Moffitt committed
30
31
32

#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
#define STATS_EVENT_SET     0
#define STATS_EVENT_INC     1
#define STATS_EVENT_DEC     2
#define STATS_EVENT_ADD     3
Karl Heyes's avatar
Karl Heyes committed
52
53
54
#define STATS_EVENT_SUB     4
#define STATS_EVENT_REMOVE  5
#define STATS_EVENT_HIDDEN  6
55

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

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

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

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

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

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


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);
90
static void _add_event_to_queue(stats_event_t *event, event_queue_t *queue);
91
92
static stats_node_t *_find_node(avl_tree *tree, const char *name);
static stats_source_t *_find_source(avl_tree *tree, const char *source);
Jack Moffitt's avatar
Jack Moffitt committed
93
static void _free_event(stats_event_t *event);
94
static stats_event_t *_get_event_from_queue (event_queue_t *queue);
Jack Moffitt's avatar
Jack Moffitt committed
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

205
206
207
208
209
    if (value && xmlCheckUTF8 ((unsigned char *)value) == 0)
    {
        WARN2 ("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value);
        return;
    }
210
211
212
    event = build_event (source, name, value);
    if (event)
        queue_global_event (event);
Jack Moffitt's avatar
Jack Moffitt committed
213
214
}

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242

/* wrapper for stats_event, this takes a charset to convert from */
void stats_event_conv(const char *mount, const char *name, const char *value, const char *charset)
{
    const char *metadata = value;
    xmlBufferPtr conv = xmlBufferCreate ();

    if (charset)
    {
        xmlCharEncodingHandlerPtr handle = xmlFindCharEncodingHandler (charset);

        if (handle)
        {
            xmlBufferPtr raw = xmlBufferCreate ();
            xmlBufferAdd (raw, (const xmlChar *)value, strlen (value));
            if (xmlCharEncInFunc (handle, conv, raw) > 0)
                metadata = (char *)xmlBufferContent (conv);
            xmlBufferFree (raw);
            xmlCharEncCloseFunc (handle);
        }
        else
            WARN1 ("No charset found for \"%s\"", charset);
    }

    stats_event (mount, name, metadata);
    xmlBufferFree (conv);
}

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* 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);
    }
}
259
260
261

/* 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
262
{
263
264
    char buf[1024];
    va_list val;
265
266
267
268
    int ret;

    if (name == NULL)
        return;
269
    va_start(val, format);
270
    ret = vsnprintf(buf, 1024, format, val);
271
    va_end(val);
Jack Moffitt's avatar
Jack Moffitt committed
272

273
274
275
276
277
278
    if (ret < 0 || (unsigned int)ret >= sizeof (buf))
    {
        WARN2 ("problem with formatting %s stat %s",
                source==NULL ? "global" : source, name);
        return;
    }
279
    stats_event(source, name, buf);
Jack Moffitt's avatar
Jack Moffitt committed
280
281
}

282
static char *_get_stats(const char *source, const char *name)
Jack Moffitt's avatar
Jack Moffitt committed
283
{
284
285
286
    stats_node_t *stats = NULL;
    stats_source_t *src = NULL;
    char *value = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
287

288
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
289

290
291
292
293
294
295
296
297
    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
298

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

301
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
302

303
    return value;
Jack Moffitt's avatar
Jack Moffitt committed
304
305
}

306
char *stats_get_value(const char *source, const char *name)
307
{
308
    return(_get_stats(source, name));
309
}
310
311
312

/* 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
313
{
314
315
316
317
318
319
    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);
320
    }
Jack Moffitt's avatar
Jack Moffitt committed
321
322
}

323
void stats_event_add(const char *source, const char *name, unsigned long value)
Jack Moffitt's avatar
Jack Moffitt committed
324
{
325
326
327
328
329
330
331
332
    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);
333
    }
Jack Moffitt's avatar
Jack Moffitt committed
334
335
}

Karl Heyes's avatar
Karl Heyes committed
336
337
338
339
340
341
342
343
344
345
346
347
void stats_event_sub(const char *source, const char *name, unsigned long value)
{
    stats_event_t *event = build_event (source, name, NULL);
    if (event)
    {
        event->value = malloc (16);
        snprintf (event->value, 16, "%ld", value);
        event->action = STATS_EVENT_SUB;
        queue_global_event (event);
    }
}

348
349
/* 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
350
{
351
352
353
354
355
356
    /* 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);
357
    }
Jack Moffitt's avatar
Jack Moffitt committed
358
359
360
361
362
}

/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
363
static stats_node_t *_find_node(avl_tree *stats_tree, const char *name)
Jack Moffitt's avatar
Jack Moffitt committed
364
{
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
    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
385
386
387
388
389
}

/* note: you must call this function only when you have exclusive access
** to the avl_tree
*/
390
static stats_source_t *_find_source(avl_tree *source_tree, const char *source)
Jack Moffitt's avatar
Jack Moffitt committed
391
{
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    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
408

409
410
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
411
412
413
414
}

static stats_event_t *_copy_event(stats_event_t *event)
{
415
    stats_event_t *copy = (stats_event_t *)calloc(1, sizeof(stats_event_t));
416
417
418
419
    if (event->source) 
        copy->source = (char *)strdup(event->source);
    else
        copy->source = NULL;
420
421
    if (event->name)
        copy->name = (char *)strdup(event->name);
422
423
424
425
    if (event->value)
        copy->value = (char *)strdup(event->value);
    else
        copy->value = NULL;
426
    copy->hidden = event->hidden;
427
    copy->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
428

429
    return copy;
Jack Moffitt's avatar
Jack Moffitt committed
430
431
}

432
433
434
435
436
437

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

438
439
440
441
442
443
444
445
    if (event->action == STATS_EVENT_HIDDEN)
    {
        if (event->value)
            node->hidden = 1;
        else
            node->hidden = 0;
        return;
    }
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
    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;
461
462
463
            case STATS_EVENT_SUB:
                value = atoll (node->value) - atoll (event->value);
                break;
464
            default:
465
                WARN2 ("unhandled event (%d) for %s", event->action, event->source);
466
467
468
469
                break;
        }
        str = malloc (16);
        snprintf (str, 16, "%d", value);
470
471
        if (event->value == NULL)
            event->value = strdup (str);
472
473
474
475
476
    }
    else
        str = (char *)strdup (event->value);
    free (node->value);
    node->value = str;
477
478
479
480
    if (event->source)
        DEBUG3 ("update \"%s\" %s (%s)", event->source, node->name, node->value);
    else
        DEBUG2 ("update global %s (%s)", node->name, node->value);
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
}


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);
527
528
529
530
        if (event->action == STATS_EVENT_HIDDEN)
            snode->hidden = 1;
        else
            snode->hidden = 0;
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

        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);
548
                node->hidden = snode->hidden;
549
550
551
552
553
554
555
556
557
558
559
560
561
562

                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;
    }
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
    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;
    }
579
580
581
582
583
584
585
586
    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
587
588
589
void stats_event_time (const char *mount, const char *name)
{
    time_t now = time(NULL);
590
    struct tm local;
Karl Heyes's avatar
Karl Heyes committed
591
592
593
594
595
596
597
598
    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);
}


Karl Heyes's avatar
Karl Heyes committed
599
600
void stats_global (ice_config_t *config)
{
601
    stats_event (NULL, "server_id", config->server_id);
Karl Heyes's avatar
Karl Heyes committed
602
603
604
605
606
607
    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
608
609
static void *_stats_thread(void *arg)
{
610
611
612
613
    stats_event_t *event;
    stats_event_t *copy;
    event_listener_t *listener;

Karl Heyes's avatar
Karl Heyes committed
614
    stats_event_time (NULL, "server_start");
615
616
617
618
619
620

    /* global currently active stats */
    stats_event (NULL, "clients", "0");
    stats_event (NULL, "connections", "0");
    stats_event (NULL, "sources", "0");
    stats_event (NULL, "stats", "0");
Karl Heyes's avatar
Karl Heyes committed
621
    stats_event (NULL, "listeners", "0");
622
623
624
625
626
627
628

    /* 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");
629
    stats_event (NULL, "listener_connections", "0");
630
631

    INFO0 ("stats thread started");
632
    while (_stats_running) {
633
        if (_global_event_queue.head != NULL) {
634
            /* grab the next event from the queue */
635
            thread_mutex_lock(&_global_event_mutex);
636
            event = _get_event_from_queue (&_global_event_queue);
637
638
            thread_mutex_unlock(&_global_event_mutex);

639
640
            if (event == NULL)
                continue;
641
642
643
            event->next = NULL;

            thread_mutex_lock(&_stats_mutex);
644
645
646
647
648
649

            /* check if we are dealing with a global or source event */
            if (event->source == NULL)
                process_global_event (event);
            else
                process_source_event (event);
650
651
652
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
653
            listener = (event_listener_t *)_event_listeners;
654
655
            while (listener) {
                copy = _copy_event(event);
656
657
658
                thread_mutex_lock (&listener->mutex);
                _add_event_to_queue (copy, &listener->queue);
                thread_mutex_unlock (&listener->mutex);
659
660
661
662
663
664
665
666

                listener = listener->next;
            }

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

            thread_mutex_unlock(&_stats_mutex);
667
            continue;
668
669
        }

670
        thread_sleep(300000);
671
672
673
    }

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
674
675
}

676
/* you must have the _stats_mutex locked here */
677
static void _unregister_listener(event_listener_t *listener)
678
679
680
681
682
{
    event_listener_t **prev = (event_listener_t **)&_event_listeners,
                     *current = *prev;
    while (current)
    {
683
        if (current == listener)
684
685
686
687
688
689
690
691
692
693
        {
            *prev = current->next;
            break;
        }
        prev = &current->next;
        current = *prev;
    }
}


Jack Moffitt's avatar
Jack Moffitt committed
694
695
static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
696
697
698
699
700
701
702
703
    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);
704
    event->hidden = node->hidden;
705
    event->action = STATS_EVENT_SET;
706
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
707

708
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
709
710
711
}


712
713
714
715
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
716
717
718
}


719
720
721
static stats_event_t *_get_event_from_queue (event_queue_t *queue)
{
    stats_event_t *event = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
722

723
724
725
726
727
728
729
    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
730

731
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
732
733
}

734
static int _send_event_to_client(stats_event_t *event, client_t *client)
Jack Moffitt's avatar
Jack Moffitt committed
735
{
736
    int len;
737
    char buf [200];
Jack Moffitt's avatar
Jack Moffitt committed
738

739
    /* send data to the client!!!! */
740
    len = snprintf (buf, sizeof (buf), "EVENT %s %s %s\n",
741
742
743
            (event->source != NULL) ? event->source : "global",
            event->name ? event->name : "null",
            event->value ? event->value : "null");
744
    if (len > 0 && len < (int)sizeof (buf))
745
746
747
748
749
750
    {
        client_send_bytes (client, buf, len);
        if (client->con->error)
            return -1;
    }
    return 0;
Jack Moffitt's avatar
Jack Moffitt committed
751
752
}

753
754

static xmlNodePtr _dump_stats_to_doc (xmlNodePtr root, const char *show_mount, int hidden)
Jack Moffitt's avatar
Jack Moffitt committed
755
{
756
757
    avl_node *avlnode;
    xmlNodePtr ret = NULL;
758
759

    thread_mutex_lock(&_stats_mutex);
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
    /* general stats first */
    avlnode = avl_get_first(_stats.global_tree);
    while (avlnode)
    {
        stats_node_t *stat = avlnode->key;
        if (stat->hidden <=  hidden)
            xmlNewTextChild (root, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
        avlnode = avl_get_next (avlnode);
    }
    /* now per mount stats */
    avlnode = avl_get_first(_stats.source_tree);
    while (avlnode)
    {
        stats_source_t *source = (stats_source_t *)avlnode->key;
        if (source->hidden <= hidden &&
                (show_mount == NULL || strcmp (show_mount, source->source) == 0))
        {
            avl_node *avlnode2 = avl_get_first (source->stats_tree);
            xmlNodePtr xmlnode = xmlNewTextChild (root, NULL, XMLSTR("source"), NULL);
779

780
781
782
783
784
785
786
787
788
            xmlSetProp (xmlnode, XMLSTR("mount"), XMLSTR(source->source));
            if (ret == NULL)
                ret = xmlnode;
            while (avlnode2)
            {
                stats_node_t *stat = avlnode2->key;
                xmlNewTextChild (xmlnode, NULL, XMLSTR(stat->name), XMLSTR(stat->value));
                avlnode2 = avl_get_next (avlnode2);
            }
789
        }
790
        avlnode = avl_get_next (avlnode);
791
792
    }
    thread_mutex_unlock(&_stats_mutex);
793
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
794
795
}

796

797
798
799
800
801
/* 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!
*/
802
static void _register_listener (event_listener_t *listener)
Jack Moffitt's avatar
Jack Moffitt committed
803
{
804
805
806
807
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
808

809
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
810

811
812
813
814
815
816
    /* 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);
817
        _add_event_to_queue (event, &listener->queue);
Jack Moffitt's avatar
Jack Moffitt committed
818

819
820
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
821

822
823
824
825
826
827
828
    /* 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);
829
            _add_event_to_queue (event, &listener->queue);
830
831
832
833
834
835

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

837
    /* now we register to receive future event notices */
838
839
    listener->next = (event_listener_t *)_event_listeners;
    _event_listeners = listener;
Jack Moffitt's avatar
Jack Moffitt committed
840

841
    thread_mutex_unlock(&_stats_mutex);
842
843
844
845
}

void *stats_connection(void *arg)
{
846
    client_t *client = (client_t *)arg;
847
    stats_event_t *event;
848
    event_listener_t listener;
849

850
851
    INFO0 ("stats client starting");

852
    event_queue_init (&listener.queue);
853
854
855
    /* increment the thread count */
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
856
    stats_event_args (NULL, "stats", "%d", _stats_threads);
857
858
    thread_mutex_unlock(&_stats_mutex);

859
    thread_mutex_create (&(listener.mutex));
860

861
    _register_listener (&listener);
862
863

    while (_stats_running) {
864
865
866
        thread_mutex_lock (&listener.mutex);
        event = _get_event_from_queue (&listener.queue);
        thread_mutex_unlock (&listener.mutex);
867
        if (event != NULL) {
868
            if (_send_event_to_client(event, client) < 0) {
869
870
871
872
873
874
                _free_event(event);
                break;
            }
            _free_event(event);
            continue;
        }
875
        thread_sleep (500000);
876
877
878
    }

    thread_mutex_lock(&_stats_mutex);
879
    _unregister_listener (&listener);
880
    _stats_threads--;
881
    stats_event_args (NULL, "stats", "%d", _stats_threads);
882
883
    thread_mutex_unlock(&_stats_mutex);

884
    thread_mutex_destroy (&listener.mutex);
885
    client_destroy (client);
886
887
    INFO0 ("stats client finished");

888
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
889
890
}

891
892
893
894
895
896
897
898
899
900
901
902
903

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
904
typedef struct _source_xml_tag {
905
906
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
907

908
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
909
910
911
} source_xml_t;


912
void stats_transform_xslt(client_t *client, const char *uri)
913
914
{
    xmlDocPtr doc;
915
    char *xslpath = util_get_path_from_normalised_uri (uri);
916
    const char *mount = httpp_get_query_param (client->parser, "mount");
917

918
    doc = stats_get_xml (0, mount);
919

920
    xslt_transform(doc, xslpath, client);
921
922

    xmlFreeDoc(doc);
923
    free (xslpath);
924
925
}

926
xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount)
Jack Moffitt's avatar
Jack Moffitt committed
927
{
928
    xmlDocPtr doc;
929
    xmlNodePtr node;
930

931
932
    doc = xmlNewDoc (XMLSTR("1.0"));
    node = xmlNewDocNode (doc, NULL, XMLSTR("icestats"), NULL);
933
934
    xmlDocSetRootElement(doc, node);

935
    node = _dump_stats_to_doc (node, show_mount, show_hidden);
936

937
    return doc;
Jack Moffitt's avatar
Jack Moffitt committed
938
939
}

940

Jack Moffitt's avatar
Jack Moffitt committed
941
942
static int _compare_stats(void *arg, void *a, void *b)
{
943
944
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
945

946
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
947
948
949
950
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
951
952
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
953

954
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
955
956
957
958
}

static int _free_stats(void *key)
{
959
960
961
962
963
964
    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
965
966
967
968
}

static int _free_source_stats(void *key)
{
969
970
971
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
972
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
973

974
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
975
976
977
978
}

static void _free_event(stats_event_t *event)
{
979
980
981
982
    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
983
}
984
985


986
refbuf_t *stats_get_streams (void)
987
{
988
#define STREAMLIST_BLKSIZE  4096
989
    avl_node *node;
990
    unsigned int remaining = STREAMLIST_BLKSIZE;
991
992
    refbuf_t *start = refbuf_new (remaining), *cur = start;
    char *buffer = cur->data;
993
994
995
996
997
998
999
1000

    /* 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;
For faster browsing, not all history is shown. View entire blame