Project

General

Profile

UPPDATE: Error /api/idnode/load -- 400 » idnode.c

bruce Herrington, 2013-11-17 19:39

 
1
/*
2
 *  Tvheadend - idnode (class) system
3
 *
4
 *  Copyright (C) 2013 Andreas Öman
5
 *
6
 *  This program is free software: you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation, either version 3 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program is distributed in the hope that it will be useful,
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19

    
20
#define _GNU_SOURCE
21
#include <sys/types.h>
22
#include <sys/stat.h>
23
#include <fcntl.h>
24
#include <string.h>
25
#include <unistd.h>
26
#include <stdlib.h>
27

    
28
#include "idnode.h"
29
#include "notify.h"
30
#include "settings.h"
31

    
32
typedef struct idclass_link
33
{
34
  const idclass_t        *idc;
35
  RB_ENTRY(idclass_link) link;
36
} idclass_link_t;
37

    
38
static int                    randfd = 0;
39
static RB_HEAD(,idnode)       idnodes;
40
static RB_HEAD(,idclass_link) idclasses;
41
static pthread_cond_t         idnode_cond;
42
static pthread_mutex_t        idnode_mutex;
43
static htsmsg_t              *idnode_queue;
44
static void*                  idnode_thread(void* p);
45

    
46
static void
47
idclass_register(const idclass_t *idc);
48

    
49
/* **************************************************************************
50
 * Utilities
51
 * *************************************************************************/
52

    
53
/**
54
 *
55
 */
56
static int
57
hexnibble(char c)
58
{
59
  switch(c) {
60
  case '0' ... '9':    return c - '0';
61
  case 'a' ... 'f':    return c - 'a' + 10;
62
  case 'A' ... 'F':    return c - 'A' + 10;
63
  default:
64
    return -1;
65
  }
66
}
67

    
68

    
69
/**
70
 *
71
 */
72
static int
73
hex2bin(uint8_t *buf, size_t buflen, const char *str)
74
{
75
  int hi, lo;
76

    
77
  while(*str) {
78
    if(buflen == 0)
79
      return -1;
80
    if((hi = hexnibble(*str++)) == -1)
81
      return -1;
82
    if((lo = hexnibble(*str++)) == -1)
83
      return -1;
84

    
85
    *buf++ = hi << 4 | lo;
86
    buflen--;
87
  }
88
  return 0;
89
}
90

    
91
/**
92
 *
93
 */
94
static void
95
bin2hex(char *dst, size_t dstlen, const uint8_t *src, size_t srclen)
96
{
97
  while(dstlen > 2 && srclen > 0) {
98
    *dst++ = "0123456789abcdef"[*src >> 4];
99
    *dst++ = "0123456789abcdef"[*src & 0xf];
100
    src++;
101
    srclen--;
102
    dstlen -= 2;
103
  }
104
  *dst = 0;
105
}
106

    
107
/**
108
 *
109
 */
110
static int
111
in_cmp(const idnode_t *a, const idnode_t *b)
112
{
113
  return memcmp(a->in_uuid, b->in_uuid, sizeof(a->in_uuid));
114
}
115

    
116
/* **************************************************************************
117
 * Registration
118
 * *************************************************************************/
119

    
120
/**
121
 *
122
 */
123
void
124
idnode_init(void)
125
{
126
  pthread_t tid;
127

    
128
  randfd = open("/dev/urandom", O_RDONLY);
129
  if(randfd == -1)
130
    exit(1);
131
  
132
  idnode_queue = NULL;
133
  pthread_mutex_init(&idnode_mutex, NULL);
134
  pthread_cond_init(&idnode_cond, NULL);
135
  tvhthread_create(&tid, NULL, idnode_thread, NULL, 1);
136
}
137

    
138
/**
139
 *
140
 */
141
int
142
idnode_insert(idnode_t *in, const char *uuid, const idclass_t *class)
143
{
144
  idnode_t *c;
145
  lock_assert(&global_lock);
146
  if(uuid == NULL) {
147
    if(read(randfd, in->in_uuid, sizeof(in->in_uuid)) != sizeof(in->in_uuid)) {
148
      perror("read(random for uuid)");
149
      exit(1);
150
    }
151
  } else {
152
    if(hex2bin(in->in_uuid, sizeof(in->in_uuid), uuid))
153
      return -1;
154
  }
155

    
156
  in->in_class = class;
157

    
158
  c = RB_INSERT_SORTED(&idnodes, in, in_link, in_cmp);
159
  if(c != NULL) {
160
    fprintf(stderr, "Id node collision\n");
161
    abort();
162
  }
163
  tvhtrace("idnode", "insert node %s", idnode_uuid_as_str(in));
164

    
165
  /* Register the class */
166
  idclass_register(class); // Note: we never actually unregister
167

    
168
  /* Fire event */
169
  idnode_notify(in, NULL, 0, 1);
170

    
171
  return 0;
172
}
173

    
174
/**
175
 *
176
 */
177
void
178
idnode_unlink(idnode_t *in)
179
{
180
  lock_assert(&global_lock);
181
  RB_REMOVE(&idnodes, in, in_link);
182
  tvhtrace("idnode", "unlink node %s", idnode_uuid_as_str(in));
183
  idnode_notify(in, NULL, 0, 1);
184
}
185

    
186
/**
187
 *
188
 */
189
void
190
idnode_delete(idnode_t *in)
191
{
192
  lock_assert(&global_lock);
193
  const idclass_t *idc = in->in_class;
194
  while (idc) {
195
    if (idc->ic_delete) {
196
      idc->ic_delete(in);
197
      break;
198
    }
199
    idc = idc->ic_super;
200
  }
201
}
202

    
203
/* **************************************************************************
204
 * Info
205
 * *************************************************************************/
206

    
207
uint32_t
208
idnode_get_short_uuid (const idnode_t *in)
209
{
210
  uint32_t u32;
211
  memcpy(&u32, in->in_uuid, sizeof(u32));
212
  return u32 & 0x7FFFFFFF; // compat needs to be +ve signed
213
}
214

    
215
/**
216
 *
217
 */
218
const char *
219
idnode_uuid_as_str0(const idnode_t *in, char *b)
220
{
221
  bin2hex(b, UUID_STR_LEN, in->in_uuid, sizeof(in->in_uuid));
222
  return b;
223
}
224
const char *
225
idnode_uuid_as_str(const idnode_t *in)
226
{
227
  static char ret[16][UUID_STR_LEN];
228
  static int p = 0;
229
  char *b = ret[p];
230
  p = (p + 1) % 16;
231
  return idnode_uuid_as_str0(in, b);
232
}
233

    
234
/**
235
 *
236
 */
237
const char *
238
idnode_get_title(idnode_t *in)
239
{
240
  const idclass_t *ic = in->in_class;
241
  for(; ic != NULL; ic = ic->ic_super) {
242
    if(ic->ic_get_title != NULL)
243
      return ic->ic_get_title(in);
244
  }
245
  return idnode_uuid_as_str(in);
246
}
247

    
248

    
249
/**
250
 *
251
 */
252
idnode_set_t *
253
idnode_get_childs(idnode_t *in)
254
{
255
  if(in == NULL)
256
    return NULL;
257

    
258
  const idclass_t *ic = in->in_class;
259
  for(; ic != NULL; ic = ic->ic_super) {
260
    if(ic->ic_get_childs != NULL)
261
      return ic->ic_get_childs(in);
262
  }
263
  return NULL;
264
}
265

    
266
/**
267
 *
268
 */
269
int
270
idnode_is_leaf(idnode_t *in)
271
{
272
  const idclass_t *ic = in->in_class;
273
  for(; ic != NULL; ic = ic->ic_super) {
274
    if(ic->ic_get_childs != NULL)
275
      return 0;
276
  }
277
  return 1;
278
}
279

    
280
int
281
idnode_is_instance(idnode_t *in, const idclass_t *idc)
282
{
283
  const idclass_t *ic = in->in_class;
284
  for(; ic != NULL; ic = ic->ic_super) {
285
    if (ic == idc) return 1;
286
  }
287
  return 0;
288
}
289

    
290
/* **************************************************************************
291
 * Properties
292
 * *************************************************************************/
293

    
294
static const property_t *
295
idnode_find_prop
296
  ( idnode_t *self, const char *key )
297
{
298
  const idclass_t *idc = self->in_class;
299
  const property_t *p;
300
  while (idc) {
301
    if ((p = prop_find(idc->ic_properties, key))) return p;
302
    idc = idc->ic_super;
303
  }
304
  return NULL;
305
}
306

    
307
/*
308
 * Get display value
309
 */
310
static char *
311
idnode_get_display
312
  ( idnode_t *self, const property_t *p )
313
{
314
  if (p->rend)
315
    return p->rend(self);
316
  if (p->islist) {
317
    htsmsg_t *l = (htsmsg_t*)p->get(self);
318
    if (l)
319
      return htsmsg_list_2_csv(l);
320
  }
321
  return NULL;
322
}
323

    
324
/*
325
 * Get field as string
326
 */
327
const char *
328
idnode_get_str
329
  ( idnode_t *self, const char *key )
330
{
331
  const property_t *p = idnode_find_prop(self, key);
332
  if (p && p->type == PT_STR) {
333
    const void *ptr;
334
    if (p->get)
335
      ptr = p->get(self);
336
    else
337
      ptr = ((void*)self) + p->off;
338
    return *(const char**)ptr;
339
  }
340

    
341
  return NULL;
342
}
343

    
344
/*
345
 * Get field as unsigned int
346
 */
347
int
348
idnode_get_u32
349
  ( idnode_t *self, const char *key, uint32_t *u32 )
350
{
351
  const property_t *p = idnode_find_prop(self, key);
352
  if (p->islist) return 1;
353
  if (p) {
354
    const void *ptr;
355
    if (p->get)
356
      ptr = p->get(self);
357
    else
358
      ptr = ((void*)self) + p->off;
359
    switch (p->type) {
360
      case PT_INT:
361
      case PT_BOOL:
362
        *u32 = *(int*)ptr;
363
        return 0;
364
      case PT_U16:
365
        *u32 = *(uint32_t*)ptr;
366
        return 0;
367
      case PT_U32:
368
        *u32 = *(uint16_t*)ptr;
369
        return 0;
370
      default:
371
        break;
372
    }
373
  }
374
  return 1;
375
}
376

    
377
/*
378
 * Get field as BOOL
379
 */
380
int
381
idnode_get_bool
382
  ( idnode_t *self, const char *key, int *b )
383
{
384
  const property_t *p = idnode_find_prop(self, key);
385
  if (p->islist) return 1;
386
  if (p) {
387
    void *ptr = self;
388
    ptr += p->off;
389
    switch (p->type) {
390
      case PT_BOOL:
391
        *b = *(int*)ptr;
392
        return 0;
393
      default:
394
        break;
395
    }
396
  }
397
  return 1; 
398
}
399

    
400
/* **************************************************************************
401
 * Lookup
402
 * *************************************************************************/
403

    
404
/**
405
 *
406
 */
407
void *
408
idnode_find(const char *uuid, const idclass_t *idc)
409
{
410
  idnode_t skel, *r;
411

    
412
  tvhtrace("idnode", "find node %s class %s", uuid, idc ? idc->ic_class : NULL);
413
  if(hex2bin(skel.in_uuid, sizeof(skel.in_uuid), uuid))
414
    return NULL;
415
  r = RB_FIND(&idnodes, &skel, in_link, in_cmp);
416
  if(r != NULL && idc != NULL) {
417
    const idclass_t *c = r->in_class;
418
    for(;c != NULL; c = c->ic_super) {
419
      if(idc == c)
420
        return r;
421
    }
422
    return NULL;
423
  }
424
  return r;
425
}
426

    
427
idnode_set_t *
428
idnode_find_all ( const idclass_t *idc )
429
{
430
  idnode_t *in;
431
  const idclass_t *ic;
432
  tvhtrace("idnode", "find class %s", idc->ic_class);
433
  idnode_set_t *is = calloc(1, sizeof(idnode_set_t));
434
  RB_FOREACH(in, &idnodes, in_link) {
435
    ic = in->in_class;
436
    while (ic) {
437
      if (ic == idc) {
438
        tvhtrace("idnode", "  add node %s", idnode_uuid_as_str(in));
439
        idnode_set_add(is, in, NULL);
440
        break;
441
      }
442
      ic = ic->ic_super;
443
    }
444
  }
445
  return is;
446
}
447

    
448
/* **************************************************************************
449
 * Set processing
450
 * *************************************************************************/
451

    
452
static int
453
idnode_cmp_title
454
  ( const void *a, const void *b )
455
{
456
  idnode_t      *ina  = *(idnode_t**)a;
457
  idnode_t      *inb  = *(idnode_t**)b;
458
  const char *sa = idnode_get_title(ina);
459
  const char *sb = idnode_get_title(inb);
460
  return strcmp(sa ?: "", sb ?: "");
461
}
462

    
463
static int
464
idnode_cmp_sort
465
  ( const void *a, const void *b, void *s )
466
{
467
  idnode_t      *ina  = *(idnode_t**)a;
468
  idnode_t      *inb  = *(idnode_t**)b;
469
  idnode_sort_t *sort = s;
470
  const property_t *p = idnode_find_prop(ina, sort->key);
471
  if (!p) return 0;
472

    
473
  /* Get display string */
474
  if (p->islist || p->list) {
475
    int r;
476
    char *stra = idnode_get_display(ina, p);
477
    char *strb = idnode_get_display(inb, p);
478
    if (sort->dir == IS_ASC)
479
      r = strcmp(stra ?: "", strb ?: "");
480
    else
481
      r = strcmp(strb ?: "", stra ?: "");
482
    free(stra);
483
    free(strb);
484
    return r;
485
  }
486

    
487
  switch (p->type) {
488
    case PT_STR:
489
      {
490
        int r;
491
        const char *stra = strdupa(idnode_get_str(ina, sort->key) ?: "");
492
        const char *strb = idnode_get_str(inb, sort->key);
493
        if (sort->dir == IS_ASC)
494
          r = strcmp(stra ?: "", strb ?: "");
495
        else
496
          r = strcmp(strb ?: "", stra ?: "");
497
        return r;
498
      }
499
      break;
500
    case PT_INT:
501
    case PT_U16:
502
    case PT_U32:
503
    case PT_BOOL:
504
      {
505
        uint32_t u32a = 0, u32b = 0;
506
        idnode_get_u32(ina, sort->key, &u32a);
507
        idnode_get_u32(inb, sort->key, &u32b);
508
        if (sort->dir == IS_ASC)
509
          return u32a - u32b;
510
        else
511
          return u32b - u32a;
512
      }
513
      break;
514
    case PT_DBL:
515
      // TODO
516
      break;
517
  }
518
  return 0;
519
}
520

    
521
int
522
idnode_filter
523
  ( idnode_t *in, idnode_filter_t *filter )
524
{
525
  idnode_filter_ele_t *f;
526
  
527
  LIST_FOREACH(f, filter, link) {
528
    if (f->type == IF_STR) {
529
      const char *str;
530
      str = idnode_get_display(in, idnode_find_prop(in, f->key));
531
      if (!str)
532
        if (!(str = idnode_get_str(in, f->key)))
533
          return 1;
534
      switch(f->comp) {
535
        case IC_IN:
536
          if (strstr(str, f->u.s) == NULL)
537
            return 1;
538
          break;
539
        case IC_EQ:
540
          if (strcmp(str, f->u.s) != 0)
541
            return 1;
542
        case IC_LT:
543
          if (strcmp(str, f->u.s) > 0)
544
            return 1;
545
          break;
546
        case IC_GT:
547
          if (strcmp(str, f->u.s) < 0)
548
            return 1;
549
          break;
550
        case IC_RE:
551
          if (regexec(&f->u.re, str, 0, NULL, 0))
552
            return 1;
553
          break;
554
      }
555
    } else if (f->type == IF_NUM || f->type == IF_BOOL) {
556
      uint32_t u32;
557
      int64_t a, b;
558
      if (idnode_get_u32(in, f->key, &u32))
559
        return 1;
560
      a = u32;
561
      b = (f->type == IF_NUM) ? f->u.n : f->u.b;
562
      switch (f->comp) {
563
        case IC_IN:
564
        case IC_RE:
565
          break; // Note: invalid
566
        case IC_EQ:
567
          if (a != b)
568
            return 1;
569
          break;
570
        case IC_LT:
571
          if (a > b)
572
            return 1;
573
          break;
574
        case IC_GT:
575
          if (a < b)
576
            return 1;
577
          break;
578
      }
579
    }
580
  }
581

    
582
  return 0;
583
}
584

    
585
void
586
idnode_filter_add_str
587
  ( idnode_filter_t *filt, const char *key, const char *val, int comp )
588
{
589
  idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t));
590
  ele->key  = strdup(key);
591
  ele->type = IF_STR;
592
  ele->comp = comp;
593
  if (comp == IC_RE) {
594
    if (regcomp(&ele->u.re, val, REG_ICASE | REG_EXTENDED | REG_NOSUB)) {
595
      free(ele);
596
      return;
597
    }
598
  } else
599
    ele->u.s  = strdup(val);
600
  LIST_INSERT_HEAD(filt, ele, link);
601
}
602

    
603
void
604
idnode_filter_add_num
605
  ( idnode_filter_t *filt, const char *key, int64_t val, int comp )
606
{
607
  idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t));
608
  ele->key  = strdup(key);
609
  ele->type = IF_NUM;
610
  ele->comp = comp;
611
  ele->u.n  = val;
612
  LIST_INSERT_HEAD(filt, ele, link);
613
}
614

    
615
void
616
idnode_filter_add_bool
617
  ( idnode_filter_t *filt, const char *key, int val, int comp )
618
{
619
  idnode_filter_ele_t *ele = calloc(1, sizeof(idnode_filter_ele_t));
620
  ele->key  = strdup(key);
621
  ele->type = IF_BOOL;
622
  ele->comp = comp;
623
  ele->u.b  = val;
624
  LIST_INSERT_HEAD(filt, ele, link);
625
}
626

    
627
void
628
idnode_filter_clear
629
  ( idnode_filter_t *filt )
630
{
631
  idnode_filter_ele_t *ele;
632
  while ((ele = LIST_FIRST(filt))) {
633
    LIST_REMOVE(ele, link);
634
    if (ele->type == IF_STR) {
635
      if (ele->comp == IC_RE)
636
        regfree(&ele->u.re);
637
      else
638
        free(ele->u.s);
639
    }
640
    free(ele);
641
  }
642
}
643

    
644
void
645
idnode_set_add
646
  ( idnode_set_t *is, idnode_t *in, idnode_filter_t *filt )
647
{
648
  if (filt && idnode_filter(in, filt))
649
    return;
650
  
651
  /* Allocate more space */
652
  if (is->is_alloc == is->is_count) {
653
    is->is_alloc = MAX(100, is->is_alloc * 2);
654
    is->is_array = realloc(is->is_array, is->is_alloc * sizeof(idnode_t*));
655
  }
656
  is->is_array[is->is_count++] = in;
657
}
658

    
659
void
660
idnode_set_sort
661
  ( idnode_set_t *is, idnode_sort_t *sort )
662
{
663
  qsort_r(is->is_array, is->is_count, sizeof(idnode_t*), idnode_cmp_sort, sort);
664
}
665

    
666
void
667
idnode_set_sort_by_title
668
  ( idnode_set_t *is )
669
{
670
  qsort(is->is_array, is->is_count, sizeof(idnode_t*), idnode_cmp_title);
671
}
672

    
673
void
674
idnode_set_free ( idnode_set_t *is )
675
{
676
  free(is->is_array);
677
  free(is);
678
}
679

    
680
/* **************************************************************************
681
 * Write
682
 * *************************************************************************/
683

    
684
static int
685
idnode_class_write_values
686
  ( idnode_t *self, const idclass_t *idc, htsmsg_t *c, int optmask )
687
{
688
  int save = 0;
689
  if (idc->ic_super)
690
    save |= idnode_class_write_values(self, idc->ic_super, c, optmask);
691
  save |= prop_write_values(self, idc->ic_properties, c, optmask, NULL);
692
  return save;
693
}
694

    
695
static void
696
idnode_savefn ( idnode_t *self )
697
{
698
  const idclass_t *idc = self->in_class;
699
  while (idc) {
700
    if (idc->ic_save) {
701
      idc->ic_save(self);
702
      break;
703
    }
704
    idc = idc->ic_super;
705
  }
706
}
707

    
708
int
709
idnode_write0 ( idnode_t *self, htsmsg_t *c, int optmask, int dosave )
710
{
711
  int save = 0;
712
  const idclass_t *idc = self->in_class;
713
  save = idnode_class_write_values(self, idc, c, optmask);
714
  if (save && dosave)
715
    idnode_savefn(self);
716
  if (dosave)
717
    idnode_notify(self, NULL, 0, 0);
718
  // Note: always output event if "dosave", reason is that UI updates on
719
  //       these, but there are some subtle cases where it will expect
720
  //       an update and not get one. This include fields being set for
721
  //       which there is user-configurable value and auto fallback so
722
  //       the UI state might not atually reflect the user config
723
  return save;
724
}
725

    
726
/* **************************************************************************
727
 * Read
728
 * *************************************************************************/
729

    
730
/*
731
 * Save
732
 */
733
void
734
idnode_read0 ( idnode_t *self, htsmsg_t *c, int optmask )
735
{
736
  const idclass_t *idc = self->in_class;
737
  for (; idc; idc = idc->ic_super)
738
    prop_read_values(self, idc->ic_properties, c, optmask, NULL);
739
}
740

    
741
/**
742
 * Recursive to get superclass nodes first
743
 */
744
static void
745
add_params
746
  (struct idnode *self, const idclass_t *ic, htsmsg_t *p, int optmask, htsmsg_t *inc)
747
{
748
  /* Parent first */
749
  if(ic->ic_super != NULL)
750
    add_params(self, ic->ic_super, p, optmask, inc);
751

    
752
  /* Seperator (if not empty) */
753
#if 0
754
  if(TAILQ_FIRST(&p->hm_fields) != NULL) {
755
    htsmsg_t *m = htsmsg_create_map();
756
    htsmsg_add_str(m, "caption",  ic->ic_caption ?: ic->ic_class);
757
    htsmsg_add_str(m, "type",     "separator");
758
    htsmsg_add_msg(p, NULL, m);
759
  }
760
#endif
761

    
762
  /* Properties */
763
  prop_serialize(self, ic->ic_properties, p, optmask, inc);
764
}
765

    
766
static htsmsg_t *
767
idnode_params (const idclass_t *idc, idnode_t *self, int optmask)
768
{
769
  htsmsg_t *p  = htsmsg_create_list();
770
  add_params(self, idc, p, optmask, NULL);
771
  return p;
772
}
773

    
774
static const char *
775
idclass_get_caption (const idclass_t *idc )
776
{
777
  while (idc) {
778
    if (idc->ic_caption)
779
      return idc->ic_caption;
780
    idc = idc->ic_super;
781
  }
782
  return NULL;
783
}
784

    
785
static const char *
786
idclass_get_class (const idclass_t *idc)
787
{
788
  while (idc) {
789
    if (idc->ic_class)
790
      return idc->ic_class;
791
    idc = idc->ic_super;
792
  }
793
  return NULL;
794
}
795

    
796
static int
797
ic_cmp ( const idclass_link_t *a, const idclass_link_t *b )
798
{
799
  assert(a->idc->ic_class);
800
  assert(b->idc->ic_class);
801
  return strcmp(a->idc->ic_class, b->idc->ic_class);
802
}
803

    
804
static void
805
idclass_register(const idclass_t *idc)
806
{
807
  static idclass_link_t *skel = NULL;
808
  while (idc) {
809
    if (!skel)
810
      skel = calloc(1, sizeof(idclass_link_t));
811
    skel->idc = idc;
812
    if (RB_INSERT_SORTED(&idclasses, skel, link, ic_cmp))
813
      break;
814
    tvhtrace("idnode", "register class %s", idc->ic_class);
815
    skel = NULL;
816
    idc = idc->ic_super;
817
  }
818
}
819

    
820
const idclass_t *
821
idclass_find ( const char *class )
822
{
823
  idclass_link_t *t, skel;
824
  idclass_t idc;
825
  skel.idc = &idc;
826
  idc.ic_class = class;
827
  tvhtrace("idnode", "find class %s", class);
828
  t = RB_FIND(&idclasses, &skel, link, ic_cmp);
829
  return t ? t->idc : NULL;
830
}
831

    
832
/*
833
 * Just get the class definition
834
 */
835
htsmsg_t *
836
idclass_serialize0(const idclass_t *idc, int optmask)
837
{
838
  const char *s;
839
  htsmsg_t *p, *m = htsmsg_create_map();
840

    
841
  /* Caption and name */
842
  if ((s = idclass_get_caption(idc)))
843
    htsmsg_add_str(m, "caption", s);
844
  if ((s = idclass_get_class(idc)))
845
    htsmsg_add_str(m, "class", s);
846

    
847
  /* Props */
848
  if ((p = idnode_params(idc, NULL, optmask)))
849
    htsmsg_add_msg(m, "props", p);
850
  
851
  return m;
852
}
853

    
854
/**
855
 *
856
 */
857
htsmsg_t *
858
idnode_serialize0(idnode_t *self, int optmask)
859
{
860
  const idclass_t *idc = self->in_class;
861
  const char *uuid, *s;
862

    
863
  htsmsg_t *m = htsmsg_create_map();
864
  uuid = idnode_uuid_as_str(self);
865
  htsmsg_add_str(m, "uuid", uuid);
866
  htsmsg_add_str(m, "id",   uuid);
867
  htsmsg_add_str(m, "text", idnode_get_title(self) ?: "");
868
  if ((s = idclass_get_caption(idc)))
869
    htsmsg_add_str(m, "caption", s);
870
  if ((s = idclass_get_class(idc)))
871
    htsmsg_add_str(m, "class", s);
872

    
873
  htsmsg_add_msg(m, "params", idnode_params(idc, self, optmask));
874

    
875
  return m;
876
}
877

    
878
/* **************************************************************************
879
 * Notifcation
880
 * *************************************************************************/
881

    
882
/**
883
 * Update internal event pipes
884
 */
885
static void
886
idnode_notify_event ( idnode_t *in )
887
{
888
  const idclass_t *ic = in->in_class;
889
  const char *uuid = idnode_uuid_as_str(in);
890
  while (ic) {
891
    if (ic->ic_event) {
892
      htsmsg_t *m = htsmsg_create_map();
893
      htsmsg_add_str(m, "uuid", uuid);
894
      notify_by_msg(ic->ic_event, m);
895
    }
896
    ic = ic->ic_super;
897
  }
898
}
899

    
900
/**
901
 * Notify on a given channel
902
 */
903
void
904
idnode_notify
905
  (idnode_t *in, const char *chn, int force, int event)
906
{
907
  const char *uuid = idnode_uuid_as_str(in);
908

    
909
  /* Forced */
910
  if (chn || force) {
911
    htsmsg_t *m = htsmsg_create_map();
912
    htsmsg_add_str(m, "uuid", uuid);
913
    notify_by_msg(chn ?: "idnodeUpdated", m);
914
  
915
  /* Rate-limited */
916
  } else {
917
    pthread_mutex_lock(&idnode_mutex);
918
    if (!idnode_queue)
919
      idnode_queue = htsmsg_create_map();
920
    htsmsg_set_u32(idnode_queue, uuid, 1);
921
    pthread_cond_signal(&idnode_cond);
922
    pthread_mutex_unlock(&idnode_mutex);
923
  }
924

    
925
  /* Send event */
926
  if (event)
927
    idnode_notify_event(in);
928
}
929

    
930
void
931
idnode_notify_simple (void *in)
932
{
933
  idnode_notify(in, NULL, 0, 0);
934
}
935

    
936
void
937
idnode_notify_title_changed (void *in)
938
{
939
  htsmsg_t *m = htsmsg_create_map();
940
  htsmsg_add_str(m, "uuid", idnode_uuid_as_str(in));
941
  htsmsg_add_str(m, "text", idnode_get_title(in));
942
  notify_by_msg("idnodeUpdated", m);
943
  idnode_notify_event(in);
944
}
945

    
946
/*
947
 * Thread for handling notifications
948
 */
949
void*
950
idnode_thread ( void *p )
951
{
952
  idnode_t *node;
953
  htsmsg_t *m, *q = NULL;
954
  htsmsg_field_t *f;
955

    
956
  pthread_mutex_lock(&idnode_mutex);
957

    
958
  while (1) {
959

    
960
    /* Get queue */
961
    if (!idnode_queue) {
962
      pthread_cond_wait(&idnode_cond, &idnode_mutex);
963
      continue;
964
    }
965
    q            = idnode_queue;
966
    idnode_queue = NULL;
967
    pthread_mutex_unlock(&idnode_mutex);
968

    
969
    /* Process */
970
    pthread_mutex_lock(&global_lock);
971

    
972
    HTSMSG_FOREACH(f, q) {
973
      node = idnode_find(f->hmf_name, NULL);
974
      m    = htsmsg_create_map();
975
      htsmsg_add_str(m, "uuid", f->hmf_name);
976
      if (node)
977
        notify_by_msg("idnodeUpdated", m);
978
      else
979
        notify_by_msg("idnodeDeleted", m);      
980
    }
981
    
982
    /* Finished */
983
    pthread_mutex_unlock(&global_lock);
984
    htsmsg_destroy(q);
985
    q = NULL;
986

    
987
    /* Wait */
988
    usleep(500000);
989
    pthread_mutex_lock(&idnode_mutex);
990
  }
991
  if (q) htsmsg_destroy(q);
992
  
993
  return NULL;
994
}
995

    
996
/******************************************************************************
997
 * Editor Configuration
998
 *
999
 * vim:sts=2:ts=2:sw=2:et
1000
 *****************************************************************************/
(2-2/5)