Browse code

Skeleton server (shamelessly ripped from newslettersan)

Dario Rodriguez authored on 25/04/2015 09:55:24
Showing 9 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,7 @@
1
+src/*.o
2
+src/recremote
3
+src/*~
4
+res/gen_res.c
5
+res/gen_res.h
6
+src/recremote.conf
7
+src/recremote.log
0 8
new file mode 100755
... ...
@@ -0,0 +1,50 @@
1
+#!/bin/sh
2
+rm -f gen_res.c gen_res.h
3
+cat >> gen_res.c <<'EoF'
4
+/* DO NOT EDIT. Automatically generated file. */
5
+#define ETAGSIZE 40
6
+typedef struct resindex { char *name; unsigned char *data; int len; char etag[ETAGSIZE+1];} resindex;
7
+EoF
8
+cp gen_res.c gen_res.h
9
+cat >> gen_res.h <<'EoF'
10
+extern resindex *resindexdata;
11
+resindex *res_find(resindex *index, char *name);
12
+EoF
13
+cat >> gen_res.c <<'EoF'
14
+#include <string.h>
15
+EoF
16
+# generate the contents
17
+find . -type f | sed "s:^\./::g" | grep -v "^gen.sh\$\|^gen_res.c\$\|^gen_res.h\$" | while read f ; do
18
+	name=`echo $f | tr -c "a-zA-Z0-9" "_" | sed "s/^\([0-9]\)/_\1/g"`
19
+	echo "static unsigned char $name[]={\"\\" >> gen_res.c
20
+	cat $f | hexdump -v -e '"\\""x" 1/1 "%02X"' | sed "s/\(....................................................................\)/\1#@/g" | tr '#@' '\\
21
+' >> gen_res.c
22
+	echo '"};' >> gen_res.c
23
+done
24
+# generate the listing
25
+(
26
+echo ""
27
+echo "static resindex resindexstaticdata[]={"
28
+find . -type f | sed "s:^\./::g" | grep -v "^gen.sh\$\|^gen_res.c\$\|^gen_res.h\$" | while read f ; do
29
+	name=`echo $f | tr -c "a-zA-Z0-9" "_" | sed "s/^\([0-9]\)/_\1/g"`
30
+	echo "{\"$f\",$name,`wc -c $f | cut -d ' '  -f 1`,{\"`sha1sum $f | cut -d ' '  -f 1 | tr -dc 0-9a-f`\"}},"
31
+done
32
+echo "{NULL,NULL,0}};"
33
+) >> gen_res.c
34
+echo "resindex *resindexdata=resindexstaticdata;" >> gen_res.c
35
+cat >>gen_res.c <<'EoF'
36
+resindex *
37
+res_find(resindex *index, char *name)
38
+{
39
+	int i;
40
+	if(index==NULL || name==NULL)
41
+		return(NULL);
42
+	for(i=0;index[i].name!=NULL;i++) {
43
+		if(strcmp(index[i].name,name)==0)
44
+			return(index+i);
45
+	}
46
+	return(NULL);
47
+}
48
+EoF
49
+
50
+exit 0
0 51
new file mode 100644
... ...
@@ -0,0 +1,5 @@
1
+<!doctype html>
2
+<html>
3
+<head><title>recremote</title></head>
4
+<body></body>
5
+</html>
0 6
new file mode 100644
... ...
@@ -0,0 +1,43 @@
1
+WEBKERNEL=../../webkernel/src
2
+CC=gcc
3
+CFLAGS=-g -Wall -I$(WEBKERNEL) -I../res -DWK_DEBUG_CONN -DBUFSIZE=16384
4
+LDFLAGS=
5
+
6
+all: recremote
7
+
8
+clean:
9
+	rm -f *.o recremote ../res/gen_res.[ch]
10
+
11
+recremote: recremote.o loglib.o parselib.o sbuf.o \
12
+		socklib.o webkernel.o gen_res.o \
13
+		rec_config.o
14
+	$(CC) $(LDFLAGS) recremote.o loglib.o parselib.o sbuf.o \
15
+		socklib.o webkernel.o gen_res.o \
16
+		rec_config.o \
17
+		-o recremote
18
+
19
+recremote.o: recremote.c ../src/gen_res.c
20
+
21
+../src/gen_res.c: ../res/index.html
22
+	( cd ../res && ./gen.sh )
23
+
24
+gen_res.o: ../src/gen_res.c
25
+	$(CC) $(CFLAGS) -c ../res/gen_res.c -o gen_res.o
26
+
27
+loglib.o: $(WEBKERNEL)/loglib.c
28
+	$(CC) $(CFLAGS) -c $(WEBKERNEL)/loglib.c -o loglib.o
29
+
30
+parselib.o: $(WEBKERNEL)/parselib.c
31
+	$(CC) $(CFLAGS) -c $(WEBKERNEL)/parselib.c -o parselib.o
32
+
33
+sbuf.o: $(WEBKERNEL)/sbuf.c
34
+	$(CC) $(CFLAGS) -c $(WEBKERNEL)/sbuf.c -o sbuf.o
35
+
36
+socklib.o: $(WEBKERNEL)/socklib.c
37
+	$(CC) $(CFLAGS) -c $(WEBKERNEL)/socklib.c -o socklib.o
38
+
39
+webkernel.o: $(WEBKERNEL)/webkernel.c
40
+	$(CC) $(CFLAGS) -c $(WEBKERNEL)/webkernel.c -o webkernel.o
41
+
42
+
43
+	
0 44
new file mode 100644
... ...
@@ -0,0 +1,158 @@
1
+/*
2
+ * rec_config.c
3
+ *
4
+ * Configuration load/save for recremote
5
+ *
6
+ * Author: Dario Rodriguez dario@softhome.net
7
+ * This program is licensed under the terms of the Affero GPL v1+
8
+ */
9
+
10
+#include <stdio.h>
11
+#include <stdlib.h>
12
+#include <unistd.h>
13
+#include <string.h>
14
+#include <sys/types.h>
15
+#include <sys/stat.h>
16
+
17
+#include "loglib.h"
18
+#include "rec_config.h"
19
+
20
+#define MAXLINESIZE 2048
21
+#define LINESBLOCK 32
22
+
23
+typedef enum esection {
24
+        section_none=0,
25
+        section_general
26
+} esection;
27
+
28
+
29
+recconfig *
30
+recconfig_init(char *configfile)
31
+{
32
+	recconfig *config;
33
+	FILE *f;
34
+	char line[MAXLINESIZE];
35
+        int len;
36
+        char *ptr,*sep,*value;
37
+        esection section;
38
+        int lineno;
39
+        char **configvalue;
40
+        if((f=fopen(configfile,"r"))==NULL)
41
+                return(NULL);
42
+        if((config=malloc(sizeof(recconfig)))==NULL) {
43
+                fclose(f),f=NULL;
44
+                return(NULL);
45
+        }
46
+        memset(config,0,sizeof(recconfig));
47
+        section=section_none;
48
+        lineno=0;
49
+        while(fgets(line,sizeof(line),f)!=NULL) {
50
+                lineno++;
51
+                line[sizeof(line)-1]='\0';
52
+                /* trim line */
53
+                for(ptr=line;*ptr!='\0' && strchr("\t ",*ptr)!=NULL;ptr++)
54
+                        ;
55
+                len=strlen(line);
56
+                while(len>0 && strchr("\n\t ",line[len-1])!=NULL)
57
+	                line[len-1]='\0',len--;
58
+		for(ptr=line;*ptr!='\0' && strchr("\t ",*ptr)!=NULL;ptr++)
59
+                        ;
60
+                len=strlen(ptr);
61
+                /* ignore empty lines and comments */
62
+                if(*ptr=='\0' || strchr("#;",*ptr)!=NULL)
63
+                        continue;
64
+                /* parse line */
65
+                if(*ptr=='[' && ptr[len-1]==']') {
66
+                        /* section */
67
+                        if(strcmp(ptr,"[general]")==0) {
68
+                                section=section_general;
69
+                        } else {
70
+                                log_write("CONF","%s:%i: unknown section name \"%s\"\n",configfile,lineno,ptr);
71
+                                section=section_none;
72
+                        }
73
+                        continue;
74
+                } else if((sep=strchr(ptr,'='))!=NULL) {
75
+                        *sep='\0';
76
+                        value=sep+1;
77
+                        /* trim key */
78
+                        while(sep>ptr && strchr(" \t",sep[-1])!=NULL) {
79
+                                sep--;
80
+                                *sep='\0';
81
+                        }
82
+                        /* trim value */
83
+                        while(*value!='\0' && strchr(" \t",*value)!=NULL)
84
+                                value++;
85
+			/* sanity check */
86
+                        if(*value=='\0') {
87
+				log_write("CONF","%s:%i: ignoring key without value; key:\"%s\"\n",configfile,lineno,ptr);
88
+                                continue;
89
+                        }
90
+			 /* assign value */
91
+                        if(section==section_none) {
92
+                                log_write("CONF","%s:%i: ignoring key-value pair in unknown section; key:\"%s\"\n",configfile,lineno,ptr);
93
+                                continue;
94
+                        }
95
+                        /* identify key-value pair */
96
+                        if(section==section_general) {
97
+                                if(strcmp(ptr,"logfile")==0) {
98
+                                        configvalue=&(config->logfile);
99
+				} else {
100
+                                        log_write("CONF","%s:%i: unknown key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
101
+                                        continue;
102
+                                }
103
+			} else
104
+                                continue;
105
+			/* store value */
106
+                        if(*configvalue!=NULL) {
107
+                                log_write("CONF","%s:%i: duplicate key, ignoring key-value pair; key:\"%s\"\n",configfile,lineno,ptr);
108
+                                continue;
109
+                        }
110
+                        if((*configvalue=strdup(value))==NULL) {
111
+                                fclose(f),f=NULL;
112
+                                recconfig_free(config),config=NULL;
113
+                                return(NULL); /* insufficient memory */
114
+                        }
115
+                        continue;
116
+                } else {
117
+                        log_write("CONF","%s:%i: malformed line, ignoring; contents:\"%s\"\n",configfile,lineno,ptr);
118
+                        continue;
119
+                }
120
+        }
121
+        fclose(f),f=NULL;
122
+        return(config);
123
+}
124
+
125
+void
126
+recconfig_free(recconfig *config)
127
+{
128
+        if(config==NULL)
129
+                return;
130
+        if(config->logfile!=NULL)
131
+                free(config->logfile),config->logfile=NULL;
132
+        free(config),config=NULL;
133
+        return;
134
+}
135
+
136
+int
137
+recconfig_exists(char *configfile)
138
+{
139
+        struct stat st;
140
+        if(stat(configfile,&st)!=0)
141
+                return(-1);
142
+        return(0);
143
+}
144
+
145
+int
146
+recconfig_write(char *configfile,char *logfile)
147
+{
148
+        FILE *f;
149
+        if((f=fopen(configfile,"w"))==NULL)
150
+                return(-1);
151
+        fprintf(f,"; recremote config file\n");
152
+        fprintf(f,"[general]\n");
153
+        fprintf(f,"logfile=%s\n",(logfile!=NULL)?logfile:"recremote.log");
154
+        return(0);
155
+}
156
+
157
+
158
+
0 159
new file mode 100644
... ...
@@ -0,0 +1,25 @@
1
+/*
2
+ * rec_config.h
3
+ *
4
+ * Configuration load/save for recremote
5
+ *
6
+ * Header file
7
+ *
8
+ * Author: Dario Rodriguez dario@softhome.net
9
+ * This program is licensed under the terms of the Affero GPL v1+
10
+ */
11
+
12
+#ifndef NEWSL_CONFIG_H
13
+#define NEWSL_CONFIG_H
14
+
15
+typedef struct recconfig {
16
+	char *logfile;
17
+} recconfig;
18
+
19
+recconfig *recconfig_init(char *configfile);
20
+void recconfig_free(recconfig *config);
21
+
22
+int recconfig_exists(char *configfile);
23
+int recconfig_write(char *configfile,char *logfile);
24
+
25
+#endif
0 26
new file mode 100644
... ...
@@ -0,0 +1,144 @@
1
+/*
2
+ * recremote.c
3
+ *
4
+ * Web frontend to control recording with ALSA 
5
+ *
6
+ * Author: Dario Rodriguez dario@softhome.net
7
+ * This program is licensed under the terms of the Affero GPL v1+
8
+ */
9
+
10
+#include <stdio.h>
11
+#include <stdlib.h>
12
+#include <unistd.h>
13
+#include <string.h>
14
+#include <signal.h>
15
+#include <sys/types.h>
16
+#include <sys/stat.h>
17
+#include "gen_res.h"
18
+#include "socklib.h"
19
+#include "loglib.h"
20
+#include "webkernel.h"
21
+#include "recremote.h"
22
+#include "rec_config.h"
23
+
24
+#define CONFIGFILE "recremote.cfg"
25
+#define CFLOGFILE "recremote.log"
26
+
27
+static int signal_init(int signum, void (*fn)(int));
28
+static void sigint(int signum);
29
+volatile int sigint_flag=0;
30
+
31
+wk_action callback_http(wk *web, int connid, wk_uri *uri, void *userptr);
32
+
33
+int
34
+main(int argc, char *argv[])
35
+{
36
+        int timeout=500;
37
+        int serverfd;
38
+	recremote *rec,recstore;
39
+	char *hostnameport;
40
+        char hostname[128];
41
+        char *host;
42
+        long hostsize;
43
+        char *ptr,*sep;
44
+        memset(&recstore,0,sizeof(recstore));
45
+        rec=&recstore;
46
+        if(argc!=2) {
47
+                printf("Syntax: %s [ip:]port\n",argv[0]);
48
+                return(1);
49
+        }
50
+        hostnameport=argv[1];
51
+        if(recconfig_exists(CONFIGFILE)!=0) {
52
+                log_setlogfile(CFLOGFILE);
53
+                log_write("INIT","Config file not found, writing default file %s",CONFIGFILE);
54
+                recconfig_write(CONFIGFILE,CFLOGFILE);
55
+        }
56
+        if((rec->config=recconfig_init(CONFIGFILE))==NULL) {
57
+                log_setlogfile(CFLOGFILE);
58
+                log_write("INIT","ERROR: insufficient memory or config file error");
59
+                return(1);
60
+        }
61
+        log_setlogfile((rec->config->logfile!=NULL)?rec->config->logfile:CFLOGFILE);
62
+        if((rec->ssel=sselect_init())==NULL) {
63
+                log_write("INIT","ERROR: insufficient memory");
64
+                return(1);
65
+        }
66
+        if((sep=strchr(hostnameport,':'))!=NULL) {
67
+                serverfd=-1;
68
+                strncpy(hostname,hostnameport,sizeof(hostname));
69
+                hostname[sizeof(hostname)-1]='\0';
70
+                if((ptr=strchr(hostname,':'))!=NULL)
71
+                        *ptr='\0';
72
+                if((host=ipv4_genip(hostname,&hostsize))!=NULL) {
73
+                        serverfd=ipv4_serverbinded(host,hostsize,atoi(sep+1));
74
+                        free(host),host=NULL;
75
+                }
76
+        } else {
77
+                serverfd=ipv4_server(atoi(hostnameport));
78
+        }
79
+        if(serverfd==-1) {
80
+                sselect_free(rec->ssel),rec->ssel=NULL;
81
+                log_write("INIT","ERROR: couldn't listen on port");
82
+                return(2);
83
+        }
84
+        sock_setunsafe(serverfd);
85
+        if((rec->web=wk_init(serverfd,rec->ssel,NULL,callback_http,NULL,NULL,rec))==NULL) {
86
+                sselect_free(rec->ssel),rec->ssel=NULL;
87
+                log_write("INIT","ERROR: couldn't init web server");
88
+                return(2);
89
+        }
90
+        sigint_flag=0;
91
+        signal_init(SIGINT,sigint);
92
+        log_write("INIT","Server initialized, waiting connections...");
93
+        while(!sigint_flag) {
94
+                sselect_wait(rec->ssel,timeout);
95
+                if(sigint_flag)
96
+                        break;
97
+                wk_service(rec->web);
98
+        }
99
+        wk_free(rec->web),rec->web=NULL;
100
+        close(serverfd),serverfd=-1;
101
+        sselect_free(rec->ssel),rec->ssel=NULL;
102
+        recconfig_free(rec->config),rec->config=NULL;
103
+        log_write("FINI","SIGINT detected, exiting...");
104
+        return(0);
105
+}
106
+
107
+static int
108
+signal_init(int signum, void (*fn)(int))
109
+{
110
+        struct sigaction sa;
111
+        sa.sa_handler=fn;
112
+        sigemptyset(&sa.sa_mask);
113
+        sa.sa_flags=0;
114
+        return(sigaction(signum,&sa,0));
115
+}
116
+
117
+static void
118
+sigint(int signum)
119
+{
120
+        sigint_flag=1;
121
+}
122
+
123
+wk_action
124
+callback_http(wk *web, int connid, wk_uri *uri, void *userptr)
125
+{
126
+        recremote *rec=(recremote *)userptr;
127
+        resindex *res;
128
+        char partialpath[1024];
129
+	if(rec==NULL)
130
+		return(wkact_finished);
131
+	strncpy(partialpath,uri->path,sizeof(partialpath)-1);
132
+        partialpath[sizeof(partialpath)-1]='\0';
133
+	if(strcmp(uri->path,"/")==0)
134
+                strcpy(partialpath,"/index.html");
135
+        if(partialpath[0]=='/' && (res=res_find(resindexdata,partialpath+1))!=NULL) {
136
+		log_write("HTTP","Serving in-memory file %s",partialpath+1);
137
+		wk_serve_etagset(web,connid,res->etag);
138
+		wk_serve_buffer_as_file(web,connid,res->data,res->len,mime_getdefault(res->name,"application/octet-stream"));
139
+		return(wkact_finished);
140
+	}
141
+	wk_serve_error(web,connid,wkerr_notfound);
142
+        return(wkact_finished);
143
+}
144
+
0 145
new file mode 100644
... ...
@@ -0,0 +1,3 @@
1
+; recremote config file
2
+[general]
3
+logfile=recremote.log
0 4
new file mode 100644
... ...
@@ -0,0 +1,25 @@
1
+/*
2
+ * recremote.h
3
+ *
4
+ * Web frontend to control recording with ALSA
5
+ *
6
+ * Header file
7
+ *
8
+ * Author: Dario Rodriguez dario@softhome.net
9
+ * This program is licensed under the terms of the Affero GPL v1+
10
+ */
11
+
12
+#ifndef RECREMOTE_H
13
+#define RECREMOTE_H
14
+
15
+#include "socklib.h"
16
+#include "webkernel.h"
17
+#include "rec_config.h"
18
+
19
+typedef struct recremote {
20
+	sselect *ssel;
21
+	wk *web;
22
+	recconfig *config;
23
+} recremote;
24
+
25
+#endif