Also, use a better naming scheme for DVB adapters (adapter vendor name + a sequence id)
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <dirent.h>
#include <libhts/htscfg.h>
#include "channels.h"
#include "transports.h"
+static void channel_group_settings_write(void);
+static void channel_settings_write(th_channel_t *ch);
+
struct th_channel_list channels;
struct th_transport_list all_transports;
int nchannels;
th_channel_group_t *defgroup;
-static int dontwritesettings;
void scanner_init(void);
TAILQ_INSERT_TAIL(&all_channel_groups, tcg, tcg_global_link);
- channel_settings_write();
-
+ channel_group_settings_write();
return tcg;
}
ch->ch_group = tcg;
TAILQ_INSERT_SORTED(&tcg->tcg_channels, ch, ch_group_link, channelcmp);
- channel_settings_write();
+
+ channel_settings_write(ch);
+}
+
+/**
+ *
+ */
+void
+channel_set_teletext_rundown(th_channel_t *ch, int v)
+{
+ ch->ch_teletext_rundown = v;
+ channel_settings_write(ch);
}
/**
TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
free((void *)tcg->tcg_name);
free(tcg);
+
+ channel_group_settings_write();
}
/**
void
channels_load(void)
{
- config_entry_t *ce, *ce1, *ce2;
- const char *name;
- th_channel_group_t *tcg;
+ struct config_head cl;
+ config_entry_t *ce;
+ char buf[400];
+ DIR *dir;
+ struct dirent *d;
+ const char *name, *grp;
th_channel_t *ch;
-
- dontwritesettings = 1;
+ th_channel_group_t *tcg;
TAILQ_INIT(&all_channel_groups);
+ TAILQ_INIT(&cl);
- TAILQ_FOREACH(ce1, &settings_list, ce_link) {
- if(ce1->ce_type != CFG_SUB || strcasecmp("channel-group", ce1->ce_key))
+ snprintf(buf, sizeof(buf), "%s/channel-group-settings.cfg", settings_dir);
+ config_read_file0(buf, &cl);
+
+ TAILQ_FOREACH(ce, &cl, ce_link) {
+ if(ce->ce_type != CFG_SUB || strcasecmp("channel-group", ce->ce_key))
+ continue;
+
+ if((name = config_get_str_sub(&ce->ce_sub, "name", NULL)) == NULL)
continue;
-
- if((name = config_get_str_sub(&ce1->ce_sub, "name", NULL)) == NULL)
+
+ printf("Added channel group %s\n", name);
+ channel_group_find(name, 1);
+ }
+ config_free0(&cl);
+
+ snprintf(buf, sizeof(buf), "%s/channels", settings_dir);
+
+ if((dir = opendir(buf)) == NULL)
+ return;
+
+ while((d = readdir(dir)) != NULL) {
+
+ if(d->d_name[0] == '.')
continue;
- tcg = channel_group_find(name, 1);
- TAILQ_FOREACH(ce2, &ce1->ce_sub, ce_link) {
- if(ce2->ce_type != CFG_SUB || strcasecmp("channel", ce2->ce_key))
- continue;
+ snprintf(buf, sizeof(buf), "%s/channels/%s", settings_dir, d->d_name);
+ TAILQ_INIT(&cl);
+ config_read_file0(buf, &cl);
- if((name = config_get_str_sub(&ce2->ce_sub, "name", NULL)) == NULL)
- continue;
+ if((name = config_get_str_sub(&cl, "name", NULL)) == NULL)
+ continue;
- ch = channel_find(name, 1, tcg);
+ if((grp = config_get_str_sub(&cl, "channel-group", NULL)) == NULL)
+ continue;
+
+ tcg = channel_group_find(grp, 1);
+ ch = channel_find(name, 1, tcg);
- ch->ch_teletext_rundown =
- atoi(config_get_str_sub(&ce2->ce_sub, "teletext-rundown", "0"));
- }
+ ch->ch_teletext_rundown =
+ atoi(config_get_str_sub(&cl, "teletext-rundown", "0"));
+
+ config_free0(&cl);
}
tcg = channel_group_find("-disabled-", 1);
tcg->tcg_cant_delete_me = 1;
tcg->tcg_hidden = 1;
-
+
defgroup = channel_group_find("Uncategorized", 1);
defgroup->tcg_cant_delete_me = 1;
+
+ /* Static services */
TAILQ_FOREACH(ce, &config_list, ce_link) {
if(ce->ce_type == CFG_SUB && !strcasecmp("service", ce->ce_key)) {
service_load(&ce->ce_sub);
}
}
- dontwritesettings = 0;
-
}
+
/**
* The index stuff should go away
*/
TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
TAILQ_INSERT_AFTER(&all_channel_groups, n, tcg, tcg_global_link);
- channel_settings_write();
+
+ channel_group_settings_write();
}
void
TAILQ_REMOVE(&all_channel_groups, tcg, tcg_global_link);
TAILQ_INSERT_BEFORE(p, tcg, tcg_global_link);
- channel_settings_write();
+ channel_group_settings_write();
}
-
/**
+ * Write out a config file with all channel groups
*
+ * We do this to maintain order of groups
*/
-void
-channel_settings_write(void)
+static void
+channel_group_settings_write(void)
{
FILE *fp;
th_channel_group_t *tcg;
- th_channel_t *ch;
- th_transport_t *t;
- if(dontwritesettings || startupcounter > 0 || settingsfile == NULL)
- return;
+ char buf[400];
+ snprintf(buf, sizeof(buf), "%s/channel-group-settings.cfg", settings_dir);
- fp = fopen(settingsfile, "w+");
- if(fp == NULL)
+ if((fp = settings_open_for_write(buf)) == NULL)
return;
TAILQ_FOREACH(tcg, &all_channel_groups, tcg_global_link) {
fprintf(fp, "channel-group {\n"
"\tname = %s\n", tcg->tcg_name);
- TAILQ_FOREACH(ch, &tcg->tcg_channels, ch_group_link) {
- if(LIST_FIRST(&ch->ch_transports) == NULL)
- continue;
-
- fprintf(fp, "\tchannel {\n"
- "\t\tname = %s\n", ch->ch_name);
- if(ch->ch_teletext_rundown)
- fprintf(fp, "\t\tteletext-rundown = %d\n", ch->ch_teletext_rundown);
- fprintf(fp, "\t}\n");
- }
fprintf(fp, "}\n");
}
+ fclose(fp);
+}
- LIST_FOREACH(t, &all_transports, tht_global_link) {
- if(t->tht_channel == NULL)
- continue;
- fprintf(fp, "transport {\n"
- "\tuniquename = %s\n"
- "\tchannel = %s\n"
- "\tprio = %d\n"
- "}\n",
- t->tht_uniquename,
- t->tht_channel->ch_name,
- t->tht_prio);
- }
+
+/**
+ * Write out a config file for a channel
+ */
+static void
+channel_settings_write(th_channel_t *ch)
+{
+ FILE *fp;
+ char buf[400];
+
+ snprintf(buf, sizeof(buf), "%s/channels/%s", settings_dir, ch->ch_sname);
+ if((fp = settings_open_for_write(buf)) == NULL)
+ return;
+
+ fprintf(fp, "name = %s\n", ch->ch_name);
+ fprintf(fp, "channel-group = %s\n", ch->ch_group->tcg_name);
+ if(ch->ch_teletext_rundown)
+ fprintf(fp, "teletext-rundown = %d\n", ch->ch_teletext_rundown);
fclose(fp);
}
+
void channel_group_move_next(th_channel_group_t *tcg);
-void channel_settings_write(void);
-
void channel_set_group(th_channel_t *ch, th_channel_group_t *tcg);
+void channel_set_teletext_rundown(th_channel_t *ch, int v);
+
#endif /* CHANNELS_H */
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
-
+#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
dvb_add_adapter(const char *path)
{
char fname[256];
- int fe;
- th_dvb_adapter_t *tda;
+ int fe, i, r;
+ th_dvb_adapter_t *tda, *x;
+ char c;
+ char buf[400], *cp;
snprintf(fname, sizeof(fname), "%s/frontend0", path);
pthread_cond_init(&tda->tda_cond, NULL);
TAILQ_INIT(&tda->tda_fe_cmd_queue);
- LIST_INSERT_HEAD(&dvb_adapters_probing, tda, tda_link);
startupcounter++;
tda->tda_info = strdup(tda->tda_fe_info->name);
+ /*
+ * Generate a decent unique name for the adapter.
+ * If multiple adapters with the same name is found, we add
+ * a sequence number at the end
+ */
+
+ for(i = 0; i < strlen(tda->tda_info); i++) {
+ c = tolower(tda->tda_info[i]);
+ if(isalnum(c))
+ buf[i] = c;
+ else
+ buf[i] = '_';
+ }
+ cp = &buf[i];
+
+ r = 0;
+
+ again:
+ if(r)
+ sprintf(cp, "-%d", r);
+ else
+ *cp = 0;
+
+ LIST_FOREACH(x, &dvb_adapters_probing, tda_link) {
+ if(!strcmp(buf, x->tda_sname)) {
+ r++;
+ goto again;
+ }
+ }
+
+ tda->tda_sname = strdup(buf);
+
+ LIST_INSERT_HEAD(&dvb_adapters_probing, tda, tda_link);
+
syslog(LOG_INFO, "Adding adapter %s (%s)", path, tda->tda_fe_info->name);
dtimer_arm(&tda->tda_fec_monitor_timer, dvb_fec_monitor, tda, 1);
t->tht_dvb_mux_instance = tdmi;
snprintf(tmp, sizeof(tmp), "%s/%04x", tdmi->tdmi_uniquename, sid);
-
free((void *)t->tht_uniquename);
t->tht_uniquename = strdup(tmp);
+
+ snprintf(tmp, sizeof(tmp), "%s/%04x", tdmi->tdmi_shortname, sid);
+ free((void *)t->tht_name);
t->tht_name = strdup(tmp);
+
LIST_INSERT_HEAD(&all_transports, t, tht_global_link);
dvb_table_add_transport(tdmi, t, pmt_pid);
/* Generate names */
snprintf(buf, sizeof(buf), "%s/%s/%.1fMHz",
- tda->tda_rootpath, typetxt,
+ tda->tda_sname, typetxt,
(float)fe_param->frequency / 1000000.0f);
tdmi->tdmi_uniquename = strdup(buf);
t->tht_scrambled ? "Yes" : "No",
t->tht_provider,
t->tht_network,
- t->tht_uniquename);
+ t->tht_name);
}
}
if((s = http_arg_get(&hc->hc_url_args, "ttrp")) != NULL)
- ch->ch_teletext_rundown = atoi(s);
+ channel_set_teletext_rundown(ch, atoi(s));
if((grp = http_arg_get(&hc->hc_url_args, "grp")) != NULL) {
tcg = channel_group_find(grp, 1);
int xmltvreload;
int startupcounter;
const char *tvheadend_streaming_host;
+const char *settings_dir;
static pthread_mutex_t tag_mutex = PTHREAD_MUTEX_INITIALIZER;
static uint32_t tag_tally;
int logfacility = LOG_DAEMON;
int disable_dvb = 0;
int p;
+ char buf[400];
signal(SIGPIPE, handle_sigpipe);
openlog("tvheadend", LOG_PID, logfacility);
- syslog(LOG_NOTICE, "Started HTS TV Headend");
+ settings_dir = config_get_str("settings-dir", "/tmp/tvheadend");
+ mkdir(settings_dir, 0777);
+
+ snprintf(buf, sizeof(buf), "%s/channels", settings_dir);
+ mkdir(buf, 0777);
+
+ snprintf(buf, sizeof(buf), "%s/transports", settings_dir);
+ mkdir(buf, 0777);
+
+
+ syslog(LOG_NOTICE, "Started HTS TV Headend, settings located in \"%s\"",
+ settings_dir);
dispatch_init();
while(running) {
if(startupcounter == 0) {
- channel_settings_write();
-
- startupcounter = -1;
syslog(LOG_NOTICE,
"Initial input setup completed, starting output modules");
fprintf(stderr,
"Initial input setup completed, starting output modules\n");
+ startupcounter = -1;
+
pvr_init();
output_multicast_setup();
client_start();
default: return "<unknown>";
}
}
+
+
+FILE *
+settings_open_for_write(const char *name)
+{
+ FILE *fp;
+
+ fp = fopen(name, "w+");
+ if(fp == NULL)
+ syslog(LOG_ALERT, "Unable to open settings file \"%s\" -- %s",
+ name, strerror(errno));
+ return fp;
+}
+
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
-
+#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static dtimer_t transport_monitor_timer;
+static const char *transport_settings_path(th_transport_t *t);
void
th_stream_t *st;
char *chname;
const char *n;
- config_entry_t *ce;
char pid[30];
char lang[30];
+ struct config_head cl;
assert(t->tht_uniquename != NULL);
- TAILQ_FOREACH(ce, &settings_list, ce_link) {
- if(ce->ce_type != CFG_SUB || strcasecmp("transport", ce->ce_key))
- continue;
+ n = transport_settings_path(t);
+ TAILQ_INIT(&cl);
+ config_read_file0(n, &cl);
- n = config_get_str_sub(&ce->ce_sub, "uniquename", NULL);
- if(n != NULL && !strcmp(n, t->tht_uniquename))
- break;
- }
+ t->tht_prio = atoi(config_get_str_sub(&cl, "prio", "0"));
+ n = config_get_str_sub(&cl, "channel", NULL);
+ if(n != NULL)
+ ch = channel_find(n, 1, NULL);
- if(ce != NULL) {
- t->tht_prio = atoi(config_get_str_sub(&ce->ce_sub, "prio", "0"));
- n = config_get_str_sub(&ce->ce_sub, "channel", NULL);
- if(n != NULL)
- ch = channel_find(n, 1, NULL);
- }
+ config_free0(&cl);
t->tht_channel = ch;
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
{
th_channel_t *ch = t->tht_channel;
+ if(t->tht_prio == prio)
+ return;
+
LIST_REMOVE(t, tht_channel_link);
t->tht_prio = prio;
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
- channel_settings_write();
+ transport_settings_write(t);
}
LIST_REMOVE(t, tht_channel_link);
t->tht_channel = ch;
LIST_INSERT_SORTED(&ch->ch_transports, t, tht_channel_link, transportcmp);
- channel_settings_write();
+ transport_settings_write(t);
}
+
+
+/**
+ * Generate a settings-dir relative path to transport config
+ *
+ * Must be free'd by caller
+ */
+static const char *
+transport_settings_path(th_transport_t *t)
+{
+ char buf[400];
+ char buf2[400];
+
+ int l, i;
+ char c;
+
+ l = strlen(t->tht_uniquename);
+ if(l >= sizeof(buf2))
+ l = sizeof(buf2) - 1;
+
+ for(i = 0; i < l; i++) {
+ c = tolower(t->tht_uniquename[i]);
+ if(isalnum(c))
+ buf2[i] = c;
+ else
+ buf2[i] = '_';
+ }
+
+ buf2[i] = 0;
+ snprintf(buf, sizeof(buf), "%s/transports/%s", settings_dir, buf2);
+ return strdup(buf);
+}
+
+
+/**
+ * Write out a config file for a channel
+ */
+void
+transport_settings_write(th_transport_t *t)
+{
+ FILE *fp;
+ const char *n;
+
+ n = transport_settings_path(t);
+
+ if((fp = settings_open_for_write(n)) != NULL) {
+ fprintf(fp,
+ "uniquename = %s\n"
+ "channel = %s\n"
+ "prio = %d\n",
+ t->tht_uniquename,
+ t->tht_channel->ch_name,
+ t->tht_prio);
+ fclose(fp);
+ }
+ free((void *)n);
+}
+
void transport_move(th_transport_t *t, th_channel_t *ch);
+void transport_settings_write(th_transport_t *t);
+
#endif /* TRANSPORTS_H */
const char *tda_rootpath;
const char *tda_info;
+ const char *tda_sname;
LIST_ENTRY(th_dvb_adapter) tda_link;
const char *htstvstreamtype2txt(tv_streamtype_t s);
uint32_t tag_get(void);
extern const char *tvheadend_streaming_host;
+extern const char *settings_dir;
+FILE *settings_open_for_write(const char *name);
+FILE *settings_open_for_read(const char *name);
#endif /* TV_HEAD_H */