1 /*
2  *  Service Mapper functions
3  *  Copyright (C) 2007 Andreas Öman
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <assert.h>
20 #include <pthread.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 
27 #include "tvheadend.h"
28 #include "channels.h"
29 #include "subscriptions.h"
30 #include "service_mapper.h"
31 #include "streaming.h"
32 #include "service.h"
33 #include "profile.h"
34 #include "bouquet.h"
35 #include "api.h"
36 
37 typedef struct service_mapper_item {
38   TAILQ_ENTRY(service_mapper_item) link;
39   service_t *s;
40   service_mapper_conf_t conf;
41 } service_mapper_item_t;
42 
43 static service_mapper_status_t service_mapper_stat;
44 static tvh_cond_t              service_mapper_cond;
45 static TAILQ_HEAD(, service_mapper_item) service_mapper_queue;
46 service_mapper_t               service_mapper_conf;
47 
48 static void *service_mapper_thread ( void *p );
49 
50 /*
51  * Get status
52  */
53 service_mapper_status_t
service_mapper_status(void)54 service_mapper_status ( void )
55 {
56   return service_mapper_stat;
57 }
58 
59 /*
60  * Start a new mapping
61  */
62 void
service_mapper_start(const service_mapper_conf_t * conf,htsmsg_t * uuids)63 service_mapper_start ( const service_mapper_conf_t *conf, htsmsg_t *uuids )
64 {
65   int e, tr, qd = 0;
66   service_mapper_item_t *smi;
67   service_t *s;
68   char ubuf[UUID_HEX_SIZE];
69 
70   /* Reset stat counters */
71   if (TAILQ_EMPTY(&service_mapper_queue))
72     service_mapper_reset_stats();
73 
74   /* Check each service */
75   TAILQ_FOREACH(s, &service_all, s_all_link) {
76     if (uuids) {
77       htsmsg_field_t *f;
78       const char *str;
79       const char *uuid = idnode_uuid_as_str(&s->s_id, ubuf);
80       HTSMSG_FOREACH(f, uuids) {
81         if (!(str = htsmsg_field_get_str(f))) continue;
82         if (!strcmp(str, uuid)) break;
83       }
84       if (!f) continue;
85     }
86     tvhtrace(LS_SERVICE_MAPPER, "check service %s (%s)",
87              s->s_nicename, idnode_uuid_as_str(&s->s_id, ubuf));
88 
89     /* Already mapped (or in progress) */
90     if (s->s_sm_onqueue) continue;
91     if (LIST_FIRST(&s->s_channels)) continue;
92     tvhtrace(LS_SERVICE_MAPPER, "  not mapped");
93     service_mapper_stat.total++;
94     service_mapper_stat.ignore++;
95 
96     /* Disabled */
97     if (!s->s_is_enabled(s, 0)) continue;
98     tvhtrace(LS_SERVICE_MAPPER, "  enabled");
99 
100     /* Get service info */
101     pthread_mutex_lock(&s->s_stream_mutex);
102     e  = service_is_encrypted(s);
103     tr = service_is_tv(s) || service_is_radio(s);
104     pthread_mutex_unlock(&s->s_stream_mutex);
105 
106     /* Skip non-TV / Radio */
107     if (!tr) continue;
108     tvhtrace(LS_SERVICE_MAPPER, "  radio or tv");
109 
110     /* Skip encrypted */
111     if (!conf->encrypted && e) continue;
112     service_mapper_stat.ignore--;
113 
114     /* Queue */
115     if (conf->check_availability) {
116       tvhtrace(LS_SERVICE_MAPPER, "  queue for checking");
117       qd = 1;
118       smi = malloc(sizeof(*smi));
119       smi->s = s;
120       smi->conf = *conf;
121       TAILQ_INSERT_TAIL(&service_mapper_queue, smi, link);
122       s->s_sm_onqueue = 1;
123 
124     /* Process */
125     } else {
126       tvhtrace(LS_SERVICE_MAPPER, "  process");
127       service_mapper_process(conf, s, NULL);
128     }
129   }
130 
131   /* Notify */
132   api_service_mapper_notify();
133 
134   /* Signal */
135   if (qd) tvh_cond_signal(&service_mapper_cond, 0);
136 }
137 
138 /*
139  * Stop everything
140  */
141 void
service_mapper_stop(void)142 service_mapper_stop ( void )
143 {
144   service_mapper_item_t *smi;
145   while ((smi = TAILQ_FIRST(&service_mapper_queue))) {
146     service_mapper_stat.total--;
147     TAILQ_REMOVE(&service_mapper_queue, smi, link);
148     free(smi);
149   }
150 
151   /* Notify */
152   api_service_mapper_notify();
153 }
154 
155 /*
156  * Remove service
157  */
158 void
service_mapper_remove(service_t * s)159 service_mapper_remove ( service_t *s )
160 {
161   service_mapper_item_t *smi;
162 
163   if (s->s_sm_onqueue) {
164     TAILQ_FOREACH(smi, &service_mapper_queue, link)
165       if (smi->s == s) break;
166     assert(smi);
167     TAILQ_REMOVE(&service_mapper_queue, smi, link);
168     free(smi);
169     s->s_sm_onqueue = 0;
170   }
171 
172   /* Notify */
173   api_service_mapper_notify();
174 }
175 
176 /*
177  * Link service and channel
178  */
179 int
service_mapper_link(service_t * s,channel_t * c,void * origin)180 service_mapper_link ( service_t *s, channel_t *c, void *origin )
181 {
182   idnode_list_mapping_t *ilm;
183 
184   ilm = idnode_list_link(&s->s_id, &s->s_channels,
185                          &c->ch_id, &c->ch_services,
186                          origin, 2);
187   if (ilm) {
188     service_mapped(s);
189     return 1;
190   }
191   return 0;
192 }
193 
194 int
service_mapper_create(idnode_t * s,idnode_t * c,void * origin)195 service_mapper_create ( idnode_t *s, idnode_t *c, void *origin )
196 {
197   return service_mapper_link((service_t *)s, (channel_t *)c, origin);
198 }
199 
200 /*
201  * Process a service
202  */
203 channel_t *
service_mapper_process(const service_mapper_conf_t * conf,service_t * s,bouquet_t * bq)204 service_mapper_process
205   ( const service_mapper_conf_t *conf, service_t *s, bouquet_t *bq )
206 {
207   channel_t *chn = NULL;
208   const char *name, *tagname;
209   htsmsg_field_t *f;
210   htsmsg_t *m;
211 
212   /* Ignore */
213   if (s->s_status == SERVICE_ZOMBIE) {
214     if (!bq)
215       service_mapper_stat.ignore++;
216     goto exit;
217   }
218 
219   /* Safety check (in-case something has been mapped manually in the interim) */
220   if (!bq && LIST_FIRST(&s->s_channels)) {
221     service_mapper_stat.ignore++;
222     goto exit;
223   }
224 
225   /* Find existing channel */
226   name = service_get_channel_name(s);
227   if (!bq && conf->merge_same_name && name && *name)
228     chn = channel_find_by_name(name);
229   if (!chn) {
230     chn = channel_create(NULL, NULL, NULL);
231     chn->ch_bouquet = bq;
232   }
233 
234   /* Map */
235   if (chn) {
236     const char *prov;
237     service_mapper_link(s, chn, chn);
238 
239     /* Type tags */
240     if (conf->type_tags) {
241       if (service_is_uhdtv(s)) {
242         channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
243         channel_tag_map(channel_tag_find_by_name("UHDTV", 1), chn, chn);
244       } else if (service_is_hdtv(s)) {
245         channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
246         channel_tag_map(channel_tag_find_by_name("HDTV", 1), chn, chn);
247       } else if (service_is_sdtv(s)) {
248         channel_tag_map(channel_tag_find_by_name("TV channels", 1), chn, chn);
249         channel_tag_map(channel_tag_find_by_name("SDTV", 1), chn, chn);
250       } else if (service_is_radio(s)) {
251         if (!channel_tag_map(channel_tag_find_by_name("Radio", 0), chn, chn))
252           channel_tag_map(channel_tag_find_by_name("Radio channels", 1), chn, chn);
253       }
254     }
255 
256     /* Custom tags */
257     if (s->s_channel_tags && (m = s->s_channel_tags(s)) != NULL)
258       HTSMSG_FOREACH(f, m)
259         if ((tagname = htsmsg_field_get_str(f)) != NULL)
260           channel_tag_map(channel_tag_find_by_name(tagname, 1), chn, chn);
261 
262     /* Provider */
263     if (conf->provider_tags)
264       if ((prov = s->s_provider_name(s)))
265         channel_tag_map(channel_tag_find_by_name(prov, 1), chn, chn);
266 
267     /* Network */
268     if (conf->network_tags) {
269       source_info_t si;
270       s->s_setsourceinfo(s, &si);
271       channel_tag_map(channel_tag_find_by_name(si.si_network, 1), chn, chn);
272     }
273 
274     /* save */
275     idnode_changed(&chn->ch_id);
276   }
277   if (!bq) {
278     service_mapper_stat.ok++;
279     tvhinfo(LS_SERVICE_MAPPER, "%s: success", s->s_nicename);
280   } else {
281     tvhinfo(LS_BOUQUET, "%s: mapped service from %s", s->s_nicename, bq->bq_name ?: "<unknown>");
282   }
283 
284   /* Remove */
285 exit:
286   service_mapper_remove(s);
287   return chn;
288 }
289 
290 /**
291  *
292  */
293 static void *
service_mapper_thread(void * aux)294 service_mapper_thread ( void *aux )
295 {
296   service_t *s;
297   service_mapper_item_t *smi;
298   profile_chain_t prch;
299   th_subscription_t *sub;
300   int run, working = 0;
301   streaming_queue_t *sq;
302   streaming_message_t *sm;
303   const char *err = NULL;
304   uint64_t timeout;
305 
306   profile_chain_init(&prch, NULL, NULL, 1);
307   prch.prch_st = &prch.prch_sq.sq_st;
308   sq = &prch.prch_sq;
309 
310   pthread_mutex_lock(&global_lock);
311 
312   while (tvheadend_is_running()) {
313 
314     /* Wait for work */
315     while (!(smi = TAILQ_FIRST(&service_mapper_queue))) {
316       if (working) {
317         working = 0;
318         tvhinfo(LS_SERVICE_MAPPER, "idle");
319       }
320       tvh_cond_wait(&service_mapper_cond, &global_lock);
321       if (!tvheadend_is_running())
322         break;
323     }
324     if (!tvheadend_is_running())
325       break;
326     s = smi->s;
327     service_mapper_remove(s);
328 
329     if (!working) {
330       working = 1;
331       tvhinfo(LS_SERVICE_MAPPER, "starting");
332     }
333 
334     /* Subscribe */
335     tvhinfo(LS_SERVICE_MAPPER, "checking %s", s->s_nicename);
336     prch.prch_id = s;
337     sub = subscription_create_from_service(&prch, NULL,
338                                            SUBSCRIPTION_PRIO_MAPPER,
339                                            "service_mapper",
340                                            SUBSCRIPTION_PACKET,
341                                            NULL, NULL, "service_mapper", NULL);
342 
343     /* Failed */
344     if (!sub) {
345       tvhinfo(LS_SERVICE_MAPPER, "%s: could not subscribe", s->s_nicename);
346       continue;
347     }
348 
349     tvhinfo(LS_SERVICE_MAPPER, "waiting for input");
350     service_ref(s);
351     service_mapper_stat.active = s;
352     api_service_mapper_notify();
353     pthread_mutex_unlock(&global_lock);
354 
355     /* Wait */
356     run = 1;
357     pthread_mutex_lock(&sq->sq_mutex);
358     timeout = mclk() + sec2mono(30);
359     while(tvheadend_is_running() && run) {
360 
361       if (timeout < mclk()) {
362         run = 0;
363         err = streaming_code2txt(SM_CODE_BAD_SOURCE);
364         break;
365       }
366 
367       /* Wait for message */
368       while((sm = TAILQ_FIRST(&sq->sq_queue)) == NULL) {
369         tvh_cond_wait(&sq->sq_cond, &sq->sq_mutex);
370         if (!tvheadend_is_running())
371           break;
372       }
373       if (!tvheadend_is_running())
374         break;
375 
376       streaming_queue_remove(sq, sm);
377       pthread_mutex_unlock(&sq->sq_mutex);
378 
379       switch (sm->sm_type) {
380       case SMT_GRACE:
381         timeout += sec2mono(sm->sm_code);
382         break;
383       case SMT_PACKET:
384         run = 0;
385         err = NULL;
386         break;
387       case SMT_SERVICE_STATUS:
388         if(sm->sm_code & TSS_ERRORS) {
389           run = 0;
390           err = service_tss2text(sm->sm_code);
391         }
392         break;
393       case SMT_NOSTART:
394         run = 0;
395         err = streaming_code2txt(sm->sm_code);
396         break;
397       default:
398         break;
399       }
400 
401       streaming_msg_free(sm);
402       pthread_mutex_lock(&sq->sq_mutex);
403     }
404     if (!tvheadend_is_running())
405       break;
406 
407     streaming_queue_clear(&sq->sq_queue);
408     pthread_mutex_unlock(&sq->sq_mutex);
409 
410     pthread_mutex_lock(&global_lock);
411     subscription_unsubscribe(sub, UNSUBSCRIBE_FINAL);
412 
413     if(err) {
414       tvhinfo(LS_SERVICE_MAPPER, "%s: failed [reason: %s]", s->s_nicename, err);
415       service_mapper_stat.fail++;
416     } else
417       service_mapper_process(&smi->conf, s, NULL);
418 
419     service_unref(s);
420     service_mapper_stat.active = NULL;
421     api_service_mapper_notify();
422   }
423 
424   pthread_mutex_unlock(&global_lock);
425   profile_chain_close(&prch);
426   return NULL;
427 }
428 
429 void
service_mapper_reset_stats(void)430 service_mapper_reset_stats (void)
431 {
432   service_mapper_stat.total  = 0;
433   service_mapper_stat.ok     = 0;
434   service_mapper_stat.ignore = 0;
435   service_mapper_stat.fail   = 0;
436   service_mapper_stat.active = NULL;
437 }
438 
439 /*
440  * Save settings
441  */
442 static htsmsg_t *
service_mapper_conf_class_save(idnode_t * self,char * filename,size_t fsize)443 service_mapper_conf_class_save ( idnode_t *self, char *filename, size_t fsize )
444 {
445   htsmsg_t *m;
446 
447   m = htsmsg_create_map();
448   idnode_save(&service_mapper_conf.idnode, m);
449   snprintf(filename, fsize, "service_mapper/config");
450 
451   if (!htsmsg_is_empty(service_mapper_conf.services))
452     service_mapper_start(&service_mapper_conf.d, service_mapper_conf.services);
453   htsmsg_destroy(service_mapper_conf.services);
454   service_mapper_conf.services = NULL;
455 
456   return m;
457 }
458 
459 /*
460  * Class
461  */
462 
463 static const void *
service_mapper_services_get(void * obj)464 service_mapper_services_get ( void *obj )
465 {
466   return NULL;
467 }
468 
469 static char *
service_mapper_services_rend(void * obj,const char * lang)470 service_mapper_services_rend ( void *obj, const char *lang )
471 {
472   return strdup("");
473 }
474 
475 static int
service_mapper_services_set(void * obj,const void * p)476 service_mapper_services_set ( void *obj, const void *p )
477 {
478   service_mapper_t *sm = obj;
479   htsmsg_destroy(sm->services);
480   sm->services = htsmsg_copy((htsmsg_t *)p);
481   return 1;
482 }
483 
484 static htsmsg_t *
service_mapper_services_enum(void * obj,const char * lang)485 service_mapper_services_enum ( void *obj, const char *lang )
486 {
487   htsmsg_t *e, *m = htsmsg_create_map();
488   htsmsg_add_str(m, "type",  "api");
489   htsmsg_add_str(m, "uri",   "service/list");
490   htsmsg_add_str(m, "event", "service");
491   e = htsmsg_create_map();
492   htsmsg_add_bool(e, "enum", 1);
493   htsmsg_add_msg(m, "params", e);
494   return m;
495 }
496 
497 CLASS_DOC(service_mapper)
498 
499 static const idclass_t service_mapper_conf_class = {
500   .ic_snode      = &service_mapper_conf.idnode,
501   .ic_class      = "service_mapper",
502   .ic_caption    = N_("Service Mapping (Map services to channels)"),
503   .ic_doc        = tvh_doc_service_mapper_class,
504   .ic_event      = "service_mapper",
505   .ic_perm_def   = ACCESS_ADMIN,
506   .ic_save       = service_mapper_conf_class_save,
507   .ic_properties = (const property_t[]){
508     {
509       .type   = PT_STR,
510       .islist = 1,
511       .id     = "services",
512       .name   = N_("Services"),
513       .desc   = N_("Select/Selected services to map."),
514       .get    = service_mapper_services_get,
515       .set    = service_mapper_services_set,
516       .list   = service_mapper_services_enum,
517       .rend   = service_mapper_services_rend
518     },
519     {
520       .type   = PT_BOOL,
521       .id     = "check_availability",
522       .name   = N_("Check availability"),
523       .desc   = N_("Check services for availability. If enabled, "
524                    "services that are not currently broadcasting (or "
525                    "can't be decrypted) will be ignored. Leave disabled "
526                    "if you want Tvheadend to also map offline services."),
527       .off    = offsetof(service_mapper_t, d.check_availability),
528       .opts   = PO_ADVANCED
529     },
530     {
531       .type   = PT_BOOL,
532       .id     = "encrypted",
533       .name   = N_("Map encrypted services"),
534       .desc   = N_("Ignore encryption flag, include encrypted services "
535                    "anyway."),
536       .off    = offsetof(service_mapper_t, d.encrypted),
537     },
538     {
539       .type   = PT_BOOL,
540       .id     = "merge_same_name",
541       .name   = N_("Merge same name"),
542       .desc   = N_("Merge services with the same name into one channel."),
543       .off    = offsetof(service_mapper_t, d.merge_same_name),
544     },
545     {
546       .type   = PT_BOOL,
547       .id     = "type_tags",
548       .name   = N_("Create type-based tags"),
549       .desc   = N_("Create SDTV/HDTV/Radio tags."),
550       .off    = offsetof(service_mapper_t, d.type_tags),
551       .opts   = PO_ADVANCED
552     },
553     {
554       .type   = PT_BOOL,
555       .id     = "provider_tags",
556       .name   = N_("Create provider name tags"),
557       .desc   = N_("Create a provider name tag."),
558       .off    = offsetof(service_mapper_t, d.provider_tags),
559       .opts   = PO_ADVANCED
560     },
561     {
562       .type   = PT_BOOL,
563       .id     = "network_tags",
564       .name   = N_("Create network name tags"),
565       .desc   = N_("Create network name tags (set by provider)."),
566       .off    = offsetof(service_mapper_t, d.network_tags),
567       .opts   = PO_ADVANCED
568     },
569     {}
570   }
571 };
572 
573 /*
574  *
575  */
576 
577 pthread_t service_mapper_tid;
578 
service_mapper_init(void)579 void service_mapper_init ( void )
580 {
581   htsmsg_t *m;
582 
583   TAILQ_INIT(&service_mapper_queue);
584   idclass_register(&service_mapper_conf_class);
585   tvh_cond_init(&service_mapper_cond);
586   tvhthread_create(&service_mapper_tid, NULL, service_mapper_thread, NULL, "svcmap");
587 
588   /* Defaults */
589   memset(&service_mapper_conf, 0, sizeof(service_mapper_conf));
590   service_mapper_conf.idnode.in_class = &service_mapper_conf_class;
591   service_mapper_conf.d.type_tags = 1;
592   service_mapper_conf.d.encrypted = 1;
593 
594   /* Load settings */
595   if ((m = hts_settings_load("service_mapper/config"))) {
596     idnode_load(&service_mapper_conf.idnode, m);
597     htsmsg_destroy(m);
598   }
599 }
600 
601 
service_mapper_done(void)602 void service_mapper_done ( void )
603 {
604   tvh_cond_signal(&service_mapper_cond, 0);
605   pthread_join(service_mapper_tid, NULL);
606   htsmsg_destroy(service_mapper_conf.services);
607   service_mapper_conf.services = NULL;
608 }
609