Browse Source

Add web status/info/control interface

Georgi Chorbadzhiyski 7 years ago
parent
commit
19f2496670
8 changed files with 630 additions and 130 deletions
  1. 1
    1
      Makefile
  2. 65
    0
      README.WEB_ACCESS
  3. 96
    0
      config.h
  4. 143
    129
      tomcast.c
  5. 127
    0
      web_pages.c
  6. 27
    0
      web_pages.h
  7. 145
    0
      web_server.c
  8. 26
    0
      web_server.h

+ 1
- 1
Makefile View File

12
 FUNCS_DIR = libfuncs
12
 FUNCS_DIR = libfuncs
13
 FUNCS_LIB = $(FUNCS_DIR)/libfuncs.a
13
 FUNCS_LIB = $(FUNCS_DIR)/libfuncs.a
14
 
14
 
15
-tomcast_OBJS =  tomcast.o $(FUNCS_LIB)
15
+tomcast_OBJS =  tomcast.o web_pages.o web_server.o $(FUNCS_LIB)
16
 
16
 
17
 all: tomcast
17
 all: tomcast
18
 
18
 

+ 65
- 0
README.WEB_ACCESS View File

1
+To enable the web access start tomcast with -b (listen address) and
2
+-p (listen port parameters).
3
+
4
+For example: tomcast -b 127.0.0.1 -p 8081
5
+
6
+Now you can access the web interface exposed by tomcast at
7
+
8
+    http://127.0.0.1:8081/
9
+
10
+The following endpoints are accessible:
11
+
12
+    http://127.0.0.1:8081/getconfig
13
+
14
+  Return currently configured channels in channels.conf format
15
+  that tomcast uses.
16
+
17
+
18
+    http://127.0.0.1:8081/reconnect
19
+
20
+  Reconnect all sources. If channels have more than one source
21
+  the source will be changed to the next available source.
22
+
23
+
24
+    http://127.0.0.1:8081/reload
25
+
26
+  Reload config file. This would start/stop new/removed channels.
27
+
28
+
29
+    http://127.0.0.1:8081/status
30
+
31
+  Return current channels status. The status looks like this:
32
+
33
+# Status   DestAddr             ConnTime  ReadBytes ChanName           ChanSource                                                       ProxyStatus
34
+CONN_OK    239.78.78.78:5000         123     148708 chan1              http://example.com:8080/stb/chan1.mpg                            Synced
35
+CONN_ERROR 239.79.79.79:5000          60      65800 chan2              http://ux.iptv.bg:8080/stb/chan2.mpg                             Synced
36
+CONN_ERROR 239.80.80.80:5000           0          0 chan3              udp://239.1.2.3:5000/                                            Connected
37
+
38
+  "Status" - current channel status which can be CONN_OK or CONN_ERROR
39
+
40
+    CONN_ERROR - Means that the source of the channel is currently
41
+                 not connected or bytes read are at least 1316 or
42
+                 ConnTime is 0
43
+
44
+  "ProxyStatus" - Last connection status
45
+
46
+    Connected
47
+    Working
48
+
49
+    Reconnecting
50
+
51
+    Dying
52
+    Forced reconnect
53
+
54
+    ERROR: Can not connect to source
55
+    ERROR: Can not resolve source host
56
+    ERROR: Can not sync mpeg
57
+    ERROR: Dns resolve timeout
58
+    ERROR: Read timeout
59
+    ERROR: Source returned invalid HTTP code
60
+    ERROR: Source returned no-signal
61
+    ERROR: Source returned unhandled error code
62
+    ERROR: Timeout while syncing mpeg
63
+    ERROR: Too many zero reads
64
+    ERROR: fdread() timeout while syncing mpeg
65
+    ERROR: fdread() timeout while syncing mpeg

+ 96
- 0
config.h View File

1
+/*
2
+ * mptsd configuration header file
3
+ * Copyright (C) 2010-2011 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+#ifndef CONFIG_H
19
+#define CONFIG_H
20
+
21
+#include <pthread.h>
22
+#include <arpa/inet.h>
23
+#include <netinet/in.h>
24
+
25
+#include "libfuncs/libfuncs.h"
26
+
27
+typedef enum { udp_sock, tcp_sock } channel_source;
28
+
29
+typedef struct {
30
+	channel_source sproto;
31
+	char *proto;
32
+	char *host;
33
+	char *path;
34
+	unsigned int port;
35
+} CHANSRC;
36
+
37
+#define MAX_CHANNEL_SOURCES 8
38
+
39
+typedef struct {
40
+	char *name;
41
+	char *source; /* Full source url */
42
+	char *sources[MAX_CHANNEL_SOURCES];
43
+	uint8_t num_src;
44
+	uint8_t curr_src;
45
+	char *dest_host;
46
+	int dest_port;
47
+} CHANNEL;
48
+
49
+typedef struct {
50
+	char  *name;
51
+	CHANNEL *channel;
52
+	int sock;				/* Server socket */
53
+	struct sockaddr_in src_sockname;
54
+	int clientsock;			/* The udp socket */
55
+	struct sockaddr_in dst_sockname;
56
+	int reconnect:1,		/* Set to 1 to force proxy reconnect */
57
+	    connected:1,		/* It's set to 1 when proxy is connected and serving clients */
58
+	    dienow:1,			/* Stop serving clients and exit now */
59
+	    freechannel:1;		/* Free channel data on object free (this is used in chanconf) */
60
+	int cookie;				/* Used in chanconf to determine if the restreamer is alrady checked */
61
+	pthread_t thread;
62
+	pthread_rwlock_t lock;
63
+	time_t conn_ts;
64
+	uint64_t read_bytes;
65
+	char status[64];
66
+} RESTREAMER;
67
+
68
+
69
+struct config {
70
+	char				*ident;
71
+	char				*pidfile;
72
+
73
+	int					syslog_active;
74
+	char				*logident;
75
+	char				*loghost;
76
+	int					logport;
77
+
78
+	struct sockaddr_in	server;
79
+	char				*server_addr;
80
+	int					server_port;
81
+	int					server_socket;
82
+	pthread_t			server_thread;
83
+
84
+	char				*channels_file;
85
+
86
+	LIST				*chanconf;
87
+	LIST				*restreamer;
88
+
89
+	pthread_mutex_t		channels_lock;
90
+};
91
+
92
+extern void do_reconnect();
93
+extern void do_reconf();
94
+extern struct config *get_config(void);
95
+
96
+#endif

+ 143
- 129
tomcast.c View File

31
 #include <netdb.h> // for uint32_t
31
 #include <netdb.h> // for uint32_t
32
 
32
 
33
 #include "libfuncs/libfuncs.h"
33
 #include "libfuncs/libfuncs.h"
34
+#include "config.h"
35
+
36
+#include "web_server.h"
34
 
37
 
35
 #define DNS_RESOLVER_TIMEOUT 5000
38
 #define DNS_RESOLVER_TIMEOUT 5000
36
 
39
 
68
 char *server_ver = "1.15";
71
 char *server_ver = "1.15";
69
 char *copyright  = "Copyright (C) 2010-2013 Unix Solutions Ltd.";
72
 char *copyright  = "Copyright (C) 2010-2013 Unix Solutions Ltd.";
70
 
73
 
71
-typedef enum { udp_sock, tcp_sock } channel_source;
72
-
73
-typedef struct {
74
-	channel_source sproto;
75
-	char *proto;
76
-	char *host;
77
-	char *path;
78
-	unsigned int port;
79
-} CHANSRC;
80
-
81
-#define MAX_CHANNEL_SOURCES 8
82
-
83
-typedef struct {
84
-	char *name;
85
-	char *source; /* Full source url */
86
-	char *sources[MAX_CHANNEL_SOURCES];
87
-	uint8_t num_src;
88
-	uint8_t curr_src;
89
-	char *dest_host;
90
-	int dest_port;
91
-} CHANNEL;
92
-
93
-typedef struct {
94
-	char  *name;
95
-	CHANNEL *channel;
96
-	int sock;				/* Server socket */
97
-	struct sockaddr_in src_sockname;
98
-	int clientsock;			/* The udp socket */
99
-	struct sockaddr_in dst_sockname;
100
-	int reconnect:1,		/* Set to 1 to force proxy reconnect */
101
-	    connected:1,		/* It's set to 1 when proxy is connected and serving clients */
102
-	    dienow:1,			/* Stop serving clients and exit now */
103
-	    freechannel:1;		/* Free channel data on object free (this is used in chanconf) */
104
-	int cookie;				/* Used in chanconf to determine if the restreamer is alrady checked */
105
-	pthread_t thread;
106
-} RESTREAMER;
74
+static struct config config;
107
 
75
 
108
 channel_source get_sproto(char *url) {
76
 channel_source get_sproto(char *url) {
109
 	return strncmp(url, "http", 4)==0 ? tcp_sock : udp_sock;
77
 	return strncmp(url, "http", 4)==0 ? tcp_sock : udp_sock;
249
 	return sendsock;
217
 	return sendsock;
250
 }
218
 }
251
 
219
 
220
+void proxy_set_status(RESTREAMER *r, const char *proxy_status) {
221
+	pthread_rwlock_wrlock(&r->lock);
222
+	snprintf(r->status, sizeof(r->status), "%s", proxy_status);
223
+	pthread_rwlock_unlock(&r->lock);
224
+}
252
 
225
 
253
 void connect_destination(RESTREAMER *r) {
226
 void connect_destination(RESTREAMER *r) {
254
 	CHANNEL *c = r->channel;
227
 	CHANNEL *c = r->channel;
275
 	r->channel = channel;
248
 	r->channel = channel;
276
 	r->clientsock = -1;
249
 	r->clientsock = -1;
277
 	r->dst_sockname = sockname;
250
 	r->dst_sockname = sockname;
251
+	pthread_rwlock_init(&r->lock, NULL);
278
 	connect_destination(r);
252
 	connect_destination(r);
279
 	return r;
253
 	return r;
280
 }
254
 }
288
 	FREE(r);
262
 	FREE(r);
289
 }
263
 }
290
 
264
 
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
 char TS_NULL_FRAME[FRAME_PACKET_SIZE];
265
 char TS_NULL_FRAME[FRAME_PACKET_SIZE];
304
 
266
 
305
 regex_t http_response;
267
 regex_t http_response;
306
 
268
 
307
-LIST *chanconf = NULL;
308
-LIST *restreamer = NULL;
309
-
310
-static pthread_mutex_t channels_lock = PTHREAD_MUTEX_INITIALIZER;
311
-
312
 void proxy_log(RESTREAMER *r, char *msg, char *info) {
269
 void proxy_log(RESTREAMER *r, char *msg, char *info) {
313
 	LOGf("%s: %sChan: %s Src: %s Dst: udp://%s:%d SrcIP: %s SrcFD: %i DstFD: %i\n",
270
 	LOGf("%s: %sChan: %s Src: %s Dst: udp://%s:%d SrcIP: %s SrcFD: %i DstFD: %i\n",
314
 		msg,
271
 		msg,
323
 	);
280
 	);
324
 }
281
 }
325
 
282
 
326
-int load_channels_config() {
283
+int load_channels_config(struct config *cfg) {
327
 	regex_t re;
284
 	regex_t re;
328
 	regmatch_t res[5];
285
 	regmatch_t res[5];
329
 	char line[1024];
286
 	char line[1024];
330
 	int fd;
287
 	int fd;
331
 	int num_channels = 0;
288
 	int num_channels = 0;
332
 
289
 
333
-	if (pthread_mutex_trylock(&channels_lock) != 0)
290
+	if (pthread_mutex_trylock(&cfg->channels_lock) != 0)
334
 		return -1;
291
 		return -1;
335
 
292
 
336
-	fd = open(channels_file, O_RDONLY);
293
+	fd = open(cfg->channels_file, O_RDONLY);
337
 
294
 
338
 	if (fd != -1) {
295
 	if (fd != -1) {
339
 		struct timeval tv;
296
 		struct timeval tv;
385
 		regfree(&re);
342
 		regfree(&re);
386
 		shutdown_fd(&fd);
343
 		shutdown_fd(&fd);
387
 		/* Save current chanconf */
344
 		/* Save current chanconf */
388
-		old_chanconf = chanconf;
345
+		old_chanconf = cfg->chanconf;
389
 		/* Switch chanconf */
346
 		/* Switch chanconf */
390
-		chanconf = new_chanconf;
347
+		cfg->chanconf = new_chanconf;
391
 		/* Rewrite restreamer channels */
348
 		/* Rewrite restreamer channels */
392
 		LNODE *lc, *lr, *lctmp, *lrtmp;
349
 		LNODE *lc, *lr, *lctmp, *lrtmp;
393
 		CHANNEL *chan;
350
 		CHANNEL *chan;
394
-		list_lock(restreamer);	// Unlocked after second list_for_each(restreamer)
351
+		list_lock(cfg->restreamer);	// Unlocked after second list_for_each(restreamer)
395
 
352
 
396
-		list_lock(chanconf);
397
-		list_for_each(chanconf, lc, lctmp) {
353
+		list_lock(cfg->chanconf);
354
+		list_for_each(cfg->chanconf, lc, lctmp) {
398
 			chan = lc->data;
355
 			chan = lc->data;
399
-			list_for_each(restreamer, lr, lrtmp) {
356
+			list_for_each(cfg->restreamer, lr, lrtmp) {
400
 				if (strcmp(chan->name, ((RESTREAMER *)lr->data)->name)==0) {
357
 				if (strcmp(chan->name, ((RESTREAMER *)lr->data)->name)==0) {
401
 					RESTREAMER *restr = lr->data;
358
 					RESTREAMER *restr = lr->data;
402
 					/* Mark the restreamer as valid */
359
 					/* Mark the restreamer as valid */
425
 				}
382
 				}
426
 			}
383
 			}
427
 		}
384
 		}
428
-		list_unlock(chanconf);
385
+		list_unlock(cfg->chanconf);
429
 
386
 
430
 		/* Kill restreamers that serve channels that no longer exist */
387
 		/* Kill restreamers that serve channels that no longer exist */
431
-		list_for_each(restreamer, lr, lrtmp) {
388
+		list_for_each(cfg->restreamer, lr, lrtmp) {
432
 			RESTREAMER *r = lr->data;
389
 			RESTREAMER *r = lr->data;
433
 			/* This restreamer should no longer serve clients */
390
 			/* This restreamer should no longer serve clients */
434
 			if (r->cookie != cookie) {
391
 			if (r->cookie != cookie) {
439
 				r->dienow = 1;
396
 				r->dienow = 1;
440
 			}
397
 			}
441
 		}
398
 		}
442
-		list_unlock(restreamer);
399
+		list_unlock(cfg->restreamer);
443
 
400
 
444
 		/* Free old_chanconf */
401
 		/* Free old_chanconf */
445
 		list_free(&old_chanconf, free_channel_p, NULL);
402
 		list_free(&old_chanconf, free_channel_p, NULL);
446
 	} else {
403
 	} else {
447
 		num_channels = -1;
404
 		num_channels = -1;
448
 	}
405
 	}
449
-	pthread_mutex_unlock(&channels_lock);
406
+	pthread_mutex_unlock(&cfg->channels_lock);
450
 	if (num_channels == -1)
407
 	if (num_channels == -1)
451
 		LOGf("CONF : Error loading channels!\n");
408
 		LOGf("CONF : Error loading channels!\n");
452
 	else
409
 	else
457
 void proxy_close(RESTREAMER *r) {
414
 void proxy_close(RESTREAMER *r) {
458
 	proxy_log(r, "STOP ","");
415
 	proxy_log(r, "STOP ","");
459
 	// If there are no clients left, no "Timeout" messages will be logged
416
 	// If there are no clients left, no "Timeout" messages will be logged
460
-	list_del_entry(restreamer, r);
417
+	list_del_entry(config.restreamer, r);
461
 	free_restreamer(r);
418
 	free_restreamer(r);
462
 }
419
 }
463
 
420
 
501
 	int active = 1;
458
 	int active = 1;
502
 	int dret = async_resolve_host(src->host, src->port, &(r->src_sockname), DNS_RESOLVER_TIMEOUT, &active);
459
 	int dret = async_resolve_host(src->host, src->port, &(r->src_sockname), DNS_RESOLVER_TIMEOUT, &active);
503
 	if (dret != 0) {
460
 	if (dret != 0) {
504
-		if (dret == 1)
461
+		if (dret == 1) {
505
 			proxy_log(r, "ERR  ","Can't resolve src host");
462
 			proxy_log(r, "ERR  ","Can't resolve src host");
506
-		if (dret == 2)
463
+			proxy_set_status(r, "ERROR: Can not resolve source host");
464
+		}
465
+		if (dret == 2) {
507
 			proxy_log(r, "ERR  ","Timeout resolving src host");
466
 			proxy_log(r, "ERR  ","Timeout resolving src host");
467
+			proxy_set_status(r, "ERROR: Dns resolve timeout");
468
+		}
508
 		DO_RECONNECT;
469
 		DO_RECONNECT;
509
 	}
470
 	}
510
 
471
 
519
 		proxy_log(r, "NEW  ","");
480
 		proxy_log(r, "NEW  ","");
520
 		if (do_connect(r->sock, (struct sockaddr *)&(r->src_sockname), sizeof(r->src_sockname), PROXY_CONNECT_TIMEOUT) < 0) {
481
 		if (do_connect(r->sock, (struct sockaddr *)&(r->src_sockname), sizeof(r->src_sockname), PROXY_CONNECT_TIMEOUT) < 0) {
521
 			LOGf("ERR  : Error connecting to %s srv_fd: %i err: %s\n", r->channel->source, r->sock, strerror(errno));
482
 			LOGf("ERR  : Error connecting to %s srv_fd: %i err: %s\n", r->channel->source, r->sock, strerror(errno));
483
+			proxy_set_status(r, "ERROR: Can not connect to source");
522
 			DO_RECONNECT;
484
 			DO_RECONNECT;
523
 		}
485
 		}
524
 
486
 
525
 		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",
487
 		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);
488
+		         src->path, src->host, src->port, server_sig, server_ver, config.ident);
527
 		buf[sizeof(buf)-1] = 0;
489
 		buf[sizeof(buf)-1] = 0;
528
 		fdwrite(r->sock, buf, strlen(buf));
490
 		fdwrite(r->sock, buf, strlen(buf));
529
 
491
 
556
 		}
518
 		}
557
 		if (*http_code == 0) { // No valid HTTP response, retry
519
 		if (*http_code == 0) { // No valid HTTP response, retry
558
 			LOGf("DEBUG: Server returned not valid HTTP code | srv_fd: %i\n", r->sock);
520
 			LOGf("DEBUG: Server returned not valid HTTP code | srv_fd: %i\n", r->sock);
521
+			proxy_set_status(r, "ERROR: Source returned invalid HTTP code");
559
 			DO_RECONNECT;
522
 			DO_RECONNECT;
560
 		}
523
 		}
561
 		if (*http_code == 504) { // No signal, exit
524
 		if (*http_code == 504) { // No signal, exit
562
 			LOGf("ERR  : Get no-signal for %s from %s on srv_fd: %i\n", r->channel->name, r->channel->source, r->sock);
525
 			LOGf("ERR  : Get no-signal for %s from %s on srv_fd: %i\n", r->channel->name, r->channel->source, r->sock);
526
+			proxy_set_status(r, "ERROR: Source returned no-signal");
563
 			FATAL_ERROR;
527
 			FATAL_ERROR;
564
 		}
528
 		}
565
 		if (*http_code > 300) { // Unhandled or error codes, exit
529
 		if (*http_code > 300) { // Unhandled or error codes, exit
566
 			LOGf("ERR  : Get code %i for %s from %s on srv_fd: %i exiting.\n", *http_code, r->channel->name, r->channel->source, r->sock);
530
 			LOGf("ERR  : Get code %i for %s from %s on srv_fd: %i exiting.\n", *http_code, r->channel->name, r->channel->source, r->sock);
531
+			proxy_set_status(r, "ERROR: Source returned unhandled error code");
567
 			FATAL_ERROR;
532
 			FATAL_ERROR;
568
 		}
533
 		}
569
 		// connected ok, continue
534
 		// connected ok, continue
604
 	if (setsockopt(r->sock, SOL_SOCKET, SO_RCVBUF, (const char *)&readbuflen, sizeof(readbuflen)) < 0)
569
 	if (setsockopt(r->sock, SOL_SOCKET, SO_RCVBUF, (const char *)&readbuflen, sizeof(readbuflen)) < 0)
605
 		log_perror("play(): setsockopt(SO_RCVBUF)", errno);
570
 		log_perror("play(): setsockopt(SO_RCVBUF)", errno);
606
 
571
 
572
+	proxy_set_status(r, "Connected");
607
 	r->connected = 1;
573
 	r->connected = 1;
608
 
574
 
609
 	free_chansrc(src);
575
 	free_chansrc(src);
613
 int check_restreamer_state(RESTREAMER *r) {
579
 int check_restreamer_state(RESTREAMER *r) {
614
 	if (r->dienow) {
580
 	if (r->dienow) {
615
 		// LOGf("PROXY: Forced disconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
581
 		// LOGf("PROXY: Forced disconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
582
+		proxy_set_status(r, "Dying");
616
 		return 2;
583
 		return 2;
617
 	}
584
 	}
618
 	if (r->reconnect) {
585
 	if (r->reconnect) {
619
 		LOGf("PROXY: Forced reconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
586
 		LOGf("PROXY: Forced reconnect on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
587
+		proxy_set_status(r, "Forced reconnect");
620
 		return 1;
588
 		return 1;
621
 	}
589
 	}
622
 	return 0;
590
 	return 0;
640
 		0 = synced ok
608
 		0 = synced ok
641
 		1 = not synced, reconnect
609
 		1 = not synced, reconnect
642
 */
610
 */
643
-int mpeg_sync(int proxysock, char *channel, channel_source source_proto) {
611
+int mpeg_sync(RESTREAMER *r, int proxysock, char *channel, channel_source source_proto) {
644
 	time_t sync_start = time(NULL);
612
 	time_t sync_start = time(NULL);
645
 	unsigned int sync_packets = 0;
613
 	unsigned int sync_packets = 0;
646
 	unsigned int read_bytes = 0;
614
 	unsigned int read_bytes = 0;
656
 resync:
624
 resync:
657
 		if (fdread_ex(proxysock, syncframe, 1, _timeout, _retries, 1) != 1) {
625
 		if (fdread_ex(proxysock, syncframe, 1, _timeout, _retries, 1) != 1) {
658
 			LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
626
 			LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
627
+			proxy_set_status(r, "ERROR: fdread() timeout while syncing mpeg");
659
 			return 1; // reconnect
628
 			return 1; // reconnect
660
 		}
629
 		}
661
 		// LOGf("DEBUG:     Read 0x%02x Offset %u Sync: %u\n", (uint8_t)syncframe[0], read_bytes, sync_packets);
630
 		// LOGf("DEBUG:     Read 0x%02x Offset %u Sync: %u\n", (uint8_t)syncframe[0], read_bytes, sync_packets);
664
 			ssize_t rdsz = fdread_ex(proxysock, syncframe, 188-1, _timeout, _retries, 1);
633
 			ssize_t rdsz = fdread_ex(proxysock, syncframe, 188-1, _timeout, _retries, 1);
665
 			if (rdsz != 188-1) {
634
 			if (rdsz != 188-1) {
666
 				LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
635
 				LOGf("DEBUG: mpeg_sync fdread() timeout | Channel: %s\n", channel);
636
+				proxy_set_status(r, "ERROR: fdread() timeout while syncing mpeg");
667
 				return 1; // reconnect
637
 				return 1; // reconnect
668
 			}
638
 			}
669
 			read_bytes += 188-1;
639
 			read_bytes += 188-1;
675
 		}
645
 		}
676
 		if (read_bytes > FRAME_PACKET_SIZE) { // Can't sync in 1316 bytes
646
 		if (read_bytes > FRAME_PACKET_SIZE) { // Can't sync in 1316 bytes
677
 			LOGf("DEBUG: Can't sync after %d bytes | Channel: %s\n", FRAME_PACKET_SIZE, channel);
647
 			LOGf("DEBUG: Can't sync after %d bytes | Channel: %s\n", FRAME_PACKET_SIZE, channel);
648
+			proxy_set_status(r, "ERROR: Can not sync mpeg");
678
 			return 1; // reconnect
649
 			return 1; // reconnect
679
 		}
650
 		}
680
 		if (sync_start+2 <= time(NULL)) { // Do not sync in two seconds
651
 		if (sync_start+2 <= time(NULL)) { // Do not sync in two seconds
681
 			LOGf("DEBUG: Timeout while syncing (read %u bytes) | Channel: %s\n", read_bytes, channel);
652
 			LOGf("DEBUG: Timeout while syncing (read %u bytes) | Channel: %s\n", read_bytes, channel);
653
+			proxy_set_status(r, "ERROR: Timeout while syncing mpeg");
682
 			return 1; // reconnect
654
 			return 1; // reconnect
683
 		}
655
 		}
684
 	} while (1);
656
 	} while (1);
657
+	pthread_rwlock_wrlock(&r->lock);
658
+	r->conn_ts = time(NULL);
659
+	r->read_bytes = read_bytes;
660
+	pthread_rwlock_unlock(&r->lock);
685
 	LOGf("SYNC : TS synced after %u bytes | Channel: %s\n", read_bytes-FRAME_PACKET_SIZE, channel);
661
 	LOGf("SYNC : TS synced after %u bytes | Channel: %s\n", read_bytes-FRAME_PACKET_SIZE, channel);
662
+	proxy_set_status(r, "Working");
686
 	return 0;
663
 	return 0;
687
 }
664
 }
688
 
665
 
807
 
784
 
808
 	int http_code = 0;
785
 	int http_code = 0;
809
 	while (1) {
786
 	while (1) {
787
+		r->conn_ts = 0;
788
+		r->read_bytes = 0;
789
+
810
 		int result = connect_source(self, 1, FRAME_PACKET_SIZE * 1000, &http_code);
790
 		int result = connect_source(self, 1, FRAME_PACKET_SIZE * 1000, &http_code);
811
 		if (result > 0)
791
 		if (result > 0)
812
 			goto RECONNECT;
792
 			goto RECONNECT;
813
 
793
 
814
 		channel_source sproto = get_sproto(r->channel->source);
794
 		channel_source sproto = get_sproto(r->channel->source);
815
 
795
 
816
-		int mpgsync = mpeg_sync(r->sock, r->channel->name, sproto);
796
+		int mpgsync = mpeg_sync(r, r->sock, r->channel->name, sproto);
817
 		if (mpgsync == 1) // Timeout
797
 		if (mpgsync == 1) // Timeout
818
 			goto RECONNECT;
798
 			goto RECONNECT;
819
 
799
 
836
 				LOGf("PROXY: zero read on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
816
 				LOGf("PROXY: zero read on srv_fd: %i | Channel: %s Source: %s\n", r->sock, r->channel->name, r->channel->source);
837
 				if (--max_zero_reads == 0) {
817
 				if (--max_zero_reads == 0) {
838
 					LOGf("PROXY: %d zero reads on srv_fd: %i | Channel: %s Source: %s\n", MAX_ZERO_READS, r->sock, r->channel->name, r->channel->source);
818
 					LOGf("PROXY: %d zero reads on srv_fd: %i | Channel: %s Source: %s\n", MAX_ZERO_READS, r->sock, r->channel->name, r->channel->source);
819
+					proxy_set_status(r, "ERROR: Too many zero reads");
839
 					break;
820
 					break;
840
 				}
821
 				}
841
 				continue;
822
 				continue;
848
 				//LOGf("DEBUG: Short read (%d) on retreamer srv_fd: %i | Channel: %s\n", readen, sock, chan->name);
829
 				//LOGf("DEBUG: Short read (%d) on retreamer srv_fd: %i | Channel: %s\n", readen, sock, chan->name);
849
 				memcpy(buf+readen, TS_NULL_FRAME+readen, FRAME_PACKET_SIZE - readen);
830
 				memcpy(buf+readen, TS_NULL_FRAME+readen, FRAME_PACKET_SIZE - readen);
850
 			}
831
 			}
832
+			pthread_rwlock_wrlock(&r->lock);
833
+			r->read_bytes += readen;
834
+			pthread_rwlock_unlock(&r->lock);
851
 
835
 
852
 			if (send_reset) {
836
 			if (send_reset) {
853
 				send_reset = 0;
837
 				send_reset = 0;
860
 			}
844
 			}
861
 		}
845
 		}
862
 		LOGf("DEBUG: fdread timeout restreamer srv_fd: %i | Channel: %s\n", r->sock, r->channel->name);
846
 		LOGf("DEBUG: fdread timeout restreamer srv_fd: %i | Channel: %s\n", r->sock, r->channel->name);
847
+		proxy_set_status(r, "ERROR: Read timeout");
863
 RECONNECT:
848
 RECONNECT:
849
+		pthread_rwlock_wrlock(&r->lock);
850
+		r->conn_ts = 0;
851
+		pthread_rwlock_unlock(&r->lock);
864
 		LOGf("DEBUG: reconnect srv_fd: %i | Channel: %s\n", r->sock, r->channel->name);
852
 		LOGf("DEBUG: reconnect srv_fd: %i | Channel: %s\n", r->sock, r->channel->name);
853
+		proxy_set_status(r, "Reconnecting");
865
 		shutdown_fd(&(r->sock));
854
 		shutdown_fd(&(r->sock));
866
 		next_channel_source(r->channel);
855
 		next_channel_source(r->channel);
867
 		continue;
856
 		continue;
893
 	puts("\t-l host\t\tSyslog host (default: disabled)");
882
 	puts("\t-l host\t\tSyslog host (default: disabled)");
894
 	puts("\t-L port\t\tSyslog port (default: 514)");
883
 	puts("\t-L port\t\tSyslog port (default: 514)");
895
 	puts("\t-R\t\tSend reset packets when changing sources.");
884
 	puts("\t-R\t\tSend reset packets when changing sources.");
885
+	puts("Server settings:");
886
+	puts("\t-b addr\t\tLocal IP address to bind.   (default: 0.0.0.0)");
887
+	puts("\t-p port\t\tPort to listen.             (default: 0)");
896
 	puts("");
888
 	puts("");
897
 }
889
 }
898
 
890
 
899
-void set_ident(char *new_ident) {
900
-	ident = new_ident;
901
-	logident = strdup(ident);
902
-	char *c = logident;
891
+void set_ident(char *new_ident, struct config *cfg) {
892
+	cfg->ident = new_ident;
893
+	cfg->logident = strdup(new_ident);
894
+	char *c = cfg->logident;
903
 	while (*c) {
895
 	while (*c) {
904
 		if (*c=='/')
896
 		if (*c=='/')
905
 			*c='-';
897
 			*c='-';
907
 	}
899
 	}
908
 }
900
 }
909
 
901
 
910
-void parse_options(int argc, char **argv) {
902
+void parse_options(int argc, char **argv, struct config *cfg) {
911
 	int j, ttl;
903
 	int j, ttl;
912
-	while ((j = getopt(argc, argv, "i:c:d:t:o:l:L:RHh")) != -1) {
904
+	cfg->server_socket = -1;
905
+	pthread_mutex_init(&cfg->channels_lock, NULL);
906
+	while ((j = getopt(argc, argv, "i:b:p:c:d:t:o:l:L:RHh")) != -1) {
913
 		switch (j) {
907
 		switch (j) {
908
+			case 'b':
909
+				cfg->server_addr = optarg;
910
+				break;
911
+			case 'p':
912
+				cfg->server_port = atoi(optarg);
913
+				break;
914
 			case 'i':
914
 			case 'i':
915
-				set_ident(optarg);
915
+				set_ident(optarg, cfg);
916
 				break;
916
 				break;
917
 			case 'c':
917
 			case 'c':
918
-				channels_file = optarg;
918
+				cfg->channels_file = optarg;
919
 				break;
919
 				break;
920
 			case 'd':
920
 			case 'd':
921
-				pidfile = optarg;
921
+				cfg->pidfile = optarg;
922
 				break;
922
 				break;
923
 			case 'o':
923
 			case 'o':
924
 				if (inet_aton(optarg, &output_intf) == 0) {
924
 				if (inet_aton(optarg, &output_intf) == 0) {
931
 				multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
931
 				multicast_ttl = (ttl && ttl < 127) ? ttl : 1;
932
 				break;
932
 				break;
933
 			case 'l':
933
 			case 'l':
934
-				loghost = optarg;
935
-				syslog_active = 1;
934
+				cfg->loghost = optarg;
935
+				cfg->syslog_active = 1;
936
 				break;
936
 				break;
937
 			case 'L':
937
 			case 'L':
938
-				logport = atoi(optarg);
938
+				cfg->logport = atoi(optarg);
939
 				break;
939
 				break;
940
 			case 'R':
940
 			case 'R':
941
 				send_reset_opt = 1;
941
 				send_reset_opt = 1;
948
 		}
948
 		}
949
 	}
949
 	}
950
 
950
 
951
-	if (!channels_file) {
951
+	if (!cfg->channels_file) {
952
 		show_usage(0);
952
 		show_usage(0);
953
 		fprintf(stderr, "ERROR: No channels file is set (use -c option).\n");
953
 		fprintf(stderr, "ERROR: No channels file is set (use -c option).\n");
954
 		exit(1);
954
 		exit(1);
955
 	}
955
 	}
956
 
956
 
957
-	if (!ident) {
958
-		set_ident("unixsol/tomcast");
957
+	if (!cfg->ident) {
958
+		set_ident("unixsol/tomcast", cfg);
959
 	}
959
 	}
960
 
960
 
961
 	printf("Configuration:\n");
961
 	printf("Configuration:\n");
962
-	printf("\tServer ident      : %s\n", ident);
963
-	printf("\tChannels file     : %s\n", channels_file);
962
+	printf("\tServer ident      : %s\n", cfg->ident);
963
+	printf("\tChannels file     : %s\n", cfg->channels_file);
964
 	printf("\tOutput iface addr : %s\n", inet_ntoa(output_intf));
964
 	printf("\tOutput iface addr : %s\n", inet_ntoa(output_intf));
965
 	printf("\tMulticast ttl     : %d\n", multicast_ttl);
965
 	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);
966
+	if (cfg->syslog_active) {
967
+		printf("\tSyslog host       : %s\n", cfg->loghost);
968
+		printf("\tSyslog port       : %d\n", cfg->logport);
969
 	} else {
969
 	} else {
970
 		printf("\tSyslog disabled.\n");
970
 		printf("\tSyslog disabled.\n");
971
 	}
971
 	}
972
 	if (send_reset_opt)
972
 	if (send_reset_opt)
973
 		printf("\tSend reset packets.\n");
973
 		printf("\tSend reset packets.\n");
974
-	if (pidfile) {
975
-		printf("\tDaemonize         : %s\n", pidfile);
974
+	if (cfg->pidfile) {
975
+		printf("\tDaemonize         : %s\n", cfg->pidfile);
976
 	} else {
976
 	} else {
977
 		printf("\tDo not daemonize.\n");
977
 		printf("\tDo not daemonize.\n");
978
 	}
978
 	}
979
+	if (cfg->server_port) {
980
+		init_server_socket(cfg->server_addr, cfg->server_port, &cfg->server, &cfg->server_socket);
981
+		printf("\tStarting web srv  : http://%s:%d/status (sock: %d)\n", cfg->server_addr, cfg->server_port, cfg->server_socket);
982
+	} else {
983
+		printf("\tNo web server\n");
984
+	}
979
 }
985
 }
980
 
986
 
981
-void init_vars() {
982
-	restreamer = list_new("restreamer");
987
+void init_vars(struct config *cfg) {
988
+	cfg->restreamer = list_new("restreamer");
983
 	regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
989
 	regcomp(&http_response, "^HTTP/1.[0-1] (([0-9]{3}) .*)", REG_EXTENDED);
984
 	memset(&TS_NULL_FRAME, 0xff, FRAME_PACKET_SIZE);
990
 	memset(&TS_NULL_FRAME, 0xff, FRAME_PACKET_SIZE);
985
 	int i;
991
 	int i;
991
 	}
997
 	}
992
 }
998
 }
993
 
999
 
994
-void spawn_proxy_threads() {
1000
+void spawn_proxy_threads(struct config *cfg) {
995
 	LNODE *lc, *lctmp;
1001
 	LNODE *lc, *lctmp;
996
 	LNODE *lr, *lrtmp;
1002
 	LNODE *lr, *lrtmp;
997
 	int spawned = 0;
1003
 	int spawned = 0;
998
-	list_for_each(chanconf, lc, lctmp) {
1004
+	list_for_each(cfg->chanconf, lc, lctmp) {
999
 		CHANNEL *c = lc->data;
1005
 		CHANNEL *c = lc->data;
1000
 		int restreamer_active = 0;
1006
 		int restreamer_active = 0;
1001
-		list_lock(restreamer);
1002
-		list_for_each(restreamer, lr, lrtmp) {
1007
+		list_lock(cfg->restreamer);
1008
+		list_for_each(cfg->restreamer, lr, lrtmp) {
1003
 			RESTREAMER *r = lr->data;
1009
 			RESTREAMER *r = lr->data;
1004
 			if (strcmp(r->name, c->name)==0) {
1010
 			if (strcmp(r->name, c->name)==0) {
1005
 				restreamer_active = 1;
1011
 				restreamer_active = 1;
1006
 				break;
1012
 				break;
1007
 			}
1013
 			}
1008
 		}
1014
 		}
1009
-		list_unlock(restreamer);
1015
+		list_unlock(cfg->restreamer);
1010
 		if (!restreamer_active) {
1016
 		if (!restreamer_active) {
1011
 			RESTREAMER *nr = new_restreamer(c->name, c);
1017
 			RESTREAMER *nr = new_restreamer(c->name, c);
1012
 			if (nr->clientsock < 0) {
1018
 			if (nr->clientsock < 0) {
1013
 				LOGf("Error creating proxy socket for %s\n", c->name);
1019
 				LOGf("Error creating proxy socket for %s\n", c->name);
1014
 				free_restreamer(nr);
1020
 				free_restreamer(nr);
1015
 			} else {
1021
 			} else {
1016
-				list_add(restreamer, nr);
1022
+				list_add(cfg->restreamer, nr);
1017
 				if (pthread_create(&nr->thread, NULL, &proxy_ts_stream, nr) == 0) {
1023
 				if (pthread_create(&nr->thread, NULL, &proxy_ts_stream, nr) == 0) {
1018
 					spawned++;
1024
 					spawned++;
1019
 					pthread_detach(nr->thread);
1025
 					pthread_detach(nr->thread);
1026
 	LOGf("INFO : %d proxy threads spawned\n", spawned);
1032
 	LOGf("INFO : %d proxy threads spawned\n", spawned);
1027
 }
1033
 }
1028
 
1034
 
1029
-void kill_proxy_threads() {
1035
+void kill_proxy_threads(struct config *cfg) {
1030
 	LNODE *l, *tmp;
1036
 	LNODE *l, *tmp;
1031
 	int killed = 0;
1037
 	int killed = 0;
1032
-	list_lock(restreamer);
1033
-	list_for_each(restreamer, l, tmp) {
1038
+	list_lock(cfg->restreamer);
1039
+	list_for_each(cfg->restreamer, l, tmp) {
1034
 		RESTREAMER *r = l->data;
1040
 		RESTREAMER *r = l->data;
1035
 		r->dienow = 1;
1041
 		r->dienow = 1;
1036
 		killed++;
1042
 		killed++;
1037
 	}
1043
 	}
1038
-	list_unlock(restreamer);
1044
+	list_unlock(cfg->restreamer);
1039
 	LOGf("INFO : %d proxy threads killed\n", killed);
1045
 	LOGf("INFO : %d proxy threads killed\n", killed);
1040
 }
1046
 }
1041
 
1047
 
1048
+int keep_going = 1;
1049
+
1042
 void signal_quit(int sig) {
1050
 void signal_quit(int sig) {
1043
-	kill_proxy_threads();
1051
+	keep_going = 0;
1052
+	kill_proxy_threads(&config);
1044
 	usleep(500000);
1053
 	usleep(500000);
1045
-	LOGf("KILL : Signal %i | %s %s (%s)\n", sig, server_sig, server_ver, ident);
1054
+	LOGf("KILL : Signal %i | %s %s (%s)\n", sig, server_sig, server_ver, config.ident);
1046
 	usleep(100000);
1055
 	usleep(100000);
1047
 	log_close();
1056
 	log_close();
1048
-	if (pidfile && strlen(pidfile))
1049
-		unlink(pidfile);
1057
+	if (config.pidfile && strlen(config.pidfile))
1058
+		unlink(config.pidfile);
1050
 	signal(sig, SIG_DFL);
1059
 	signal(sig, SIG_DFL);
1051
 	raise(sig);
1060
 	raise(sig);
1052
 }
1061
 }
1053
 
1062
 
1063
+struct config *get_config(void) {
1064
+	return &config;
1065
+}
1066
+
1054
 void do_reconnect() {
1067
 void do_reconnect() {
1055
 	LNODE *l, *tmp;
1068
 	LNODE *l, *tmp;
1056
-	list_lock(restreamer);
1057
-	list_for_each(restreamer, l, tmp) {
1069
+	list_lock(config.restreamer);
1070
+	list_for_each(config.restreamer, l, tmp) {
1058
 		RESTREAMER *r = l->data;
1071
 		RESTREAMER *r = l->data;
1059
 		r->reconnect = 1;
1072
 		r->reconnect = 1;
1060
 	}
1073
 	}
1061
-	list_unlock(restreamer);
1074
+	list_unlock(config.restreamer);
1062
 }
1075
 }
1063
 
1076
 
1064
 void do_reconf() {
1077
 void do_reconf() {
1065
-	load_channels_config();
1066
-	spawn_proxy_threads();
1078
+	load_channels_config(&config);
1079
+	spawn_proxy_threads(&config);
1067
 }
1080
 }
1068
 
1081
 
1069
 void init_signals() {
1082
 void init_signals() {
1077
 	signal(SIGTERM, signal_quit);
1090
 	signal(SIGTERM, signal_quit);
1078
 }
1091
 }
1079
 
1092
 
1080
-void do_daemonize() {
1081
-	if (!pidfile)
1093
+void do_daemonize(struct config *cfg) {
1094
+	if (!cfg->pidfile)
1082
 		return;
1095
 		return;
1083
 	fprintf(stderr, "Daemonizing.\n");
1096
 	fprintf(stderr, "Daemonizing.\n");
1084
 	pid_t pid = fork();
1097
 	pid_t pid = fork();
1085
 	if (pid > 0) {
1098
 	if (pid > 0) {
1086
-		FILE *F = fopen(pidfile,"w");
1099
+		FILE *F = fopen(cfg->pidfile,"w");
1087
 		if (F) {
1100
 		if (F) {
1088
 			fprintf(F,"%i\n",pid);
1101
 			fprintf(F,"%i\n",pid);
1089
 			fclose(F);
1102
 			fclose(F);
1098
 }
1111
 }
1099
 
1112
 
1100
 /* Must be called after daemonize! */
1113
 /* 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);
1114
+void init_logger(struct config *cfg) {
1115
+	if (cfg->syslog_active)
1116
+		fprintf(stderr, "Logging to %s:%d\n", cfg->loghost, cfg->logport);
1117
+	log_init(cfg->logident, cfg->syslog_active, cfg->pidfile == NULL, cfg->loghost, cfg->logport);
1105
 }
1118
 }
1106
 
1119
 
1107
 int main(int argc, char **argv) {
1120
 int main(int argc, char **argv) {
1108
 	set_http_response_server_ident(server_sig, server_ver);
1121
 	set_http_response_server_ident(server_sig, server_ver);
1109
 	show_usage(1); // Show copyright and version
1122
 	show_usage(1); // Show copyright and version
1110
-	init_vars();
1111
-	parse_options(argc, argv);
1112
-	do_daemonize();
1113
-	init_logger();
1123
+	init_vars(&config);
1124
+	parse_options(argc, argv, &config);
1125
+	do_daemonize(&config);
1126
+	init_logger(&config);
1114
 	init_signals();
1127
 	init_signals();
1115
 
1128
 
1116
-	LOGf("INIT : %s %s (%s)\n" , server_sig, server_ver, ident);
1129
+	LOGf("INIT : %s %s (%s)\n" , server_sig, server_ver, config.ident);
1117
 
1130
 
1118
-	load_channels_config();
1119
-	spawn_proxy_threads();
1131
+	load_channels_config(&config);
1132
+	spawn_proxy_threads(&config);
1133
+	web_server_start(&config);
1120
 
1134
 
1121
 	do {
1135
 	do {
1122
 		sleep(60);
1136
 		sleep(60);

+ 127
- 0
web_pages.c View File

1
+/*
2
+ * mptsd internal web pages
3
+ * Copyright (C) 2010-2011 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+#include <stdlib.h>
19
+#include <string.h>
20
+#include <sys/types.h>
21
+#include <sys/stat.h>
22
+#include <fcntl.h>
23
+#include <ctype.h>
24
+#include <unistd.h>
25
+
26
+#include "libfuncs/io.h"
27
+#include "libfuncs/log.h"
28
+#include "libfuncs/list.h"
29
+#include "libfuncs/http_response.h"
30
+
31
+#include "config.h"
32
+
33
+extern struct config *config;
34
+
35
+void cmd_index(int clientsock) {
36
+	send_200_ok(clientsock);
37
+	send_header_textplain(clientsock);
38
+	fdputs(clientsock, "\nHi from tomcast.\n");
39
+}
40
+
41
+void cmd_status(int clientsock) {
42
+	send_200_ok(clientsock);
43
+	send_header_textplain(clientsock);
44
+	fdputs(clientsock, "\n");
45
+
46
+	LNODE *l, *tmp;
47
+	struct config *cfg = get_config();
48
+
49
+	time_t now = time(NULL);
50
+	fdputsf(clientsock, "%-10s %-20s %8s %10s %-18s %-64s %s\n",
51
+		"# Status",
52
+		"DestAddr",
53
+		"ConnTime",
54
+		"ReadBytes",
55
+		"ChanName",
56
+		"ChanSource",
57
+		"ProxyStatus"
58
+	);
59
+	pthread_mutex_lock(&cfg->channels_lock);
60
+	list_lock(cfg->restreamer);
61
+	list_for_each(cfg->restreamer, l, tmp) {
62
+		char dest[32];
63
+		int conn_status = 0;
64
+		RESTREAMER *r = l->data;
65
+		pthread_rwlock_rdlock(&r->lock);
66
+		snprintf(dest, sizeof(dest), "%s:%d", r->channel->dest_host, r->channel->dest_port);
67
+		if (r->connected && r->conn_ts > 0 && r->read_bytes >= 1316)
68
+			conn_status = 1;
69
+		fdputsf(clientsock, "%-10s %-20s %8lu %10llu %-18s %-64s %s\n",
70
+			conn_status ? "CONN_OK" : "CONN_ERROR",
71
+			dest,
72
+			r->conn_ts ? now - r->conn_ts : 0,
73
+			r->read_bytes,
74
+			r->channel->name,
75
+			r->channel->source,
76
+			r->status
77
+		);
78
+		pthread_rwlock_unlock(&r->lock);
79
+	}
80
+	list_unlock(cfg->restreamer);
81
+	pthread_mutex_unlock(&cfg->channels_lock);
82
+}
83
+
84
+void cmd_getconfig(int clientsock) {
85
+	send_200_ok(clientsock);
86
+	send_header_textplain(clientsock);
87
+	fdputs(clientsock, "\n");
88
+
89
+	LNODE *l, *tmp;
90
+	struct config *cfg = get_config();
91
+
92
+	pthread_mutex_lock(&cfg->channels_lock);
93
+	list_lock(cfg->restreamer);
94
+	list_for_each(cfg->restreamer, l, tmp) {
95
+		RESTREAMER *r = l->data;
96
+		pthread_rwlock_rdlock(&r->lock);
97
+		int i;
98
+		for (i = 0; i < r->channel->num_src; i++) {
99
+			fdputsf(clientsock, "%s\t%s:%d\t%s\n",
100
+				r->channel->name,
101
+				r->channel->dest_host,
102
+				r->channel->dest_port,
103
+				r->channel->sources[i]
104
+			);
105
+		}
106
+		pthread_rwlock_unlock(&r->lock);
107
+	}
108
+	list_unlock(cfg->restreamer);
109
+	pthread_mutex_unlock(&cfg->channels_lock);
110
+}
111
+
112
+void cmd_reconnect(int clientsock) {
113
+	send_200_ok(clientsock);
114
+	send_header_textplain(clientsock);
115
+	struct config *cfg = get_config();
116
+	pthread_mutex_lock(&cfg->channels_lock);
117
+	fdputsf(clientsock, "\nReconnecting %d inputs.\n", cfg->chanconf->items);
118
+	pthread_mutex_unlock(&cfg->channels_lock);
119
+	do_reconnect();
120
+}
121
+
122
+void cmd_reload(int clientsock) {
123
+	send_200_ok(clientsock);
124
+	send_header_textplain(clientsock);
125
+	fdputs(clientsock, "\nReloading config\n");
126
+	do_reconf();
127
+}

+ 27
- 0
web_pages.h View File

1
+/*
2
+ * mptsd internal web pages header file
3
+ * Copyright (C) 2010-2011 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+#ifndef WEB_PAGES_H
19
+#define WEB_PAGES_H
20
+
21
+void cmd_index(int clientsock);
22
+void cmd_status(int clientsock);
23
+void cmd_getconfig(int clientsock);
24
+void cmd_reconnect(int clientsock);
25
+void cmd_reload(int clientsock);
26
+
27
+#endif

+ 145
- 0
web_server.c View File

1
+/*
2
+ * mptsd internal web server
3
+ * Copyright (C) 2010-2011 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+#include <stdlib.h>
19
+#include <regex.h>
20
+#include <errno.h>
21
+#include <string.h>
22
+#include <signal.h>
23
+#include <arpa/inet.h>
24
+#include <netinet/in.h>
25
+
26
+#include "libfuncs/libfuncs.h"
27
+
28
+#include "web_pages.h"
29
+#include "web_server.h"
30
+
31
+typedef struct req_info {
32
+	int clientsock;
33
+	struct sockaddr_in client;
34
+} request_info;
35
+
36
+extern int keep_going;
37
+
38
+#define NEXT_CLIENT { FREE(path); FREE(buf); pthread_exit(0); }
39
+#define SHUTDOWN_CLIENT { FREE(path); FREE(buf); shutdown_fd(&clientsock); pthread_exit(0); }
40
+#define BUF_SIZE 1024
41
+
42
+void *process_web_request(void *);
43
+
44
+void *web_server_thread(void *data) {
45
+	struct config *conf = data;
46
+	while (keep_going) {
47
+		struct sockaddr_in client;
48
+		unsigned int clientlen = sizeof(client);
49
+		int clientsock;
50
+		clientsock = accept(conf->server_socket, (struct sockaddr *) &client, &clientlen);
51
+		if (clientsock < 0) {
52
+			if (conf->server_socket > -1)	// The server_socket is closed on exit, so do not report errors
53
+				LOGf("ERROR : Failed to accept client fd: %i err: %s\n", clientsock, strerror(errno));
54
+			if (errno==EMFILE || errno==ENFILE) /* No more FDs */
55
+				break;
56
+		} else {
57
+			request_info *req;
58
+			pthread_t req_thread;
59
+			req = malloc(sizeof(request_info));
60
+			if (!req) {
61
+				log_perror("Can't allocate request_info", errno);
62
+				continue;
63
+			}
64
+			req->clientsock = clientsock;
65
+			req->client = client;
66
+			if (pthread_create(&req_thread, NULL, (void *)&process_web_request, (void *)req)) {
67
+				log_perror("Error creating request processing thread.", errno);
68
+				exit(1);
69
+			}
70
+			pthread_detach(req_thread);
71
+		}
72
+	}
73
+
74
+	pthread_exit(0);
75
+}
76
+
77
+void web_server_start(struct config *conf) {
78
+	if (conf->server_socket > -1)
79
+		pthread_create(&conf->server_thread, NULL, &web_server_thread, conf);
80
+}
81
+
82
+void web_server_stop(struct config *conf) {
83
+	if (conf->server_socket > -1) {
84
+		shutdown_fd(&conf->server_socket);
85
+		pthread_join(conf->server_thread, NULL);
86
+	}
87
+}
88
+
89
+void *process_web_request(void *in_req) {
90
+	request_info *req = (request_info *)in_req;
91
+	int clientsock = req->clientsock;
92
+	regmatch_t res[3];
93
+	char *path=NULL, *buf=NULL;
94
+	FREE(req);
95
+
96
+	signal(SIGPIPE, SIG_IGN);
97
+
98
+	if (!keep_going)
99
+		pthread_exit(0);
100
+
101
+	buf = malloc(BUF_SIZE);
102
+	if (!buf) {
103
+		log_perror("Can't allocate buffer", errno);
104
+		SHUTDOWN_CLIENT;
105
+	}
106
+
107
+	if (fdgetline(clientsock,buf,BUF_SIZE)<=0) {
108
+		SHUTDOWN_CLIENT;
109
+	}
110
+
111
+	regex_t request_get;
112
+	regcomp(&request_get, "^GET /([^ ]*) HTTP/1.*$", REG_EXTENDED);
113
+	if (regexec(&request_get,buf,2,res,0)==REG_NOMATCH) {
114
+		send_501_not_implemented(clientsock);
115
+		SHUTDOWN_CLIENT;
116
+	}
117
+
118
+	buf[res[1].rm_eo]=0;
119
+	chomp(buf+res[1].rm_so);
120
+	if (buf[res[1].rm_eo-1]=='/') buf[res[1].rm_eo-1]=0;
121
+	path = strdup(buf+res[1].rm_so);
122
+	regfree(&request_get);
123
+
124
+	while (fdgetline(clientsock,buf,BUF_SIZE) > 0) {
125
+		if (buf[0] == '\n' || buf[0] == '\r') // End of headers
126
+			break;
127
+	}
128
+
129
+	if (strlen(path) == 0) {
130
+		cmd_index(clientsock);
131
+	} else if (strstr(path,"getconfig")==path) {
132
+		cmd_getconfig(clientsock);
133
+	} else if (strstr(path,"reconnect")==path) {
134
+		cmd_reconnect(clientsock);
135
+	} else if (strstr(path,"reload")==path) {
136
+		cmd_reload(clientsock);
137
+	} else if (strstr(path,"status")==path) {
138
+		cmd_status(clientsock);
139
+	} else {
140
+		send_404_not_found(clientsock);
141
+	}
142
+
143
+	SHUTDOWN_CLIENT;
144
+}
145
+

+ 26
- 0
web_server.h View File

1
+/*
2
+ * mptsd internal web server header file
3
+ * Copyright (C) 2010-2011 Unix Solutions Ltd.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+#ifndef WEB_SERVER_H
19
+# define WEB_SERVER_H
20
+
21
+#include "config.h"
22
+
23
+void web_server_start(struct config *conf);
24
+void web_server_stop(struct config *conf);
25
+
26
+#endif

Loading…
Cancel
Save