free(ae->ae_id);
free(ae->ae_username);
free(ae->ae_password);
+ free(ae->ae_comment);
TAILQ_REMOVE(&access_entries, ae, ae_link);
free(ae);
}
htsmsg_destroy(m);
}
}
+
+void
+access_done(void)
+{
+ access_entry_t *ae;
+
+ dtable_delete("accesscontrol");
+ while ((ae = TAILQ_FIRST(&access_entries)) != NULL)
+ access_entry_destroy(ae);
+}
*
*/
void access_init(int createdefault, int noacl);
+void access_done(void);
#endif /* ACCESS_H_ */
} api_link_t;
RB_HEAD(,api_link) api_hook_tree;
+SKEL_DECLARE(api_skel, api_link_t);
static int ah_cmp
( api_link_t *a, api_link_t *b )
void
api_register ( const api_hook_t *hook )
{
- static api_link_t *t, *skel = NULL;
- if (!skel)
- skel = calloc(1, sizeof(api_link_t));
- skel->hook = hook;
- t = RB_INSERT_SORTED(&api_hook_tree, skel, link, ah_cmp);
- if (t)
+ api_link_t *t;
+ SKEL_ALLOC(api_skel);
+ api_skel->hook = hook;
+ t = RB_INSERT_SORTED(&api_hook_tree, api_skel, link, ah_cmp);
+ if (t) {
tvherror("api", "trying to re-register subsystem");
- else
- skel = NULL;
+ } else {
+ SKEL_USED(api_skel);
+ }
}
void
api_status_init();
api_imagecache_init();
}
+
+void api_done ( void )
+{
+ api_link_t *t;
+
+ while ((t = RB_FIRST(&api_hook_tree)) != NULL) {
+ RB_REMOVE(&api_hook_tree, t, link);
+ free(t);
+ }
+ SKEL_FREE(api_skel);
+}
* Initialise
*/
void api_init ( void );
+void api_done ( void );
void api_idnode_init ( void );
void api_input_init ( void );
void api_service_init ( void );
if (e)
htsmsg_add_msg(l, NULL, e);
}
+ free(is->is_array);
+ free(is);
}
*resp = htsmsg_create_map();
htsmsg_add_msg(*resp, "entries", l);
static AvahiEntryGroup *group = NULL;
static char *name = NULL;
+static AvahiSimplePoll *avahi_asp = NULL;
static void create_services(AvahiClient *c);
static void *
avahi_thread(void *aux)
{
- AvahiSimplePoll *asp = avahi_simple_poll_new();
const AvahiPoll *ap = avahi_simple_poll_get(asp);
name = avahi_strdup("Tvheadend");
avahi_client_new(ap, AVAHI_CLIENT_NO_FAIL, client_callback, NULL, NULL);
- while((avahi_simple_poll_iterate(asp, -1)) != -1) {}
+ while(avahi_simple_poll_iterate(asp, -1) == 0);
return NULL;
/**
*
*/
+pthread_t avahi_tid;
+
void
avahi_init(void)
{
- pthread_t tid;
+ avahi_asp = avahi_simple_poll_new();
+ tvhthread_create(&avahi_tid, NULL, avahi_thread, NULL, 0);
+}
- tvhthread_create(&tid, NULL, avahi_thread, NULL, 1);
+void
+avahi_done(void)
+{
+ avahi_simple_poll_quit(avahi_asp);
+ pthread_kill(avahi_tid, SIGTERM);
+ pthread_join(avahi_tid, NULL);
}
+#ifdef CONFIG_AVAHI
void avahi_init(void);
+void avahi_done(void);
+#else
+static inline void avahi_init(void) { }
+static inline void avahi_done(void) { }
+#endif
static dtable_t *channeltags_dtable;
static void channel_tag_init ( void );
+static void channel_tag_done ( void );
static channel_tag_t *channel_tag_find(const char *id, int create);
static void channel_tag_mapping_destroy(channel_tag_mapping_t *ctm,
int flags);
+static void channel_tag_destroy(channel_tag_t *ct, int delconf);
+
#define CTM_DESTROY_UPDATE_TAG 0x1
#define CTM_DESTROY_UPDATE_CHANNEL 0x2
static void
channel_class_delete ( idnode_t *self )
{
- channel_delete((channel_t*)self);
+ channel_delete((channel_t*)self, 1);
}
static const void *
}
void
-channel_delete ( channel_t *ch )
+channel_delete ( channel_t *ch, int delconf )
{
th_subscription_t *s;
channel_tag_mapping_t *ctm;
lock_assert(&global_lock);
- tvhinfo("channel", "%s - deleting", channel_get_name(ch));
+ if (delconf)
+ tvhinfo("channel", "%s - deleting", channel_get_name(ch));
/* Tags */
while((ctm = LIST_FIRST(&ch->ch_ctms)) != NULL)
channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_TAG);
/* DVR */
- autorec_destroy_by_channel(ch);
+ autorec_destroy_by_channel(ch, delconf);
dvr_destroy_by_channel(ch);
/* Services */
epg_channel_unlink(ch);
/* Settings */
- hts_settings_remove("channel/%s", idnode_uuid_as_str(&ch->ch_id));
+ if (delconf)
+ hts_settings_remove("channel/%s", idnode_uuid_as_str(&ch->ch_id));
/* Free memory */
RB_REMOVE(&channels, ch, ch_link);
htsmsg_destroy(c);
}
+/**
+ *
+ */
+void
+channel_done ( void )
+{
+ channel_t *ch;
+
+ pthread_mutex_lock(&global_lock);
+ while ((ch = RB_FIRST(&channels)) != NULL)
+ channel_delete(ch, 0);
+ pthread_mutex_unlock(&global_lock);
+ channel_tag_done();
+}
+
/* ***
* Channel tags TODO
*/
*
*/
static void
-channel_tag_destroy(channel_tag_t *ct)
+channel_tag_destroy(channel_tag_t *ct, int delconf)
{
channel_tag_mapping_t *ctm;
channel_t *ch;
- while((ctm = LIST_FIRST(&ct->ct_ctms)) != NULL) {
- ch = ctm->ctm_channel;
- channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_CHANNEL);
- channel_save(ch);
+ if (delconf) {
+ while((ctm = LIST_FIRST(&ct->ct_ctms)) != NULL) {
+ ch = ctm->ctm_channel;
+ channel_tag_mapping_destroy(ctm, CTM_DESTROY_UPDATE_CHANNEL);
+ channel_save(ch);
+ }
}
if(ct->ct_enabled && !ct->ct_internal)
if((ct = channel_tag_find(id, 0)) == NULL)
return -1;
- channel_tag_destroy(ct);
+ channel_tag_destroy(ct, 1);
return 0;
}
channeltags_dtable = dtable_create(&channel_tags_dtc, "channeltags", NULL);
dtable_load(channeltags_dtable);
}
+
+static void
+channel_tag_done ( void )
+{
+ channel_tag_t *ct;
+
+ pthread_mutex_lock(&global_lock);
+ while ((ct = TAILQ_FIRST(&channel_tags)) != NULL)
+ channel_tag_destroy(ct, 0);
+ pthread_mutex_unlock(&global_lock);
+ dtable_delete("channeltags");
+}
extern const idclass_t channel_class;
void channel_init(void);
+void channel_done(void);
channel_t *channel_create0
(channel_t *ch, const idclass_t *idc, const char *uuid, htsmsg_t *conf,
#define channel_create(u, c, n)\
channel_create0(calloc(1, sizeof(channel_t)), &channel_class, u, c, n)
-void channel_delete(channel_t *ch);
+void channel_delete(channel_t *ch, int delconf);
channel_t *channel_find_by_name(const char *name);
#define channel_find_by_uuid(u)\
}
}
+void config_done ( void )
+{
+ htsmsg_destroy(config);
+}
+
void config_save ( void )
{
hts_settings_save(config, "config");
#include "htsmsg.h"
void config_init ( void );
+void config_done ( void );
void config_save ( void );
htsmsg_t *config_get_all ( void );
LIST_HEAD(caid_list, caid);
void descrambler_init ( void );
+void descrambler_done ( void );
void descrambler_service_start ( struct service *t );
const char *descrambler_caid2name(uint16_t caid);
uint16_t descrambler_name2caid(const char *str);
struct capmt_service_list capmt_services;
+ pthread_t capmt_tid;
+
/* from capmt configuration */
char *capmt_sockfile;
char *capmt_hostname;
static capmt_t *
capmt_entry_find(const char *id, int create)
{
- pthread_t ptid;
char buf[20];
capmt_t *capmt;
static int tally;
TAILQ_INSERT_TAIL(&capmts, capmt, capmt_link);
- tvhthread_create(&ptid, NULL, capmt_thread, capmt, 1);
+ tvhthread_create(&capmt->capmt_tid, NULL, capmt_thread, capmt, 1);
return capmt;
}
dtable_load(dt);
}
+void
+capmt_done(void)
+{
+ capmt_t *capmt, *n;
+ pthread_t tid;
+
+ for (capmt = TAILQ_FIRST(&capmts); capmt != NULL; capmt = n) {
+ n = TAILQ_NEXT(capmt, capmt_link);
+ pthread_mutex_lock(&global_lock);
+ tid = capmt->capmt_tid;
+ capmt_destroy(capmt);
+ pthread_mutex_unlock(&global_lock);
+ pthread_join(tid, NULL);
+ }
+ dtable_delete("capmt");
+}
void capmt_init(void);
+void capmt_done(void);
+
void capmt_service_start(struct service *t);
#endif /* CAPMT_H_ */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
+#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <openssl/des.h>
typedef struct cs_card_data {
LIST_ENTRY(cs_card_data) cs_card;
-
+
/* Card caid */
- uint16_t cwc_caid;
+ uint16_t cwc_caid;
/* Card type */
card_type_t cwc_card_type;
int cwc_retry_delay;
+ pthread_t cwc_tid;
+
pthread_cond_t cwc_cond;
pthread_mutex_t cwc_writer_mutex;
int seq = atomic_add(&cwc->cwc_seq, 1);
- memset(buf, 0, 12);
-
+ buf[0] = buf[1] = 0;
buf[2] = (seq >> 8) & 0xff;
buf[3] = seq & 0xff;
buf[4] = (sid >> 8) & 0xff;
}
tvhtrace("cwc", "sending message sid %d len %"PRIsize_t" enq %d", sid, len, enq);
- tvhlog_hexdump("cwc", msg, len);
+ tvhlog_hexdump("cwc", buf, len);
buf[0] = (len - 2) >> 8;
buf[1] = (len - 2) & 0xff;
cwc->cwc_hostname, cwc->cwc_port);
}
+ if(cwc->cwc_running == 0) continue;
if(attempts == 1) continue; // Retry immediately
d = 3;
free((void *)cwc->cwc_password);
free((void *)cwc->cwc_password_salted);
free((void *)cwc->cwc_username);
+ free((void *)cwc->cwc_comment);
free((void *)cwc->cwc_hostname);
free((void *)cwc->cwc_id);
free((void *)cwc->cwc_viaccess_emm.shared_emm);
static cwc_t *
cwc_entry_find(const char *id, int create)
{
- pthread_t ptid;
char buf[20];
cwc_t *cwc;
static int tally;
cwc->cwc_running = 1;
TAILQ_INSERT_TAIL(&cwcs, cwc, cwc_link);
- tvhthread_create(&ptid, NULL, cwc_thread, cwc, 1);
+ tvhthread_create(&cwc->cwc_tid, NULL, cwc_thread, cwc, 0);
return cwc;
}
}
+/**
+ *
+ */
+void
+cwc_done(void)
+{
+ cwc_t *cwc;
+ pthread_t tid;
+
+ dtable_delete("cwc");
+ pthread_mutex_lock(&cwc_mutex);
+ while ((cwc = TAILQ_FIRST(&cwcs)) != NULL) {
+ tid = cwc->cwc_tid;
+ cwc_destroy(cwc);
+ pthread_mutex_unlock(&cwc_mutex);
+ pthread_kill(tid, SIGTERM);
+ pthread_join(tid, NULL);
+ pthread_mutex_lock(&cwc_mutex);
+ }
+ pthread_mutex_unlock(&cwc_mutex);
+}
+
+
#include <openssl/md5.h>
/*
void cwc_init(void);
+void cwc_done(void);
+
void cwc_service_start(struct service *t);
void cwc_emm(uint8_t *data, int len, uint16_t caid, void *ca_update_id);
#endif
}
+void
+descrambler_done ( void )
+{
+ capmt_done();
+ cwc_done();
+}
+
void
descrambler_service_start ( service_t *t )
{
return dt;
}
+/**
+ *
+ */
+void
+dtable_delete(const char *name)
+{
+ dtable_t *dt = dtable_find(name);
+
+ if (dt) {
+ pthread_mutex_lock(&global_lock);
+ LIST_REMOVE(dt, dt_link);
+ pthread_mutex_unlock(&global_lock);
+ free(dt->dt_tablename);
+ free(dt);
+ }
+}
+
/**
*
*/
dtable_t *dtable_create(const dtable_class_t *dtc, const char *name,
void *opaque);
+void dtable_delete(const char *name);
+
int dtable_load(dtable_t *dt);
dtable_t *dtable_find(const char *name);
void dvr_init(void);
+void dvr_done(void);
+
void dvr_autorec_init(void);
+void dvr_autorec_done(void);
+
void dvr_autorec_update(void);
void dvr_destroy_by_channel(channel_t *ch);
void dvr_autorec_check_serieslink(epg_serieslink_t *s);
-void autorec_destroy_by_channel(channel_t *ch);
+void autorec_destroy_by_channel(channel_t *ch, int delconf);
dvr_autorec_entry_t *autorec_entry_find(const char *id, int create);
* Inotify support
*/
void dvr_inotify_init ( void );
+void dvr_inotify_done ( void );
void dvr_inotify_add ( dvr_entry_t *de );
void dvr_inotify_del ( dvr_entry_t *de );
dvr_autorec_in_init = 0;
}
+void
+dvr_autorec_done(void)
+{
+ dvr_autorec_entry_t *dae;
+
+ pthread_mutex_lock(&global_lock);
+ while ((dae = TAILQ_FIRST(&autorec_entries)) != NULL) {
+ TAILQ_REMOVE(&autorec_entries, dae, dae_link);
+ free(dae);
+ }
+ pthread_mutex_unlock(&global_lock);
+ dtable_delete("autorec");
+}
+
void
dvr_autorec_update(void)
{
*
*/
void
-autorec_destroy_by_channel(channel_t *ch)
+autorec_destroy_by_channel(channel_t *ch, int delconf)
{
dvr_autorec_entry_t *dae;
htsmsg_t *m;
while((dae = LIST_FIRST(&ch->ch_autorecs)) != NULL) {
- dtable_record_erase(autorec_dt, dae->dae_id);
+ if (delconf)
+ dtable_record_erase(autorec_dt, dae->dae_id);
autorec_entry_destroy(dae);
}
if(de->de_autorec != NULL)
LIST_REMOVE(de, de_autorec_link);
+ free(de->de_filename);
free(de->de_config_name);
free(de->de_creator);
if (de->de_title) lang_str_destroy(de->de_title);
*
*/
static void
-dvr_entry_remove(dvr_entry_t *de)
+dvr_entry_remove(dvr_entry_t *de, int delconf)
{
- hts_settings_remove("dvr/log/%d", de->de_id);
+ if (delconf)
+ hts_settings_remove("dvr/log/%d", de->de_id);
htsp_dvr_entry_delete(de);
LIST_REMOVE(de, de_global_link);
de->de_channel = NULL;
free(de->de_channel_name);
+ de->de_channel_name = NULL;
dvrdb_changed();
dvr_timer_expire(void *aux)
{
dvr_entry_t *de = aux;
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
}
/* If this was craeted by autorec - just remove it, it'll get recreated */
if (de->de_autorec) {
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
/* Find match */
} else {
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
return NULL;
case DVR_RECORDING:
return de;
case DVR_COMPLETED:
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
return NULL;
case DVR_MISSED_TIME:
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
return NULL;
default:
dvr_autorec_update();
}
+/**
+ *
+ */
+void
+dvr_done(void)
+{
+ dvr_config_t *cfg;
+ dvr_entry_t *de;
+
+#if ENABLE_INOTIFY
+ dvr_inotify_done();
+#endif
+ pthread_mutex_lock(&global_lock);
+ while ((cfg = LIST_FIRST(&dvrconfigs)) != NULL) {
+ LIST_REMOVE(cfg, config_link);
+ free(cfg->dvr_storage);
+ free(cfg->dvr_config_name);
+ free(cfg);
+ }
+ while ((de = LIST_FIRST(&dvrentries)) != NULL)
+ dvr_entry_remove(de, 0);
+ pthread_mutex_unlock(&global_lock);
+ dvr_autorec_done();
+}
+
/**
* find a dvr config by name, return NULL if not found
*/
cfg->dvr_config_name);
hts_settings_remove("dvr/config%s", cfg->dvr_config_name);
LIST_REMOVE(cfg, config_link);
+ free(cfg->dvr_config_name);
free(cfg);
dvrconfig_changed();
}
}
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
}
/**
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
break;
case DVR_RECORDING:
break;
case DVR_MISSED_TIME:
- dvr_entry_remove(de);
+ dvr_entry_remove(de, 1);
break;
default:
#include <unistd.h>
#include <assert.h>
#include <poll.h>
+#include <signal.h>
+#include <pthread.h>
#include <sys/inotify.h>
#include <sys/stat.h>
struct dvr_entry_list entries;
} dvr_inotify_entry_t;
+static SKEL_DECLARE(dvr_inotify_entry_skel, dvr_inotify_entry_t);
+
static void* _dvr_inotify_thread ( void *p );
static int _str_cmp ( void *a, void *b )
/**
* Initialise threads
*/
+pthread_t dvr_inotify_tid;
+
void dvr_inotify_init ( void )
{
- pthread_t tid;
-
_inot_fd = inotify_init();
if (_inot_fd == -1) {
tvhlog(LOG_ERR, "dvr", "failed to initialise inotify (err=%s)",
return;
}
- tvhthread_create(&tid, NULL, _dvr_inotify_thread, NULL, 1);
+ tvhthread_create(&dvr_inotify_tid, NULL, _dvr_inotify_thread, NULL, 0);
+}
+
+/**
+ *
+ */
+void dvr_inotify_done ( void )
+{
+ int fd = _inot_fd;
+ _inot_fd = -1;
+ close(fd);
+ pthread_kill(dvr_inotify_tid, SIGTERM);
+ pthread_join(dvr_inotify_tid, NULL);
+ SKEL_FREE(dvr_inotify_entry_skel);
}
/**
*/
void dvr_inotify_add ( dvr_entry_t *de )
{
- static dvr_inotify_entry_t *skel = NULL;
dvr_inotify_entry_t *e;
char *path;
struct stat st;
path = strdup(de->de_filename);
- if (!skel)
- skel = calloc(1, sizeof(dvr_inotify_entry_t));
- skel->path = dirname(path);
+ SKEL_ALLOC(dvr_inotify_entry_skel);
+ dvr_inotify_entry_skel->path = dirname(path);
- if (stat(skel->path, &st))
+ if (stat(dvr_inotify_entry_skel->path, &st))
return;
- e = RB_INSERT_SORTED(&_inot_tree, skel, link, _str_cmp);
+ e = RB_INSERT_SORTED(&_inot_tree, dvr_inotify_entry_skel, link, _str_cmp);
if (!e) {
- e = skel;
- skel = NULL;
+ e = dvr_inotify_entry_skel;
+ SKEL_USED(dvr_inotify_entry_skel);
e->path = strdup(e->path);
e->fd = inotify_add_watch(_inot_fd, e->path, EVENT_MASK);
if (e->fd == -1) {
from = NULL;
i = 0;
len = read(_inot_fd, buf, EVENT_BUF_LEN);
+ if (_inot_fd < 0)
+ break;
/* Process */
pthread_mutex_lock(&global_lock);
if ( t ) return md5sum(t);
return NULL;
}
+
+void epg_skel_done(void)
+{
+ epg_object_t **skel;
+ epg_broadcast_t **broad;
+
+ skel = _epg_brand_skel();
+ free(*skel); *skel = NULL;
+ skel = _epg_season_skel();
+ free(*skel); *skel = NULL;
+ skel = _epg_episode_skel();
+ free(*skel); *skel = NULL;
+ broad = _epg_broadcast_skel();
+ free(*broad); *broad = NULL;
+}
* ***********************************************************************/
void epg_init (void);
-void epg_save (void*);
+void epg_done (void);
+void epg_skel_done (void);
+void epg_save (void);
+void epg_save_callback (void *p);
void epg_updated (void);
/* ************************************************************************
/*
* Process v2 data
*/
-static void _epgdb_v2_process ( htsmsg_t *m, epggrab_stats_t *stats )
+static void _epgdb_v2_process (
+ char **sect, htsmsg_t *m, epggrab_stats_t *stats )
{
int save = 0;
const char *s;
- static char *sect;
/* New section */
if ( (s = htsmsg_get_str(m, "__section__")) ) {
- if (sect) free(sect);
- sect = strdup(s);
+ if (*sect) free(*sect);
+ *sect = strdup(s);
/* Brand */
- } else if ( !strcmp(sect, "brands") ) {
+ } else if ( !strcmp(*sect, "brands") ) {
if (epg_brand_deserialize(m, 1, &save)) stats->brands.total++;
/* Season */
- } else if ( !strcmp(sect, "seasons") ) {
+ } else if ( !strcmp(*sect, "seasons") ) {
if (epg_season_deserialize(m, 1, &save)) stats->seasons.total++;
/* Episode */
- } else if ( !strcmp(sect, "episodes") ) {
+ } else if ( !strcmp(*sect, "episodes") ) {
if (epg_episode_deserialize(m, 1, &save)) stats->episodes.total++;
/* Series link */
- } else if ( !strcmp(sect, "serieslinks") ) {
+ } else if ( !strcmp(*sect, "serieslinks") ) {
if (epg_serieslink_deserialize(m, 1, &save)) stats->seasons.total++;
/* Broadcasts */
- } else if ( !strcmp(sect, "broadcasts") ) {
+ } else if ( !strcmp(*sect, "broadcasts") ) {
if (epg_broadcast_deserialize(m, 1, &save)) stats->broadcasts.total++;
/* Unknown */
} else {
- tvhlog(LOG_DEBUG, "epgdb", "malformed database section [%s]", sect);
+ tvhlog(LOG_DEBUG, "epgdb", "malformed database section [%s]", *sect);
//htsmsg_print(m);
}
}
uint8_t *mem, *rp;
epggrab_stats_t stats;
int ver = EPG_DB_VERSION;
+ char *sect = NULL;
/* Find the right file (and version) */
while (fd < 0 && ver > 0) {
/* Process */
switch (ver) {
case 2:
- _epgdb_v2_process(m, &stats);
+ _epgdb_v2_process(§, m, &stats);
break;
default:
break;
htsmsg_destroy(m);
}
+ free(sect);
+
/* Stats */
tvhlog(LOG_INFO, "epgdb", "loaded v%d", ver);
tvhlog(LOG_INFO, "epgdb", " channels %d", stats.channels.total);
close(fd);
}
+void epg_done ( void )
+{
+ channel_t *ch;
+
+ pthread_mutex_lock(&global_lock);
+ CHANNEL_FOREACH(ch)
+ epg_channel_unlink(ch);
+ epg_skel_done();
+ pthread_mutex_unlock(&global_lock);
+}
+
/* **************************************************************************
* Save
* *************************************************************************/
return _epg_write(fd, m);
}
-void epg_save ( void *p )
+void epg_save_callback ( void *p )
+{
+ epg_save();
+}
+
+void epg_save ( void )
{
int fd;
epg_object_t *eo;
extern gtimer_t epggrab_save_timer;
if (epggrab_epgdb_periodicsave)
- gtimer_arm(&epggrab_save_timer, epg_save, NULL, epggrab_epgdb_periodicsave);
+ gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL, epggrab_epgdb_periodicsave);
fd = hts_settings_open_file(1, "epgdb.v%d", EPG_DB_VERSION);
static int epggrab_confver;
pthread_mutex_t epggrab_mutex;
static pthread_cond_t epggrab_cond;
+int epggrab_running;
/* Config */
uint32_t epggrab_interval;
/* Check for config change */
pthread_mutex_lock(&epggrab_mutex);
- while ( confver == epggrab_confver ) {
+ while ( epggrab_running && confver == epggrab_confver ) {
if (epggrab_module) {
err = pthread_cond_timedwait(&epggrab_cond, &epggrab_mutex, &ts);
} else {
ts.tv_sec += epggrab_interval;
pthread_mutex_unlock(&epggrab_mutex);
+ if ( !epggrab_running)
+ break;
+
/* Run grabber */
if (mod) _epggrab_module_grab(mod);
}
htsmsg_get_u32(m, "channel_reicon", &epggrab_channel_reicon);
htsmsg_get_u32(m, "epgdb_periodicsave", &epggrab_epgdb_periodicsave);
if (epggrab_epgdb_periodicsave)
- gtimer_arm(&epggrab_save_timer, epg_save, NULL,
+ gtimer_arm(&epggrab_save_timer, epg_save_callback, NULL,
epggrab_epgdb_periodicsave);
if (!htsmsg_get_u32(m, old ? "grab-interval" : "interval",
&epggrab_interval)) {
if (!e)
gtimer_disarm(&epggrab_save_timer);
else
- epg_save(NULL); // will arm the timer
+ epg_save(); // will arm the timer
pthread_mutex_unlock(&global_lock);
save = 1;
}
/*
* Initialise
*/
+pthread_t epggrab_tid;
+
void epggrab_init ( void )
{
/* Defaults */
_epggrab_load();
/* Start internal grab thread */
- pthread_t tid;
- pthread_attr_t tattr;
- pthread_attr_init(&tattr);
- pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
- tvhthread_create(&tid, &tattr, _epggrab_internal_thread, NULL, 1);
- pthread_attr_destroy(&tattr);
+ epggrab_running = 1;
+ tvhthread_create(&epggrab_tid, NULL, _epggrab_internal_thread, NULL, 0);
}
+/*
+ * Cleanup
+ */
+void epggrab_done ( void )
+{
+ epggrab_module_t *mod;
+
+ epggrab_running = 0;
+ pthread_cond_signal(&epggrab_cond);
+ pthread_join(epggrab_tid, NULL);
+
+ pthread_mutex_lock(&global_lock);
+ while ((mod = LIST_FIRST(&epggrab_modules)) != NULL) {
+ LIST_REMOVE(mod, link);
+ if (mod->type == EPGGRAB_OTA && ((epggrab_module_ota_t *)mod)->done)
+ ((epggrab_module_ota_t *)mod)->done((epggrab_module_ota_t *)mod);
+ if (mod->type == EPGGRAB_INT || mod->type == EPGGRAB_EXT)
+ free((void *)((epggrab_module_int_t *)mod)->path);
+ free((void *)mod->id);
+ free((void *)mod->name);
+ free(mod);
+ }
+ pthread_mutex_unlock(&global_lock);
+ epggrab_ota_done_();
+ opentv_done();
+}
/* Transponder tuning */
void (*start) ( epggrab_module_ota_t *m, struct mpegts_mux *mm );
+ void (*done) ( epggrab_module_ota_t *m );
};
/*
*/
extern epggrab_module_list_t epggrab_modules;
extern pthread_mutex_t epggrab_mutex;
+extern int epggrab_running;
extern uint32_t epggrab_interval;
extern epggrab_module_int_t* epggrab_module;
extern uint32_t epggrab_channel_rename;
* Load/Save
*/
void epggrab_init ( void );
+void epggrab_done ( void );
void epggrab_save ( void );
void epggrab_ota_init ( void );
+void epggrab_ota_done_ ( void );
/* **************************************************************************
* Global Functions
epggrab_module_ext_t *mod = (epggrab_module_ext_t*)p;
tvhlog(LOG_INFO, mod->id, "external socket enabled");
- while ( mod->enabled && mod->sock ) {
+ while ( epggrab_running && mod->enabled && mod->sock ) {
tvhlog(LOG_DEBUG, mod->id, "waiting for connection");
s = accept(mod->sock, NULL, NULL);
if (s <= 0) continue;
void (*start) (epggrab_module_ota_t*m,
struct mpegts_mux *dm),
int (*enable) (void *m, uint8_t e ),
+ void (*done) (epggrab_module_ota_t *m),
epggrab_channel_tree_t *channels )
{
if (!skel) skel = calloc(1, sizeof(epggrab_module_ota_t));
skel->type = EPGGRAB_OTA;
skel->enable = enable;
skel->start = start;
+ skel->done = done;
//TAILQ_INIT(&skel->muxes);
return skel;
uint16_t onid, tsid, sid;
uint32_t extraid;
mpegts_service_t *svc;
- mpegts_mux_t *mm = mt->mt_mux;;
+ mpegts_mux_t *mm = mt->mt_mux;
epggrab_module_t *mod = mt->mt_opaque;
epggrab_ota_mux_t *ota = NULL;
mpegts_table_state_t *st;
void eit_init ( void )
{
epggrab_module_ota_create(NULL, "eit", "EIT: DVB Grabber", 1,
- _eit_start, NULL, NULL);
+ _eit_start, NULL, NULL, NULL);
epggrab_module_ota_create(NULL, "uk_freesat", "UK: Freesat", 5,
- _eit_start, NULL, NULL);
+ _eit_start, NULL, NULL, NULL);
epggrab_module_ota_create(NULL, "uk_freeview", "UK: Freeview", 5,
- _eit_start, NULL, NULL);
+ _eit_start, NULL, NULL, NULL);
epggrab_module_ota_create(NULL, "viasat_baltic", "VIASAT: Baltic", 5,
- _eit_start, NULL, NULL);
+ _eit_start, NULL, NULL, NULL);
}
htsmsg_destroy(m);
}
+static void _opentv_done( epggrab_module_ota_t *m )
+{
+ opentv_module_t *mod = (opentv_module_t *)m;
+ free(mod->channel);
+ free(mod->title);
+ free(mod->summary);
+}
+
static int _opentv_prov_load_one ( const char *id, htsmsg_t *m )
{
char ibuf[100], nbuf[1000];
epggrab_module_ota_create(calloc(1, sizeof(opentv_module_t)),
ibuf, nbuf, 2,
_opentv_start, NULL,
- NULL);
+ _opentv_done, NULL);
/* Add provider details */
mod->dict = dict;
tvhlog(LOG_DEBUG, "opentv", "providers loaded");
}
+void opentv_done ( void )
+{
+ opentv_dict_t *dict;
+ opentv_genre_t *genre;
+
+ while ((dict = RB_FIRST(&_opentv_dicts)) != NULL) {
+ RB_REMOVE(&_opentv_dicts, dict, h_link);
+ huffman_tree_destroy(dict->codes);
+ free(dict->id);
+ free(dict);
+ }
+ while ((genre = RB_FIRST(&_opentv_genres)) != NULL) {
+ RB_REMOVE(&_opentv_genres, genre, h_link);
+ free(genre->id);
+ free(genre);
+ }
+}
+
void opentv_load ( void )
{
// TODO: do we want to keep a list of channels stored?
gtimer_t epggrab_ota_pending_timer;
gtimer_t epggrab_ota_active_timer;
+SKEL_DECLARE(epggrab_ota_mux_skel, epggrab_ota_mux_t);
+
static void epggrab_ota_active_timer_cb ( void *p );
static void epggrab_ota_pending_timer_cb ( void *p );
int interval, int timeout )
{
int save = 0;
- static epggrab_ota_mux_t *skel = NULL;
epggrab_ota_map_t *map;
epggrab_ota_mux_t *ota;
/* Find mux entry */
const char *uuid = idnode_uuid_as_str(&mm->mm_id);
- if (!skel)
- skel = calloc(1, sizeof(epggrab_ota_mux_t));
- skel->om_mux_uuid = (char*)uuid;
+ SKEL_ALLOC(epggrab_ota_mux_skel);
+ epggrab_ota_mux_skel->om_mux_uuid = (char*)uuid;
- ota = RB_INSERT_SORTED(&epggrab_ota_all, skel, om_global_link, om_id_cmp);
+ ota = RB_INSERT_SORTED(&epggrab_ota_all, epggrab_ota_mux_skel, om_global_link, om_id_cmp);
if (!ota) {
char buf[256];
mm->mm_display_name(mm, buf, sizeof(buf));
tvhinfo(mod->id, "registering mux %s", buf);
- ota = skel;
- skel = NULL;
+ ota = epggrab_ota_mux_skel;
+ SKEL_USED(epggrab_ota_mux_skel);
ota->om_mux_uuid = strdup(uuid);
ota->om_when = dispatch_clock + epggrab_ota_timeout(ota);
ota->om_active = 1;
}
htsmsg_add_msg(c, "modules", l);
hts_settings_save(c, "epggrab/otamux/%s", ota->om_mux_uuid);
+ htsmsg_destroy(c);
}
static void
NULL, 0);
}
+static void
+epggrab_ota_free ( epggrab_ota_mux_t *ota )
+{
+ epggrab_ota_map_t *map;
+
+ LIST_REMOVE(ota, om_q_link);
+ while ((map = LIST_FIRST(&ota->om_modules)) != NULL) {
+ LIST_REMOVE(map, om_link);
+ free(map);
+ }
+ free(ota->om_mux_uuid);
+ free(ota);
+}
+
+void
+epggrab_ota_done_ ( void )
+{
+ epggrab_ota_mux_t *ota;
+
+ pthread_mutex_lock(&global_lock);
+ while ((ota = LIST_FIRST(&epggrab_ota_active)) != NULL)
+ epggrab_ota_free(ota);
+ while ((ota = LIST_FIRST(&epggrab_ota_pending)) != NULL)
+ epggrab_ota_free(ota);
+ pthread_mutex_unlock(&global_lock);
+ SKEL_FREE(epggrab_ota_mux_skel);
+}
+
/******************************************************************************
* Editor Configuration
*
void (*start) (epggrab_module_ota_t*m,
struct mpegts_mux *mm),
int (*enable) (void *m, uint8_t e ),
+ void (*done) (epggrab_module_ota_t*m),
epggrab_channel_tree_t *channels );
/* **************************************************************************
/* OpenTV module */
void opentv_init ( void );
+void opentv_done ( void );
void opentv_load ( void );
/* PyEPG module */
#if ENABLE_INOTIFY
+#include <signal.h>
#include <sys/inotify.h>
#include <sys/stat.h>
/* Wait for event */
c = read(fsmonitor_fd, buf, sizeof(buf));
+ if (fsmonitor_fd < 0)
+ break;
/* Process */
pthread_mutex_lock(&global_lock);
/*
* Start the fsmonitor subsystem
*/
+pthread_t fsmonitor_tid;
+
void
fsmonitor_init ( void )
{
- pthread_t tid;
-
/* Intialise inotify */
fsmonitor_fd = inotify_init();
- tvhthread_create0(&tid, NULL, fsmonitor_thread, NULL, "fsmonitor", 1);
+ tvhthread_create0(&fsmonitor_tid, NULL, fsmonitor_thread, NULL, "fsmonitor", 0);
+}
+
+/*
+ * Stop the fsmonitor subsystem
+ */
+void
+fsmonitor_done ( void )
+{
+ int fd = fsmonitor_fd;
+ fsmonitor_fd = -1;
+ close(fd);
+ pthread_kill(fsmonitor_tid, SIGTERM);
+ pthread_join(fsmonitor_tid, NULL);
}
/*
fsmonitor_add ( const char *path, fsmonitor_t *fsm )
{
int mask;
- static fsmonitor_path_t *skel = NULL;
+ fsmonitor_path_t *skel;
fsmonitor_path_t *fmp;
fsmonitor_link_t *fml;
lock_assert(&global_lock);
- if (!skel) skel = calloc(1, sizeof(fsmonitor_path_t));
+ skel = calloc(1, sizeof(fsmonitor_path_t));
skel->fmp_path = (char*)path;
/* Build mask */
fmp = RB_INSERT_SORTED(&fsmonitor_paths, skel, fmp_link, fmp_cmp);
if (!fmp) {
fmp = skel;
- fmp->fmp_fd
- = inotify_add_watch(fsmonitor_fd, path, mask);
+ fmp->fmp_fd = inotify_add_watch(fsmonitor_fd, path, mask);
/* Failed */
if (fmp->fmp_fd <= 0) {
- RB_REMOVE(&fsmonitor_paths, skel, fmp_link);
+ RB_REMOVE(&fsmonitor_paths, fmp, fmp_link);
+ free(fmp);
tvhdebug("fsmonitor", "failed to add %s (exists?)", path);
printf("ERROR: failed to add %s\n", path);
return -1;
}
/* Setup */
- skel = NULL;
fmp->fmp_path = strdup(path);
tvhdebug("fsmonitor", "watch %s", fmp->fmp_path);
+ } else {
+ free(skel);
}
/* Check doesn't exist */
void
fsmonitor_del ( const char *path, fsmonitor_t *fsm )
{
- static fsmonitor_path_t *skel = NULL;
+ static fsmonitor_path_t skel;
fsmonitor_path_t *fmp;
fsmonitor_link_t *fml;
lock_assert(&global_lock);
- if (!skel) skel = calloc(1, sizeof(fsmonitor_path_t));
- skel->fmp_path = (char*)path;
+ skel.fmp_path = (char*)path;
/* Find path */
- fmp = RB_FIND(&fsmonitor_paths, skel, fmp_link, fmp_cmp);
+ fmp = RB_FIND(&fsmonitor_paths, &skel, fmp_link, fmp_cmp);
if (fmp) {
/* Find link */
}
/* Remove path */
- if (!LIST_FIRST(&fmp->fmp_monitors)) {
+ if (LIST_EMPTY(&fmp->fmp_monitors)) {
tvhdebug("fsmonitor", "unwatch %s", fmp->fmp_path);
RB_REMOVE(&fsmonitor_paths, fmp, fmp_link);
inotify_rm_watch(fsmonitor_fd, fmp->fmp_fd);
};
void fsmonitor_init ( void );
+void fsmonitor_done ( void );
int fsmonitor_add ( const char *path, fsmonitor_t *fsm );
void fsmonitor_del ( const char *path, fsmonitor_t *fsm );
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
+#include <signal.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
/* Session main loop */
- while(1) {
+ while(tvheadend_running) {
readmsg:
+ tvhlog(LOG_INFO, "htsp", "read_loop");
if((r = htsp_read_message(htsp, &m, 0)) != 0)
return r;
htsmsg_destroy(m);
}
+ return 0;
}
/**
pthread_mutex_lock(&htsp->htsp_out_mutex);
- while(1) {
+ while(tvheadend_running) {
if((hmq = TAILQ_FIRST(&htsp->htsp_active_output_queues)) == NULL) {
/* No active queues at all */
htsmsg_add_str(m, "user", htsp->htsp_username);
}
+/*
+ * Cancel callback
+ */
+static void
+htsp_server_cancel ( void *opaque )
+{
+}
+
/**
* Fire up HTSP server
*/
.start = htsp_serve,
.stop = NULL,
.status = htsp_server_status,
+ .cancel = htsp_server_cancel
};
htsp_server = tcp_server_create(bindaddr, tvheadend_htsp_port, &ops, NULL);
if(tvheadend_htsp_port_extra)
htsp_server_2 = tcp_server_create(bindaddr, tvheadend_htsp_port_extra, &ops, NULL);
}
+/**
+ * Fire down HTSP server
+ */
+void
+htsp_done(void)
+{
+ if (htsp_server_2)
+ tcp_server_delete(htsp_server_2);
+ if (htsp_server)
+ tcp_server_delete(htsp_server);
+}
+
/* **************************************************************************
* Asynchronous updates
* *************************************************************************/
#include "dvr/dvr.h"
void htsp_init(const char *bindaddr);
+void htsp_done(void);
void htsp_channel_update_nownext(channel_t *ch);
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
+#include <signal.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
{
htsbuf_queue_t spill;
http_connection_t hc;
-
+
// Note: global_lock held on entry */
pthread_mutex_unlock(&global_lock);
memset(&hc, 0, sizeof(http_connection_t));
};
http_server = tcp_server_create(bindaddr, tvheadend_webui_port, &ops, NULL);
}
+
+void
+http_server_done(void)
+{
+ http_path_t *hp;
+
+ pthread_mutex_lock(&global_lock);
+ while ((hp = LIST_FIRST(&http_paths)) != NULL) {
+ LIST_REMOVE(hp, hp_link);
+ free((void *)hp->hp_path);
+ free(hp);
+ }
+ pthread_mutex_unlock(&global_lock);
+ tcp_server_delete(http_server);
+}
http_callback_t *callback, uint32_t accessmask);
void http_server_init(const char *bindaddr);
+void http_server_done(void);
int http_access_verify(http_connection_t *hc, int mask);
typedef void (http_client_fail_cb) (void *p);
void http_client_init ( void );
+void http_client_done ( void );
http_client_t*
http_connect ( const url_t *url,
http_client_conn_cb conn_cb,
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
+#include <signal.h>
/*
tvhpoll_event_t ev;
http_client_t *hc;
- while (1) {
+ while (tvheadend_running) {
n = tvhpoll_wait(http_poll, &ev, 1, -1);
if (n < 0) {
tvherror("http_client", "tvhpoll_wait() error");
/*
* Initialise subsystem
*/
+pthread_t http_client_tid;
+
void
http_client_init ( void )
{
- pthread_t tid;
-
/* Setup list */
pthread_mutex_init(&http_lock, NULL);
TAILQ_INIT(&http_clients);
http_poll = tvhpoll_create(10);
/* Setup thread */
- tvhthread_create(&tid, NULL, http_thread, NULL, 1);
+ tvhthread_create(&http_client_tid, NULL, http_thread, NULL, 0);
+}
+
+void
+http_client_done ( void )
+{
+ pthread_kill(http_client_tid, SIGTERM);
+ pthread_join(http_client_tid, NULL);
+ tvhpoll_destroy(http_poll);
+ curl_multi_cleanup(http_curlm);
+ curl_global_cleanup();
}
#else /* ENABLE_CURL */
{
}
+void
+http_client_done ( void )
+{
+}
+
#endif /* ENABLE_CURL */
static htsmsg_t *idnode_queue;
static void* idnode_thread(void* p);
+SKEL_DECLARE(idclasses_skel, idclass_link_t);
+
static void
idclass_register(const idclass_t *idc);
/**
*
*/
+pthread_t idnode_tid;
+
void
idnode_init(void)
{
- pthread_t tid;
-
randfd = open("/dev/urandom", O_RDONLY);
if(randfd == -1)
exit(1);
idnode_queue = NULL;
pthread_mutex_init(&idnode_mutex, NULL);
pthread_cond_init(&idnode_cond, NULL);
- tvhthread_create(&tid, NULL, idnode_thread, NULL, 1);
+ tvhthread_create(&idnode_tid, NULL, idnode_thread, NULL, 0);
+}
+
+void
+idnode_done(void)
+{
+ idclass_link_t *il;
+
+ pthread_cond_signal(&idnode_cond);
+ pthread_join(idnode_tid, NULL);
+ pthread_mutex_lock(&idnode_mutex);
+ htsmsg_destroy(idnode_queue);
+ idnode_queue = NULL;
+ pthread_mutex_unlock(&idnode_mutex);
+ while ((il = RB_FIRST(&idclasses)) != NULL) {
+ RB_REMOVE(&idclasses, il, link);
+ free(il);
+ }
+ SKEL_FREE(idclasses_skel);
}
/**
static void
idclass_register(const idclass_t *idc)
{
- static idclass_link_t *skel = NULL;
while (idc) {
- if (!skel)
- skel = calloc(1, sizeof(idclass_link_t));
- skel->idc = idc;
- if (RB_INSERT_SORTED(&idclasses, skel, link, ic_cmp))
+ SKEL_ALLOC(idclasses_skel);
+ idclasses_skel->idc = idc;
+ if (RB_INSERT_SORTED(&idclasses, idclasses_skel, link, ic_cmp))
break;
+ SKEL_USED(idclasses_skel);
tvhtrace("idnode", "register class %s", idc->ic_class);
- skel = NULL;
idc = idc->ic_super;
}
}
{
const char *uuid = idnode_uuid_as_str(in);
+ if (!tvheadend_running)
+ return;
+
/* Forced */
if (chn || force) {
htsmsg_t *m = htsmsg_create_map();
pthread_mutex_lock(&idnode_mutex);
- while (1) {
+ while (tvheadend_running) {
/* Get queue */
if (!idnode_queue) {
pthread_mutex_lock(&idnode_mutex);
}
if (q) htsmsg_destroy(q);
+ pthread_mutex_unlock(&idnode_mutex);
return NULL;
}
typedef LIST_HEAD(,idnode_filter_ele) idnode_filter_t;
void idnode_init(void);
+void idnode_done(void);
int idnode_insert(idnode_t *in, const char *uuid, const idclass_t *idc);
void idnode_unlink(idnode_t *in);
imagecache_image_t *img;
pthread_mutex_lock(&global_lock);
- while (1) {
+ while (tvheadend_running) {
/* Check we're enabled */
if (!imagecache_conf.enabled) {
/* Fetch */
(void)imagecache_image_fetch(img);
}
+ pthread_mutex_unlock(&global_lock);
+ fprintf(stderr, "imagecache thread end\n");
return NULL;
}
/*
* Initialise
*/
+#if ENABLE_IMAGECACHE
+pthread_t imagecache_tid;
+#endif
+
void
imagecache_init ( void )
{
/* Start threads */
#if ENABLE_IMAGECACHE
- {
- pthread_t tid;
- tvhthread_create(&tid, NULL, imagecache_thread, NULL, 1);
- }
+ tvhthread_create(&imagecache_tid, NULL, imagecache_thread, NULL, 0);
/* Re-try timer */
// TODO: this could be more efficient by being targetted, however
#endif
}
+/*
+ * Shutdown
+ */
+void
+imagecache_done ( void )
+{
+ imagecache_image_t *img;
+
+#if ENABLE_IMAGECACHE
+ pthread_cond_broadcast(&imagecache_cond);
+ pthread_join(imagecache_tid, NULL);
+#endif
+ while ((img = RB_FIRST(&imagecache_by_url)) != NULL) {
+ RB_REMOVE(&imagecache_by_url, img, url_link);
+ RB_REMOVE(&imagecache_by_id, img, id_link);
+ free((void *)img->url);
+ free(img);
+ }
+}
+
+
#if ENABLE_IMAGECACHE
/*
extern pthread_mutex_t imagecache_mutex;
void imagecache_init ( void );
+void imagecache_done ( void );
htsmsg_t *imagecache_get_config ( void );
int imagecache_set_config ( htsmsg_t *c );
* Functions
*/
- void (*mm_delete) (mpegts_mux_t *mm);
+ void (*mm_delete) (mpegts_mux_t *mm, int delconf);
void (*mm_config_save) (mpegts_mux_t *mm);
void (*mm_display_name) (mpegts_mux_t*, char *buf, size_t len);
int (*mm_is_enabled) (mpegts_mux_t *mm);
pthread_t mi_thread_id;
th_pipe_t mi_thread_pipe;
+ int mi_delivery_running;
+ pthread_t mi_thread_table_id;
+
/*
* Functions
*/
mpegts_input_create0(calloc(1, sizeof(mpegts_input_t)),\
&mpegts_input_class, u, c)
-void mpegts_input_delete ( mpegts_input_t *mi );
+void mpegts_input_delete ( mpegts_input_t *mi, int delconf );
#define mpegts_input_find(u) idnode_find(u, &mpegts_input_class);
( const idclass_t *idc,
mpegts_network_t *(*build)(const idclass_t *idc, htsmsg_t *conf) );
+void mpegts_network_unregister_builder
+ ( const idclass_t *idc );
+
mpegts_network_t *mpegts_network_build
( const char *clazz, htsmsg_t *conf );
mpegts_mux_t *mpegts_network_find_mux
(mpegts_network_t *mn, uint16_t onid, uint16_t tsid);
-
-void mpegts_network_delete ( mpegts_network_t *mn );
+
+void mpegts_network_class_delete ( const idclass_t *idc, int delconf );
+
+void mpegts_network_delete ( mpegts_network_t *mn, int delconf );
void mpegts_network_schedule_initial_scan
( mpegts_network_t *mm );
#define mpegts_mux_find(u)\
idnode_find(u, &mpegts_mux_class)
-#define mpegts_mux_delete_by_uuid(u)\
- { mpegts_mux_t *mm = mpegts_mux_find(u); if (mm) mm->mm_delete(mm); }
+#define mpegts_mux_delete_by_uuid(u, delconf)\
+ { mpegts_mux_t *mm = mpegts_mux_find(u); if (mm) mm->mm_delete(mm, delconf); }
void mpegts_mux_initial_scan_done ( mpegts_mux_t *mm, int log );
void mpegts_mux_initial_scan_fail ( mpegts_mux_t *mm );
-void mpegts_mux_delete ( mpegts_mux_t *mm );
+void mpegts_mux_delete ( mpegts_mux_t *mm, int delconf );
void mpegts_mux_save ( mpegts_mux_t *mm, htsmsg_t *c );
(mpegts_input_t *mi, mpegts_mux_instance_t *mmi, uint8_t *tsb, size_t len,
int64_t *pcr, uint16_t *pcr_pid, const char *name);
-void *mpegts_input_table_thread ( void *aux );
+void mpegts_input_table_thread_start( mpegts_input_t *mi );
+
+void mpegts_input_table_thread_stop( mpegts_input_t *mi );
int mpegts_input_is_free ( mpegts_input_t *mi );
void mpegts_service_save ( mpegts_service_t *s, htsmsg_t *c );
-void mpegts_service_delete ( service_t *s );
+void mpegts_service_delete ( service_t *s, int delconf );
/*
* MPEG-TS event handler
#endif /* ENABLE_DVBAPI */
+void dvb_done ( void );
+
#endif /* DVB_SUPPORT_H */
_charset_load_file();
}
+/*
+ *
+ */
+void dvb_charset_done ( void )
+{
+ dvb_charset_t *enc;
+
+ while ((enc = LIST_FIRST(&dvb_charset_list)) != NULL) {
+ LIST_REMOVE(enc, link);
+ free((void *)enc->charset);
+ free(enc);
+ }
+}
+
/*
* Find default charset
*/
} dvb_charset_t;
void dvb_charset_init ( void );
+void dvb_charset_done ( void );
struct mpegts_network;
struct mpegts_mux;
#include <linux/dvb/version.h>
#include <linux/dvb/frontend.h>
+SKEL_DECLARE(mpegts_table_state_skel, struct mpegts_table_state);
+
static int
psi_parse_pmt(mpegts_service_t *t, const uint8_t *ptr, int len);
mpegts_table_state_find
( mpegts_table_t *mt, int tableid, uint64_t extraid, int last )
{
- static struct mpegts_table_state *st, *skel = NULL;
+ struct mpegts_table_state *st;
/* Find state */
- if (!skel)
- skel = calloc(1, sizeof(*skel));
- skel->tableid = tableid;
- skel->extraid = extraid;
- st = RB_INSERT_SORTED(&mt->mt_state, skel, link, sect_cmp);
+ SKEL_ALLOC(mpegts_table_state_skel);
+ mpegts_table_state_skel->tableid = tableid;
+ mpegts_table_state_skel->extraid = extraid;
+ st = RB_INSERT_SORTED(&mt->mt_state, mpegts_table_state_skel, link, sect_cmp);
if (!st) {
- st = skel;
- skel = NULL;
+ st = mpegts_table_state_skel;
+ SKEL_USED(mpegts_table_state_skel);
mt->mt_incomplete++;
mpegts_table_state_reset(mt, st, last);
}
#include "tvheadend.h"
#include "dvb.h"
#include "dvb_charset_tables.h"
+#include "../mpegts.h"
static int convert_iso_8859[16] = {
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 11, 12, 13
}
#endif /* ENABLE_DVBAPI */
+
+/**
+ *
+ */
+void dvb_done( void )
+{
+ extern SKEL_DECLARE(mpegts_table_state_skel, struct mpegts_table_state);
+ extern SKEL_DECLARE(mpegts_pid_sub_skel, mpegts_pid_sub_t);
+ extern SKEL_DECLARE(mpegts_pid_skel, mpegts_pid_t);
+
+ SKEL_FREE(mpegts_table_state_skel);
+ SKEL_FREE(mpegts_pid_sub_skel);
+ SKEL_FREE(mpegts_pid_skel);
+}
#define __IPTV_H__
void iptv_init ( void );
+void iptv_done ( void );
#endif /* __IPTV_H__ */
#include <unistd.h>
#include <regex.h>
#include <errno.h>
+#include <signal.h>
+#include <pthread.h>
/* **************************************************************************
* IPTV state
* *************************************************************************/
-iptv_input_t iptv_input;
-iptv_network_t iptv_network;
+iptv_input_t *iptv_input;
+iptv_network_t *iptv_network;
tvhpoll_t *iptv_poll;
pthread_t iptv_thread;
pthread_mutex_t iptv_lock;
c++;
/* Limit reached */
- if (iptv_network.in_max_streams && c >= iptv_network.in_max_streams) {
+ if (iptv_network->in_max_streams && c >= iptv_network->in_max_streams) {
return 0;
}
/* Bandwidth reached */
- if (iptv_network.in_bw_limited) {
+ if (iptv_network->in_bw_limited) {
return 0;
}
im->mm_iptv_tsb = NULL;
/* Clear bw limit */
- iptv_network.in_bw_limited = 0;
+ iptv_network->in_bw_limited = 0;
pthread_mutex_unlock(&iptv_lock);
}
iptv_mux_t *im;
tvhpoll_event_t ev;
- while ( 1 ) {
+ while ( tvheadend_running ) {
nfds = tvhpoll_wait(iptv_poll, &ev, 1, -1);
if ( nfds < 0 ) {
tvhlog(LOG_ERR, "iptv", "poll() error %s, sleeping 1 second",
iptv_input_recv_packets ( iptv_mux_t *im, size_t off, size_t len )
{
static time_t t1 = 0, t2;
- iptv_network.in_bps += len * 8;
+ iptv_network->in_bps += len * 8;
time(&t2);
if (t2 != t1) {
- if (iptv_network.in_max_bandwidth &&
- iptv_network.in_bps > iptv_network.in_max_bandwidth * 1024) {
- if (!iptv_network.in_bw_limited) {
+ if (iptv_network->in_max_bandwidth &&
+ iptv_network->in_bps > iptv_network->in_max_bandwidth * 1024) {
+ if (!iptv_network->in_bw_limited) {
tvhinfo("iptv", "bandwidth limited exceeded");
- iptv_network.in_bw_limited = 1;
+ iptv_network->in_bw_limited = 1;
}
}
- iptv_network.in_bps = 0;
+ iptv_network->in_bps = 0;
t1 = t2;
}
/* Pass on */
im->mm_iptv_pos
- = mpegts_input_recv_packets((mpegts_input_t*)&iptv_input,
+ = mpegts_input_recv_packets((mpegts_input_t*)iptv_input,
im->mm_active,
im->mm_iptv_tsb + off,
im->mm_iptv_pos + len - off,
void iptv_init ( void )
{
- pthread_t tid;
htsmsg_t *conf;
const char *uuid = NULL;
iptv_http_init();
iptv_udp_init();
+ iptv_input = calloc(1, sizeof(iptv_input_t));
+
/* Init Input */
- mpegts_input_create0((mpegts_input_t*)&iptv_input,
+ mpegts_input_create0((mpegts_input_t*)iptv_input,
&iptv_input_class, NULL, NULL);
- iptv_input.mi_start_mux = iptv_input_start_mux;
- iptv_input.mi_stop_mux = iptv_input_stop_mux;
- iptv_input.mi_is_free = iptv_input_is_free;
- iptv_input.mi_get_weight = iptv_input_get_weight;
- iptv_input.mi_display_name = iptv_input_display_name;
- iptv_input.mi_enabled = 1;
+ iptv_input->mi_start_mux = iptv_input_start_mux;
+ iptv_input->mi_stop_mux = iptv_input_stop_mux;
+ iptv_input->mi_is_free = iptv_input_is_free;
+ iptv_input->mi_get_weight = iptv_input_get_weight;
+ iptv_input->mi_display_name = iptv_input_display_name;
+ iptv_input->mi_enabled = 1;
/* Load settings */
if ((conf = hts_settings_load("input/iptv/config")))
uuid = htsmsg_get_str(conf, "uuid");
+ iptv_network = calloc(1, sizeof(iptv_network_t));
+
/* Init Network */
- mpegts_network_create0((mpegts_network_t*)&iptv_network,
+ mpegts_network_create0((mpegts_network_t*)iptv_network,
&iptv_network_class, uuid, "IPTV Network", conf);
- iptv_network.mn_create_service = iptv_network_create_service;
- iptv_network.mn_mux_class = iptv_network_mux_class;
- iptv_network.mn_mux_create2 = iptv_network_create_mux2;
- iptv_network.mn_config_save = iptv_network_config_save;
+ iptv_network->mn_create_service = iptv_network_create_service;
+ iptv_network->mn_mux_class = iptv_network_mux_class;
+ iptv_network->mn_mux_create2 = iptv_network_create_mux2;
+ iptv_network->mn_config_save = iptv_network_config_save;
/* Defaults */
if (!conf) {
- iptv_network.mn_skipinitscan = 1;
+ iptv_network->mn_skipinitscan = 1;
}
/* Link */
- mpegts_input_set_network((mpegts_input_t*)&iptv_input,
- (mpegts_network_t*)&iptv_network);
+ mpegts_input_set_network((mpegts_input_t*)iptv_input,
+ (mpegts_network_t*)iptv_network);
/* Set table thread */
- tvhthread_create(&tid, NULL, mpegts_input_table_thread, &iptv_input, 1);
+ mpegts_input_table_thread_start((mpegts_input_t *)iptv_input);
/* Setup TS thread */
iptv_poll = tvhpoll_create(10);
pthread_mutex_init(&iptv_lock, NULL);
- tvhthread_create(&iptv_thread, NULL, iptv_input_thread, NULL, 1);
+ tvhthread_create(&iptv_thread, NULL, iptv_input_thread, NULL, 0);
/* Load config */
iptv_mux_load_all();
}
+void iptv_done ( void )
+{
+ mpegts_input_table_thread_stop((mpegts_input_t *)iptv_input);
+ pthread_kill(iptv_thread, SIGTERM);
+ pthread_join(iptv_thread, NULL);
+ tvhpoll_destroy(iptv_poll);
+ pthread_mutex_lock(&global_lock);
+ mpegts_network_delete((mpegts_network_t *)iptv_network, 0);
+ mpegts_input_delete((mpegts_input_t *)iptv_input, 0);
+ pthread_mutex_unlock(&global_lock);
+}
+
/******************************************************************************
* Editor Configuration
*
}
static void
-iptv_mux_delete ( mpegts_mux_t *mm )
+iptv_mux_delete ( mpegts_mux_t *mm, int delconf )
{
- hts_settings_remove("input/iptv/muxes/%s/config",
+ if (delconf)
+ hts_settings_remove("input/iptv/muxes/%s/config",
idnode_uuid_as_str(&mm->mm_id));
- mpegts_mux_delete(mm);
+ mpegts_mux_delete(mm, delconf);
}
static void
( iptv_mux_t *im, uint16_t sid, uint16_t pmt_pid,
const char *uuid, htsmsg_t *conf );
-extern iptv_input_t iptv_input;
-extern iptv_network_t iptv_network;
+extern iptv_input_t *iptv_input;
+extern iptv_network_t *iptv_network;
void iptv_mux_load_all ( void );
void linuxdvb_init ( int mask );
+void linuxdvb_done ( void );
+
idnode_set_t *linuxdvb_root ( void );
#endif /* __TVH_LINUX_DVB_H__ */
/* Initialsie devices */
linuxdvb_adapter_init();
}
+
+void linuxdvb_done ( void )
+{
+ linuxdvb_network_done();
+ linuxdvb_adapter_done();
+ dvb_charset_done();
+ scanfile_done();
+}
/* Setup */
sprintf(buf, "%s [%s]", path, dfi->name);
+ free(la->la_rootpath);
la->la_rootpath = strdup(path);
la->la_name = strdup(buf);
la->la_dvb_number = number;
/* Create */
if (!(la = linuxdvb_adapter_create(uuid, conf, path, a, &dfi))) {
tvhlog(LOG_ERR, "linuxdvb", "failed to create %s", path);
+ htsmsg_destroy(conf);
return; // Note: save to return here as global_lock is held
}
}
}
#endif
pthread_mutex_unlock(&global_lock);
+ htsmsg_destroy(conf);
}
/* Relock before exit */
/* Delete */
tvh_hardware_delete((tvh_hardware_t*)la);
+
+ free(la);
}
}
linuxdvb_adapter_scan();
}
}
+
+void
+linuxdvb_adapter_done ( void )
+{
+ linuxdvb_adapter_t *la;
+ tvh_hardware_t *th, *n;
+
+ pthread_mutex_lock(&global_lock);
+ fsmonitor_del("/dev/dvb", &devdvbmon);
+ fsmonitor_del("/dev", &devmon);
+ for (th = LIST_FIRST(&tvh_hardware); th != NULL; th = n) {
+ n = LIST_NEXT(th, th_link);
+ if (idnode_is_instance(&th->th_id, &linuxdvb_adapter_class)) {
+ la = (linuxdvb_adapter_t*)th;
+ linuxdvb_adapter_del(la->la_rootpath);
+ }
+ }
+ pthread_mutex_unlock(&global_lock);
+}
tvhpoll_add(efd, ev, 2);
/* Read */
- while (1) {
+ while (tvheadend_running) {
nfds = tvhpoll_wait(efd, ev, 1, 10);
if (nfds < 1) continue;
if (ev[0].data.fd != dvr) break;
char id[12], name[256];
linuxdvb_frontend_t *lfe;
htsmsg_t *scconf = NULL;
- pthread_t tid;
/* Internal config ID */
snprintf(id, sizeof(id), "%s #%d", dvb_type2str(dfi->type), number);
pthread_cond_init(&lfe->lfe_dvr_cond, NULL);
/* Start table thread */
- tvhthread_create(&tid, NULL, mpegts_input_table_thread, lfe, 1);
+ mpegts_input_table_thread_start((mpegts_input_t *)lfe);
/* Satconf */
if (conf) {
if (lfe->lfe_fe_fd > 0)
close(lfe->lfe_fe_fd);
+ mpegts_input_table_thread_stop((mpegts_input_t *)lfe);
+
/* Remove from adapter */
LIST_REMOVE(lfe, lfe_link);
linuxdvb_satconf_delete(lfe->lfe_satconf, 0);
/* Finish */
- mpegts_input_delete((mpegts_input_t*)lfe);
+ mpegts_input_delete((mpegts_input_t*)lfe, 0);
}
/******************************************************************************
* *************************************************************************/
static void
-linuxdvb_mux_delete ( mpegts_mux_t *mm );
+linuxdvb_mux_delete ( mpegts_mux_t *mm, int delconf );
extern const idclass_t mpegts_mux_class;
}
static void
-linuxdvb_mux_delete ( mpegts_mux_t *mm )
+linuxdvb_mux_delete ( mpegts_mux_t *mm, int delconf )
{
/* Remove config */
- hts_settings_remove("input/linuxdvb/networks/%s/muxes/%s",
+ if (delconf)
+ hts_settings_remove("input/linuxdvb/networks/%s/muxes/%s",
idnode_uuid_as_str(&mm->mm_network->mn_id),
idnode_uuid_as_str(&mm->mm_id));
/* Delete the mux */
- mpegts_mux_delete(mm);
+ mpegts_mux_delete(mm, delconf);
}
/* **************************************************************************
idnode_uuid_as_str(in));
/* Parent delete */
- mpegts_network_delete(mn);
+ mpegts_network_delete(mn, 1);
}
static const void *
return (mpegts_network_t*)linuxdvb_network_create0(NULL, idc, conf);
}
+static const idclass_t* linuxdvb_network_classes[] = {
+ &linuxdvb_network_dvbt_class,
+ &linuxdvb_network_dvbc_class,
+ &linuxdvb_network_dvbs_class,
+ &linuxdvb_network_atsc_class,
+};
+
void linuxdvb_network_init ( void )
{
htsmsg_t *c, *e;
const char *s;
int i;
- const idclass_t* classes[] = {
- &linuxdvb_network_dvbt_class,
- &linuxdvb_network_dvbc_class,
- &linuxdvb_network_dvbs_class,
- &linuxdvb_network_atsc_class,
- };
-
/* Register class builders */
- for (i = 0; i < ARRAY_SIZE(classes); i++)
- mpegts_network_register_builder(classes[i], linuxdvb_network_builder);
+ for (i = 0; i < ARRAY_SIZE(linuxdvb_network_classes); i++)
+ mpegts_network_register_builder(linuxdvb_network_classes[i],
+ linuxdvb_network_builder);
/* Load settings */
if (!(c = hts_settings_load_r(1, "input/linuxdvb/networks")))
if (!(e = htsmsg_get_map_by_field(f))) continue;
if (!(e = htsmsg_get_map(e, "config"))) continue;
if (!(s = htsmsg_get_str(e, "class"))) continue;
- for (i = 0; i < ARRAY_SIZE(classes); i++) {
- if(!strcmp(classes[i]->ic_class, s)) {
- (void)linuxdvb_network_create0(f->hmf_name, classes[i], e);
+ for (i = 0; i < ARRAY_SIZE(linuxdvb_network_classes); i++) {
+ if(!strcmp(linuxdvb_network_classes[i]->ic_class, s)) {
+ (void)linuxdvb_network_create0(f->hmf_name, linuxdvb_network_classes[i], e);
break;
}
}
htsmsg_destroy(c);
}
+void linuxdvb_network_done ( void )
+{
+ int i;
+
+ pthread_mutex_lock(&global_lock);
+ /* Unregister class builders */
+ for (i = 0; i < ARRAY_SIZE(linuxdvb_network_classes); i++) {
+ mpegts_network_unregister_builder(linuxdvb_network_classes[i]);
+ mpegts_network_class_delete(linuxdvb_network_classes[i], 0);
+ }
+ pthread_mutex_unlock(&global_lock);
+}
+
/* ****************************************************************************
* Search
* ***************************************************************************/
void linuxdvb_adapter_init ( void );
+void linuxdvb_adapter_done ( void );
+
void linuxdvb_adapter_save ( linuxdvb_adapter_t *la );
int linuxdvb_adapter_is_free ( linuxdvb_adapter_t *la );
};
void linuxdvb_network_init ( void );
+void linuxdvb_network_done ( void );
linuxdvb_network_t *linuxdvb_network_find_by_uuid(const char *uuid);
linuxdvb_network_t *linuxdvb_network_create0
if (ls->ls_switch) linuxdvb_switch_destroy(ls->ls_switch);
if (ls->ls_rotor) linuxdvb_rotor_destroy(ls->ls_rotor);
if (ls->ls_en50494) linuxdvb_en50494_destroy(ls->ls_en50494);
- mpegts_input_delete((mpegts_input_t*)ls);
+ mpegts_input_delete((mpegts_input_t*)ls, 1);
}
linuxdvb_satconf_ele_t *
linuxdvb_rotor_destroy(lse->ls_rotor);
if (lse->ls_en50494)
linuxdvb_en50494_destroy(lse->ls_en50494);
- mpegts_input_delete((mpegts_input_t*)lse);
+ mpegts_input_delete((mpegts_input_t*)lse, delconf);
}
+ idnode_unlink(&ls->ls_id);
+ free(ls);
}
/******************************************************************************
linuxdvb_diseqc_destroy ( linuxdvb_diseqc_t *ld )
{
idnode_unlink(&ld->ld_id);
+ free((void *)ld->ld_type);
}
int
scanfile_load_dir(path, NULL, 0);
}
+/*
+ * Destroy the mux list
+ */
+static void
+scanfile_done_region( scanfile_region_list_t *list )
+{
+ scanfile_region_t *reg;
+ scanfile_network_t *net;
+ dvb_mux_conf_t *mux;
+
+ while ((reg = LIST_FIRST(list)) != NULL) {
+ LIST_REMOVE(reg, sfr_link);
+ while ((net = LIST_FIRST(®->sfr_networks)) != NULL) {
+ LIST_REMOVE(net, sfn_link);
+ while ((mux = LIST_FIRST(&net->sfn_muxes)) != NULL) {
+ LIST_REMOVE(mux, dmc_link);
+ free(mux);
+ }
+ free((void *)net->sfn_id);
+ free((void *)net->sfn_name);
+ free(net);
+ }
+ free((void *)reg->sfr_id);
+ free((void *)reg->sfr_name);
+ free(reg);
+ }
+}
+
+void
+scanfile_done ( void )
+{
+ scanfile_done_region(&scanfile_regions_DVBS);
+ scanfile_done_region(&scanfile_regions_DVBT);
+ scanfile_done_region(&scanfile_regions_DVBC);
+ scanfile_done_region(&scanfile_regions_ATSC);
+}
+
/*
* Find scanfile
*/
extern scanfile_region_list_t scanfile_regions_ATSC;
void scanfile_init ( void );
+void scanfile_done ( void );
scanfile_network_t *scanfile_find ( const char *id );
#include <pthread.h>
#include <assert.h>
+SKEL_DECLARE(mpegts_pid_sub_skel, mpegts_pid_sub_t);
+
+
/* **************************************************************************
* Class definition
* *************************************************************************/
mpegts_pid_t *mp;
assert(owner != NULL);
if ((mp = mpegts_mux_find_pid(mm, pid, 1))) {
- static mpegts_pid_sub_t *skel = NULL;
- if (!skel)
- skel = calloc(1, sizeof(mpegts_pid_sub_t));
- skel->mps_type = type;
- skel->mps_owner = owner;
- if (!RB_INSERT_SORTED(&mp->mp_subs, skel, mps_link, mps_cmp)) {
+ SKEL_ALLOC(mpegts_pid_sub_skel);
+ mpegts_pid_sub_skel->mps_type = type;
+ mpegts_pid_sub_skel->mps_owner = owner;
+ if (!RB_INSERT_SORTED(&mp->mp_subs, mpegts_pid_sub_skel, mps_link, mps_cmp)) {
mm->mm_display_name(mm, buf, sizeof(buf));
tvhdebug("mpegts", "%s - open PID %04X (%d) [%d/%p]",
buf, mp->mp_pid, mp->mp_pid, type, owner);
- skel = NULL;
+ SKEL_USED(mpegts_pid_sub_skel);
}
}
return mp;
}
}
-void *
+static void *
mpegts_input_table_thread ( void *aux )
{
mpegts_table_feed_t *mtf;
mpegts_input_t *mi = aux;
- while (1) {
+ pthread_mutex_lock(&mi->mi_delivery_mutex);
+ while (mi->mi_delivery_running) {
/* Wait for data */
- pthread_mutex_lock(&mi->mi_delivery_mutex);
- while(!(mtf = TAILQ_FIRST(&mi->mi_table_feed)))
+ while(!(mtf = TAILQ_FIRST(&mi->mi_table_feed))) {
+ if (!mi->mi_delivery_running)
+ break;
pthread_cond_wait(&mi->mi_table_feed_cond, &mi->mi_delivery_mutex);
- TAILQ_REMOVE(&mi->mi_table_feed, mtf, mtf_link);
- pthread_mutex_unlock(&mi->mi_delivery_mutex);
-
+ }
+ if (mtf)
+ TAILQ_REMOVE(&mi->mi_table_feed, mtf, mtf_link);
+
/* Process */
- pthread_mutex_lock(&global_lock);
- mpegts_input_table_dispatch(mtf->mtf_mux, mtf);
- pthread_mutex_unlock(&global_lock);
+ if (mtf) {
+ pthread_mutex_unlock(&mi->mi_delivery_mutex);
+ pthread_mutex_lock(&global_lock);
+ mpegts_input_table_dispatch(mtf->mtf_mux, mtf);
+ pthread_mutex_unlock(&global_lock);
+ free(mtf);
+ pthread_mutex_lock(&mi->mi_delivery_mutex);
+ }
+ }
+ while ((mtf = TAILQ_FIRST(&mi->mi_table_feed)) != NULL) {
+ TAILQ_REMOVE(&mi->mi_table_feed, mtf, mtf_link);
free(mtf);
}
+ pthread_mutex_unlock(&mi->mi_delivery_mutex);
return NULL;
}
+void
+mpegts_input_table_thread_start( mpegts_input_t *mi )
+{
+ mi->mi_delivery_running = 1;
+ tvhthread_create(&mi->mi_thread_table_id, NULL,
+ mpegts_input_table_thread, mi, 0);
+}
+
+void
+mpegts_input_table_thread_stop( mpegts_input_t *mi )
+{
+ pthread_mutex_lock(&mi->mi_delivery_mutex);
+ mi->mi_delivery_running = 0;
+ pthread_cond_signal(&mi->mi_table_feed_cond);
+ pthread_mutex_unlock(&mi->mi_delivery_mutex);
+ pthread_join(mi->mi_thread_table_id, NULL);
+}
+
void
mpegts_input_flush_mux
( mpegts_input_t *mi, mpegts_mux_t *mm )
}
void
-mpegts_input_delete ( mpegts_input_t *mi )
+mpegts_input_delete ( mpegts_input_t *mi, int delconf )
{
+ mpegts_input_set_network(mi, NULL);
idnode_unlink(&mi->ti_id);
pthread_mutex_destroy(&mi->mi_delivery_mutex);
pthread_cond_destroy(&mi->mi_table_feed_cond);
tvh_pipe_close(&mi->mi_thread_pipe);
LIST_REMOVE(mi, ti_link);
LIST_REMOVE(mi, mi_global_link);
+ free(mi->mi_name);
free(mi);
}
#include <assert.h>
+SKEL_DECLARE(mpegts_pid_skel, mpegts_pid_t);
+
static void
mpegts_mux_initial_scan_timeout ( void *aux );
static void
mpegts_mux_class_delete ( idnode_t *self )
{
mpegts_mux_t *mm = (mpegts_mux_t*)self;
- if (mm->mm_delete) mm->mm_delete(mm);
+ if (mm->mm_delete) mm->mm_delete(mm, 1);
}
static const void *
}
void
-mpegts_mux_delete ( mpegts_mux_t *mm )
+mpegts_mux_delete ( mpegts_mux_t *mm, int delconf )
{
mpegts_mux_instance_t *mmi;
mpegts_network_t *mn = mm->mm_network;
/* Delete services */
while ((s = LIST_FIRST(&mm->mm_services))) {
- service_destroy((service_t*)s);
+ service_destroy((service_t*)s, delconf);
}
/* Free memory */
tvhtrace("mpegts", "%s - flush tables", buf);
mpegts_table_flush_all(mm);
+ tvhtrace("mpegts", "%s - mi=%p", buf, (void *)mi);
/* Flush table data queue */
if (mi)
mpegts_input_flush_mux(mi, mm);
skel.mp_pid = pid;
mp = RB_FIND(&mm->mm_pids, &skel, mp_link, mp_cmp);
} else {
- static mpegts_pid_t *skel = NULL;
- if (!skel)
- skel = calloc(1, sizeof(mpegts_pid_t));
- skel->mp_pid = pid;
- mp = RB_INSERT_SORTED(&mm->mm_pids, skel, mp_link, mp_cmp);
+ SKEL_ALLOC(mpegts_pid_skel);
+ mpegts_pid_skel->mp_pid = pid;
+ mp = RB_INSERT_SORTED(&mm->mm_pids, mpegts_pid_skel, mp_link, mp_cmp);
if (!mp) {
- mp = skel;
- skel = NULL;
+ mp = mpegts_pid_skel;
+ SKEL_USED(mpegts_pid_skel);
mp->mp_fd = -1;
}
}
void
mpegts_network_delete
- ( mpegts_network_t *mn )
+ ( mpegts_network_t *mn, int delconf )
{
mpegts_input_t *mi;
mpegts_mux_t *mm;
/* Delete all muxes */
while ((mm = LIST_FIRST(&mn->mn_muxes))) {
- mm->mm_delete(mm);
+ mm->mm_delete(mm, delconf);
}
/* Check */
return mn;
}
+void
+mpegts_network_class_delete(const idclass_t *idc, int delconf)
+{
+ mpegts_network_t *mn, *n;
+
+ for (mn = LIST_FIRST(&mpegts_network_all); mn != NULL; mn = n) {
+ n = LIST_NEXT(mn, mn_global_link);
+ if (mn->mn_id.in_class == idc)
+ mpegts_network_delete(mn, delconf);
+ }
+}
+
int
mpegts_network_set_nid
( mpegts_network_t *mn, uint16_t nid )
LIST_INSERT_HEAD(&mpegts_network_builders, mnb, link);
}
+void
+mpegts_network_unregister_builder
+ ( const idclass_t *idc )
+{
+ mpegts_network_builder_t *mnb;
+ LIST_FOREACH(mnb, &mpegts_network_builders, link) {
+ if (mnb->idc == idc) {
+ LIST_REMOVE(mnb, link);
+ free(mnb);
+ return;
+ }
+ }
+}
+
mpegts_network_t *
mpegts_network_build
( const char *clazz, htsmsg_t *conf )
}
void
-mpegts_service_delete ( service_t *t )
+mpegts_service_delete ( service_t *t, int delconf )
{
mpegts_service_t *ms = (mpegts_service_t*)t;
mpegts_mux_t *mm = ms->s_dvb_mux;
/* Remove config */
- hts_settings_remove("input/linuxdvb/networks/%s/muxes/%s/services/%s",
+ if (delconf)
+ hts_settings_remove("input/linuxdvb/networks/%s/muxes/%s/services/%s",
idnode_uuid_as_str(&mm->mm_network->mn_id),
idnode_uuid_as_str(&mm->mm_id),
idnode_uuid_as_str(&t->s_id));
free(ms->s_dvb_provider);
free(ms->s_dvb_charset);
LIST_REMOVE(ms, s_dvb_mux_link);
+ sbuf_free(&ms->s_tsbuf);
// Note: the ultimate deletion and removal from the idnode list
// is done in service_destroy
mpegts_input_t *
tsfile_input_create ( int idx )
{
- pthread_t tid;
mpegts_input_t *mi;
/* Create object */
mi->mi_name = strdup("TSFile");
/* Start table thread */
- tvhthread_create(&tid, NULL, mpegts_input_table_thread, mi, 1);
+ mpegts_input_table_thread_start(mi);
return mi;
}
return ret;
}
+
+static void lang_code_free( lang_code_lookup_t *l )
+{
+ lang_code_lookup_element_t *element;
+ if (l == NULL)
+ return;
+ while ((element = RB_FIRST(l)) != NULL) {
+ RB_REMOVE(l, element, link);
+ free(element);
+ }
+ free(l);
+}
+
+void lang_code_done( void )
+{
+ lang_code_free(lang_codes_code2b);
+ lang_code_free(lang_codes_code1);
+ lang_code_free(lang_codes_code2t);
+}
typedef RB_HEAD(lang_code_lookup, lang_code_lookup_element) lang_code_lookup_t;
+void lang_code_done( void );
+
#endif /* __TVH_LANG_CODES_H__ */
#include "redblack.h"
#include "lang_codes.h"
#include "lang_str.h"
+#include "tvheadend.h"
+
+SKEL_DECLARE(lang_str_ele_skel, lang_str_ele_t);
/* ************************************************************************
* Support
( lang_str_t *ls, const char *str, const char *lang, int update, int append )
{
int save = 0;
- static lang_str_ele_t *skel = NULL;
lang_str_ele_t *e;
if (!str) return 0;
if (!(lang = lang_code_get(lang))) return 0;
/* Create skel */
- if (!skel) skel = calloc(1, sizeof(lang_str_ele_t));
- skel->lang = lang;
+ SKEL_ALLOC(lang_str_ele_skel);
+ lang_str_ele_skel->lang = lang;
/* Create */
- e = RB_INSERT_SORTED(ls, skel, link, _lang_cmp);
+ e = RB_INSERT_SORTED(ls, lang_str_ele_skel, link, _lang_cmp);
if (!e) {
- skel->str = strdup(str);
- skel = NULL;
+ lang_str_ele_skel->str = strdup(str);
+ SKEL_USED(lang_str_ele_skel);
save = 1;
/* Append */
}
return ret;
}
+
+void lang_str_done( void )
+{
+ SKEL_FREE(lang_str_ele_skel);
+}
lang_str_t *lang_str_deserialize
( htsmsg_t *m, const char *f );
+/* Init/Done */
+void lang_str_done( void );
+
#endif /* __TVH_LANG_STR_H__ */
#include "imagecache.h"
#include "timeshift.h"
#include "fsmonitor.h"
+#include "lang_codes.h"
#if ENABLE_LIBAV
#include "libav.h"
#include "plumbing/transcoding.h"
#include <sys/prctl.h>
#endif
+pthread_t main_tid;
+
/* Command line option struct */
typedef struct str_list
{
return;
}
-static void
+void
doexit(int x)
{
+ if (pthread_self() != main_tid)
+ pthread_kill(main_tid, SIGTERM);
+ pthread_cond_signal(>imer_cond);
tvheadend_running = 0;
+ signal(x, doexit);
}
static int
const char *log_debug = NULL, *log_trace = NULL;
char buf[512];
+ main_tid = pthread_self();
+
/* Setup global mutexes */
pthread_mutex_init(&ffmpeg_lock, NULL);
pthread_mutex_init(&fork_lock, NULL);
umask(0);
}
+ tvheadend_running = 1;
+
/* Start log thread (must be done post fork) */
tvhlog_start();
if(opt_subscribe != NULL)
subscription_dummy_join(opt_subscribe, 1);
-#ifdef CONFIG_AVAHI
avahi_init();
-#endif
epg_updated(); // cleanup now all prev ref's should have been created
* Wait for SIGTERM / SIGINT, but only in this thread
*/
- tvheadend_running = 1;
sigemptyset(&set);
sigaddset(&set, SIGTERM);
sigaddset(&set, SIGINT);
mainloop();
+ tvhftrace("main", htsp_done);
+ tvhftrace("main", http_server_done);
+ tvhftrace("main", webui_done);
+ tvhftrace("main", http_client_done);
+ tvhftrace("main", fsmonitor_done);
+#if ENABLE_IPTV
+ tvhftrace("main", iptv_done);
+#endif
+#if ENABLE_LINUXDVB
+ tvhftrace("main", linuxdvb_done);
+#endif
+
// Note: the locking is obviously a bit redundant, but without
// we need to disable the gtimer_arm call in epg_save()
pthread_mutex_lock(&global_lock);
- epg_save(NULL);
+ tvhftrace("main", epg_save);
#if ENABLE_TIMESHIFT
- timeshift_term();
+ tvhftrace("main", timeshift_term);
#endif
pthread_mutex_unlock(&global_lock);
+ tvhftrace("main", epggrab_done);
+ tvhftrace("main", tcp_server_done);
+ tvhftrace("main", subscription_done);
+ tvhftrace("main", descrambler_done);
+ tvhftrace("main", service_mapper_done);
+ tvhftrace("main", service_done);
+ tvhftrace("main", channel_done);
+ tvhftrace("main", dvr_done);
+ tvhftrace("main", access_done);
+ tvhftrace("main", epg_done);
+ tvhftrace("main", avahi_done);
+ tvhftrace("main", imagecache_done);
+ tvhftrace("main", idnode_done);
+ tvhftrace("main", lang_code_done);
+ tvhftrace("main", api_done);
+ tvhftrace("main", config_done);
+ tvhftrace("main", hts_settings_done);
+ tvhftrace("main", dvb_done);
+ tvhftrace("main", lang_str_done);
+
tvhlog(LOG_NOTICE, "STOP", "Exiting HTS Tvheadend");
tvhlog_end();
if(opt_fork)
unlink(opt_pidpath);
+
+ free(opt_tsfile.str);
return 0;
}
void
service_stream_destroy(service_t *t, elementary_stream_t *es)
{
+ caid_t *c;
+
if(t->s_status == SERVICE_RUNNING)
stream_clean(es);
avgstat_flush(&es->es_cc_errors);
TAILQ_REMOVE(&t->s_components, es, es_link);
+
+ while ((c = LIST_FIRST(&es->es_caids)) != NULL) {
+ LIST_REMOVE(c, link);
+ free(c);
+ }
+
free(es->es_section);
free(es->es_nicename);
free(es);
void
service_unref(service_t *t)
{
- if((atomic_add(&t->s_refcount, -1)) == 1)
+ if((atomic_add(&t->s_refcount, -1)) == 1) {
+ free(t->s_nicename);
free(t);
+ }
}
* Destroy a service
*/
void
-service_destroy(service_t *t)
+service_destroy(service_t *t, int delconf)
{
elementary_stream_t *st;
th_subscription_t *s;
channel_service_mapping_t *csm;
if(t->s_delete != NULL)
- t->s_delete(t);
+ t->s_delete(t, delconf);
lock_assert(&global_lock);
int restart;
pthread_mutex_lock(&pending_save_mutex);
- while(1) {
+ while(tvheadend_running) {
if((t = TAILQ_FIRST(&pending_save_queue)) == NULL) {
pthread_cond_wait(&pending_save_cond, &pending_save_mutex);
/**
*
*/
+pthread_t service_saver_tid;
+
void
service_init(void)
{
- pthread_t tid;
TAILQ_INIT(&pending_save_queue);
TAILQ_INIT(&service_all);
pthread_mutex_init(&pending_save_mutex, NULL);
pthread_cond_init(&pending_save_cond, NULL);
- tvhthread_create(&tid, NULL, service_saver, NULL, 1);
+ tvhthread_create(&service_saver_tid, NULL, service_saver, NULL, 0);
}
+void
+service_done(void)
+{
+ pthread_cond_signal(&pending_save_cond);
+ pthread_join(service_saver_tid, NULL);
+}
/**
*
int (*s_grace_period)(struct service *t);
- void (*s_delete)(struct service *t);
+ void (*s_delete)(struct service *t, int delconf);
/**
* Channel info
void service_init(void);
+void service_done(void);
int service_start(service_t *t, int instance);
int service_is_encrypted ( service_t *t );
-void service_destroy(service_t *t);
+void service_destroy(service_t *t, int delconf);
void service_remove_subscriber(service_t *t, struct th_subscription *s,
int reason);
/**
* Initialise
*/
+pthread_t service_mapper_tid;
+
void
service_mapper_init ( void )
{
- pthread_t tid;
TAILQ_INIT(&service_mapper_queue);
pthread_cond_init(&service_mapper_cond, NULL);
- tvhthread_create(&tid, NULL, service_mapper_thread, NULL, 1);
+ tvhthread_create(&service_mapper_tid, NULL, service_mapper_thread, NULL, 0);
+}
+
+void
+service_mapper_done ( void )
+{
+ pthread_cond_signal(&service_mapper_cond);
+ pthread_join(service_mapper_tid, NULL);
}
/*
pthread_mutex_lock(&global_lock);
- while (1) {
+ while (tvheadend_running) {
/* Wait for work */
while (!(s = TAILQ_FIRST(&service_mapper_queue))) {
tvhinfo("service_mapper", "idle");
}
pthread_cond_wait(&service_mapper_cond, &global_lock);
+ if (!tvheadend_running)
+ break;
}
+ if (!tvheadend_running)
+ break;
service_mapper_remove(s);
if (!working) {
/* Wait */
run = 1;
pthread_mutex_lock(&sq.sq_mutex);
- while(run) {
+ while(tvheadend_running && run) {
/* Wait for message */
- while((sm = TAILQ_FIRST(&sq.sq_queue)) == NULL)
+ while((sm = TAILQ_FIRST(&sq.sq_queue)) == NULL) {
pthread_cond_wait(&sq.sq_cond, &sq.sq_mutex);
+ if (!tvheadend_running)
+ break;
+ }
+ if (!tvheadend_running)
+ break;
+
TAILQ_REMOVE(&sq.sq_queue, sm, sm_link);
pthread_mutex_unlock(&sq.sq_mutex);
streaming_msg_free(sm);
pthread_mutex_lock(&sq.sq_mutex);
}
+ if (!tvheadend_running)
+ break;
streaming_queue_clear(&sq.sq_queue);
pthread_mutex_unlock(&sq.sq_mutex);
service_mapper_stat.active = NULL;
api_service_mapper_notify();
}
+
+ pthread_mutex_unlock(&global_lock);
return NULL;
}
} service_mapper_status_t;
void service_mapper_init ( void );
+void service_mapper_done ( void );
// Start new mapping
void service_mapper_start
}
}
+/**
+ *
+ */
+void
+hts_settings_done(void)
+{
+ free(settingspath);
+}
+
/**
*
*/
void hts_settings_init(const char *confpath);
+void hts_settings_done(void);
+
void hts_settings_save(htsmsg_t *record, const char *pathfmt, ...);
htsmsg_t *hts_settings_load(const char *pathfmt, ...);
if(s->ths_start_message != NULL)
streaming_msg_free(s->ths_start_message);
+
+ if(s->ths_output->st_cb == subscription_input_null)
+ free(s->ths_output);
free(s->ths_title);
free(s->ths_hostname);
subscription_status_callback(NULL);
}
+/**
+ * Shutdown subsystem
+ */
+void
+subscription_done(void)
+{
+ th_subscription_t *s;
+
+ pthread_mutex_lock(&global_lock);
+ while ((s = LIST_FIRST(&subscriptions)) != NULL)
+ subscription_unsubscribe(s);
+ pthread_mutex_unlock(&global_lock);
+}
+
/* **************************************************************************
* Subscription control
* *************************************************************************/
*/
void subscription_init(void);
+void subscription_done(void);
+
void subscription_unsubscribe(th_subscription_t *s);
void subscription_set_weight(th_subscription_t *s, unsigned int weight);
#include <stdarg.h>
#include <fcntl.h>
#include <errno.h>
+#include <signal.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include "notify.h"
int tcp_preferred_address_family = AF_INET;
+int tcp_server_running;
+th_pipe_t tcp_server_pipe;
/**
*
x = poll(&fds, 1, timeout);
if(x == 0)
return ETIMEDOUT;
+ if(x == -1) {
+ if (errno == EAGAIN)
+ continue;
+ return errno;
+ }
x = recv(fd, buf + tot, len - tot, MSG_DONTWAIT);
if(x == -1) {
} tcp_server_t;
typedef struct tcp_server_launch {
+ pthread_t tid;
int fd;
tcp_server_ops_t ops;
void *opaque;
struct sockaddr_storage self;
time_t started;
LIST_ENTRY(tcp_server_launch) link;
+ LIST_ENTRY(tcp_server_launch) alink;
} tcp_server_launch_t;
static LIST_HEAD(, tcp_server_launch) tcp_server_launches = { 0 };
+static LIST_HEAD(, tcp_server_launch) tcp_server_active = { 0 };
/**
*
LIST_REMOVE(tsl, link);
notify_reload("connections");
}
+ LIST_REMOVE(tsl, alink);
pthread_mutex_unlock(&global_lock);
free(tsl);
tvhpoll_event_t ev;
tcp_server_t *ts;
tcp_server_launch_t *tsl;
- pthread_attr_t attr;
- pthread_t tid;
socklen_t slen;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-
- while(1) {
+ while(tcp_server_running) {
r = tvhpoll_wait(tcp_server_poll, &ev, 1, -1);
if(r == -1) {
perror("tcp_server: tvhpoll_wait");
if (r == 0) continue;
ts = ev.data.ptr;
+ if (ts == (tcp_server_t *)&tcp_server_pipe)
+ break;
if(ev.events & TVHPOLL_HUP) {
close(ts->serverfd);
continue;
}
- tvhthread_create(&tid, &attr, tcp_server_start, tsl, 1);
+ pthread_mutex_lock(&global_lock);
+ LIST_INSERT_HEAD(&tcp_server_active, tsl, alink);
+ pthread_mutex_unlock(&global_lock);
+ tvhthread_create(&tsl->tid, NULL, tcp_server_start, tsl, 0);
}
}
+ tvhtrace("tcp", "server thread finished");
return NULL;
}
return ts;
}
+/**
+ *
+ */
+void
+tcp_server_delete(void *server)
+{
+ tcp_server_t *ts = server;
+ tvhpoll_event_t ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.fd = ts->serverfd;
+ ev.events = TVHPOLL_IN;
+ ev.data.ptr = ts;
+ tvhpoll_rem(tcp_server_poll, &ev, 1);
+ free(ts);
+}
+
/*
* Connections status
*/
/**
*
*/
+pthread_t tcp_server_tid;
+
void
tcp_server_init(int opt_ipv6)
{
- pthread_t tid;
-
+ tvhpoll_event_t ev;
if(opt_ipv6)
tcp_preferred_address_family = AF_INET6;
+ tvh_pipe(O_NONBLOCK, &tcp_server_pipe);
tcp_server_poll = tvhpoll_create(10);
- tvhthread_create(&tid, NULL, tcp_server_loop, NULL, 1);
+
+ memset(&ev, 0, sizeof(ev));
+ ev.fd = tcp_server_pipe.rd;
+ ev.events = TVHPOLL_IN;
+ ev.data.ptr = &tcp_server_pipe;
+ tvhpoll_add(tcp_server_poll, &ev, 1);
+
+ tcp_server_running = 1;
+ tvhthread_create(&tcp_server_tid, NULL, tcp_server_loop, NULL, 0);
+}
+
+void
+tcp_server_done(void)
+{
+ pthread_t tid;
+ tcp_server_launch_t *tsl;
+ char c = 'E';
+
+ tcp_server_running = 0;
+ write(tcp_server_pipe.wr, &c, 1);
+
+ pthread_mutex_lock(&global_lock);
+ LIST_FOREACH(tsl, &tcp_server_active, alink) {
+ if (tsl->ops.cancel)
+ tsl->ops.cancel(tsl->opaque);
+ close(tsl->fd);
+ tsl->fd = -1;
+ pthread_kill(tsl->tid, SIGTERM);
+ }
+ pthread_mutex_unlock(&global_lock);
+
+ pthread_join(tcp_server_tid, NULL);
+ tvh_pipe_close(&tcp_server_pipe);
+ tvhpoll_destroy(tcp_server_poll);
+
+ pthread_mutex_lock(&global_lock);
+ while ((tsl = LIST_FIRST(&tcp_server_active)) != NULL) {
+ tid = tsl->tid;
+ pthread_mutex_unlock(&global_lock);
+ pthread_join(tid, NULL);
+ pthread_mutex_lock(&global_lock);
+ }
+ pthread_mutex_unlock(&global_lock);
}
struct sockaddr_storage *self);
void (*stop) (void *opaque);
void (*status) (void *opaque, htsmsg_t *m);
+ void (*cancel) (void *opaque);
} tcp_server_ops_t;
extern int tcp_preferred_address_family;
void tcp_server_init(int opt_ipv6);
+void tcp_server_done(void);
int tcp_connect(const char *hostname, int port, char *errbuf,
size_t errbufsize, int timeout);
void *tcp_server_create(const char *bindaddr, int port,
tcp_server_ops_t *ops, void *opaque);
+void tcp_server_delete(void *server);
+
int tcp_read(int fd, void *buf, size_t len);
char *tcp_read_line(int fd, htsbuf_queue_t *spill);
#define PTS_UNSET INT64_C(0x8000000000000000)
+extern int tvheadend_running;
+
extern pthread_mutex_t global_lock;
extern pthread_mutex_t ffmpeg_lock;
extern pthread_mutex_t fork_lock;
*p = s ? strdup(s) : NULL;
}
+void doexit(int x);
+
int tvhthread_create0
(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg,
char *regexp_escape ( const char *str );
+#define SKEL_DECLARE(name, type) type *name;
+#define SKEL_ALLOC(name) do { if (!name) name = calloc(1, sizeof(*name)); } while (0)
+#define SKEL_USED(name) do { name = NULL; } while (0)
+#define SKEL_FREE(name) do { free(name); name = NULL; } while (0)
+
#ifdef PLATFORM_LINUX
/* glibc wrapper */
#if !__GLIBC_PREREQ(2,8)
/* Wait */
if (!(msg = TAILQ_FIRST(&tvhlog_queue))) {
- if (tvhlog_run != 1) break;
+ if (!tvhlog_run) break;
if (fp) {
fclose(fp); // only issue here is we close with mutex!
// but overall performance will be higher
pthread_cond_wait(&tvhlog_cond, &tvhlog_mutex);
continue;
}
+ if (!msg) break;
TAILQ_REMOVE(&tvhlog_queue, msg, link);
tvhlog_queue_size--;
if (tvhlog_queue_size < (TVHLOG_QUEUE_MAXSIZE / 2))
tvhlog_process(msg, options, &fp, path);
pthread_mutex_lock(&tvhlog_mutex);
}
-
+ if (fp)
+ fclose(fp);
+ pthread_mutex_unlock(&tvhlog_mutex);
return NULL;
}
tvhlog_start ( void )
{
tvhlog_run = 1;
- tvhthread_create(&tvhlog_tid, NULL, tvhlog_thread, NULL, 1);
+ tvhthread_create(&tvhlog_tid, NULL, tvhlog_thread, NULL, 0);
}
void
tvhlog_end ( void )
{
pthread_mutex_lock(&tvhlog_mutex);
- tvhlog_run = 2;
+ tvhlog_run = 0;
pthread_cond_signal(&tvhlog_cond);
pthread_mutex_unlock(&tvhlog_mutex);
pthread_join(tvhlog_tid, NULL);
+ free(tvhlog_path);
+ htsmsg_destroy(tvhlog_debug);
+ htsmsg_destroy(tvhlog_trace);
}
#define tvhlog_hexdump(...) (void)0
#endif
+#define tvhftrace(subsys, fcn) do { \
+ tvhtrace(subsys, "%s() enter", #fcn); \
+ fcn(); \
+ tvhtrace(subsys, "%s() leave", #fcn); \
+} while (0)
+
#define tvhdebug(...) tvhlog(LOG_DEBUG, ##__VA_ARGS__)
#define tvhinfo(...) tvhlog(LOG_INFO, ##__VA_ARGS__)
#define tvhwarn(...) tvhlog(LOG_WARNING, ##__VA_ARGS__)
static LIST_HEAD(, comet_mailbox) mailboxes;
int mailbox_tally;
+int comet_running;
typedef struct comet_mailbox {
char *cmb_boxid; /* SHA-1 hash */
usleep(100000); /* Always sleep 0.1 sec to avoid comet storms */
pthread_mutex_lock(&comet_mutex);
+ if (!comet_running) {
+ pthread_mutex_unlock(&comet_mutex);
+ return 400;
+ }
if(cometid != NULL)
LIST_FOREACH(cmb, &mailboxes, cmb_link)
cmb->cmb_last_used = 0; /* Make sure we're not flushed out */
- if(!im && cmb->cmb_messages == NULL)
+ if(!im && cmb->cmb_messages == NULL) {
pthread_cond_timedwait(&comet_cond, &comet_mutex, &ts);
+ if (!comet_running) {
+ pthread_mutex_unlock(&comet_mutex);
+ return 400;
+ }
+ }
m = htsmsg_create_map();
htsmsg_add_str(m, "boxid", cmb->cmb_boxid);
void
comet_init(void)
{
+ pthread_mutex_lock(&comet_mutex);
+ comet_running = 1;
+ pthread_mutex_unlock(&comet_mutex);
http_path_add("/comet/poll", NULL, comet_mailbox_poll, ACCESS_WEB_INTERFACE);
http_path_add("/comet/debug", NULL, comet_mailbox_dbg, ACCESS_WEB_INTERFACE);
}
+void
+comet_done(void)
+{
+ comet_mailbox_t *cmb;
+
+ pthread_mutex_lock(&comet_mutex);
+ comet_running = 0;
+ while ((cmb = LIST_FIRST(&mailboxes)) != NULL)
+ cmb_destroy(cmb);
+ pthread_mutex_unlock(&comet_mutex);
+}
/**
*
pthread_mutex_lock(&comet_mutex);
- LIST_FOREACH(cmb, &mailboxes, cmb_link) {
+ if (comet_running) {
+ LIST_FOREACH(cmb, &mailboxes, cmb_link) {
- if(isdebug && !cmb->cmb_debug)
- continue;
+ if(isdebug && !cmb->cmb_debug)
+ continue;
- if(cmb->cmb_messages == NULL)
- cmb->cmb_messages = htsmsg_create_list();
- htsmsg_add_msg(cmb->cmb_messages, NULL, htsmsg_copy(m));
+ if(cmb->cmb_messages == NULL)
+ cmb->cmb_messages = htsmsg_create_list();
+ htsmsg_add_msg(cmb->cmb_messages, NULL, htsmsg_copy(m));
+ }
}
pthread_cond_broadcast(&comet_cond);
TAILQ_FOREACH(f, &in->hm_fields, hmf_link) {
if((id = htsmsg_field_get_string(f)) != NULL &&
(t = service_find_by_identifier(id)) != NULL)
- service_destroy(t);
+ service_destroy(t, 1);
}
}
"<epgflush>1</epgflush>\n");
pthread_mutex_lock(&global_lock);
- epg_save(NULL);
+ epg_save();
pthread_mutex_unlock(&global_lock);
http_output_content(hc, "text/xml");
tp.tv_usec = 0;
setsockopt(hc->hc_fd, SOL_SOCKET, SO_SNDTIMEO, &tp, sizeof(tp));
- while(run) {
+ while(run && tvheadend_running) {
pthread_mutex_lock(&sq->sq_mutex);
sm = TAILQ_FIRST(&sq->sq_queue);
if(sm == NULL) {
static void
webui_static_content(const char *http_path, const char *source)
{
- http_path_add(http_path, strdup(source), page_static_file,
+ http_path_add(http_path, (void *)source, page_static_file,
ACCESS_WEB_INTERFACE);
}
webui_api_init();
}
+
+void
+webui_done(void)
+{
+ comet_done();
+}
#include "http.h"
void webui_init(void);
+void webui_done(void);
void simpleui_start(void);
*/
void comet_init(void);
+void comet_done(void);
+
void comet_mailbox_add_message(htsmsg_t *m, int isdebug);
void comet_flush(void);
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
+#include <signal.h>
#include <pthread.h>
#ifdef PLATFORM_LINUX
thread_wrapper ( void *p )
{
struct thread_state *ts = p;
+ sigset_t set;
#if defined(PLATFORM_LINUX)
/* Set name */
pthread_set_name_np(pthread_self(), ts->name);
#endif
+ sigemptyset(&set);
+ sigaddset(&set, SIGTERM);
+ pthread_sigmask(SIG_UNBLOCK, &set, NULL);
+
+ signal(SIGTERM, doexit);
+
/* Run */
tvhdebug("thread", "created thread %ld [%s / %p(%p)]",
(long)pthread_self(), ts->name, ts->run, ts->arg);