1249 lines
33 KiB
C
1249 lines
33 KiB
C
/*
|
|
* TV headend - General configuration settings
|
|
* Copyright (C) 2012 Adam Sutton
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "tvheadend.h"
|
|
#include "settings.h"
|
|
#include "config.h"
|
|
#include "uuid.h"
|
|
#include "htsbuf.h"
|
|
#include "spawn.h"
|
|
|
|
/* *************************************************************************
|
|
* Global data
|
|
* ************************************************************************/
|
|
|
|
static htsmsg_t *config;
|
|
|
|
/* *************************************************************************
|
|
* Config migration
|
|
* ************************************************************************/
|
|
|
|
typedef void (*config_migrate_t) (void);
|
|
|
|
/*
|
|
* Get channel UUID (by number)
|
|
*/
|
|
static const char *
|
|
config_migrate_v1_chn_id_to_uuid ( htsmsg_t *chns, uint32_t id )
|
|
{
|
|
htsmsg_field_t *f;
|
|
htsmsg_t *e;
|
|
uint32_t u32;
|
|
HTSMSG_FOREACH(f, chns) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (htsmsg_get_u32(e, "channelid", &u32)) continue;
|
|
if (u32 == id) {
|
|
return htsmsg_get_str(e, "uuid");
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Get channel UUID (by name)
|
|
*/
|
|
static const char *
|
|
config_migrate_v1_chn_name_to_uuid ( htsmsg_t *chns, const char *name )
|
|
{
|
|
htsmsg_t *chn = htsmsg_get_map(chns, name);
|
|
if (chn)
|
|
return htsmsg_get_str(chn, "uuid");
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Helper to add service to channel
|
|
*/
|
|
static void
|
|
config_migrate_v1_chn_add_svc
|
|
( htsmsg_t *chns, const char *chnname, const char *svcuuid )
|
|
{
|
|
htsmsg_t *chn = htsmsg_get_map(chns, chnname);
|
|
if (chn) {
|
|
htsmsg_t *l = htsmsg_get_list(chn, "services");
|
|
if (l)
|
|
htsmsg_add_str(l, NULL, svcuuid);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper function to migrate a muxes services
|
|
*/
|
|
static void
|
|
config_migrate_v1_dvb_svcs
|
|
( const char *name, const char *netu, const char *muxu, htsmsg_t *channels )
|
|
{
|
|
tvh_uuid_t svcu;
|
|
htsmsg_t *c, *e, *svc;
|
|
htsmsg_field_t *f;
|
|
const char *str;
|
|
uint32_t u32;
|
|
|
|
if ((c = hts_settings_load_r(1, "dvbtransports/%s", name))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
|
|
svc = htsmsg_create_map();
|
|
uuid_init_hex(&svcu, NULL);
|
|
if (!htsmsg_get_u32(e, "service_id", &u32))
|
|
htsmsg_add_u32(svc, "sid", u32);
|
|
if ((str = htsmsg_get_str(e, "servicename")))
|
|
htsmsg_add_str(svc, "svcname", str);
|
|
if ((str = htsmsg_get_str(e, "provider")))
|
|
htsmsg_add_str(svc, "provider", str);
|
|
if (!(htsmsg_get_u32(e, "type", &u32)))
|
|
htsmsg_add_u32(svc, "dvb_servicetype", u32);
|
|
if (!htsmsg_get_u32(e, "channel", &u32))
|
|
htsmsg_add_u32(svc, "lcn", u32);
|
|
if (!htsmsg_get_u32(e, "disabled", &u32))
|
|
htsmsg_add_u32(svc, "enabled", u32 ? 0 : 1);
|
|
if ((str = htsmsg_get_str(e, "charset")))
|
|
htsmsg_add_str(svc, "charset", str);
|
|
if ((str = htsmsg_get_str(e, "default_authority")))
|
|
htsmsg_add_str(svc, "cridauth", str);
|
|
|
|
// TODO: dvb_eit_enable
|
|
|
|
hts_settings_save(svc, "input/linuxdvb/networks/%s/muxes/%s/services/%s",
|
|
netu, muxu, svcu.hex);
|
|
|
|
/* Map to channel */
|
|
if ((str = htsmsg_get_str(e, "channelname")))
|
|
config_migrate_v1_chn_add_svc(channels, str, svcu.hex);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Helper to convert a DVB network
|
|
*/
|
|
static void
|
|
config_migrate_v1_dvb_network
|
|
( const char *name, htsmsg_t *c, htsmsg_t *channels )
|
|
{
|
|
int i;
|
|
tvh_uuid_t netu, muxu;
|
|
htsmsg_t *e, *net, *mux, *tun;
|
|
htsmsg_field_t *f;
|
|
const char *str, *type;
|
|
uint32_t u32;
|
|
const char *mux_str_props[] = {
|
|
"bandwidth",
|
|
"consellation",
|
|
"transmission_mode",
|
|
"guard_interval",
|
|
"hierarchy",
|
|
"fec_hi",
|
|
"fec_lo",
|
|
"fec"
|
|
};
|
|
|
|
|
|
/* Load the adapter config */
|
|
if (!(tun = hts_settings_load("dvbadapters/%s", name))) return;
|
|
if (!(str = htsmsg_get_str(tun, "type"))) return;
|
|
type = str;
|
|
|
|
/* Create network entry */
|
|
uuid_init_hex(&netu, NULL);
|
|
net = htsmsg_create_map();
|
|
if (!strcmp(str, "ATSC"))
|
|
htsmsg_add_str(net, "class", "linuxdvb_network_atsc");
|
|
else if (!strcmp(str, "DVB-S"))
|
|
htsmsg_add_str(net, "class", "linuxdvb_network_dvbs");
|
|
else if (!strcmp(str, "DVB-C"))
|
|
htsmsg_add_str(net, "class", "linuxdvb_network_dvbc");
|
|
else
|
|
htsmsg_add_str(net, "class", "linuxdvb_network_dvbt");
|
|
if (!htsmsg_get_u32(tun, "autodiscovery", &u32))
|
|
htsmsg_add_u32(net, "autodiscovery", u32);
|
|
if (!htsmsg_get_u32(tun, "skip_initialscan", &u32))
|
|
htsmsg_add_u32(net, "skipinitscan", u32);
|
|
|
|
/* Each mux */
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
mux = htsmsg_create_map();
|
|
|
|
if (!htsmsg_get_u32(e, "transportstreamid", &u32))
|
|
htsmsg_add_u32(mux, "tsid", u32);
|
|
if (!htsmsg_get_u32(e, "originalnetworkid", &u32))
|
|
htsmsg_add_u32(mux, "onid", u32);
|
|
if (!htsmsg_get_u32(e, "enabled", &u32))
|
|
htsmsg_add_u32(mux, "enabled", u32);
|
|
if (!htsmsg_get_u32(e, "initialscan", &u32))
|
|
htsmsg_add_u32(mux, "initscan", u32);
|
|
if ((str = htsmsg_get_str(e, "default_authority")))
|
|
htsmsg_add_str(mux, "cridauth", str);
|
|
if ((str = htsmsg_get_str(e, "network")) && *str)
|
|
name = str;
|
|
|
|
if (!htsmsg_get_u32(e, "symbol_rate", &u32))
|
|
htsmsg_add_u32(mux, "symbolrate", u32);
|
|
if (!htsmsg_get_u32(e, "frequency", &u32))
|
|
htsmsg_add_u32(mux, "frequency", u32);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(mux_str_props); i++) {
|
|
if ((str = htsmsg_get_str(e, mux_str_props[i])))
|
|
htsmsg_add_str(mux, mux_str_props[i], str);
|
|
}
|
|
|
|
if ((str = htsmsg_get_str(e, "polarisation"))) {
|
|
char tmp[2] = { *str, 0 };
|
|
htsmsg_add_str(mux, "polarisation", tmp);
|
|
}
|
|
if ((str = htsmsg_get_str(e, "modulation"))) {
|
|
if (!strcmp(str, "PSK_8"))
|
|
htsmsg_add_str(mux, "modulation", "8PSK");
|
|
else
|
|
htsmsg_add_str(mux, "modulation", str);
|
|
}
|
|
if ((str = htsmsg_get_str(e, "rolloff")))
|
|
if (strlen(str) > 8)
|
|
htsmsg_add_str(mux, "rolloff", str+8);
|
|
|
|
if ((str = htsmsg_get_str(e, "delivery_system")) &&
|
|
strlen(str) > 4 )
|
|
htsmsg_add_str(mux, "delsys", str+4);
|
|
else if (!strcmp(type, "ATSC"))
|
|
htsmsg_add_str(mux, "delsys", "ATSC");
|
|
else if (!strcmp(type, "DVB-S"))
|
|
htsmsg_add_str(mux, "delsys", "DVBS");
|
|
else if (!strcmp(type, "DVB-C"))
|
|
htsmsg_add_str(mux, "delsys", "DVBC_ANNEX_AC");
|
|
else
|
|
htsmsg_add_str(mux, "delsys", "DVBT");
|
|
|
|
/* Save */
|
|
uuid_init_hex(&muxu, NULL);
|
|
hts_settings_save(mux, "input/linuxdvb/networks/%s/muxes/%s/config",
|
|
netu.hex, muxu.hex);
|
|
htsmsg_destroy(mux);
|
|
|
|
/* Services */
|
|
config_migrate_v1_dvb_svcs(f->hmf_name, netu.hex, muxu.hex, channels);
|
|
}
|
|
|
|
/* Add properties derived from network */
|
|
htsmsg_add_str(net, "networkname", name);
|
|
hts_settings_save(net, "input/linuxdvb/networks/%s/config", netu.hex);
|
|
htsmsg_destroy(net);
|
|
}
|
|
|
|
/*
|
|
* Migrate DVR/autorec entries
|
|
*/
|
|
static void
|
|
config_migrate_v1_dvr ( const char *path, htsmsg_t *channels )
|
|
{
|
|
htsmsg_t *c, *e, *m;
|
|
htsmsg_field_t *f;
|
|
const char *str;
|
|
|
|
if ((c = hts_settings_load_r(1, path))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if ((str = htsmsg_get_str(e, "channel"))) {
|
|
m = htsmsg_copy(e);
|
|
if (!htsmsg_get_str(e, "channelname"))
|
|
htsmsg_add_str(m, "channelname", str);
|
|
if ((str = config_migrate_v1_chn_name_to_uuid(channels, str))) {
|
|
htsmsg_delete_field(m, "channel");
|
|
htsmsg_add_str(m, "channel", str);
|
|
hts_settings_save(m, "%s/%s", path, f->hmf_name);
|
|
}
|
|
htsmsg_destroy(m);
|
|
}
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Migrate Epggrab entries
|
|
*/
|
|
static void
|
|
config_migrate_v1_epggrab ( const char *path, htsmsg_t *channels )
|
|
{
|
|
htsmsg_t *c, *e, *m, *l, *chns;
|
|
htsmsg_field_t *f, *f2;
|
|
const char *str;
|
|
uint32_t u32;
|
|
|
|
if ((c = hts_settings_load_r(1, path))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
m = htsmsg_copy(e);
|
|
chns = htsmsg_create_list();
|
|
if ((l = htsmsg_get_list(e, "channels"))) {
|
|
htsmsg_delete_field(m, "channels");
|
|
HTSMSG_FOREACH(f2, l) {
|
|
if (!htsmsg_field_get_u32(f2, &u32))
|
|
if ((str = config_migrate_v1_chn_id_to_uuid(channels, u32)))
|
|
htsmsg_add_str(chns, NULL, str);
|
|
}
|
|
} else if (!htsmsg_get_u32(e, "channel", &u32)) {
|
|
htsmsg_delete_field(m, "channel");
|
|
if ((str = config_migrate_v1_chn_id_to_uuid(channels, u32)))
|
|
htsmsg_add_str(chns, NULL, str);
|
|
}
|
|
htsmsg_add_msg(m, "channels", chns);
|
|
hts_settings_save(m, "%s/%s", path, f->hmf_name);
|
|
htsmsg_destroy(m);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* v0 -> v1 : 3.4 to initial 4.0)
|
|
*
|
|
* Strictly speaking there were earlier versions than this, but most people
|
|
* using early versions of 4.0 would already have been on this version.
|
|
*/
|
|
static void
|
|
config_migrate_v1 ( void )
|
|
{
|
|
tvh_uuid_t netu, muxu, svcu, chnu;
|
|
htsmsg_t *c, *m, *e, *l;
|
|
htsmsg_field_t *f;
|
|
uint32_t u32;
|
|
const char *str;
|
|
char buf[1024];
|
|
htsmsg_t *channels = htsmsg_create_map();
|
|
|
|
/* Channels */
|
|
if ((c = hts_settings_load_r(1, "channels"))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
|
|
/* Build entry */
|
|
uuid_init_hex(&chnu, NULL);
|
|
m = htsmsg_create_map();
|
|
htsmsg_add_u32(m, "channelid", atoi(f->hmf_name));
|
|
htsmsg_add_str(m, "uuid", chnu.hex);
|
|
htsmsg_add_msg(m, "services", htsmsg_create_list());
|
|
if (!htsmsg_get_u32(e, "dvr_extra_time_pre", &u32))
|
|
htsmsg_add_u32(m, "dvr_pre_time", u32);
|
|
if (!htsmsg_get_u32(e, "dvr_extra_time_pst", &u32))
|
|
htsmsg_add_u32(m, "dvr_pst_time", u32);
|
|
if (!htsmsg_get_u32(e, "channel_number", &u32))
|
|
htsmsg_add_u32(m, "number", u32);
|
|
if ((str = htsmsg_get_str(e, "icon")))
|
|
htsmsg_add_str(m, "icon", str);
|
|
if ((l = htsmsg_get_list(e, "tags")))
|
|
htsmsg_add_msg(m, "tags", htsmsg_copy(l));
|
|
if ((str = htsmsg_get_str(e, "name"))) {
|
|
htsmsg_add_str(m, "name", str);
|
|
htsmsg_add_msg(channels, str, m);
|
|
}
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
/* IPTV */
|
|
// Note: this routine actually converts directly to v2 for simplicity
|
|
if ((c = hts_settings_load_r(1, "iptvservices"))) {
|
|
|
|
/* Create a network */
|
|
uuid_init_hex(&netu, NULL);
|
|
m = htsmsg_create_map();
|
|
htsmsg_add_str(m, "networkname", "IPTV Network");
|
|
htsmsg_add_u32(m, "skipinitscan", 1);
|
|
htsmsg_add_u32(m, "autodiscovery", 0);
|
|
htsmsg_add_u32(m, "max_streams", 0);
|
|
htsmsg_add_u32(m, "max_bandwidth", 0);
|
|
hts_settings_save(m, "input/iptv/networks/%s/config", netu.hex);
|
|
htsmsg_destroy(m);
|
|
|
|
/* Process services */
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (!(str = htsmsg_get_str(e, "group"))) continue;
|
|
if (htsmsg_get_u32(e, "port", &u32)) continue;
|
|
|
|
/* Create mux entry */
|
|
uuid_init_hex(&muxu, NULL);
|
|
m = htsmsg_create_map();
|
|
snprintf(buf, sizeof(buf), "udp://%s:%d", str, u32);
|
|
htsmsg_add_str(m, "iptv_url", buf);
|
|
if ((str = htsmsg_get_str(e, "interface")))
|
|
htsmsg_add_str(m, "iptv_interface", str);
|
|
if ((str = htsmsg_get_str(e, "channelname")))
|
|
htsmsg_add_str(m, "iptv_svcname", str);
|
|
htsmsg_add_u32(m, "enabled",
|
|
!!htsmsg_get_u32_or_default(e, "disabled", 0));
|
|
htsmsg_add_u32(m, "initscan", 1);
|
|
hts_settings_save(m, "input/iptv/networks/%s/muxes/%s/config",
|
|
netu.hex, muxu.hex);
|
|
htsmsg_destroy(m);
|
|
|
|
/* Create svc entry */
|
|
uuid_init_hex(&svcu, NULL);
|
|
m = htsmsg_create_map();
|
|
if (!htsmsg_get_u32(e, "pmt", &u32))
|
|
htsmsg_add_u32(m, "pmt", u32);
|
|
if (!htsmsg_get_u32(e, "pcr", &u32))
|
|
htsmsg_add_u32(m, "pcr", u32);
|
|
if (!htsmsg_get_u32(e, "disabled", &u32))
|
|
htsmsg_add_u32(m, "disabled", u32);
|
|
if ((str = htsmsg_get_str(e, "channelname"))) {
|
|
htsmsg_add_str(m, "svcname", str);
|
|
config_migrate_v1_chn_add_svc(channels, str, svcu.hex);
|
|
}
|
|
hts_settings_save(m, "input/iptv/networks/%s/muxes/%s/services/%s",
|
|
netu.hex, muxu.hex, svcu.hex);
|
|
htsmsg_destroy(m);
|
|
}
|
|
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
/* DVB Networks */
|
|
if ((c = hts_settings_load_r(2, "dvbmuxes"))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
config_migrate_v1_dvb_network(f->hmf_name, e, channels);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
/* Update DVR records */
|
|
config_migrate_v1_dvr("dvr/log", channels);
|
|
config_migrate_v1_dvr("autorec", channels);
|
|
|
|
/* Update EPG grabbers */
|
|
hts_settings_remove("epggrab/otamux");
|
|
config_migrate_v1_epggrab("epggrab/xmltv/channels", channels);
|
|
|
|
/* Save the channels */
|
|
// Note: UUID will be stored in the file (redundant) but that's no biggy
|
|
HTSMSG_FOREACH(f, channels) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (!(str = htsmsg_get_str(e, "uuid"))) continue;
|
|
hts_settings_save(e, "channel/%s", str);
|
|
}
|
|
htsmsg_destroy(channels);
|
|
}
|
|
|
|
/*
|
|
* v1 -> v2 : changes to IPTV arrangements
|
|
*/
|
|
static void
|
|
config_migrate_v2 ( void )
|
|
{
|
|
htsmsg_t *m;
|
|
tvh_uuid_t u;
|
|
char src[1024], dst[1024];
|
|
|
|
/* Do we have IPTV config to migrate ? */
|
|
if (hts_settings_exists("input/iptv/muxes")) {
|
|
|
|
/* Create a dummy network */
|
|
uuid_init_hex(&u, NULL);
|
|
m = htsmsg_create_map();
|
|
htsmsg_add_str(m, "networkname", "IPTV Network");
|
|
htsmsg_add_u32(m, "skipinitscan", 1);
|
|
htsmsg_add_u32(m, "autodiscovery", 0);
|
|
hts_settings_save(m, "input/iptv/networks/%s/config", u.hex);
|
|
htsmsg_destroy(m);
|
|
|
|
/* Move muxes */
|
|
hts_settings_buildpath(src, sizeof(src),
|
|
"input/iptv/muxes");
|
|
hts_settings_buildpath(dst, sizeof(dst),
|
|
"input/iptv/networks/%s/muxes", u.hex);
|
|
rename(src, dst);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* v2 -> v3 : changes to DVB layout
|
|
*/
|
|
static void
|
|
config_migrate_v3 ( void )
|
|
{
|
|
char src[1024], dst[1024];
|
|
|
|
/* Due to having to potentially run this twice! */
|
|
hts_settings_buildpath(dst, sizeof(dst), "input/dvb/networks");
|
|
if (!access(dst, R_OK | W_OK))
|
|
return;
|
|
|
|
hts_settings_makedirs(dst);
|
|
hts_settings_buildpath(src, sizeof(src), "input/linuxdvb/networks");
|
|
rename(src, dst);
|
|
}
|
|
|
|
/*
|
|
* v3 -> v5 : fix broken DVB network / mux files
|
|
*/
|
|
static void
|
|
config_migrate_v5 ( void )
|
|
{
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
const char *str;
|
|
|
|
/* Remove linux prefix from class */
|
|
if ((c = hts_settings_load_r(1, "input/dvb/networks"))) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (!(e = htsmsg_get_map(e, "config"))) continue;
|
|
if (!(str = htsmsg_get_str(e, "class"))) continue;
|
|
if (!strncmp(str, "linux", 5)) {
|
|
str = tvh_strdupa(str+5);
|
|
htsmsg_delete_field(e, "class");
|
|
htsmsg_add_str(e, "class", str);
|
|
hts_settings_save(e, "input/dvb/networks/%s/config", f->hmf_name);
|
|
}
|
|
}
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
/*
|
|
* v5 -> v6 : epggrab changes, also xmltv/config
|
|
*/
|
|
static void
|
|
config_migrate_v6 ( void )
|
|
{
|
|
htsmsg_t *c, *m;
|
|
htsmsg_field_t *f;
|
|
const char *str;
|
|
uint32_t interval;
|
|
char buf[128];
|
|
const char *s;
|
|
int old = 0;
|
|
|
|
c = hts_settings_load_r(1, "epggrab/config");
|
|
|
|
/* xmltv/config -> egpgrab/config */
|
|
if (!c && (c = hts_settings_load("xmltv/config")))
|
|
old = 1;
|
|
|
|
if (c) {
|
|
if (!htsmsg_get_u32(c, old ? "grab-interval" : "interval", &interval)) {
|
|
if (old) interval *= 3600;
|
|
if (interval <= 600)
|
|
strcpy(buf, "*/10 * * * *");
|
|
else if (interval <= 900)
|
|
strcpy(buf, "*/15 * * * *");
|
|
else if (interval <= 1200)
|
|
strcpy(buf, "*/30 * * * *");
|
|
else if (interval <= 3600)
|
|
strcpy(buf, "4 * * * *");
|
|
else if (interval <= 7200)
|
|
strcpy(buf, "4 */2 * * *");
|
|
else if (interval <= 14400)
|
|
strcpy(buf, "4 */4 * * *");
|
|
else if (interval <= 28800)
|
|
strcpy(buf, "4 */8 * * *");
|
|
else if (interval <= 43200)
|
|
strcpy(buf, "4 */12 * * *");
|
|
else
|
|
strcpy(buf, "4 0 * * *");
|
|
} else
|
|
strcpy(buf, "4 */12 * * *");
|
|
htsmsg_add_str(c, "cron", buf);
|
|
htsmsg_delete_field(c, old ? "grab-interval" : "interval");
|
|
if (old) {
|
|
s = htsmsg_get_str(c, "current-grabber");
|
|
if (s) {
|
|
htsmsg_add_str(c, "module", s);
|
|
htsmsg_delete_field(c, "current-grabber");
|
|
}
|
|
}
|
|
if (old) {
|
|
m = htsmsg_get_map(c, "mod_enabled");
|
|
if (!m) {
|
|
m = htsmsg_create_map();
|
|
htsmsg_add_msg(c, "mod_enabled", m);
|
|
}
|
|
htsmsg_add_u32(m, "eit", 1);
|
|
htsmsg_add_u32(m, "uk_freesat", 1);
|
|
htsmsg_add_u32(m, "uk_freeview", 1);
|
|
htsmsg_add_u32(m, "viasat_baltic", 1);
|
|
htsmsg_add_u32(m, "opentv-skyuk", 1);
|
|
htsmsg_add_u32(m, "opentv-skyit", 1);
|
|
htsmsg_add_u32(m, "opentv-ausat", 1);
|
|
}
|
|
|
|
hts_settings_save(c, "epggrab/config");
|
|
}
|
|
if (old) {
|
|
hts_settings_remove("xmltv/config");
|
|
|
|
/* Migrate XMLTV channels */
|
|
htsmsg_t *xc, *ch;
|
|
htsmsg_t *xchs = hts_settings_load("xmltv/channels");
|
|
htsmsg_t *chs = hts_settings_load_r(1, "channel");
|
|
if (xchs) {
|
|
HTSMSG_FOREACH(f, chs) {
|
|
if ((ch = htsmsg_get_map_by_field(f))) {
|
|
if ((str = htsmsg_get_str(ch, "xmltv-channel"))) {
|
|
if ((xc = htsmsg_get_map(xchs, str))) {
|
|
htsmsg_add_u32(xc, "channel", atoi(f->hmf_name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
HTSMSG_FOREACH(f, xchs) {
|
|
if ((xc = htsmsg_get_map_by_field(f))) {
|
|
hts_settings_save(xc, "epggrab/xmltv/channels/%s", f->hmf_name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
|
|
/*
|
|
* v6 -> v7 : acesscontrol changes
|
|
*/
|
|
static void
|
|
config_migrate_simple ( const char *dir, htsmsg_t *list,
|
|
void (*modify)(htsmsg_t *record,
|
|
uint32_t id,
|
|
const char *uuid,
|
|
void *aux),
|
|
void *aux )
|
|
{
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
tvh_uuid_t u;
|
|
uint32_t index = 1, id;
|
|
|
|
if (!(c = hts_settings_load(dir)))
|
|
return;
|
|
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
uuid_init_hex(&u, NULL);
|
|
if (htsmsg_get_u32(e, "id", &id))
|
|
id = 0;
|
|
else if (list) {
|
|
htsmsg_t *m = htsmsg_create_map();
|
|
char buf[16];
|
|
snprintf(buf, sizeof(buf), "%d", id);
|
|
htsmsg_add_str(m, "id", buf);
|
|
htsmsg_add_str(m, "uuid", u.hex);
|
|
htsmsg_add_msg(list, NULL, m);
|
|
}
|
|
htsmsg_delete_field(e, "id");
|
|
htsmsg_add_u32(e, "index", index++);
|
|
if (modify)
|
|
modify(e, id, u.hex, aux);
|
|
hts_settings_save(e, "%s/%s", dir, u.hex);
|
|
hts_settings_remove("%s/%s", dir, f->hmf_name);
|
|
}
|
|
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
static void
|
|
config_modify_acl( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
|
{
|
|
uint32_t a, b;
|
|
const char *s;
|
|
if (htsmsg_get_u32(c, "adv_streaming", &a))
|
|
if (!htsmsg_get_u32(c, "streaming", &b))
|
|
htsmsg_add_u32(c, "adv_streaming", b);
|
|
if ((s = htsmsg_get_str(c, "password")) != NULL) {
|
|
char buf[256], result[300];
|
|
snprintf(buf, sizeof(buf), "TVHeadend-Hide-%s", s);
|
|
base64_encode(result, sizeof(result), (uint8_t *)buf, strlen(buf));
|
|
htsmsg_add_str(c, "password2", result);
|
|
htsmsg_delete_field(c, "password");
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_migrate_v7 ( void )
|
|
{
|
|
config_migrate_simple("accesscontrol", NULL, config_modify_acl, NULL);
|
|
}
|
|
|
|
static void
|
|
config_modify_tag( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
|
{
|
|
htsmsg_t *ch = (htsmsg_t *)aux;
|
|
htsmsg_t *e, *m, *t;
|
|
htsmsg_field_t *f, *f2;
|
|
uint32_t u32;
|
|
|
|
htsmsg_delete_field(c, "index");
|
|
|
|
if (ch == NULL)
|
|
return;
|
|
|
|
HTSMSG_FOREACH(f, ch) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
m = htsmsg_get_list(e, "tags");
|
|
if (m == NULL)
|
|
continue;
|
|
t = htsmsg_get_list(e, "tags_new");
|
|
HTSMSG_FOREACH(f2, m) {
|
|
if (!htsmsg_field_get_u32(f2, &u32) && u32 == id) {
|
|
if (t == NULL) {
|
|
t = htsmsg_create_list();
|
|
htsmsg_add_msg(e, "tags_new", t);
|
|
t = htsmsg_get_list(e, "tags_new");
|
|
}
|
|
htsmsg_add_str(t, NULL, uuid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_migrate_v8 ( void )
|
|
{
|
|
htsmsg_t *ch, *e, *m;
|
|
htsmsg_field_t *f;
|
|
|
|
ch = hts_settings_load_r(1, "channel");
|
|
config_migrate_simple("channeltags", NULL, config_modify_tag, ch);
|
|
if (ch == NULL)
|
|
return;
|
|
HTSMSG_FOREACH(f, ch) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
htsmsg_delete_field(e, "tags");
|
|
m = htsmsg_get_list(e, "tags_new");
|
|
if (m) {
|
|
htsmsg_add_msg(e, "tags", htsmsg_copy(m));
|
|
htsmsg_delete_field(e, "tags_new");
|
|
}
|
|
hts_settings_save(e, "channel/%s", f->hmf_name);
|
|
}
|
|
htsmsg_destroy(ch);
|
|
}
|
|
|
|
static void
|
|
config_modify_autorec( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
|
{
|
|
uint32_t u32;
|
|
htsmsg_delete_field(c, "index");
|
|
if (!htsmsg_get_u32(c, "approx_time", &u32)) {
|
|
if (u32 == 0)
|
|
u32 = -1;
|
|
htsmsg_delete_field(c, "approx_time");
|
|
htsmsg_add_u32(c, "start", u32);
|
|
}
|
|
if (!htsmsg_get_u32(c, "contenttype", &u32)) {
|
|
htsmsg_delete_field(c, "contenttype");
|
|
htsmsg_add_u32(c, "content_type", u32 / 16);
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_modify_dvr_log( htsmsg_t *c, uint32_t id, const char *uuid, void *aux )
|
|
{
|
|
htsmsg_t *list = aux;
|
|
const char *chname = htsmsg_get_str(c, "channelname");
|
|
const char *chuuid = htsmsg_get_str(c, "channel");
|
|
htsmsg_t *e;
|
|
htsmsg_field_t *f;
|
|
tvh_uuid_t uuid0;
|
|
const char *s1;
|
|
uint32_t u32;
|
|
|
|
htsmsg_delete_field(c, "index");
|
|
if (chname == NULL || (chuuid != NULL && uuid_init_bin(&uuid0, chuuid))) {
|
|
chname = strdup(chuuid);
|
|
htsmsg_delete_field(c, "channelname");
|
|
htsmsg_delete_field(c, "channel");
|
|
htsmsg_add_str(c, "channelname", chname);
|
|
free((char *)chname);
|
|
if (!htsmsg_get_u32(c, "contenttype", &u32)) {
|
|
htsmsg_delete_field(c, "contenttype");
|
|
htsmsg_add_u32(c, "content_type", u32 / 16);
|
|
}
|
|
}
|
|
if ((s1 = htsmsg_get_str(c, "autorec")) != NULL) {
|
|
s1 = strdup(s1);
|
|
htsmsg_delete_field(c, "autorec");
|
|
HTSMSG_FOREACH(f, list) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (strcmp(s1, htsmsg_get_str(e, "id")) == 0) {
|
|
htsmsg_add_str(c, "autorec", htsmsg_get_str(e, "uuid"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_migrate_v9 ( void )
|
|
{
|
|
htsmsg_t *list = htsmsg_create_list();
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
tvh_uuid_t u;
|
|
|
|
config_migrate_simple("autorec", list, config_modify_autorec, NULL);
|
|
config_migrate_simple("dvr/log", NULL, config_modify_dvr_log, list);
|
|
htsmsg_destroy(list);
|
|
|
|
if ((c = hts_settings_load("dvr")) != NULL) {
|
|
/* step 1: only "config" */
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (strcmp(f->hmf_name, "config")) continue;
|
|
htsmsg_add_str(e, "name", f->hmf_name + 6);
|
|
uuid_init_hex(&u, NULL);
|
|
hts_settings_remove("dvr/%s", f->hmf_name);
|
|
hts_settings_save(e, "dvr/config/%s", u.hex);
|
|
}
|
|
/* step 2: reset (without "config") */
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (strcmp(f->hmf_name, "config") == 0) continue;
|
|
if (strncmp(f->hmf_name, "config", 6)) continue;
|
|
htsmsg_add_str(e, "name", f->hmf_name + 6);
|
|
uuid_init_hex(&u, NULL);
|
|
hts_settings_remove("dvr/%s", f->hmf_name);
|
|
hts_settings_save(e, "dvr/config/%s", u.hex);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
}
|
|
|
|
static void
|
|
config_migrate_move ( const char *dir,
|
|
const char *newdir )
|
|
{
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
|
|
if (!(c = hts_settings_load(dir)))
|
|
return;
|
|
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
hts_settings_save(e, "%s/%s", newdir, f->hmf_name);
|
|
hts_settings_remove("%s/%s", dir, f->hmf_name);
|
|
}
|
|
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
static void
|
|
config_migrate_v10 ( void )
|
|
{
|
|
config_migrate_move("channel", "channel/config");
|
|
config_migrate_move("channeltags", "channel/tag");
|
|
}
|
|
|
|
static const char *
|
|
config_find_uuid( htsmsg_t *map, const char *name, const char *value )
|
|
{
|
|
htsmsg_t *e;
|
|
htsmsg_field_t *f;
|
|
const char *s;
|
|
|
|
HTSMSG_FOREACH(f, map) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if ((s = htsmsg_get_str(e, name)) != NULL) {
|
|
if (!strcmp(s, value))
|
|
return f->hmf_name;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
config_modify_acl_dvallcfg( htsmsg_t *c, htsmsg_t *dvr_config )
|
|
{
|
|
uint32_t a;
|
|
const char *username, *uuid;
|
|
|
|
username = htsmsg_get_str(c, "username");
|
|
if (!htsmsg_get_u32(c, "dvallcfg", &a))
|
|
if (a == 0) {
|
|
uuid = username ? config_find_uuid(dvr_config, "name", username) : NULL;
|
|
if (uuid)
|
|
htsmsg_add_str(c, "dvr_config", uuid);
|
|
}
|
|
htsmsg_delete_field(c, "dvallcfg");
|
|
}
|
|
|
|
static void
|
|
config_modify_acl_tag_only( htsmsg_t *c, htsmsg_t *channel_tag )
|
|
{
|
|
uint32_t a;
|
|
const char *username, *tag, *uuid;
|
|
|
|
username = htsmsg_get_str(c, "username");
|
|
tag = htsmsg_get_str(c, "channel_tag");
|
|
if (!tag || tag[0] == '\0')
|
|
tag = NULL;
|
|
if (tag == NULL && !htsmsg_get_u32(c, "tag_only", &a)) {
|
|
if (a) {
|
|
uuid = username ? config_find_uuid(channel_tag, "name", username) : NULL;
|
|
if (uuid)
|
|
htsmsg_add_str(c, "channel_tag", uuid);
|
|
}
|
|
} else if (tag) {
|
|
uuid = config_find_uuid(channel_tag, "name", tag);
|
|
if (uuid) {
|
|
htsmsg_delete_field(c, "channel_tag");
|
|
htsmsg_add_str(c, "channel_tag", uuid);
|
|
}
|
|
}
|
|
htsmsg_delete_field(c, "tag_only");
|
|
}
|
|
|
|
static void
|
|
config_modify_dvr_config_name( htsmsg_t *c, htsmsg_t *dvr_config )
|
|
{
|
|
const char *config_name, *uuid;
|
|
|
|
config_name = htsmsg_get_str(c, "config_name");
|
|
uuid = config_name ? config_find_uuid(dvr_config, "name", config_name) : NULL;
|
|
htsmsg_delete_field(c, "config_name");
|
|
htsmsg_add_str(c, "config_name", uuid ?: "");
|
|
}
|
|
|
|
|
|
static void
|
|
config_migrate_v11 ( void )
|
|
{
|
|
htsmsg_t *dvr_config;
|
|
htsmsg_t *channel_tag;
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
|
|
dvr_config = hts_settings_load("dvr/config");
|
|
channel_tag = hts_settings_load("channel/tag");
|
|
|
|
if ((c = hts_settings_load("accesscontrol")) != NULL) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
config_modify_acl_dvallcfg(e, dvr_config);
|
|
config_modify_acl_tag_only(e, channel_tag);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
if ((c = hts_settings_load("dvr/log")) != NULL) {
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
config_modify_dvr_config_name(e, dvr_config);
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
htsmsg_destroy(channel_tag);
|
|
htsmsg_destroy(dvr_config);
|
|
}
|
|
|
|
/*
|
|
* Perform backup
|
|
*/
|
|
static void
|
|
dobackup(const char *oldver)
|
|
{
|
|
char outfile[PATH_MAX], cwd[PATH_MAX];
|
|
const char *argv[] = {
|
|
"/usr/bin/tar", "cjf", outfile, "--exclude", "backup", ".", NULL
|
|
};
|
|
const char *root = hts_settings_get_root();
|
|
char errtxt[128];
|
|
const char **arg;
|
|
int code;
|
|
|
|
tvhinfo("config", "backup: migrating config from %s (running %s)",
|
|
oldver, tvheadend_version);
|
|
|
|
if (getcwd(cwd, sizeof(cwd)) == NULL) {
|
|
tvherror("config", "unable to get the current working directory");
|
|
goto fatal;
|
|
}
|
|
|
|
if (!access("/bin/tar", X_OK))
|
|
argv[0] = "/bin/tar";
|
|
else if (!access("/usr/bin/tar", X_OK))
|
|
argv[0] = "/usr/bin/tar";
|
|
else if (!access("/usr/local/bin/tar", X_OK))
|
|
argv[0] = "/usr/local/bin/tar";
|
|
else {
|
|
tvherror("config", "unable to find tar program");
|
|
goto fatal;
|
|
}
|
|
|
|
snprintf(outfile, sizeof(outfile), "%s/backup", root);
|
|
if (makedirs(outfile, 0700))
|
|
goto fatal;
|
|
if (chdir(root)) {
|
|
tvherror("config", "unable to find directory '%s'", root);
|
|
goto fatal;
|
|
}
|
|
|
|
snprintf(outfile, sizeof(outfile), "%s/backup/%s.tar.bz2",
|
|
root, oldver);
|
|
tvhinfo("config", "backup: running, output file %s", outfile);
|
|
|
|
spawnv(argv[0], (void *)argv);
|
|
|
|
while ((code = spawn_reap(errtxt, sizeof(errtxt))) == -EAGAIN)
|
|
usleep(20000);
|
|
|
|
if (code) {
|
|
htsbuf_queue_t q;
|
|
char *s;
|
|
htsbuf_queue_init(&q, 0);
|
|
for (arg = argv; *arg; arg++) {
|
|
htsbuf_append(&q, *arg, strlen(*arg));
|
|
if (arg[1])
|
|
htsbuf_append(&q, " ", 1);
|
|
}
|
|
s = htsbuf_to_string(&q);
|
|
tvherror("config", "command '%s' returned error code %d", s, code);
|
|
free(s);
|
|
htsbuf_queue_flush(&q);
|
|
goto fatal;
|
|
}
|
|
|
|
if (chdir(cwd)) {
|
|
tvherror("config", "unable to change directory to '%s'", cwd);
|
|
goto fatal;
|
|
}
|
|
return;
|
|
|
|
fatal:
|
|
tvherror("config", "backup: fatal error");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Migration table
|
|
*/
|
|
static const config_migrate_t config_migrate_table[] = {
|
|
config_migrate_v1,
|
|
config_migrate_v2,
|
|
config_migrate_v3,
|
|
config_migrate_v3, // Re-run due to bug in previous version of function
|
|
config_migrate_v5,
|
|
config_migrate_v6,
|
|
config_migrate_v7,
|
|
config_migrate_v8,
|
|
config_migrate_v9,
|
|
config_migrate_v10,
|
|
config_migrate_v11
|
|
};
|
|
|
|
/*
|
|
* Perform migrations (if required)
|
|
*/
|
|
static int
|
|
config_migrate ( int backup )
|
|
{
|
|
uint32_t v;
|
|
const char *s;
|
|
|
|
/* Get the current version */
|
|
v = htsmsg_get_u32_or_default(config, "version", 0);
|
|
s = htsmsg_get_str(config, "fullversion") ?: "unknown";
|
|
|
|
if (backup && strcmp(s, tvheadend_version))
|
|
dobackup(s);
|
|
else
|
|
backup = 0;
|
|
|
|
/* Attempt to auto-detect versions prior to v2 */
|
|
if (!v) {
|
|
if (hts_settings_exists("input/iptv/networks"))
|
|
v = 2;
|
|
else if (hts_settings_exists("input"))
|
|
v = 1;
|
|
}
|
|
|
|
/* No changes required */
|
|
if (v == ARRAY_SIZE(config_migrate_table)) {
|
|
if (backup)
|
|
goto update;
|
|
return 0;
|
|
}
|
|
|
|
/* Run migrations */
|
|
for ( ; v < ARRAY_SIZE(config_migrate_table); v++) {
|
|
tvhinfo("config", "migrating config from v%d to v%d", v, v+1);
|
|
config_migrate_table[v]();
|
|
}
|
|
|
|
/* Update */
|
|
update:
|
|
htsmsg_set_u32(config, "version", v);
|
|
htsmsg_set_str(config, "fullversion", tvheadend_version);
|
|
config_save();
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
static void
|
|
config_check_one ( const char *dir )
|
|
{
|
|
htsmsg_t *c, *e;
|
|
htsmsg_field_t *f;
|
|
|
|
if (!(c = hts_settings_load(dir)))
|
|
return;
|
|
|
|
HTSMSG_FOREACH(f, c) {
|
|
if (!(e = htsmsg_field_get_map(f))) continue;
|
|
if (strlen(f->hmf_name) != UUID_HEX_SIZE - 1) {
|
|
tvherror("START", "filename %s/%s/%s is invalid", hts_settings_get_root(), dir, f->hmf_name);
|
|
exit(1);
|
|
}
|
|
}
|
|
htsmsg_destroy(c);
|
|
}
|
|
|
|
/*
|
|
* Perform a simple check for UUID files
|
|
*/
|
|
static void
|
|
config_check ( void )
|
|
{
|
|
config_check_one("accesscontrol");
|
|
config_check_one("channel/config");
|
|
config_check_one("channel/tag");
|
|
config_check_one("dvr/config");
|
|
config_check_one("dvr/log");
|
|
config_check_one("dvr/autorec");
|
|
config_check_one("esfilter");
|
|
}
|
|
|
|
/* **************************************************************************
|
|
* Initialisation / Shutdown / Saving
|
|
* *************************************************************************/
|
|
|
|
void
|
|
config_init ( const char *path, int backup )
|
|
{
|
|
struct stat st;
|
|
char buf[1024];
|
|
const char *homedir = getenv("HOME");
|
|
int new = 0;
|
|
|
|
/* Generate default */
|
|
if (!path) {
|
|
snprintf(buf, sizeof(buf), "%s/.hts/tvheadend", homedir);
|
|
path = buf;
|
|
}
|
|
|
|
/* Ensure directory exists */
|
|
if (stat(path, &st)) {
|
|
new = 1;
|
|
if (makedirs(path, 0700)) {
|
|
tvhwarn("START", "failed to create settings directory %s,"
|
|
" settings will not be saved", path);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* And is usable */
|
|
else if (access(path, R_OK | W_OK)) {
|
|
tvhwarn("START", "configuration path %s is not r/w"
|
|
" for UID:%d GID:%d [e=%s],"
|
|
" settings will not be saved",
|
|
path, getuid(), getgid(), strerror(errno));
|
|
return;
|
|
}
|
|
|
|
/* Configure settings routines */
|
|
hts_settings_init(path);
|
|
|
|
/* Load global settings */
|
|
config = hts_settings_load("config");
|
|
if (!config) {
|
|
tvhlog(LOG_DEBUG, "config", "no configuration, loading defaults");
|
|
config = htsmsg_create_map();
|
|
}
|
|
|
|
/* Store version number */
|
|
if (new) {
|
|
htsmsg_set_u32(config, "version", ARRAY_SIZE(config_migrate_table));
|
|
htsmsg_set_str(config, "fullversion", tvheadend_version);
|
|
config_save();
|
|
|
|
/* Perform migrations */
|
|
} else {
|
|
if (config_migrate(backup))
|
|
config_check();
|
|
}
|
|
}
|
|
|
|
void config_done ( void )
|
|
{
|
|
htsmsg_destroy(config);
|
|
}
|
|
|
|
void config_save ( void )
|
|
{
|
|
hts_settings_save(config, "config");
|
|
}
|
|
|
|
/* **************************************************************************
|
|
* Access / Update routines
|
|
* *************************************************************************/
|
|
|
|
htsmsg_t *config_get_all ( void )
|
|
{
|
|
return htsmsg_copy(config);
|
|
}
|
|
|
|
static int
|
|
_config_set_str ( const char *fld, const char *val )
|
|
{
|
|
const char *c = htsmsg_get_str(config, fld);
|
|
if (!c || strcmp(c, val)) {
|
|
if (c) htsmsg_delete_field(config, fld);
|
|
htsmsg_add_str(config, fld, val);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *config_get_language ( void )
|
|
{
|
|
return htsmsg_get_str(config, "language");
|
|
}
|
|
|
|
int config_set_language ( const char *lang )
|
|
{
|
|
return _config_set_str("language", lang);
|
|
}
|
|
|
|
const char *config_get_muxconfpath ( void )
|
|
{
|
|
return htsmsg_get_str(config, "muxconfpath");
|
|
}
|
|
|
|
int config_set_muxconfpath ( const char *path )
|
|
{
|
|
return _config_set_str("muxconfpath", path);
|
|
}
|