From c07824a410a2b2ccaf969a6025caaf749da2eca4 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 17 Feb 2014 11:43:01 +0100 Subject: [PATCH] Graceful exit It's necessary to do serious checks for the memory leaks using tools like valgrind. This patch tries to implement a graceful exit for all tvheadend components and free allocated memory. Also, some memory leaks were fixed. --- src/access.c | 11 +++ src/access.h | 1 + src/api.c | 28 ++++-- src/api.h | 1 + src/api/api_idnode.c | 2 + src/avahi.c | 19 +++- src/avahi.h | 6 ++ src/channels.c | 56 ++++++++--- src/channels.h | 3 +- src/config2.c | 5 + src/config2.h | 1 + src/descrambler.h | 1 + src/descrambler/capmt.c | 21 ++++- src/descrambler/capmt.h | 2 + src/descrambler/cwc.c | 40 ++++++-- src/descrambler/cwc.h | 2 + src/descrambler/descrambler.c | 7 ++ src/dtable.c | 17 ++++ src/dtable.h | 2 + src/dvr/dvr.h | 7 +- src/dvr/dvr_autorec.c | 19 +++- src/dvr/dvr_db.c | 49 ++++++++-- src/dvr/dvr_inotify.c | 39 +++++--- src/epg.c | 15 +++ src/epg.h | 5 +- src/epgdb.c | 45 ++++++--- src/epggrab.c | 46 +++++++-- src/epggrab.h | 4 + src/epggrab/module.c | 4 +- src/epggrab/module/eit.c | 10 +- src/epggrab/module/opentv.c | 28 +++++- src/epggrab/otamux.c | 43 +++++++-- src/epggrab/private.h | 2 + src/fsmonitor.c | 44 ++++++--- src/fsmonitor.h | 1 + src/htsp_server.c | 28 +++++- src/htsp_server.h | 1 + src/http.c | 18 +++- src/http.h | 2 + src/http/http_client.c | 24 ++++- src/idnode.c | 42 +++++++-- src/idnode.h | 1 + src/imagecache.c | 34 ++++++- src/imagecache.h | 1 + src/input/mpegts.h | 28 ++++-- src/input/mpegts/dvb.h | 2 + src/input/mpegts/dvb_charset.c | 14 +++ src/input/mpegts/dvb_charset.h | 1 + src/input/mpegts/dvb_psi.c | 17 ++-- src/input/mpegts/dvb_support.c | 15 +++ src/input/mpegts/iptv.h | 1 + src/input/mpegts/iptv/iptv.c | 79 ++++++++++------ src/input/mpegts/iptv/iptv_mux.c | 7 +- src/input/mpegts/iptv/iptv_private.h | 4 +- src/input/mpegts/linuxdvb.h | 2 + src/input/mpegts/linuxdvb/linuxdvb.c | 8 ++ src/input/mpegts/linuxdvb/linuxdvb_adapter.c | 24 +++++ src/input/mpegts/linuxdvb/linuxdvb_frontend.c | 9 +- src/input/mpegts/linuxdvb/linuxdvb_mux.c | 9 +- src/input/mpegts/linuxdvb/linuxdvb_network.c | 40 +++++--- src/input/mpegts/linuxdvb/linuxdvb_private.h | 3 + src/input/mpegts/linuxdvb/linuxdvb_satconf.c | 7 +- src/input/mpegts/linuxdvb/scanfile.c | 37 ++++++++ src/input/mpegts/linuxdvb/scanfile.h | 1 + src/input/mpegts/mpegts_input.c | 69 ++++++++++---- src/input/mpegts/mpegts_mux.c | 21 +++-- src/input/mpegts/mpegts_network.c | 30 +++++- src/input/mpegts/mpegts_service.c | 6 +- src/input/mpegts/tsfile/tsfile_input.c | 3 +- src/lang_codes.c | 19 ++++ src/lang_codes.h | 2 + src/lang_str.c | 19 ++-- src/lang_str.h | 3 + src/main.c | 54 +++++++++-- src/service.c | 29 ++++-- src/service.h | 5 +- src/service_mapper.c | 32 ++++++- src/service_mapper.h | 1 + src/settings.c | 9 ++ src/settings.h | 2 + src/subscriptions.c | 17 ++++ src/subscriptions.h | 2 + src/tcp.c | 94 +++++++++++++++++-- src/tcp.h | 4 + src/tvheadend.h | 9 ++ src/tvhlog.c | 14 ++- src/tvhlog.h | 6 ++ src/webui/comet.c | 40 ++++++-- src/webui/extjs.c | 2 +- src/webui/simpleui.c | 2 +- src/webui/webui.c | 10 +- src/webui/webui.h | 3 + src/wrappers.c | 8 ++ 93 files changed, 1269 insertions(+), 291 deletions(-) diff --git a/src/access.c b/src/access.c index 19d8eef6..443dfd03 100644 --- a/src/access.c +++ b/src/access.c @@ -520,6 +520,7 @@ access_entry_destroy(access_entry_t *ae) 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); } @@ -769,3 +770,13 @@ access_init(int createdefault, int noacl) 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); +} diff --git a/src/access.h b/src/access.h index 2edb0ab9..e1db01ab 100644 --- a/src/access.h +++ b/src/access.h @@ -111,5 +111,6 @@ uint32_t access_get_by_addr(struct sockaddr *src); * */ void access_init(int createdefault, int noacl); +void access_done(void); #endif /* ACCESS_H_ */ diff --git a/src/api.c b/src/api.c index 7db5fa1d..97546982 100644 --- a/src/api.c +++ b/src/api.c @@ -29,6 +29,7 @@ typedef struct api_link { } 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 ) @@ -39,15 +40,15 @@ static int ah_cmp 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 @@ -125,3 +126,14 @@ void api_init ( 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); +} diff --git a/src/api.h b/src/api.h index c22abe8b..629330e8 100644 --- a/src/api.h +++ b/src/api.h @@ -56,6 +56,7 @@ int api_exec ( const char *subsystem, htsmsg_t *args, htsmsg_t **resp ); * Initialise */ void api_init ( void ); +void api_done ( void ); void api_idnode_init ( void ); void api_input_init ( void ); void api_service_init ( void ); diff --git a/src/api/api_idnode.c b/src/api/api_idnode.c index 26319e32..e8452ac4 100644 --- a/src/api/api_idnode.c +++ b/src/api/api_idnode.c @@ -170,6 +170,8 @@ api_idnode_load_by_class if (e) htsmsg_add_msg(l, NULL, e); } + free(is->is_array); + free(is); } *resp = htsmsg_create_map(); htsmsg_add_msg(*resp, "entries", l); diff --git a/src/avahi.c b/src/avahi.c index c474dd7c..e290f818 100644 --- a/src/avahi.c +++ b/src/avahi.c @@ -54,6 +54,7 @@ static AvahiEntryGroup *group = NULL; static char *name = NULL; +static AvahiSimplePoll *avahi_asp = NULL; static void create_services(AvahiClient *c); @@ -247,14 +248,13 @@ client_callback(AvahiClient *c, AvahiClientState state, void *userdata) 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; @@ -264,10 +264,19 @@ avahi_thread(void *aux) /** * */ +pthread_t avahi_tid; + void avahi_init(void) { - pthread_t tid; - - tvhthread_create(&tid, NULL, avahi_thread, NULL, 1); + avahi_asp = avahi_simple_poll_new(); + tvhthread_create(&avahi_tid, NULL, avahi_thread, NULL, 0); +} + +void +avahi_done(void) +{ + avahi_simple_poll_quit(avahi_asp); + pthread_kill(avahi_tid, SIGTERM); + pthread_join(avahi_tid, NULL); } diff --git a/src/avahi.h b/src/avahi.h index 891820cd..ca10aa01 100644 --- a/src/avahi.h +++ b/src/avahi.h @@ -1 +1,7 @@ +#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 diff --git a/src/channels.c b/src/channels.c index 130d3be6..bddce831 100644 --- a/src/channels.c +++ b/src/channels.c @@ -48,9 +48,12 @@ struct channel_tag_queue channel_tags; 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 @@ -74,7 +77,7 @@ channel_class_save ( idnode_t *self ) static void channel_class_delete ( idnode_t *self ) { - channel_delete((channel_t*)self); + channel_delete((channel_t*)self, 1); } static const void * @@ -540,7 +543,7 @@ channel_create0 } void -channel_delete ( channel_t *ch ) +channel_delete ( channel_t *ch, int delconf ) { th_subscription_t *s; channel_tag_mapping_t *ctm; @@ -548,14 +551,15 @@ channel_delete ( channel_t *ch ) 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 */ @@ -573,7 +577,8 @@ channel_delete ( channel_t *ch ) 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); @@ -619,6 +624,21 @@ channel_init ( void ) 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 */ @@ -732,15 +752,17 @@ channel_tag_find(const char *id, int create) * */ 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) @@ -878,7 +900,7 @@ channel_tag_record_delete(void *opaque, const char *id) if((ct = channel_tag_find(id, 0)) == NULL) return -1; - channel_tag_destroy(ct); + channel_tag_destroy(ct, 1); return 0; } @@ -949,3 +971,15 @@ channel_tag_init ( void ) 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"); +} diff --git a/src/channels.h b/src/channels.h index 9fa688c5..370ab7c5 100644 --- a/src/channels.h +++ b/src/channels.h @@ -120,6 +120,7 @@ typedef struct channel_service_mapping { 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, @@ -127,7 +128,7 @@ channel_t *channel_create0 #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)\ diff --git a/src/config2.c b/src/config2.c index cfeeb127..f22321b1 100644 --- a/src/config2.c +++ b/src/config2.c @@ -33,6 +33,11 @@ void config_init ( void ) } } +void config_done ( void ) +{ + htsmsg_destroy(config); +} + void config_save ( void ) { hts_settings_save(config, "config"); diff --git a/src/config2.h b/src/config2.h index cd68e306..f090dde1 100644 --- a/src/config2.h +++ b/src/config2.h @@ -24,6 +24,7 @@ #include "htsmsg.h" void config_init ( void ); +void config_done ( void ); void config_save ( void ); htsmsg_t *config_get_all ( void ); diff --git a/src/descrambler.h b/src/descrambler.h index b9ccd50a..9608db44 100755 --- a/src/descrambler.h +++ b/src/descrambler.h @@ -77,6 +77,7 @@ typedef enum { 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); diff --git a/src/descrambler/capmt.c b/src/descrambler/capmt.c index 258bc2bd..e317c9c5 100644 --- a/src/descrambler/capmt.c +++ b/src/descrambler/capmt.c @@ -185,6 +185,8 @@ typedef struct capmt { struct capmt_service_list capmt_services; + pthread_t capmt_tid; + /* from capmt configuration */ char *capmt_sockfile; char *capmt_hostname; @@ -1114,7 +1116,6 @@ capmt_destroy(capmt_t *capmt) static capmt_t * capmt_entry_find(const char *id, int create) { - pthread_t ptid; char buf[20]; capmt_t *capmt; static int tally; @@ -1143,7 +1144,7 @@ capmt_entry_find(const char *id, int create) 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; } @@ -1281,3 +1282,19 @@ capmt_init(void) 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"); +} diff --git a/src/descrambler/capmt.h b/src/descrambler/capmt.h index 5cbf159a..711c95d9 100644 --- a/src/descrambler/capmt.h +++ b/src/descrambler/capmt.h @@ -21,6 +21,8 @@ void capmt_init(void); +void capmt_done(void); + void capmt_service_start(struct service *t); #endif /* CAPMT_H_ */ diff --git a/src/descrambler/cwc.c b/src/descrambler/cwc.c index 9a068476..37fa28cc 100755 --- a/src/descrambler/cwc.c +++ b/src/descrambler/cwc.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -184,9 +185,9 @@ typedef struct cwc_provider { 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; @@ -210,6 +211,8 @@ typedef struct cwc { int cwc_retry_delay; + pthread_t cwc_tid; + pthread_cond_t cwc_cond; pthread_mutex_t cwc_writer_mutex; @@ -465,8 +468,7 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq, uint1 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; @@ -488,7 +490,7 @@ cwc_send_msg(cwc_t *cwc, const uint8_t *msg, size_t len, int sid, int enq, uint1 } 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; @@ -1194,6 +1196,7 @@ cwc_thread(void *aux) cwc->cwc_hostname, cwc->cwc_port); } + if(cwc->cwc_running == 0) continue; if(attempts == 1) continue; // Retry immediately d = 3; @@ -1224,6 +1227,7 @@ cwc_thread(void *aux) 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); @@ -2066,7 +2070,6 @@ cwc_destroy(cwc_t *cwc) static cwc_t * cwc_entry_find(const char *id, int create) { - pthread_t ptid; char buf[20]; cwc_t *cwc; static int tally; @@ -2094,7 +2097,7 @@ cwc_entry_find(const char *id, int create) 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; } @@ -2337,6 +2340,29 @@ cwc_init(void) } +/** + * + */ +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 /* diff --git a/src/descrambler/cwc.h b/src/descrambler/cwc.h index 3c28710a..ace3c657 100644 --- a/src/descrambler/cwc.h +++ b/src/descrambler/cwc.h @@ -21,6 +21,8 @@ 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); diff --git a/src/descrambler/descrambler.c b/src/descrambler/descrambler.c index 8ab6a098..b466e568 100755 --- a/src/descrambler/descrambler.c +++ b/src/descrambler/descrambler.c @@ -115,6 +115,13 @@ descrambler_init ( void ) #endif } +void +descrambler_done ( void ) +{ + capmt_done(); + cwc_done(); +} + void descrambler_service_start ( service_t *t ) { diff --git a/src/dtable.c b/src/dtable.c index f86100f1..f63e75f1 100644 --- a/src/dtable.c +++ b/src/dtable.c @@ -63,6 +63,23 @@ dtable_create(const dtable_class_t *dtc, const char *name, void *opaque) 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); + } +} + /** * */ diff --git a/src/dtable.h b/src/dtable.h index 5a480ac2..033ccec2 100644 --- a/src/dtable.h +++ b/src/dtable.h @@ -58,6 +58,8 @@ typedef struct dtable { 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); diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h index 3e99ca19..30b19d7b 100644 --- a/src/dvr/dvr.h +++ b/src/dvr/dvr.h @@ -296,8 +296,12 @@ dvr_entry_t *dvr_entry_update 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); @@ -384,7 +388,7 @@ void dvr_autorec_check_season(epg_season_t *s); 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); @@ -399,6 +403,7 @@ const char *dvr_val2pri(dvr_prio_t v); * 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 ); diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c index 52fcef68..ec92ce3d 100644 --- a/src/dvr/dvr_autorec.c +++ b/src/dvr/dvr_autorec.c @@ -480,6 +480,20 @@ dvr_autorec_init(void) 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) { @@ -638,13 +652,14 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge) * */ 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); } diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c index 54599054..e80dae06 100644 --- a/src/dvr/dvr_db.c +++ b/src/dvr/dvr_db.c @@ -490,6 +490,7 @@ dvr_entry_dec_ref(dvr_entry_t *de) 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); @@ -506,9 +507,10 @@ dvr_entry_dec_ref(dvr_entry_t *de) * */ 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); @@ -523,6 +525,7 @@ dvr_entry_remove(dvr_entry_t *de) LIST_REMOVE(de, de_global_link); de->de_channel = NULL; free(de->de_channel_name); + de->de_channel_name = NULL; dvrdb_changed(); @@ -729,7 +732,7 @@ static void dvr_timer_expire(void *aux) { dvr_entry_t *de = aux; - dvr_entry_remove(de); + dvr_entry_remove(de, 1); } @@ -857,7 +860,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e) /* 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 { @@ -1025,7 +1028,7 @@ dvr_entry_cancel(dvr_entry_t *de) { switch(de->de_sched_state) { case DVR_SCHEDULED: - dvr_entry_remove(de); + dvr_entry_remove(de, 1); return NULL; case DVR_RECORDING: @@ -1034,11 +1037,11 @@ dvr_entry_cancel(dvr_entry_t *de) 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: @@ -1208,6 +1211,31 @@ dvr_init(void) 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 */ @@ -1306,6 +1334,7 @@ dvr_config_delete(const char *name) 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(); @@ -1647,7 +1676,7 @@ dvr_entry_delete(dvr_entry_t *de) } } - dvr_entry_remove(de); + dvr_entry_remove(de, 1); } /** @@ -1658,7 +1687,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de) { switch(de->de_sched_state) { case DVR_SCHEDULED: - dvr_entry_remove(de); + dvr_entry_remove(de, 1); break; case DVR_RECORDING: @@ -1669,7 +1698,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de) break; case DVR_MISSED_TIME: - dvr_entry_remove(de); + dvr_entry_remove(de, 1); break; default: diff --git a/src/dvr/dvr_inotify.c b/src/dvr/dvr_inotify.c index db2c64f7..15c80e6b 100644 --- a/src/dvr/dvr_inotify.c +++ b/src/dvr/dvr_inotify.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -45,6 +47,8 @@ typedef struct dvr_inotify_entry 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 ) @@ -55,10 +59,10 @@ 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)", @@ -66,7 +70,20 @@ void dvr_inotify_init ( void ) 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); } /** @@ -74,7 +91,6 @@ void dvr_inotify_init ( void ) */ 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; @@ -87,17 +103,16 @@ void dvr_inotify_add ( dvr_entry_t *de ) 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) { @@ -258,6 +273,8 @@ void* _dvr_inotify_thread ( void *p ) from = NULL; i = 0; len = read(_inot_fd, buf, EVENT_BUF_LEN); + if (_inot_fd < 0) + break; /* Process */ pthread_mutex_lock(&global_lock); diff --git a/src/epg.c b/src/epg.c index f2a522d6..03322ccd 100644 --- a/src/epg.c +++ b/src/epg.c @@ -2314,3 +2314,18 @@ char *epg_hash ( const char *t, const char *s, const char *d ) 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; +} diff --git a/src/epg.h b/src/epg.h index f8724e7b..0f177492 100644 --- a/src/epg.h +++ b/src/epg.h @@ -556,7 +556,10 @@ void epg_query(epg_query_result_t *eqr, const char *channel, const char *tag, * ***********************************************************************/ 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); /* ************************************************************************ diff --git a/src/epgdb.c b/src/epgdb.c index 86cbd6d5..ec576d3d 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -100,40 +100,40 @@ static void _epgdb_v1_process ( htsmsg_t *c, epggrab_stats_t *stats ) /* * 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); } } @@ -149,6 +149,7 @@ void epg_init ( void ) 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) { @@ -207,7 +208,7 @@ void epg_init ( void ) /* Process */ switch (ver) { case 2: - _epgdb_v2_process(m, &stats); + _epgdb_v2_process(§, m, &stats); break; default: break; @@ -217,6 +218,8 @@ void epg_init ( void ) htsmsg_destroy(m); } + free(sect); + /* Stats */ tvhlog(LOG_INFO, "epgdb", "loaded v%d", ver); tvhlog(LOG_INFO, "epgdb", " channels %d", stats.channels.total); @@ -230,6 +233,17 @@ void epg_init ( void ) 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 * *************************************************************************/ @@ -264,7 +278,12 @@ static int _epg_write_sect ( int fd, const char *sect ) 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; @@ -274,7 +293,7 @@ void epg_save ( void *p ) 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); diff --git a/src/epggrab.c b/src/epggrab.c index 7a662c8a..33e015b2 100644 --- a/src/epggrab.c +++ b/src/epggrab.c @@ -41,6 +41,7 @@ static int epggrab_confver; pthread_mutex_t epggrab_mutex; static pthread_cond_t epggrab_cond; +int epggrab_running; /* Config */ uint32_t epggrab_interval; @@ -96,7 +97,7 @@ static void* _epggrab_internal_thread ( void* p ) /* 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 { @@ -109,6 +110,9 @@ static void* _epggrab_internal_thread ( void* p ) ts.tv_sec += epggrab_interval; pthread_mutex_unlock(&epggrab_mutex); + if ( !epggrab_running) + break; + /* Run grabber */ if (mod) _epggrab_module_grab(mod); } @@ -143,7 +147,7 @@ static void _epggrab_load ( void ) 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)) { @@ -319,7 +323,7 @@ int epggrab_set_periodicsave ( uint32_t e ) 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; } @@ -368,6 +372,8 @@ void epggrab_resched ( void ) /* * Initialise */ +pthread_t epggrab_tid; + void epggrab_init ( void ) { /* Defaults */ @@ -396,11 +402,33 @@ void epggrab_init ( void ) _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(); +} diff --git a/src/epggrab.h b/src/epggrab.h index 6ab1c02c..f7ea7628 100644 --- a/src/epggrab.h +++ b/src/epggrab.h @@ -221,6 +221,7 @@ struct epggrab_module_ota /* Transponder tuning */ void (*start) ( epggrab_module_ota_t *m, struct mpegts_mux *mm ); + void (*done) ( epggrab_module_ota_t *m ); }; /* @@ -238,6 +239,7 @@ htsmsg_t* epggrab_module_list ( void ); */ 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; @@ -262,8 +264,10 @@ int epggrab_enable_module_by_id ( const char *id, uint8_t e ); * 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 diff --git a/src/epggrab/module.c b/src/epggrab/module.c index a1112d73..da0ac173 100644 --- a/src/epggrab/module.c +++ b/src/epggrab/module.c @@ -345,7 +345,7 @@ static void *_epggrab_socket_thread ( void *p ) 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; @@ -452,6 +452,7 @@ epggrab_module_ota_t *epggrab_module_ota_create 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)); @@ -463,6 +464,7 @@ epggrab_module_ota_t *epggrab_module_ota_create skel->type = EPGGRAB_OTA; skel->enable = enable; skel->start = start; + skel->done = done; //TAILQ_INIT(&skel->muxes); return skel; diff --git a/src/epggrab/module/eit.c b/src/epggrab/module/eit.c index 65c961d4..896ce929 100644 --- a/src/epggrab/module/eit.c +++ b/src/epggrab/module/eit.c @@ -555,7 +555,7 @@ _eit_callback 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; @@ -680,11 +680,11 @@ static void _eit_start 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); } diff --git a/src/epggrab/module/opentv.c b/src/epggrab/module/opentv.c index 51c4008d..c28650f2 100644 --- a/src/epggrab/module/opentv.c +++ b/src/epggrab/module/opentv.c @@ -686,6 +686,14 @@ static void _opentv_dict_load ( htsmsg_t *m ) 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]; @@ -726,7 +734,7 @@ static int _opentv_prov_load_one ( const char *id, htsmsg_t *m ) 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; @@ -785,6 +793,24 @@ void opentv_init ( void ) 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? diff --git a/src/epggrab/otamux.c b/src/epggrab/otamux.c index fc90e18f..8beb4451 100644 --- a/src/epggrab/otamux.c +++ b/src/epggrab/otamux.c @@ -37,6 +37,8 @@ LIST_HEAD(,epggrab_ota_mux) epggrab_ota_active; 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 ); @@ -191,23 +193,21 @@ epggrab_ota_register 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; @@ -375,6 +375,7 @@ epggrab_ota_save ( epggrab_ota_mux_t *ota ) } htsmsg_add_msg(c, "modules", l); hts_settings_save(c, "epggrab/otamux/%s", ota->om_mux_uuid); + htsmsg_destroy(c); } static void @@ -457,6 +458,34 @@ epggrab_ota_init ( 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 * diff --git a/src/epggrab/private.h b/src/epggrab/private.h index 50c31697..51940a18 100644 --- a/src/epggrab/private.h +++ b/src/epggrab/private.h @@ -91,6 +91,7 @@ epggrab_module_ota_t *epggrab_module_ota_create 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 ); /* ************************************************************************** @@ -162,6 +163,7 @@ void eit_load ( void ); /* OpenTV module */ void opentv_init ( void ); +void opentv_done ( void ); void opentv_load ( void ); /* PyEPG module */ diff --git a/src/fsmonitor.c b/src/fsmonitor.c index 46a02e09..5881ef25 100644 --- a/src/fsmonitor.c +++ b/src/fsmonitor.c @@ -24,6 +24,7 @@ #if ENABLE_INOTIFY +#include #include #include @@ -56,6 +57,8 @@ fsmonitor_thread ( void* p ) /* Wait for event */ c = read(fsmonitor_fd, buf, sizeof(buf)); + if (fsmonitor_fd < 0) + break; /* Process */ pthread_mutex_lock(&global_lock); @@ -94,14 +97,27 @@ fsmonitor_thread ( void* p ) /* * 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); } /* @@ -111,13 +127,13 @@ int 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 */ @@ -127,21 +143,22 @@ fsmonitor_add ( const char *path, fsmonitor_t *fsm ) 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 */ @@ -165,17 +182,16 @@ fsmonitor_add ( const char *path, fsmonitor_t *fsm ) 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 */ @@ -191,7 +207,7 @@ fsmonitor_del ( const char *path, fsmonitor_t *fsm ) } /* 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); diff --git a/src/fsmonitor.h b/src/fsmonitor.h index 8fb3d8ca..d3c586a9 100644 --- a/src/fsmonitor.h +++ b/src/fsmonitor.h @@ -47,6 +47,7 @@ struct fsmonitor { }; 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 ); diff --git a/src/htsp_server.c b/src/htsp_server.c index 8a018373..45cccfc4 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -1986,8 +1987,9 @@ htsp_read_loop(htsp_connection_t *htsp) /* 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; @@ -2036,6 +2038,7 @@ readmsg: htsmsg_destroy(m); } + return 0; } /** @@ -2052,7 +2055,7 @@ htsp_write_scheduler(void *aux) 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 */ @@ -2213,6 +2216,14 @@ htsp_server_status ( void *opaque, htsmsg_t *m ) htsmsg_add_str(m, "user", htsp->htsp_username); } +/* + * Cancel callback + */ +static void +htsp_server_cancel ( void *opaque ) +{ +} + /** * Fire up HTSP server */ @@ -2224,12 +2235,25 @@ htsp_init(const char *bindaddr) .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 * *************************************************************************/ diff --git a/src/htsp_server.h b/src/htsp_server.h index f551ab89..af5f400b 100644 --- a/src/htsp_server.h +++ b/src/htsp_server.h @@ -23,6 +23,7 @@ #include "dvr/dvr.h" void htsp_init(const char *bindaddr); +void htsp_done(void); void htsp_channel_update_nownext(channel_t *ch); diff --git a/src/http.c b/src/http.c index a01bde42..4cacf1bf 100644 --- a/src/http.c +++ b/src/http.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -789,7 +790,7 @@ http_serve(int fd, void **opaque, struct sockaddr_storage *peer, { 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)); @@ -845,3 +846,18 @@ http_server_init(const char *bindaddr) }; 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); +} diff --git a/src/http.h b/src/http.h index acba6240..120dda8f 100644 --- a/src/http.h +++ b/src/http.h @@ -136,6 +136,7 @@ http_path_t *http_path_add(const char *path, void *opaque, 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); @@ -148,6 +149,7 @@ typedef size_t (http_client_data_cb) (void *p, void *buf, size_t len); 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, diff --git a/src/http/http_client.c b/src/http/http_client.c index 0448537e..e7a0534f 100644 --- a/src/http/http_client.c +++ b/src/http/http_client.c @@ -30,6 +30,7 @@ #include #include #include +#include /* @@ -141,7 +142,7 @@ http_thread ( void *p ) 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"); @@ -217,11 +218,11 @@ http_close ( http_client_t *hc ) /* * 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); @@ -235,7 +236,17 @@ http_client_init ( void ) 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 */ @@ -245,4 +256,9 @@ http_client_init ( void ) { } +void +http_client_done ( void ) +{ +} + #endif /* ENABLE_CURL */ diff --git a/src/idnode.c b/src/idnode.c index ab27207c..64ac2c5b 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -43,6 +43,8 @@ static pthread_mutex_t idnode_mutex; 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); @@ -120,11 +122,11 @@ in_cmp(const idnode_t *a, const idnode_t *b) /** * */ +pthread_t idnode_tid; + void idnode_init(void) { - pthread_t tid; - randfd = open("/dev/urandom", O_RDONLY); if(randfd == -1) exit(1); @@ -132,7 +134,25 @@ idnode_init(void) 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); } /** @@ -810,15 +830,13 @@ ic_cmp ( const idclass_link_t *a, const idclass_link_t *b ) 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; } } @@ -912,6 +930,9 @@ idnode_notify { const char *uuid = idnode_uuid_as_str(in); + if (!tvheadend_running) + return; + /* Forced */ if (chn || force) { htsmsg_t *m = htsmsg_create_map(); @@ -961,7 +982,7 @@ idnode_thread ( void *p ) pthread_mutex_lock(&idnode_mutex); - while (1) { + while (tvheadend_running) { /* Get queue */ if (!idnode_queue) { @@ -995,6 +1016,7 @@ idnode_thread ( void *p ) pthread_mutex_lock(&idnode_mutex); } if (q) htsmsg_destroy(q); + pthread_mutex_unlock(&idnode_mutex); return NULL; } diff --git a/src/idnode.h b/src/idnode.h index af8f3d17..a10c1056 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -109,6 +109,7 @@ typedef struct idnode_filter_ele 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); diff --git a/src/imagecache.c b/src/imagecache.c index 56a5a87e..36202ada 100644 --- a/src/imagecache.c +++ b/src/imagecache.c @@ -199,7 +199,7 @@ imagecache_thread ( void *p ) imagecache_image_t *img; pthread_mutex_lock(&global_lock); - while (1) { + while (tvheadend_running) { /* Check we're enabled */ if (!imagecache_conf.enabled) { @@ -220,7 +220,9 @@ imagecache_thread ( void *p ) /* Fetch */ (void)imagecache_image_fetch(img); } + pthread_mutex_unlock(&global_lock); + fprintf(stderr, "imagecache thread end\n"); return NULL; } @@ -245,6 +247,10 @@ imagecache_timer_cb ( void *p ) /* * Initialise */ +#if ENABLE_IMAGECACHE +pthread_t imagecache_tid; +#endif + void imagecache_init ( void ) { @@ -307,10 +313,7 @@ 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 @@ -320,6 +323,27 @@ imagecache_init ( void ) #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 /* diff --git a/src/imagecache.h b/src/imagecache.h index 39e24a5e..c5cdc4e6 100644 --- a/src/imagecache.h +++ b/src/imagecache.h @@ -33,6 +33,7 @@ extern struct imagecache_config imagecache_conf; 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 ); diff --git a/src/input/mpegts.h b/src/input/mpegts.h index 6f37f341..751d5433 100644 --- a/src/input/mpegts.h +++ b/src/input/mpegts.h @@ -295,7 +295,7 @@ struct mpegts_mux * 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); @@ -445,6 +445,9 @@ struct mpegts_input pthread_t mi_thread_id; th_pipe_t mi_thread_pipe; + int mi_delivery_running; + pthread_t mi_thread_table_id; + /* * Functions */ @@ -500,7 +503,7 @@ mpegts_input_t *mpegts_input_create0 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); @@ -515,6 +518,9 @@ void mpegts_network_register_builder ( 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 ); @@ -532,8 +538,10 @@ extern const idclass_t mpegts_network_class; 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 ); @@ -557,13 +565,13 @@ mpegts_mux_t *mpegts_mux_create0 #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 ); @@ -599,7 +607,9 @@ size_t mpegts_input_recv_packets (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 ); @@ -646,7 +656,7 @@ mpegts_service_t *mpegts_service_find 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 diff --git a/src/input/mpegts/dvb.h b/src/input/mpegts/dvb.h index 62d4ebb0..3963efa6 100644 --- a/src/input/mpegts/dvb.h +++ b/src/input/mpegts/dvb.h @@ -276,4 +276,6 @@ int dvb_delsys2type ( enum fe_delivery_system ds ); #endif /* ENABLE_DVBAPI */ +void dvb_done ( void ); + #endif /* DVB_SUPPORT_H */ diff --git a/src/input/mpegts/dvb_charset.c b/src/input/mpegts/dvb_charset.c index 9388d6a8..d2a30378 100644 --- a/src/input/mpegts/dvb_charset.c +++ b/src/input/mpegts/dvb_charset.c @@ -78,6 +78,20 @@ void dvb_charset_init ( void ) _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 */ diff --git a/src/input/mpegts/dvb_charset.h b/src/input/mpegts/dvb_charset.h index 3ef6267a..12eedc07 100644 --- a/src/input/mpegts/dvb_charset.h +++ b/src/input/mpegts/dvb_charset.h @@ -28,6 +28,7 @@ typedef struct dvb_charset { } dvb_charset_t; void dvb_charset_init ( void ); +void dvb_charset_done ( void ); struct mpegts_network; struct mpegts_mux; diff --git a/src/input/mpegts/dvb_psi.c b/src/input/mpegts/dvb_psi.c index c6a98c6c..866c264f 100644 --- a/src/input/mpegts/dvb_psi.c +++ b/src/input/mpegts/dvb_psi.c @@ -33,6 +33,8 @@ #include #include +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); @@ -445,17 +447,16 @@ static struct mpegts_table_state * 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); } diff --git a/src/input/mpegts/dvb_support.c b/src/input/mpegts/dvb_support.c index 8c9e8d2a..acba753b 100644 --- a/src/input/mpegts/dvb_support.c +++ b/src/input/mpegts/dvb_support.c @@ -31,6 +31,7 @@ #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 @@ -813,3 +814,17 @@ dvb_bandwidth ( fe_bandwidth_t bw ) } #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); +} diff --git a/src/input/mpegts/iptv.h b/src/input/mpegts/iptv.h index 34cc8b79..83b998d2 100644 --- a/src/input/mpegts/iptv.h +++ b/src/input/mpegts/iptv.h @@ -21,6 +21,7 @@ #define __IPTV_H__ void iptv_init ( void ); +void iptv_done ( void ); #endif /* __IPTV_H__ */ diff --git a/src/input/mpegts/iptv/iptv.c b/src/input/mpegts/iptv/iptv.c index 58d6d53d..9cc1f729 100644 --- a/src/input/mpegts/iptv/iptv.c +++ b/src/input/mpegts/iptv/iptv.c @@ -32,13 +32,15 @@ #include #include #include +#include +#include /* ************************************************************************** * 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; @@ -108,12 +110,12 @@ iptv_input_is_free ( mpegts_input_t *mi ) 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; } @@ -241,7 +243,7 @@ iptv_input_stop_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *mmi ) 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); } @@ -261,7 +263,7 @@ iptv_input_thread ( void *aux ) 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", @@ -298,23 +300,23 @@ void 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, @@ -423,7 +425,6 @@ iptv_network_config_save ( mpegts_network_t *mn ) void iptv_init ( void ) { - pthread_t tid; htsmsg_t *conf; const char *uuid = NULL; @@ -431,48 +432,64 @@ void iptv_init ( void ) 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 * diff --git a/src/input/mpegts/iptv/iptv_mux.c b/src/input/mpegts/iptv/iptv_mux.c index c8d99858..39b0bc1f 100644 --- a/src/input/mpegts/iptv/iptv_mux.c +++ b/src/input/mpegts/iptv/iptv_mux.c @@ -65,12 +65,13 @@ iptv_mux_config_save ( mpegts_mux_t *mm ) } 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 diff --git a/src/input/mpegts/iptv/iptv_private.h b/src/input/mpegts/iptv/iptv_private.h index c0283ce9..bd59c334 100644 --- a/src/input/mpegts/iptv/iptv_private.h +++ b/src/input/mpegts/iptv/iptv_private.h @@ -99,8 +99,8 @@ iptv_service_t *iptv_service_create0 ( 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 ); diff --git a/src/input/mpegts/linuxdvb.h b/src/input/mpegts/linuxdvb.h index a196882f..2bcd38ac 100644 --- a/src/input/mpegts/linuxdvb.h +++ b/src/input/mpegts/linuxdvb.h @@ -22,6 +22,8 @@ void linuxdvb_init ( int mask ); +void linuxdvb_done ( void ); + idnode_set_t *linuxdvb_root ( void ); #endif /* __TVH_LINUX_DVB_H__ */ diff --git a/src/input/mpegts/linuxdvb/linuxdvb.c b/src/input/mpegts/linuxdvb/linuxdvb.c index 4c4d5a3a..e8395fe5 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb.c +++ b/src/input/mpegts/linuxdvb/linuxdvb.c @@ -41,3 +41,11 @@ void linuxdvb_init ( int adapter_mask ) /* Initialsie devices */ linuxdvb_adapter_init(); } + +void linuxdvb_done ( void ) +{ + linuxdvb_network_done(); + linuxdvb_adapter_done(); + dvb_charset_done(); + scanfile_done(); +} diff --git a/src/input/mpegts/linuxdvb/linuxdvb_adapter.c b/src/input/mpegts/linuxdvb/linuxdvb_adapter.c index 4ac2f616..0ce30af5 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_adapter.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_adapter.c @@ -144,6 +144,7 @@ linuxdvb_adapter_create /* 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; @@ -267,6 +268,7 @@ linuxdvb_adapter_add ( const char *path ) /* 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 } } @@ -291,6 +293,7 @@ linuxdvb_adapter_add ( const char *path ) } #endif pthread_mutex_unlock(&global_lock); + htsmsg_destroy(conf); } /* Relock before exit */ @@ -330,6 +333,8 @@ linuxdvb_adapter_del ( const char *path ) /* Delete */ tvh_hardware_delete((tvh_hardware_t*)la); + + free(la); } } @@ -420,3 +425,22 @@ linuxdvb_adapter_init ( void ) 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); +} diff --git a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c index 908b3f45..e487ce61 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_frontend.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_frontend.c @@ -686,7 +686,7 @@ linuxdvb_frontend_input_thread ( void *aux ) 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; @@ -904,7 +904,6 @@ linuxdvb_frontend_create 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); @@ -968,7 +967,7 @@ linuxdvb_frontend_create 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) { @@ -1024,6 +1023,8 @@ linuxdvb_frontend_delete ( linuxdvb_frontend_t *lfe ) 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); @@ -1037,7 +1038,7 @@ linuxdvb_frontend_delete ( linuxdvb_frontend_t *lfe ) linuxdvb_satconf_delete(lfe->lfe_satconf, 0); /* Finish */ - mpegts_input_delete((mpegts_input_t*)lfe); + mpegts_input_delete((mpegts_input_t*)lfe, 0); } /****************************************************************************** diff --git a/src/input/mpegts/linuxdvb/linuxdvb_mux.c b/src/input/mpegts/linuxdvb/linuxdvb_mux.c index 96cfb424..803c9803 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_mux.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_mux.c @@ -36,7 +36,7 @@ * *************************************************************************/ 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; @@ -611,15 +611,16 @@ linuxdvb_mux_create_instances ( mpegts_mux_t *mm ) } 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); } /* ************************************************************************** diff --git a/src/input/mpegts/linuxdvb/linuxdvb_network.c b/src/input/mpegts/linuxdvb/linuxdvb_network.c index 178c7788..3c775226 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_network.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_network.c @@ -51,7 +51,7 @@ linuxdvb_network_class_delete ( idnode_t *in ) idnode_uuid_as_str(in)); /* Parent delete */ - mpegts_network_delete(mn); + mpegts_network_delete(mn, 1); } static const void * @@ -356,6 +356,13 @@ linuxdvb_network_builder 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; @@ -363,16 +370,10 @@ void linuxdvb_network_init ( void ) 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"))) @@ -382,9 +383,9 @@ void linuxdvb_network_init ( void ) 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; } } @@ -392,6 +393,19 @@ void linuxdvb_network_init ( void ) 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 * ***************************************************************************/ diff --git a/src/input/mpegts/linuxdvb/linuxdvb_private.h b/src/input/mpegts/linuxdvb/linuxdvb_private.h index 12aebded..eb84519a 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_private.h +++ b/src/input/mpegts/linuxdvb/linuxdvb_private.h @@ -202,6 +202,8 @@ struct linuxdvb_en50494 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 ); @@ -240,6 +242,7 @@ struct linuxdvb_network }; 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 diff --git a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c index 71d5f259..723a4abe 100644 --- a/src/input/mpegts/linuxdvb/linuxdvb_satconf.c +++ b/src/input/mpegts/linuxdvb/linuxdvb_satconf.c @@ -1085,7 +1085,7 @@ linuxdvb_satconf_ele_destroy ( linuxdvb_satconf_ele_t *ls ) 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 * @@ -1160,8 +1160,10 @@ linuxdvb_satconf_delete ( linuxdvb_satconf_t *ls, int delconf ) 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); } /****************************************************************************** @@ -1217,6 +1219,7 @@ void linuxdvb_diseqc_destroy ( linuxdvb_diseqc_t *ld ) { idnode_unlink(&ld->ld_id); + free((void *)ld->ld_type); } int diff --git a/src/input/mpegts/linuxdvb/scanfile.c b/src/input/mpegts/linuxdvb/scanfile.c index dcc34209..2851c0a2 100644 --- a/src/input/mpegts/linuxdvb/scanfile.c +++ b/src/input/mpegts/linuxdvb/scanfile.c @@ -428,6 +428,43 @@ scanfile_init ( void ) 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 */ diff --git a/src/input/mpegts/linuxdvb/scanfile.h b/src/input/mpegts/linuxdvb/scanfile.h index cda2da76..ee08a95c 100644 --- a/src/input/mpegts/linuxdvb/scanfile.h +++ b/src/input/mpegts/linuxdvb/scanfile.h @@ -40,6 +40,7 @@ extern scanfile_region_list_t scanfile_regions_DVBS; extern scanfile_region_list_t scanfile_regions_ATSC; void scanfile_init ( void ); +void scanfile_done ( void ); scanfile_network_t *scanfile_find ( const char *id ); diff --git a/src/input/mpegts/mpegts_input.c b/src/input/mpegts/mpegts_input.c index ad2a9e53..4e85ede6 100644 --- a/src/input/mpegts/mpegts_input.c +++ b/src/input/mpegts/mpegts_input.c @@ -27,6 +27,9 @@ #include #include +SKEL_DECLARE(mpegts_pid_sub_skel, mpegts_pid_sub_t); + + /* ************************************************************************** * Class definition * *************************************************************************/ @@ -166,16 +169,14 @@ mpegts_input_open_pid 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; @@ -486,30 +487,60 @@ mpegts_input_table_dispatch ( mpegts_mux_t *mm, mpegts_table_feed_t *mtf ) } } -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 ) @@ -667,14 +698,16 @@ mpegts_input_create0 } 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); } diff --git a/src/input/mpegts/mpegts_mux.c b/src/input/mpegts/mpegts_mux.c index 17c02cce..6c44daf9 100644 --- a/src/input/mpegts/mpegts_mux.c +++ b/src/input/mpegts/mpegts_mux.c @@ -25,6 +25,8 @@ #include +SKEL_DECLARE(mpegts_pid_skel, mpegts_pid_t); + static void mpegts_mux_initial_scan_timeout ( void *aux ); static void @@ -180,7 +182,7 @@ 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 * @@ -330,7 +332,7 @@ mpegts_mux_display_name ( mpegts_mux_t *mm, char *buf, size_t len ) } 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; @@ -354,7 +356,7 @@ mpegts_mux_delete ( mpegts_mux_t *mm ) /* Delete services */ while ((s = LIST_FIRST(&mm->mm_services))) { - service_destroy((service_t*)s); + service_destroy((service_t*)s, delconf); } /* Free memory */ @@ -527,6 +529,7 @@ mpegts_mux_stop ( mpegts_mux_t *mm, int force ) 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); @@ -905,14 +908,12 @@ mpegts_mux_find_pid ( mpegts_mux_t *mm, int pid, int create ) 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; } } diff --git a/src/input/mpegts/mpegts_network.c b/src/input/mpegts/mpegts_network.c index f3bcc59f..f531e6a9 100644 --- a/src/input/mpegts/mpegts_network.c +++ b/src/input/mpegts/mpegts_network.c @@ -210,7 +210,7 @@ mpegts_network_mux_create2 void mpegts_network_delete - ( mpegts_network_t *mn ) + ( mpegts_network_t *mn, int delconf ) { mpegts_input_t *mi; mpegts_mux_t *mm; @@ -220,7 +220,7 @@ mpegts_network_delete /* Delete all muxes */ while ((mm = LIST_FIRST(&mn->mn_muxes))) { - mm->mm_delete(mm); + mm->mm_delete(mm, delconf); } /* Check */ @@ -340,6 +340,18 @@ mpegts_network_create0 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 ) @@ -384,6 +396,20 @@ mpegts_network_register_builder 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 ) diff --git a/src/input/mpegts/mpegts_service.c b/src/input/mpegts/mpegts_service.c index fce0ac8e..efab8419 100644 --- a/src/input/mpegts/mpegts_service.c +++ b/src/input/mpegts/mpegts_service.c @@ -338,13 +338,14 @@ mpegts_service_provider_name ( service_t *s ) } 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)); @@ -354,6 +355,7 @@ mpegts_service_delete ( service_t *t ) 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 diff --git a/src/input/mpegts/tsfile/tsfile_input.c b/src/input/mpegts/tsfile/tsfile_input.c index 2bc20210..29105d10 100644 --- a/src/input/mpegts/tsfile/tsfile_input.c +++ b/src/input/mpegts/tsfile/tsfile_input.c @@ -240,7 +240,6 @@ tsfile_input_start_mux ( mpegts_input_t *mi, mpegts_mux_instance_t *t ) mpegts_input_t * tsfile_input_create ( int idx ) { - pthread_t tid; mpegts_input_t *mi; /* Create object */ @@ -254,7 +253,7 @@ tsfile_input_create ( int idx ) 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; } diff --git a/src/lang_codes.c b/src/lang_codes.c index a6e9b730..d7ea86bc 100644 --- a/src/lang_codes.c +++ b/src/lang_codes.c @@ -668,3 +668,22 @@ const lang_code_t **lang_code_split2 ( const char *codes ) 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); +} diff --git a/src/lang_codes.h b/src/lang_codes.h index 9be6d174..13f52ba7 100644 --- a/src/lang_codes.h +++ b/src/lang_codes.h @@ -48,4 +48,6 @@ typedef struct lang_code_lookup_element { typedef RB_HEAD(lang_code_lookup, lang_code_lookup_element) lang_code_lookup_t; +void lang_code_done( void ); + #endif /* __TVH_LANG_CODES_H__ */ diff --git a/src/lang_str.c b/src/lang_str.c index 04508c7a..4fe4aea8 100644 --- a/src/lang_str.c +++ b/src/lang_str.c @@ -22,6 +22,9 @@ #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 @@ -107,7 +110,6 @@ static int _lang_str_add ( 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; @@ -116,14 +118,14 @@ static int _lang_str_add 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 */ @@ -189,3 +191,8 @@ lang_str_t *lang_str_deserialize ( htsmsg_t *m, const char *n ) } return ret; } + +void lang_str_done( void ) +{ + SKEL_FREE(lang_str_ele_skel); +} diff --git a/src/lang_str.h b/src/lang_str.h index 7c178076..ecd22413 100644 --- a/src/lang_str.h +++ b/src/lang_str.h @@ -52,4 +52,7 @@ void lang_str_serialize lang_str_t *lang_str_deserialize ( htsmsg_t *m, const char *f ); +/* Init/Done */ +void lang_str_done( void ); + #endif /* __TVH_LANG_STR_H__ */ diff --git a/src/main.c b/src/main.c index 322a2517..4216fee2 100644 --- a/src/main.c +++ b/src/main.c @@ -59,6 +59,7 @@ #include "imagecache.h" #include "timeshift.h" #include "fsmonitor.h" +#include "lang_codes.h" #if ENABLE_LIBAV #include "libav.h" #include "plumbing/transcoding.h" @@ -68,6 +69,8 @@ #include #endif +pthread_t main_tid; + /* Command line option struct */ typedef struct str_list { @@ -169,10 +172,14 @@ handle_sigpipe(int x) 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 @@ -424,6 +431,8 @@ main(int argc, char **argv) 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); @@ -704,6 +713,8 @@ main(int argc, char **argv) umask(0); } + tvheadend_running = 1; + /* Start log thread (must be done post fork) */ tvhlog_start(); @@ -789,9 +800,7 @@ main(int argc, char **argv) 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 @@ -801,7 +810,6 @@ main(int argc, char **argv) * Wait for SIGTERM / SIGINT, but only in this thread */ - tvheadend_running = 1; sigemptyset(&set); sigaddset(&set, SIGTERM); sigaddset(&set, SIGINT); @@ -822,21 +830,55 @@ main(int argc, char **argv) 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; } diff --git a/src/service.c b/src/service.c index 13a97cd2..592e7b61 100644 --- a/src/service.c +++ b/src/service.c @@ -239,6 +239,8 @@ stream_clean(elementary_stream_t *st) void service_stream_destroy(service_t *t, elementary_stream_t *es) { + caid_t *c; + if(t->s_status == SERVICE_RUNNING) stream_clean(es); @@ -246,6 +248,12 @@ service_stream_destroy(service_t *t, elementary_stream_t *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); @@ -441,8 +449,10 @@ service_find_instance 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); + } } @@ -461,14 +471,14 @@ service_ref(service_t *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); @@ -964,7 +974,7 @@ service_saver(void *aux) 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); @@ -996,17 +1006,24 @@ service_saver(void *aux) /** * */ +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); +} /** * diff --git a/src/service.h b/src/service.h index 7afc3ca1..6fa15037 100644 --- a/src/service.h +++ b/src/service.h @@ -283,7 +283,7 @@ typedef struct service { int (*s_grace_period)(struct service *t); - void (*s_delete)(struct service *t); + void (*s_delete)(struct service *t, int delconf); /** * Channel info @@ -426,6 +426,7 @@ typedef struct service { void service_init(void); +void service_done(void); int service_start(service_t *t, int instance); @@ -466,7 +467,7 @@ int service_is_other(service_t *t); 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); diff --git a/src/service_mapper.c b/src/service_mapper.c index b35fc767..5b0d7c50 100644 --- a/src/service_mapper.c +++ b/src/service_mapper.c @@ -44,13 +44,21 @@ static void *service_mapper_thread ( void *p ); /** * 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); } /* @@ -289,7 +297,7 @@ service_mapper_thread ( void *aux ) pthread_mutex_lock(&global_lock); - while (1) { + while (tvheadend_running) { /* Wait for work */ while (!(s = TAILQ_FIRST(&service_mapper_queue))) { @@ -298,7 +306,11 @@ service_mapper_thread ( void *aux ) 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) { @@ -327,11 +339,17 @@ service_mapper_thread ( void *aux ) /* 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); @@ -353,6 +371,8 @@ service_mapper_thread ( void *aux ) 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); @@ -370,5 +390,7 @@ service_mapper_thread ( void *aux ) service_mapper_stat.active = NULL; api_service_mapper_notify(); } + + pthread_mutex_unlock(&global_lock); return NULL; } diff --git a/src/service_mapper.h b/src/service_mapper.h index 702bdd7a..a6c5fb54 100644 --- a/src/service_mapper.h +++ b/src/service_mapper.h @@ -37,6 +37,7 @@ typedef struct service_mapper_status } service_mapper_status_t; void service_mapper_init ( void ); +void service_mapper_done ( void ); // Start new mapping void service_mapper_start diff --git a/src/settings.c b/src/settings.c index 232c6868..dfce0704 100644 --- a/src/settings.c +++ b/src/settings.c @@ -77,6 +77,15 @@ hts_settings_init(const char *confpath) } } +/** + * + */ +void +hts_settings_done(void) +{ + free(settingspath); +} + /** * */ diff --git a/src/settings.h b/src/settings.h index 8d29d54b..51582824 100644 --- a/src/settings.h +++ b/src/settings.h @@ -24,6 +24,8 @@ 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, ...); diff --git a/src/subscriptions.c b/src/subscriptions.c index bde6f6cb..ebb6e2f3 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -403,6 +403,9 @@ subscription_unsubscribe(th_subscription_t *s) 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); @@ -754,6 +757,20 @@ subscription_init(void) 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 * *************************************************************************/ diff --git a/src/subscriptions.h b/src/subscriptions.h index eca8c8d6..86ff2c6c 100644 --- a/src/subscriptions.h +++ b/src/subscriptions.h @@ -100,6 +100,8 @@ typedef struct th_subscription { */ 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); diff --git a/src/tcp.c b/src/tcp.c index 8d78809a..136a842e 100644 --- a/src/tcp.c +++ b/src/tcp.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,8 @@ #include "notify.h" int tcp_preferred_address_family = AF_INET; +int tcp_server_running; +th_pipe_t tcp_server_pipe; /** * @@ -330,6 +333,11 @@ tcp_read_timeout(int fd, void *buf, size_t len, int timeout) 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) { @@ -385,6 +393,7 @@ typedef struct tcp_server { } tcp_server_t; typedef struct tcp_server_launch { + pthread_t tid; int fd; tcp_server_ops_t ops; void *opaque; @@ -392,9 +401,11 @@ typedef struct tcp_server_launch { 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 }; /** * @@ -448,6 +459,7 @@ tcp_server_start(void *aux) LIST_REMOVE(tsl, link); notify_reload("connections"); } + LIST_REMOVE(tsl, alink); pthread_mutex_unlock(&global_lock); free(tsl); @@ -465,14 +477,9 @@ tcp_server_loop(void *aux) 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"); @@ -482,6 +489,8 @@ tcp_server_loop(void *aux) 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); @@ -511,9 +520,13 @@ tcp_server_loop(void *aux) 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; } @@ -600,6 +613,23 @@ tcp_server_create 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 */ @@ -635,14 +665,58 @@ tcp_server_connections ( void ) /** * */ +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); } diff --git a/src/tcp.h b/src/tcp.h index b0954462..7006e4d2 100644 --- a/src/tcp.h +++ b/src/tcp.h @@ -29,11 +29,13 @@ typedef struct tcp_server_ops 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); @@ -45,6 +47,8 @@ typedef void (tcp_server_callback_t)(int fd, void *opaque, 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); diff --git a/src/tvheadend.h b/src/tvheadend.h index 0bb2be80..b8c68f86 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -69,6 +69,8 @@ static inline htsmsg_t *tvheadend_capabilities_list(int check) #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; @@ -539,6 +541,8 @@ static inline void mystrset(char **p, const char *s) *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, @@ -603,6 +607,11 @@ int rmtree ( const char *path ); 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) diff --git a/src/tvhlog.c b/src/tvhlog.c index ece17b33..772fbd86 100644 --- a/src/tvhlog.c +++ b/src/tvhlog.c @@ -224,7 +224,7 @@ tvhlog_thread ( void *p ) /* 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 @@ -233,6 +233,7 @@ tvhlog_thread ( void *p ) 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)) @@ -252,7 +253,9 @@ tvhlog_thread ( void *p ) tvhlog_process(msg, options, &fp, path); pthread_mutex_lock(&tvhlog_mutex); } - + if (fp) + fclose(fp); + pthread_mutex_unlock(&tvhlog_mutex); return NULL; } @@ -420,15 +423,18 @@ void 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); } diff --git a/src/tvhlog.h b/src/tvhlog.h index f5f5c454..06bb4732 100644 --- a/src/tvhlog.h +++ b/src/tvhlog.h @@ -86,6 +86,12 @@ void _tvhlog_hexdump ( const char *file, int line, #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__) diff --git a/src/webui/comet.c b/src/webui/comet.c index aee51b78..574a9fa2 100644 --- a/src/webui/comet.c +++ b/src/webui/comet.c @@ -48,6 +48,7 @@ static pthread_cond_t comet_cond = PTHREAD_COND_INITIALIZER; static LIST_HEAD(, comet_mailbox) mailboxes; int mailbox_tally; +int comet_running; typedef struct comet_mailbox { char *cmb_boxid; /* SHA-1 hash */ @@ -198,6 +199,10 @@ comet_mailbox_poll(http_connection_t *hc, const char *remain, void *opaque) 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) @@ -216,8 +221,13 @@ comet_mailbox_poll(http_connection_t *hc, const char *remain, void *opaque) 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); @@ -279,10 +289,24 @@ comet_mailbox_dbg(http_connection_t *hc, const char *remain, void *opaque) 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); +} /** * @@ -294,14 +318,16 @@ comet_mailbox_add_message(htsmsg_t *m, int isdebug) 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); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 8e309774..443769db 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -1360,7 +1360,7 @@ extjs_service_delete(htsmsg_t *in) 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); } } diff --git a/src/webui/simpleui.c b/src/webui/simpleui.c index 9d8a523c..01aedaba 100644 --- a/src/webui/simpleui.c +++ b/src/webui/simpleui.c @@ -484,7 +484,7 @@ page_epgsave(http_connection_t *hc, "1\n"); pthread_mutex_lock(&global_lock); - epg_save(NULL); + epg_save(); pthread_mutex_unlock(&global_lock); http_output_content(hc, "text/xml"); diff --git a/src/webui/webui.c b/src/webui/webui.c index cf10d8b5..ca5f5f52 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -241,7 +241,7 @@ http_stream_run(http_connection_t *hc, streaming_queue_t *sq, 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) { @@ -1081,7 +1081,7 @@ page_imagecache(http_connection_t *hc, const char *remain, void *opaque) 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); } @@ -1130,3 +1130,9 @@ webui_init(void) webui_api_init(); } + +void +webui_done(void) +{ + comet_done(); +} diff --git a/src/webui/webui.h b/src/webui/webui.h index 409f745e..2a2e3e5c 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -24,6 +24,7 @@ #include "http.h" void webui_init(void); +void webui_done(void); void simpleui_start(void); @@ -54,6 +55,8 @@ void webui_api_init ( void ); */ void comet_init(void); +void comet_done(void); + void comet_mailbox_add_message(htsmsg_t *m, int isdebug); void comet_flush(void); diff --git a/src/wrappers.c b/src/wrappers.c index 4343fc50..c8ec1967 100644 --- a/src/wrappers.c +++ b/src/wrappers.c @@ -4,6 +4,7 @@ #include /* See NOTES */ #include #include +#include #include #ifdef PLATFORM_LINUX @@ -100,6 +101,7 @@ static void * thread_wrapper ( void *p ) { struct thread_state *ts = p; + sigset_t set; #if defined(PLATFORM_LINUX) /* Set name */ @@ -109,6 +111,12 @@ thread_wrapper ( void *p ) 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);