/* tsiproxy server configuration */ #include #include #include #include #include #include #include #include #include #include "libfuncs/libfuncs.h" #include "data.h" #include "conf.h" extern LIST *netconf, *chanconf, *restreamer; static pthread_mutex_t networks_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t channels_lock = PTHREAD_MUTEX_INITIALIZER; int load_channels(CONFIG *config) { regex_t re; regmatch_t res[3]; char line[1024]; int fd; int num_channels = 0; if (pthread_mutex_trylock(&channels_lock) != 0) return -1; fd = open(config->channels_file, O_RDONLY); if (fd != -1) { struct timeval tv; gettimeofday(&tv, NULL); unsigned int randstate = tv.tv_usec; int cookie = rand_r(&randstate); regcomp(&re, "^([A-Za-z0-9/\\.]+)\t+([A-Za-z0-9/\\.:-]+)", REG_EXTENDED); time_t curtime = time(NULL); LIST *old_chanconf; LIST *new_chanconf = list_new("chanconf"); while (fdgetline(fd,line,sizeof(line)) > 0) { chomp(line); if (regexec(&re,line,3,res,0)==0) { char *name, *source; char *org = strdup(line); name = org+res[1].rm_so; org[res[1].rm_eo]=0; source = org+res[2].rm_so; org[res[2].rm_eo]=0; if (!is_valid_url(source)) { LOGf("CCONF: Invalid url: %s\n", source); FREE(org); goto report_error; } /* Search for already added channel */ LNODE *l, *tmp; CHANNEL *chan = NULL; list_for_each_reverse(new_chanconf, l, tmp) { if (strcmp(name, ((CHANNEL *)l->data)->name)==0) { chan = l->data; break; } } if (!chan) { list_add(new_chanconf, new_channel(name, source)); num_channels++; } else { add_channel_source(chan, source); } FREE(org); } else { report_error: if (strlen(line) > 2 && line[0] != '#') { LOGf("CCONF: Invalid config line: %s\n", line); } } if (time(NULL) - curtime > CONFIG_LOAD_TIMEOUT) { LOGf("CCONF: Read timeout after %d lines on %s\n", num_channels, line); break; } } regfree(&re); shutdown_fd(&fd); /* Save current chanconf */ old_chanconf = chanconf; /* Switch chanconf */ chanconf = new_chanconf; /* Rewrite restreamer channels */ LNODE *lc, *lr, *lctmp, *lrtmp; CHANNEL *chan; list_lock(restreamer); // Unlocked after second list_for_each(restreamer) list_lock(chanconf); list_for_each(chanconf, lc, lctmp) { chan = lc->data; list_for_each(restreamer, lr, lrtmp) { if (strcmp(chan->name, ((RESTREAMER *)lr->data)->name)==0) { RESTREAMER *restr = lr->data; /* Mark the restreamer as valid */ restr->cookie = cookie; /* Check if current source exists in new channel configuration */ int i, src_found = -1; char *old_source = restr->channel->source; for (i=0; inum_src; i++) { if (strcmp(old_source, chan->sources[i]) == 0) { src_found = i; } } if (src_found > -1) { /* New configuration contains existing source, just update the reference */ set_channel_source(chan, src_found); restr->channel = chan; } else { /* New configuration *DO NOT* contain existing source. Force reconnect */ LOGf("RESTR: Source changed | Channel: %s srv_fd: %d Old:%s New:%s\n", chan->name, restr->sock, restr->channel->source, chan->source); /* The order is important! */ set_channel_source(chan, chan->num_src-1); /* Set source to last one. On reconnect next source will be used. */ restr->channel = chan; restr->reconnect = 1; } break; } } } list_unlock(chanconf); /* Kill restreamers that serve channels that no longer exist */ list_for_each(restreamer, lr, lrtmp) { RESTREAMER *r = lr->data; /* This restreamer should no longer serve clients */ if (r->cookie != cookie) { LOGf("RESTR: Channel removed | Channel: %s Source: %s\n", r->channel->name, r->channel->source); /* Replace channel reference with real object and instruct free_restreamer to free it */ r->channel = new_channel(r->channel->name, r->channel->source); r->freechannel = 1; r->dienow = 1; } } list_unlock(restreamer); /* Free old_chanconf */ list_free(&old_chanconf, (void (*)(void *))free_channel, NULL); } else { num_channels = -1; } pthread_mutex_unlock(&channels_lock); return num_channels; } int load_networks(CONFIG *config) { char line[1024]; regex_t re; regmatch_t res[8]; int fd; int num_networks = 0; if (pthread_mutex_trylock(&networks_lock) != 0) return -1; fd = open(config->networks_file, O_RDONLY); if (fd != -1) { regcomp(&re,"^([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})/([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})\t*([CFNSA]*)", REG_EXTENDED); time_t curtime = time(NULL); LIST *new_netconf = list_new("netconf"); while (fdgetline(fd,line,sizeof(line))>0){ if (regexec(&re,line,4,res,0)==0){ chomp(line); char *_net, *_mask, *_flags; _net = line+res[1].rm_so; line[res[1].rm_eo]=0; _mask = line+res[2].rm_so; line[res[2].rm_eo]=0; _flags= line+res[3].rm_so; line[res[3].rm_eo]=0; uint32_t net, mask; inet_pton(AF_INET, _net, &net); inet_pton(AF_INET,_mask, &mask); net = net & mask; acl_t acl; acl.reconf = strchr(_flags,'F') != NULL; acl.stats = strchr(_flags,'S') != NULL; acl.access = strchr(_flags,'A') != NULL; // bg network? list_add(new_netconf, new_network(net, mask, acl)); num_networks++; } else { if (strlen(line) > 2 && line[0] != '#') { LOGf("NCONF: Invalid config line: %s\n", line); } } if (time(NULL) - curtime > CONFIG_LOAD_TIMEOUT) { LOGf("NCONF: Read timeout after %d lines on %s\n", num_networks, line); break; } } regfree(&re); shutdown_fd(&fd); /* Save current netconf */ LIST *old_netconf = netconf; /* Switch chanconf */ netconf = new_netconf; /* Free old_netconf */ list_free(&old_netconf, (void (*)(void *))free_network, NULL); } else { num_networks = -1; } pthread_mutex_unlock(&networks_lock); return num_networks; } CONFIG *config_alloc(void) { CONFIG *c = calloc(1, sizeof(CONFIG)); c->server_port = 8080; return c; } int config_redirect_enabled(CONFIG *conf, int clientsock, char *path) { char *url; if (!conf->redirect_url) return 0; asprintf(&url, "%s%s", conf->redirect_url, path); send_302_redirect(clientsock, url); free(url); return 1; } void config_free(CONFIG **pconf) { CONFIG *conf = *pconf; if (conf) { if (conf->pidfile) unlink(conf->pidfile); FREE(conf->host_ptr); FREE(conf->logident); FREE(conf->redirect_url); FREE(*pconf); } }