|
@@ -31,6 +31,9 @@
|
31
|
31
|
#include <netdb.h> // for uint32_t
|
32
|
32
|
|
33
|
33
|
#include "libfuncs/libfuncs.h"
|
|
34
|
+#include "config.h"
|
|
35
|
+
|
|
36
|
+#include "web_server.h"
|
34
|
37
|
|
35
|
38
|
#define DNS_RESOLVER_TIMEOUT 5000
|
36
|
39
|
|
|
@@ -105,6 +108,8 @@ typedef struct {
|
105
|
108
|
pthread_t thread;
|
106
|
109
|
} RESTREAMER;
|
107
|
110
|
|
|
111
|
+static struct config config;
|
|
112
|
+
|
108
|
113
|
channel_source get_sproto(char *url) {
|
109
|
114
|
return strncmp(url, "http", 4)==0 ? tcp_sock : udp_sock;
|
110
|
115
|
}
|
|
@@ -288,27 +293,10 @@ void free_restreamer(RESTREAMER *r) {
|
288
|
293
|
FREE(r);
|
289
|
294
|
}
|
290
|
295
|
|
291
|
|
-
|
292
|
|
-
|
293
|
|
-
|
294
|
|
-char *pidfile = NULL;
|
295
|
|
-char *ident = NULL;
|
296
|
|
-char *logident = NULL;
|
297
|
|
-char *loghost = NULL;
|
298
|
|
-int logport = 514;
|
299
|
|
-
|
300
|
|
-char *channels_file = NULL;
|
301
|
|
-int syslog_active = 0;
|
302
|
|
-
|
303
|
296
|
char TS_NULL_FRAME[FRAME_PACKET_SIZE];
|
304
|
297
|
|
305
|
298
|
regex_t http_response;
|
306
|
299
|
|
307
|
|
-LIST *chanconf = NULL;
|
308
|
|
-LIST *restreamer = NULL;
|
309
|
|
-
|
310
|
|
-static pthread_mutex_t channels_lock = PTHREAD_MUTEX_INITIALIZER;
|
311
|
|
-
|
312
|
300
|
void proxy_log(RESTREAMER *r, char *msg, char *info) {
|
313
|
301
|
LOGf("%s: %sChan: %s Src: %s Dst: udp://%s:%d SrcIP: %s SrcFD: %i DstFD: %i\n",
|
314
|
302
|
msg,
|
|
@@ -323,17 +311,17 @@ void proxy_log(RESTREAMER *r, char *msg, char *info) {
|
323
|
311
|
);
|
324
|
312
|
}
|
325
|
313
|
|
326
|
|
-int load_channels_config() {
|
|
314
|
+int load_channels_config(struct config *cfg) {
|
327
|
315
|
regex_t re;
|
328
|
316
|
regmatch_t res[5];
|
329
|
317
|
char line[1024];
|
330
|
318
|
int fd;
|
331
|
319
|
int num_channels = 0;
|
332
|
320
|
|
333
|
|
- if (pthread_mutex_trylock(&channels_lock) != 0)
|
|
321
|
+ if (pthread_mutex_trylock(&cfg->channels_lock) != 0)
|
334
|
322
|
return -1;
|
335
|
323
|
|
336
|
|
- fd = open(channels_file, O_RDONLY);
|
|
324
|
+ fd = open(cfg->channels_file, O_RDONLY);
|
337
|
325
|
|
338
|
326
|
if (fd != -1) {
|
339
|
327
|
struct timeval tv;
|
|
@@ -385,18 +373,18 @@ report_error:
|
385
|
373
|
regfree(&re);
|
386
|
374
|
shutdown_fd(&fd);
|
387
|
375
|
/* Save current chanconf */
|
388
|
|
- old_chanconf = chanconf;
|
|
376
|
+ old_chanconf = cfg->chanconf;
|
389
|
377
|
/* Switch chanconf */
|
390
|
|
- chanconf = new_chanconf;
|
|
378
|
+ cfg->chanconf = new_chanconf;
|
391
|
379
|
/* Rewrite restreamer channels */
|
392
|
380
|
LNODE *lc, *lr, *lctmp, *lrtmp;
|
393
|
381
|
CHANNEL *chan;
|
394
|
|
- list_lock(restreamer); // Unlocked after second list_for_each(restreamer)
|
|
382
|
+ list_lock(cfg->restreamer); // Unlocked after second list_for_each(restreamer)
|
395
|
383
|
|
396
|
|
- list_lock(chanconf);
|
397
|
|
- list_for_each(chanconf, lc, lctmp) {
|
|
384
|
+ list_lock(cfg->chanconf);
|
|
385
|
+ list_for_each(cfg->chanconf, lc, lctmp) {
|
398
|
386
|
chan = lc->data;
|
399
|
|
- list_for_each(restreamer, lr, lrtmp) {
|
|
387
|
+ list_for_each(cfg->restreamer, lr, lrtmp) {
|
400
|
388
|
if (strcmp(chan->name, ((RESTREAMER *)lr->data)->name)==0) {
|
401
|
389
|
RESTREAMER *restr = lr->data;
|
402
|
390
|
/* Mark the restreamer as valid */
|
|
@@ -425,10 +413,10 @@ report_error:
|
425
|
413
|
}
|
426
|
414
|
}
|
427
|
415
|
}
|
428
|
|
- list_unlock(chanconf);
|
|
416
|
+ list_unlock(cfg->chanconf);
|
429
|
417
|
|
430
|
418
|
/* Kill restreamers that serve channels that no longer exist */
|
431
|
|
- list_for_each(restreamer, lr, lrtmp) {
|
|
419
|
+ list_for_each(cfg->restreamer, lr, lrtmp) {
|
432
|
420
|
RESTREAMER *r = lr->data;
|
433
|
421
|
/* This restreamer should no longer serve clients */
|
434
|
422
|
if (r->cookie != cookie) {
|
|
@@ -439,14 +427,14 @@ report_error:
|
439
|
427
|
r->dienow = 1;
|
440
|
428
|
}
|
441
|
429
|
}
|
442
|
|
- list_unlock(restreamer);
|
|
430
|
+ list_unlock(cfg->restreamer);
|
443
|
431
|
|
444
|
432
|
/* Free old_chanconf */
|
445
|
433
|
list_free(&old_chanconf, free_channel_p, NULL);
|
446
|
434
|
} else {
|
447
|
435
|
num_channels = -1;
|
448
|
436
|
}
|
449
|
|
- pthread_mutex_unlock(&channels_lock);
|
|
437
|
+ pthread_mutex_unlock(&cfg->channels_lock);
|
450
|
438
|
if (num_channels == -1)
|
451
|
439
|
LOGf("CONF : Error loading channels!\n");
|
452
|
440
|
else
|
|
@@ -457,7 +445,7 @@ report_error:
|
457
|
445
|
void proxy_close(RESTREAMER *r) {
|
458
|
446
|
proxy_log(r, "STOP ","");
|
459
|
447
|
// If there are no clients left, no "Timeout" messages will be logged
|
460
|
|
- list_del_entry(restreamer, r);
|
|
448
|
+ list_del_entry(config.restreamer, r);
|
461
|
449
|
free_restreamer(r);
|
462
|
450
|
}
|
463
|
451
|
|
|
@@ -523,7 +511,7 @@ int connect_source(RESTREAMER *r, int retries, int readbuflen, int *http_code) {
|
523
|
511
|
}
|
524
|
512
|
|
525
|
513
|
snprintf(buf,sizeof(buf)-1, "GET /%s HTTP/1.0\r\nHost: %s:%u\r\nX-Smart-Client: yes\r\nUser-Agent: %s %s (%s)\r\n\r\n",
|
526
|
|
- src->path, src->host, src->port, server_sig, server_ver, ident);
|
|
514
|
+ src->path, src->host, src->port, server_sig, server_ver, config.ident);
|
527
|
515
|
buf[sizeof(buf)-1] = 0;
|
528
|
516
|
fdwrite(r->sock, buf, strlen(buf));
|
529
|
517
|
|
|
@@ -893,13 +881,16 @@ void show_usage(int ident_only) {
|
893
|
881
|
puts("\t-l host\t\tSyslog host (default: disabled)");
|
894
|
882
|
puts("\t-L port\t\tSyslog port (default: 514)");
|
895
|
883
|
puts("\t-R\t\tSend reset packets when changing sources.");
|
|
884
|
+ puts("Server settings:");
|
|
885
|
+ puts("\t-b addr\t\tLocal IP address to bind. (default: 0.0.0.0)");
|
|
886
|
+ puts("\t-p port\t\tPort to listen. (default: 0)");
|
896
|
887
|
puts("");
|
897
|
888
|
}
|
898
|
889
|
|
899
|
|
-void set_ident(char *new_ident) {
|
900
|
|
- ident = new_ident;
|
901
|
|
- logident = strdup(ident);
|
902
|
|
- char *c = logident;
|
|
890
|
+void set_ident(char *new_ident, struct config *cfg) {
|
|
891
|
+ cfg->ident = new_ident;
|
|
892
|
+ cfg->logident = strdup(new_ident);
|
|
893
|
+ char *c = cfg->logident;
|
903
|
894
|
while (*c) {
|
904
|
895
|
if (*c=='/')
|
905
|
896
|
*c='-';
|
|
@@ -907,18 +898,24 @@ void set_ident(char *new_ident) {
|
907
|
898
|
}
|
908
|
899
|
}
|
909
|
900
|
|
910
|
|
-void parse_options(int argc, char **argv) {
|
|
901
|
+void parse_options(int argc, char **argv, struct config *cfg) {
|
911
|
902
|
int j, ttl;
|
912
|
903
|
while ((j = getopt(argc, argv, "i:c:d:t:o:l:L:RHh")) != -1) {
|
913
|
904
|
switch (j) {
|
|
905
|
+ case 'b':
|
|
906
|
+ cfg->server_addr = optarg;
|
|
907
|
+ break;
|
|
908
|
+ case 'p':
|
|
909
|
+ cfg->server_port = atoi(optarg);
|
|
910
|
+ break;
|
914
|
911
|
case 'i':
|
915
|
|
- set_ident(optarg);
|
|
912
|
+ set_ident(optarg, cfg);
|
916
|
913
|
break;
|
917
|
914
|
case 'c':
|
918
|
|
- channels_file = optarg;
|
|
915
|
+ cfg->channels_file = optarg;
|
919
|
916
|
break;
|
920
|
917
|
case 'd':
|
921
|
|
- pidfile = optarg;
|
|
918
|
+ cfg->pidfile = optarg;
|
922
|
919
|
break;
|
923
|
920
|
case 'o':
|
924
|
921
|
if (inet_aton(optarg, &output_intf) == 0) {
|
|
@@ -931,11 +928,11 @@ void parse_options(int argc, char **argv) {
|
931
|
928
|
multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
|
932
|
929
|
break;
|
933
|
930
|
case 'l':
|
934
|
|
- loghost = optarg;
|
935
|
|
- syslog_active = 1;
|
|
931
|
+ cfg->loghost = optarg;
|
|
932
|
+ cfg->syslog_active = 1;
|
936
|
933
|
break;
|
937
|
934
|
case 'L':
|
938
|
|
- logport = atoi(optarg);
|
|
935
|
+ cfg->logport = atoi(optarg);
|
939
|
936
|
break;
|
940
|
937
|
case 'R':
|
941
|
938
|
send_reset_opt = 1;
|
|
@@ -948,38 +945,41 @@ void parse_options(int argc, char **argv) {
|
948
|
945
|
}
|
949
|
946
|
}
|
950
|
947
|
|
951
|
|
- if (!channels_file) {
|
|
948
|
+ if (!cfg->channels_file) {
|
952
|
949
|
show_usage(0);
|
953
|
950
|
fprintf(stderr, "ERROR: No channels file is set (use -c option).\n");
|
954
|
951
|
exit(1);
|
955
|
952
|
}
|
956
|
953
|
|
957
|
|
- if (!ident) {
|
958
|
|
- set_ident("unixsol/tomcast");
|
|
954
|
+ if (!cfg->ident) {
|
|
955
|
+ set_ident("unixsol/tomcast", cfg);
|
959
|
956
|
}
|
960
|
957
|
|
961
|
958
|
printf("Configuration:\n");
|
962
|
|
- printf("\tServer ident : %s\n", ident);
|
963
|
|
- printf("\tChannels file : %s\n", channels_file);
|
|
959
|
+ printf("\tServer ident : %s\n", cfg->ident);
|
|
960
|
+ printf("\tChannels file : %s\n", cfg->channels_file);
|
964
|
961
|
printf("\tOutput iface addr : %s\n", inet_ntoa(output_intf));
|
965
|
962
|
printf("\tMulticast ttl : %d\n", multicast_ttl);
|
966
|
|
- if (syslog_active) {
|
967
|
|
- printf("\tSyslog host : %s\n", loghost);
|
968
|
|
- printf("\tSyslog port : %d\n", logport);
|
|
963
|
+ if (cfg->syslog_active) {
|
|
964
|
+ printf("\tSyslog host : %s\n", cfg->loghost);
|
|
965
|
+ printf("\tSyslog port : %d\n", cfg->logport);
|
969
|
966
|
} else {
|
970
|
967
|
printf("\tSyslog disabled.\n");
|
971
|
968
|
}
|
972
|
969
|
if (send_reset_opt)
|
973
|
970
|
printf("\tSend reset packets.\n");
|
974
|
|
- if (pidfile) {
|
975
|
|
- printf("\tDaemonize : %s\n", pidfile);
|
|
971
|
+ if (cfg->pidfile) {
|
|
972
|
+ printf("\tDaemonize : %s\n", cfg->pidfile);
|
976
|
973
|
} else {
|
977
|
974
|
printf("\tDo not daemonize.\n");
|
978
|
975
|
}
|
|
976
|
+
|
|
977
|
+ if (cfg->server_port)
|
|
978
|
+ init_server_socket(cfg->server_addr, cfg->server_port, &cfg->server, &cfg->server_socket);
|
979
|
979
|
}
|
980
|
980
|
|
981
|
|
-void init_vars() {
|
982
|
|
- restreamer = list_new("restreamer");
|
|
981
|
+void init_vars(struct config *cfg) {
|
|
982
|
+ cfg->restreamer = list_new("restreamer");
|
983
|
983
|
regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
|
984
|
984
|
memset(&TS_NULL_FRAME, 0xff, FRAME_PACKET_SIZE);
|
985
|
985
|
int i;
|
|
@@ -991,29 +991,29 @@ void init_vars() {
|
991
|
991
|
}
|
992
|
992
|
}
|
993
|
993
|
|
994
|
|
-void spawn_proxy_threads() {
|
|
994
|
+void spawn_proxy_threads(struct config *cfg) {
|
995
|
995
|
LNODE *lc, *lctmp;
|
996
|
996
|
LNODE *lr, *lrtmp;
|
997
|
997
|
int spawned = 0;
|
998
|
|
- list_for_each(chanconf, lc, lctmp) {
|
|
998
|
+ list_for_each(cfg->chanconf, lc, lctmp) {
|
999
|
999
|
CHANNEL *c = lc->data;
|
1000
|
1000
|
int restreamer_active = 0;
|
1001
|
|
- list_lock(restreamer);
|
1002
|
|
- list_for_each(restreamer, lr, lrtmp) {
|
|
1001
|
+ list_lock(cfg->restreamer);
|
|
1002
|
+ list_for_each(cfg->restreamer, lr, lrtmp) {
|
1003
|
1003
|
RESTREAMER *r = lr->data;
|
1004
|
1004
|
if (strcmp(r->name, c->name)==0) {
|
1005
|
1005
|
restreamer_active = 1;
|
1006
|
1006
|
break;
|
1007
|
1007
|
}
|
1008
|
1008
|
}
|
1009
|
|
- list_unlock(restreamer);
|
|
1009
|
+ list_unlock(cfg->restreamer);
|
1010
|
1010
|
if (!restreamer_active) {
|
1011
|
1011
|
RESTREAMER *nr = new_restreamer(c->name, c);
|
1012
|
1012
|
if (nr->clientsock < 0) {
|
1013
|
1013
|
LOGf("Error creating proxy socket for %s\n", c->name);
|
1014
|
1014
|
free_restreamer(nr);
|
1015
|
1015
|
} else {
|
1016
|
|
- list_add(restreamer, nr);
|
|
1016
|
+ list_add(cfg->restreamer, nr);
|
1017
|
1017
|
if (pthread_create(&nr->thread, NULL, &proxy_ts_stream, nr) == 0) {
|
1018
|
1018
|
spawned++;
|
1019
|
1019
|
pthread_detach(nr->thread);
|
|
@@ -1026,44 +1026,47 @@ void spawn_proxy_threads() {
|
1026
|
1026
|
LOGf("INFO : %d proxy threads spawned\n", spawned);
|
1027
|
1027
|
}
|
1028
|
1028
|
|
1029
|
|
-void kill_proxy_threads() {
|
|
1029
|
+void kill_proxy_threads(struct config *cfg) {
|
1030
|
1030
|
LNODE *l, *tmp;
|
1031
|
1031
|
int killed = 0;
|
1032
|
|
- list_lock(restreamer);
|
1033
|
|
- list_for_each(restreamer, l, tmp) {
|
|
1032
|
+ list_lock(cfg->restreamer);
|
|
1033
|
+ list_for_each(cfg->restreamer, l, tmp) {
|
1034
|
1034
|
RESTREAMER *r = l->data;
|
1035
|
1035
|
r->dienow = 1;
|
1036
|
1036
|
killed++;
|
1037
|
1037
|
}
|
1038
|
|
- list_unlock(restreamer);
|
|
1038
|
+ list_unlock(cfg->restreamer);
|
1039
|
1039
|
LOGf("INFO : %d proxy threads killed\n", killed);
|
1040
|
1040
|
}
|
1041
|
1041
|
|
|
1042
|
+int keep_going = 1;
|
|
1043
|
+
|
1042
|
1044
|
void signal_quit(int sig) {
|
1043
|
|
- kill_proxy_threads();
|
|
1045
|
+ keep_going = 0;
|
|
1046
|
+ kill_proxy_threads(&config);
|
1044
|
1047
|
usleep(500000);
|
1045
|
|
- LOGf("KILL : Signal %i | %s %s (%s)\n", sig, server_sig, server_ver, ident);
|
|
1048
|
+ LOGf("KILL : Signal %i | %s %s (%s)\n", sig, server_sig, server_ver, config.ident);
|
1046
|
1049
|
usleep(100000);
|
1047
|
1050
|
log_close();
|
1048
|
|
- if (pidfile && strlen(pidfile))
|
1049
|
|
- unlink(pidfile);
|
|
1051
|
+ if (config.pidfile && strlen(config.pidfile))
|
|
1052
|
+ unlink(config.pidfile);
|
1050
|
1053
|
signal(sig, SIG_DFL);
|
1051
|
1054
|
raise(sig);
|
1052
|
1055
|
}
|
1053
|
1056
|
|
1054
|
1057
|
void do_reconnect() {
|
1055
|
1058
|
LNODE *l, *tmp;
|
1056
|
|
- list_lock(restreamer);
|
1057
|
|
- list_for_each(restreamer, l, tmp) {
|
|
1059
|
+ list_lock(config.restreamer);
|
|
1060
|
+ list_for_each(config.restreamer, l, tmp) {
|
1058
|
1061
|
RESTREAMER *r = l->data;
|
1059
|
1062
|
r->reconnect = 1;
|
1060
|
1063
|
}
|
1061
|
|
- list_unlock(restreamer);
|
|
1064
|
+ list_unlock(config.restreamer);
|
1062
|
1065
|
}
|
1063
|
1066
|
|
1064
|
1067
|
void do_reconf() {
|
1065
|
|
- load_channels_config();
|
1066
|
|
- spawn_proxy_threads();
|
|
1068
|
+ load_channels_config(&config);
|
|
1069
|
+ spawn_proxy_threads(&config);
|
1067
|
1070
|
}
|
1068
|
1071
|
|
1069
|
1072
|
void init_signals() {
|
|
@@ -1077,13 +1080,13 @@ void init_signals() {
|
1077
|
1080
|
signal(SIGTERM, signal_quit);
|
1078
|
1081
|
}
|
1079
|
1082
|
|
1080
|
|
-void do_daemonize() {
|
1081
|
|
- if (!pidfile)
|
|
1083
|
+void do_daemonize(struct config *cfg) {
|
|
1084
|
+ if (!cfg->pidfile)
|
1082
|
1085
|
return;
|
1083
|
1086
|
fprintf(stderr, "Daemonizing.\n");
|
1084
|
1087
|
pid_t pid = fork();
|
1085
|
1088
|
if (pid > 0) {
|
1086
|
|
- FILE *F = fopen(pidfile,"w");
|
|
1089
|
+ FILE *F = fopen(cfg->pidfile,"w");
|
1087
|
1090
|
if (F) {
|
1088
|
1091
|
fprintf(F,"%i\n",pid);
|
1089
|
1092
|
fclose(F);
|
|
@@ -1098,25 +1101,26 @@ void do_daemonize() {
|
1098
|
1101
|
}
|
1099
|
1102
|
|
1100
|
1103
|
/* Must be called after daemonize! */
|
1101
|
|
-void init_logger() {
|
1102
|
|
- if (syslog_active)
|
1103
|
|
- fprintf(stderr, "Logging to %s:%d\n", loghost, logport);
|
1104
|
|
- log_init(logident, syslog_active, pidfile == NULL, loghost, logport);
|
|
1104
|
+void init_logger(struct config *cfg) {
|
|
1105
|
+ if (cfg->syslog_active)
|
|
1106
|
+ fprintf(stderr, "Logging to %s:%d\n", cfg->loghost, cfg->logport);
|
|
1107
|
+ log_init(cfg->logident, cfg->syslog_active, cfg->pidfile == NULL, cfg->loghost, cfg->logport);
|
1105
|
1108
|
}
|
1106
|
1109
|
|
1107
|
1110
|
int main(int argc, char **argv) {
|
1108
|
1111
|
set_http_response_server_ident(server_sig, server_ver);
|
1109
|
1112
|
show_usage(1); // Show copyright and version
|
1110
|
|
- init_vars();
|
1111
|
|
- parse_options(argc, argv);
|
1112
|
|
- do_daemonize();
|
1113
|
|
- init_logger();
|
|
1113
|
+ init_vars(&config);
|
|
1114
|
+ parse_options(argc, argv, &config);
|
|
1115
|
+ do_daemonize(&config);
|
|
1116
|
+ init_logger(&config);
|
1114
|
1117
|
init_signals();
|
1115
|
1118
|
|
1116
|
|
- LOGf("INIT : %s %s (%s)\n" , server_sig, server_ver, ident);
|
|
1119
|
+ LOGf("INIT : %s %s (%s)\n" , server_sig, server_ver, config.ident);
|
1117
|
1120
|
|
1118
|
|
- load_channels_config();
|
1119
|
|
- spawn_proxy_threads();
|
|
1121
|
+ load_channels_config(&config);
|
|
1122
|
+ spawn_proxy_threads(&config);
|
|
1123
|
+ web_server_start(&config);
|
1120
|
1124
|
|
1121
|
1125
|
do {
|
1122
|
1126
|
sleep(60);
|