stats.c 23.4 KB
Newer Older
1
2
3
4
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
5
6
7
8
9
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

Michael Smith's avatar
Michael Smith committed
10
11
12
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
Jack Moffitt's avatar
Jack Moffitt committed
13
14
15
16
17
18
19
20
21
22
23
24

#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"
25
#include "xslt.h"
Jack Moffitt's avatar
Jack Moffitt committed
26

27
#ifdef _WIN32
28
#define vsnprintf _vsnprintf
29
#endif
Jack Moffitt's avatar
Jack Moffitt committed
30
31
32

typedef struct _event_listener_tag
{
33
34
    stats_event_t **queue;
    mutex_t *mutex;
Jack Moffitt's avatar
Jack Moffitt committed
35

36
    struct _event_listener_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
37
38
} event_listener_t;

Michael Smith's avatar
Michael Smith committed
39
int _stats_running = 0;
40
thread_type *_stats_thread_id;
41
int _stats_threads = 0;
Jack Moffitt's avatar
Jack Moffitt committed
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

stats_t _stats;
mutex_t _stats_mutex;

stats_event_t *_global_event_queue;
mutex_t _global_event_mutex;

cond_t _event_signal_cond;

event_listener_t *_event_listeners;



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

void stats_initialize()
{
67
    _event_listeners = NULL;
68

69
70
71
    /* 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
72

73
74
    /* set up global mutex */
    thread_mutex_create(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
75

76
77
    /* set up event signaler */
    thread_cond_create(&_event_signal_cond);
Jack Moffitt's avatar
Jack Moffitt committed
78

79
80
81
    /* set up stats queues */
    _global_event_queue = NULL;
    thread_mutex_create(&_global_event_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
82

83
84
85
    /* 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
86
87
88
89
}

void stats_shutdown()
{
90
    int n;
91
    stats_event_t *event, *next;
Jack Moffitt's avatar
Jack Moffitt committed
92

93
94
95
    if(!_stats_running) /* We can't shutdown if we're not running. */
        return;

96
97
98
    /* wait for thread to exit */
    _stats_running = 0;
    thread_join(_stats_thread_id);
Jack Moffitt's avatar
Jack Moffitt committed
99

100
101
102
103
104
105
106
    /* 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);
107

108
    /* free the queues */
Jack Moffitt's avatar
Jack Moffitt committed
109

110
111
    /* destroy the queue mutexes */
    thread_mutex_destroy(&_global_event_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
112

113
114
115
116
117
    /* tear it all down */
    thread_cond_destroy(&_event_signal_cond);
    thread_mutex_destroy(&_stats_mutex);
    avl_tree_free(_stats.source_tree, _free_source_stats);
    avl_tree_free(_stats.global_tree, _free_stats);
118
119
120
121
122
123
124

    event = _global_event_queue;
    while(event) {
        if(event->source)
            free(event->source);
        if(event->value)
            free(event->value);
125
126
        if(event->name)
            free(event->name);
127
128
129
130
        next = event->next;
        free(event);
        event = next;
    }
Jack Moffitt's avatar
Jack Moffitt committed
131
132
133
134
}

stats_t *stats_get_stats()
{
135
136
137
    /* lock global stats
    
     copy stats
Jack Moffitt's avatar
Jack Moffitt committed
138

139
     unlock global stats
Jack Moffitt's avatar
Jack Moffitt committed
140

141
     return copied stats */
Jack Moffitt's avatar
Jack Moffitt committed
142

143
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
144
145
146
147
}

void stats_event(char *source, char *name, char *value)
{
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    stats_event_t *node;
    stats_event_t *event;

    if (name == NULL || strcmp(name, "") == 0) return;

    /* build event */
    event = (stats_event_t *)malloc(sizeof(stats_event_t));
    event->source = NULL;
    if (source != NULL) event->source = (char *)strdup(source);
    event->name = (char *)strdup(name);
    event->value = NULL;
    event->next = NULL;
    if (value != NULL) event->value = (char *)strdup(value);

    /* queue event */
    thread_mutex_lock(&_global_event_mutex);
    if (_global_event_queue == NULL) {
        _global_event_queue = event;
    } else {
        node = _global_event_queue;
        while (node->next) node = node->next;
        node->next = event;
    }
    thread_mutex_unlock(&_global_event_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
172
173
174
175
}

void stats_event_args(char *source, char *name, char *format, ...)
{
176
177
178
179
180
181
    char buf[1024];
    va_list val;
    
    va_start(val, format);
    vsnprintf(buf, 1024, format, val);
    va_end(val);
Jack Moffitt's avatar
Jack Moffitt committed
182

183
    stats_event(source, name, buf);
Jack Moffitt's avatar
Jack Moffitt committed
184
185
186
187
}

static char *_get_stats(char *source, char *name)
{
188
189
190
    stats_node_t *stats = NULL;
    stats_source_t *src = NULL;
    char *value = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
191

192
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
193

194
195
196
197
198
199
200
201
    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
202

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

205
    thread_mutex_unlock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
206

207
    return value;
Jack Moffitt's avatar
Jack Moffitt committed
208
209
}

210
211
char *stats_get_value(char *source, char *name)
{
212
    return(_get_stats(source, name));
213
}
Jack Moffitt's avatar
Jack Moffitt committed
214
215
void stats_event_inc(char *source, char *name)
{
216
217
218
219
220
221
222
223
224
225
226
    char *old_value;
    int new_value;
    
    old_value = _get_stats(source, name);
    if (old_value != NULL) {
        new_value = atoi(old_value);
        free(old_value);
        new_value++;
    } else {
        new_value = 1;
    }
Jack Moffitt's avatar
Jack Moffitt committed
227

228
    stats_event_args(source, name, "%d", new_value);
Jack Moffitt's avatar
Jack Moffitt committed
229
230
231
232
}

void stats_event_add(char *source, char *name, unsigned long value)
{
233
234
    char *old_value;
    unsigned long new_value;
Jack Moffitt's avatar
Jack Moffitt committed
235

236
237
238
239
240
241
242
243
    old_value = _get_stats(source, name);
    if (old_value != NULL) {
        new_value = atol(old_value);
        free(old_value);
        new_value += value;
    } else {
        new_value = value;
    }
Jack Moffitt's avatar
Jack Moffitt committed
244

245
    stats_event_args(source, name, "%ld", new_value);
Jack Moffitt's avatar
Jack Moffitt committed
246
247
248
249
}

void stats_event_dec(char *source, char *name)
{
250
251
252
253
254
255
256
257
258
259
260
261
    char *old_value;
    int new_value;

    old_value = _get_stats(source, name);
    if (old_value != NULL) {
        new_value = atoi(old_value);
        free(old_value);
        new_value--;
        if (new_value < 0) new_value = 0;
    } else {
        new_value = 0;
    }
Jack Moffitt's avatar
Jack Moffitt committed
262

263
    stats_event_args(source, name, "%d", new_value);
Jack Moffitt's avatar
Jack Moffitt committed
264
265
266
267
268
269
270
}

/* 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)
{
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
    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
291
292
293
294
295
296
297
}

/* 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)
{
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
    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
314

315
316
    /* didn't find it */
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
317
318
319
320
}

static stats_event_t *_copy_event(stats_event_t *event)
{
321
322
323
324
325
326
327
328
329
330
331
    stats_event_t *copy = (stats_event_t *)malloc(sizeof(stats_event_t));
    if (event->source) 
        copy->source = (char *)strdup(event->source);
    else
        copy->source = NULL;
    copy->name = (char *)strdup(event->name);
    if (event->value)
        copy->value = (char *)strdup(event->value);
    else
        copy->value = NULL;
    copy->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
332

333
    return copy;
Jack Moffitt's avatar
Jack Moffitt committed
334
335
336
337
}

static void *_stats_thread(void *arg)
{
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
    stats_event_t *event;
    stats_event_t *copy;
    stats_node_t *node;
    stats_node_t *anode;
    stats_source_t *snode;
    stats_source_t *asnode;
    event_listener_t *listener;
    avl_node *avlnode;

    while (_stats_running) {
        thread_mutex_lock(&_global_event_mutex);
        if (_global_event_queue != NULL) {
            /* grab the next event from the queue */
            event = _global_event_queue;
            _global_event_queue = event->next;
            event->next = NULL;
            thread_mutex_unlock(&_global_event_mutex);

            thread_mutex_lock(&_stats_mutex);
            if (event->source == NULL) {
                /* we have a global event */
                if (event->value != NULL) {
                    /* adding/updating */
                    node = _find_node(_stats.global_tree, event->name);
                    if (node == NULL) {
                        /* add node */
                        anode = (stats_node_t *)malloc(sizeof(stats_node_t));
                        anode->name = (char *)strdup(event->name);
                        anode->value = (char *)strdup(event->value);

                        avl_insert(_stats.global_tree, (void *)anode);
                    } else {
                        /* update node */
                        free(node->value);
                        node->value = (char *)strdup(event->value);
                    }

                } else {
                    /* we're deleting */
                    node = _find_node(_stats.global_tree, event->name);
                    if (node != NULL)
                        avl_delete(_stats.global_tree, (void *)node, _free_stats);
                }
            } else {
                /* we have a source event */

                snode = _find_source(_stats.source_tree, event->source);
                if (snode != NULL) {
                    /* this is a source we already have a tree for */
                    if (event->value != NULL) {
                        /* we're add/updating */
                        node = _find_node(snode->stats_tree, event->name);
                        if (node == NULL) {
                            /* adding node */
                            anode = (stats_node_t *)malloc(sizeof(stats_node_t));
                            anode->name = (char *)strdup(event->name);
                            anode->value = (char *)strdup(event->value);

                            avl_insert(snode->stats_tree, (void *)anode);
                        } else {
                            /* updating node */
                            free(node->value);
                            node->value = (char *)strdup(event->value);
                        }
                    } else {
                        /* we're deleting */
                        node = _find_node(snode->stats_tree, event->name);
                        if (node != NULL) {
                            avl_delete(snode->stats_tree, (void *)node, _free_stats);

                                avlnode = avl_get_first(snode->stats_tree);
                            if (avlnode == NULL) {
                                avl_delete(_stats.source_tree, (void *)snode, _free_source_stats);
                            }
                        }
                    }
                } else {
                    /* this is a new source */
                    asnode = (stats_source_t *)malloc(sizeof(stats_source_t));
                    asnode->source = (char *)strdup(event->source);
                    asnode->stats_tree = avl_tree_new(_compare_stats, NULL);

                    anode = (stats_node_t *)malloc(sizeof(stats_node_t));
                    anode->name = (char *)strdup(event->name);
                    anode->value = (char *)strdup(event->value);
                    
                    avl_insert(asnode->stats_tree, (void *)anode);

                    avl_insert(_stats.source_tree, (void *)asnode);
                }
            }
            
            /* now we have an event that's been processed into the running stats */
            /* this event should get copied to event listeners' queues */
            listener = _event_listeners;
            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;
            }
            thread_cond_broadcast(&_event_signal_cond);

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

            thread_mutex_unlock(&_stats_mutex);
        } else {
            thread_mutex_unlock(&_global_event_mutex);
        }

        thread_sleep(300000);
    }

    /* wake the other threads so they can shut down cleanly */
    thread_cond_broadcast(&_event_signal_cond);

    thread_exit(0);

    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
460
461
462
463
464
}

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

468
469
470
    evli->queue = queue;
    evli->mutex = mutex;
    evli->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
471

472
473
474
475
476
477
478
    if (_event_listeners == NULL) {
        _event_listeners = evli;
    } else {
        node = _event_listeners;
        while (node->next) node = node->next;
        node->next = evli;
    }
Jack Moffitt's avatar
Jack Moffitt committed
479
480
481
482
}

static stats_event_t *_make_event_from_node(stats_node_t *node, char *source)
{
483
484
485
486
487
488
489
490
491
    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);
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
492

493
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
494
495
496
497
}

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

500
501
502
503
504
505
506
    if (*queue == NULL) {
        *queue = event;
    } else {
        node = *queue;
        while (node->next) node = node->next;
        node->next = event;
    }
Jack Moffitt's avatar
Jack Moffitt committed
507
508
509
510
}

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

513
    if (*queue == NULL) return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
514

515
516
517
    event = *queue;
    *queue = (*queue)->next;
    event->next = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
518

519
    return event;
Jack Moffitt's avatar
Jack Moffitt committed
520
521
522
523
}

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

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

529
    return (ret == -1) ? 0 : 1;
Jack Moffitt's avatar
Jack Moffitt committed
530
531
532
533
}

void _dump_stats_to_queue(stats_event_t **queue)
{
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
    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
565
566
}

567
568
569
570
571
572
/* 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
573
{
574
575
576
577
    avl_node *node;
    avl_node *node2;
    stats_event_t *event;
    stats_source_t *source;
Jack Moffitt's avatar
Jack Moffitt committed
578

579
    thread_mutex_lock(&_stats_mutex);
Jack Moffitt's avatar
Jack Moffitt committed
580

581
582
583
584
585
586
587
    /* 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
588

589
590
        node = avl_get_next(node);
    }
Jack Moffitt's avatar
Jack Moffitt committed
591

592
593
594
595
596
597
598
599
600
601
602
603
604
605
    /* 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
606

607
608
    /* now we register to receive future event notices */
    _register_listener(queue, mutex);
Jack Moffitt's avatar
Jack Moffitt committed
609

610
    thread_mutex_unlock(&_stats_mutex);
611
612
613
614
}

void *stats_connection(void *arg)
{
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
    stats_connection_t *statcon = (stats_connection_t *)arg;
    stats_event_t *local_event_queue = NULL;
    mutex_t local_event_mutex;
    stats_event_t *event;

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

    thread_mutex_create(&local_event_mutex);

    _atomic_get_and_register(&local_event_queue, &local_event_mutex);

    while (_stats_running) {
        thread_mutex_lock(&local_event_mutex);
        event = _get_event_from_queue(&local_event_queue);
        if (event != NULL) {
            if (!_send_event_to_client(event, statcon->con)) {
                _free_event(event);
                thread_mutex_unlock(&local_event_mutex);
                break;
            }
            _free_event(event);
        } else {
            thread_mutex_unlock(&local_event_mutex);
            thread_cond_wait(&_event_signal_cond);
            continue;
        }
                   
        thread_mutex_unlock(&local_event_mutex);
    }

    thread_mutex_destroy(&local_event_mutex);

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

    thread_exit(0);
    
    return NULL;
Jack Moffitt's avatar
Jack Moffitt committed
657
658
}

659
660
661
662
663
664
665
/* this function is primarily to support gui code needing to get stats updates
** create a thread with this as the start proc with an arg of the callback function
** on each stats event the callback will be called with the event
** and the callback is responsible for copying the data if it needs it after returning
*/
void *stats_callback(void *arg)
{
666
    void (*callback)(stats_event_t *event);
667

668
669
    stats_event_t *local_event_queue = NULL;
    mutex_t local_event_mutex;
670

671
    stats_event_t *event;
672

673
    callback = arg;
674

675
676
677
    thread_mutex_lock(&_stats_mutex);
    _stats_threads++;
    thread_mutex_unlock(&_stats_mutex);
678

679
    thread_mutex_create(&local_event_mutex);
680

681
    _atomic_get_and_register(&local_event_queue, &local_event_mutex);
682

683
684
685
686
687
688
689
690
691
692
693
694
695
696
    while (_stats_running) {
        thread_mutex_lock(&local_event_mutex);
        event = _get_event_from_queue(&local_event_queue);
        if (event != NULL) {
            callback(event);
            _free_event(event);
        } else {
            thread_mutex_unlock(&local_event_mutex);
            thread_cond_wait(&_event_signal_cond);
            continue;
        }
        
        thread_mutex_unlock(&local_event_mutex);
    }
697

698
    thread_mutex_destroy(&local_event_mutex);
699

700
701
702
    thread_mutex_lock(&_stats_mutex);
    _stats_threads--;
    thread_mutex_unlock(&_stats_mutex);
703

704
    thread_exit(0);
705

706
    return NULL;
707
708
}

Jack Moffitt's avatar
Jack Moffitt committed
709
typedef struct _source_xml_tag {
710
711
    char *mount;
    xmlNodePtr node;
Jack Moffitt's avatar
Jack Moffitt committed
712

713
    struct _source_xml_tag *next;
Jack Moffitt's avatar
Jack Moffitt committed
714
715
716
717
} source_xml_t;

static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root)
{
718
719
720
721
722
723
724
725
726
727
728
729
    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
730

731
    if (found) return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
732

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

735
736
737
738
739
740
    /* 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
741

742
743
744
745
746
747
748
749
    /* 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
750

751
    return node->node;
Jack Moffitt's avatar
Jack Moffitt committed
752
753
}

754
755
756
757
758
759
void stats_transform_xslt(client_t *client, char *xslpath)
{
    xmlDocPtr doc;

    stats_get_xml(&doc);

760
    xslt_transform(doc, xslpath, client);
761
762
763
764
765
766

    xmlFreeDoc(doc);
}

void stats_get_xml(xmlDocPtr *doc)
{
767
768
769
770
771
    stats_event_t *event;
    stats_event_t *queue;
    xmlNodePtr node, srcnode;
    source_xml_t *src_nodes = NULL;
    source_xml_t *next;
772

773
774
    queue = NULL;
    _dump_stats_to_queue(&queue);
775

776
777
778
    *doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(*doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(*doc, node);
779
780


781
782
783
784
785
786
787
788
    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);
        }
789

790
791
792
        _free_event(event);
        event = _get_event_from_queue(&queue);
    }
793

794
795
796
797
798
799
    while (src_nodes) {
        next = src_nodes->next;
        free(src_nodes->mount);
        free(src_nodes);
        src_nodes = next;
    }
800
}
Jack Moffitt's avatar
Jack Moffitt committed
801
802
void stats_sendxml(client_t *client)
{
803
804
805
806
807
808
    int bytes;
    stats_event_t *event;
    stats_event_t *queue;
    xmlDocPtr doc;
    xmlNodePtr node, srcnode;
    int len;
809
    xmlChar *buff = NULL;
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
    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);
    }

834
    xmlDocDumpMemory(doc, &buff, &len);
835
836
837
838
839
840
841
842
843
844
845
846
    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;

    bytes = sock_write_bytes(client->con->sock, buff, len);
    if (bytes > 0) client->con->sent_bytes += bytes;
Jack Moffitt's avatar
Jack Moffitt committed
847
848

 send_error:
849
850
851
852
853
854
855
    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
856
857
858
859
}

static int _compare_stats(void *arg, void *a, void *b)
{
860
861
    stats_node_t *nodea = (stats_node_t *)a;
    stats_node_t *nodeb = (stats_node_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
862

863
    return strcmp(nodea->name, nodeb->name);
Jack Moffitt's avatar
Jack Moffitt committed
864
865
866
867
}

static int _compare_source_stats(void *arg, void *a, void *b)
{
868
869
    stats_source_t *nodea = (stats_source_t *)a;
    stats_source_t *nodeb = (stats_source_t *)b;
Jack Moffitt's avatar
Jack Moffitt committed
870

871
    return strcmp(nodea->source, nodeb->source);
Jack Moffitt's avatar
Jack Moffitt committed
872
873
874
875
}

static int _free_stats(void *key)
{
876
877
878
879
880
881
    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
882
883
884
885
}

static int _free_source_stats(void *key)
{
886
887
888
    stats_source_t *node = (stats_source_t *)key;
    avl_tree_free(node->stats_tree, _free_stats);
    free(node->source);
889
    free(node);
Jack Moffitt's avatar
Jack Moffitt committed
890

891
    return 1;
Jack Moffitt's avatar
Jack Moffitt committed
892
893
894
895
}

static void _free_event(stats_event_t *event)
{
896
897
898
899
    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
900
}