Updated to current mtstdopen interface.
[statserve.git] / statserve.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6 #include <sys/socket.h>
7 #include <ashd/req.h>
8 #include <ashd/resp.h>
9 #include <ashd/log.h>
10 #include <ashd/mt.h>
11 #include <ashd/mtio.h>
12
13 #include "statserve.h"
14
15 static struct source *sources = NULL;
16
17 static struct source *parsesource(char *arg)
18 {
19     if(arg[strlen(arg) - 1] == '/') {
20         return(mkfssrc(arg));
21     } else {
22         return(mkdbsrc(arg, NULL));
23     }
24 }
25
26 static int cached(struct hthead *req, struct fileinfo f)
27 {
28     char *hdr;
29     time_t cdate;
30     
31     if((hdr = getheader(req, "If-Modified-Since")) != NULL) {
32         cdate = parsehttpdate(hdr);
33         return((cdate > 0) && !(cdate < f.mtime));
34     }
35     return(0);
36 }
37
38 static void serve(struct muth *muth, va_list args)
39 {
40     vavar(struct hthead *, req);
41     vavar(int, fd);
42     FILE *out;
43     struct source *src;
44     struct fileinfo f;
45     
46     out = NULL;
47     for(src = sources; src != NULL; src = src->next) {
48         f = src->serve(src, req->rest);
49         if(f.data != NULL)
50             break;
51     }
52     if(src == NULL) {
53         simpleerror(fd, 404, "Resource not found", "The resource %s was not found", htmlquote(req->rest));
54         goto out;
55     }
56     out = mtstdopen(fd, 1, 60, "r+", NULL);
57     if(cached(req, f)) {
58         fprintf(out, "HTTP/1.1 304 Not Modified\n");
59         fprintf(out, "Content-Length: 0\n");
60         fprintf(out, "\n");
61     } else {
62         fprintf(out, "HTTP/1.1 200 OK\n");
63         fprintf(out, "Content-Type: %s\n", f.ctype);
64         fprintf(out, "Content-Length: %zi\n", f.sz);
65         fprintf(out, "Last-Modified: %s\n", fmthttpdate(f.mtime));
66         fprintf(out, "\n");
67         fwrite(f.data, 1, f.sz, out);
68     }
69     free(f.data);
70     
71 out:
72     if(out != NULL)
73         fclose(out);
74     else
75         close(fd);
76     freehthead(req);
77 }
78
79 static void usage(FILE *out)
80 {
81     fprintf(out, "usage: statserve [-h] [-P PAGESIZE] SOURCE...\n");
82 }
83
84 static void listenloop(struct muth *muth, va_list args)
85 {
86     vavar(int, lfd);
87     int fd;
88     struct hthead *req;
89     struct source *src;
90     
91     while(1) {
92         block(0, EV_READ, 0);
93         if((fd = recvreq(lfd, &req)) < 0) {
94             if(errno != 0)
95                 flog(LOG_ERR, "recvreq: %s", strerror(errno));
96             break;
97         }
98         mustart(serve, req, fd);
99         for(src = sources; src != NULL; src = src->next) {
100             if(src->idle)
101                 src->idle(src);
102         }
103     }
104 }
105
106 static void sigterm(int sig)
107 {
108     shutdown(0, SHUT_RDWR);
109 }
110
111 static void closeall(void)
112 {
113     struct source *src;
114     
115     for(src = sources; src != NULL; src = src->next) {
116         if(src->close)
117             src->close(src);
118     }
119 }
120
121 int main(int argc, char **argv)
122 {
123     int c;
124     struct source *last, *src;
125     
126     while((c = getopt(argc, argv, "+hP:")) >= 0) {
127         switch(c) {
128         case 'P':
129             dbpagesize = atoi(optarg);
130             break;
131         case 'h':
132             usage(stdout);
133             return(0);
134         default:
135             usage(stderr);
136             return(1);
137         }
138     }
139     last = NULL;
140     while(optind < argc) {
141         if((src = parsesource(argv[optind++])) == NULL) {
142             closeall();
143             return(1);
144         }
145         if(!sources)
146             sources = src;
147         if(last)
148             last->next = src;
149         last = src;
150     }
151     if(!sources) {
152         usage(stderr);
153         closeall();
154         return(1);
155     }
156     mustart(listenloop, 0);
157     signal(SIGINT, sigterm);
158     signal(SIGTERM, sigterm);
159     ioloop();
160     closeall();
161     return(0);
162 }