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 | 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 | 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 |