Updated to current mtstdopen interface.
[statserve.git] / statserve.c
CommitLineData
34d725a5
FT
1#include <stdlib.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <errno.h>
5#include <string.h>
a823d5b4 6#include <sys/socket.h>
34d725a5
FT
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
15static struct source *sources = NULL;
16
17static 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
86d0fd88
FT
26static 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
34d725a5
FT
38static 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 }
fb33d4f0 56 out = mtstdopen(fd, 1, 60, "r+", NULL);
86d0fd88
FT
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 }
34d725a5
FT
69 free(f.data);
70
71out:
72 if(out != NULL)
73 fclose(out);
74 else
75 close(fd);
76 freehthead(req);
77}
78
79static void usage(FILE *out)
80{
ea4e0b71 81 fprintf(out, "usage: statserve [-h] [-P PAGESIZE] SOURCE...\n");
34d725a5
FT
82}
83
84static 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
a823d5b4
FT
106static void sigterm(int sig)
107{
108 shutdown(0, SHUT_RDWR);
109}
110
111static 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
34d725a5
FT
121int main(int argc, char **argv)
122{
123 int c;
124 struct source *last, *src;
125
ea4e0b71 126 while((c = getopt(argc, argv, "+hP:")) >= 0) {
34d725a5 127 switch(c) {
ea4e0b71
FT
128 case 'P':
129 dbpagesize = atoi(optarg);
130 break;
34d725a5
FT
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) {
a823d5b4
FT
141 if((src = parsesource(argv[optind++])) == NULL) {
142 closeall();
143 return(1);
144 }
34d725a5
FT
145 if(!sources)
146 sources = src;
147 if(last)
148 last->next = src;
149 last = src;
150 }
151 if(!sources) {
152 usage(stderr);
a823d5b4 153 closeall();
34d725a5
FT
154 return(1);
155 }
156 mustart(listenloop, 0);
a823d5b4
FT
157 signal(SIGINT, sigterm);
158 signal(SIGTERM, sigterm);
34d725a5 159 ioloop();
a823d5b4 160 closeall();
34d725a5
FT
161 return(0);
162}