htpipe: Fixed simple initialization bug.
[ashd.git] / src / htpipe.c
... / ...
CommitLineData
1/*
2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
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 as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <errno.h>
23#include <err.h>
24#include <sys/poll.h>
25#include <sys/socket.h>
26#include <sys/un.h>
27#include <sys/stat.h>
28#include <sys/wait.h>
29
30#ifdef HAVE_CONFIG_H
31#include <config.h>
32#endif
33#include <utils.h>
34#include <log.h>
35#include <req.h>
36#include <proc.h>
37
38static void usage(FILE *out)
39{
40 fprintf(out, "usge: htpipe [-h] [-CS] SOCKET-PATH [CHILD ARGS...]\n");
41}
42
43static int clconnect(char *path)
44{
45 int sk;
46 struct sockaddr_un unm;
47
48 if((sk = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
49 return(-1);
50 memset(&unm, 0, sizeof(unm));
51 unm.sun_family = AF_UNIX;
52 strcpy(unm.sun_path, path);
53 if(connect(sk, (struct sockaddr *)&unm, sizeof(unm))) {
54 close(sk);
55 return(-1);
56 }
57 return(sk);
58}
59
60static int mklisten(char *path)
61{
62 int sk;
63 struct sockaddr_un unm;
64 struct stat sb;
65
66 if(!stat(path, &sb) && S_ISSOCK(sb.st_mode))
67 unlink(path);
68 if((sk = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
69 return(-1);
70 memset(&unm, 0, sizeof(unm));
71 unm.sun_family = AF_UNIX;
72 strcpy(unm.sun_path, path);
73 if(bind(sk, (struct sockaddr *)&unm, sizeof(unm)) || listen(sk, 128)) {
74 close(sk);
75 return(-1);
76 }
77 return(sk);
78}
79
80static void runclient(int sk)
81{
82 int fd;
83 struct hthead *req;
84
85 while(1) {
86 if((fd = recvreq(0, &req)) < 0) {
87 if(errno == 0)
88 break;
89 flog(LOG_ERR, "htpipe: error in recvreq: %s", strerror(errno));
90 exit(1);
91 }
92 if(sendreq(sk, req, fd)) {
93 flog(LOG_ERR, "htpipe: could not pass request across pipe: %s", strerror(errno));
94 exit(1);
95 }
96 freehthead(req);
97 close(fd);
98 }
99}
100
101static void runserver(int lsk, int ch)
102{
103 int i, o, ret, rfd, ncl, *cl, acl;
104 struct hthead *req;
105
106 ncl = 0;
107 cl = NULL;
108 while(1) {
109 struct pollfd pfd[ncl + 1];
110 for(i = 0; i < ncl; i++) {
111 pfd[i].fd = cl[i];
112 pfd[i].events= POLLIN;
113 }
114 pfd[i].fd = lsk;
115 pfd[i].events = POLLIN;
116 if((ret = poll(pfd, ncl + 1, -1)) < 0) {
117 if(errno != EINTR) {
118 flog(LOG_ERR, "htpipe: error in poll: %s", strerror(errno));
119 exit(1);
120 }
121 }
122 for(i = 0; i < ncl; i++) {
123 if(pfd[i].revents & POLLIN) {
124 if((rfd = recvreq(cl[i], &req)) < 0) {
125 if(errno != 0)
126 flog(LOG_ERR, "htpipe: error from client: %s", strerror(errno));
127 cl[i] = -1;
128 } else {
129 if(sendreq(ch, req, rfd)) {
130 flog(LOG_ERR, "htpipe: could not pass request to child: %s", strerror(errno));
131 exit(1);
132 }
133 freehthead(req);
134 close(rfd);
135 }
136 }
137 }
138 if(pfd[i].revents & POLLIN) {
139 if((acl = accept(lsk, NULL, 0)) < 0) {
140 flog(LOG_ERR, "htpipe: error in accept: %s", strerror(errno));
141 } else {
142 cl = srealloc(cl, sizeof(*cl) * (ncl + 1));
143 cl[ncl++] = acl;
144 }
145 }
146 for(i = o = 0; i < ncl; i++) {
147 if(cl[i] >= 0)
148 cl[o++] = cl[i];
149 }
150 ncl = o;
151 }
152}
153
154int main(int argc, char **argv)
155{
156 int c, cli, srv, sk, ch, sst;
157 pid_t sproc;
158 char *path, **chspec;
159
160 cli = srv = 0;
161 while((c = getopt(argc, argv, "+hCS")) >= 0) {
162 switch(c) {
163 case 'h':
164 usage(stdout);
165 exit(0);
166 case 'C':
167 cli = 1;
168 break;
169 case 'S':
170 srv = 1;
171 break;
172 }
173 }
174 if(argc - optind < 1) {
175 usage(stderr);
176 exit(1);
177 }
178 path = argv[optind++];
179 chspec = argv + optind;
180 if(cli) {
181 if((sk = clconnect(path)) < 0) {
182 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
183 exit(1);
184 }
185 runclient(sk);
186 } else if(srv) {
187 if(!*chspec) {
188 usage(stderr);
189 exit(1);
190 }
191 if((sk = mklisten(path)) < 0) {
192 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
193 exit(1);
194 }
195 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
196 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
197 exit(1);
198 }
199 runserver(sk, ch);
200 } else {
201 if(!*chspec) {
202 usage(stderr);
203 exit(1);
204 }
205 if((sk = clconnect(path)) < 0) {
206 if((sproc = fork()) < 0)
207 err(1, "fork");
208 if(sproc == 0) {
209 if((sk = mklisten(path)) < 0) {
210 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
211 exit(1);
212 }
213 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
214 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
215 exit(1);
216 }
217 daemon(0, 1);
218 runserver(sk, ch);
219 abort();
220 }
221 if((waitpid(sproc, &sst, 0)) != sproc) {
222 flog(LOG_ERR, "htpipe: could not wait for server process: %s", strerror(errno));
223 exit(1);
224 }
225 if((sk = clconnect(path)) < 0) {
226 flog(LOG_ERR, "htpipe: could not connect to newly forked server: %s", strerror(errno));
227 exit(1);
228 }
229 }
230 runclient(sk);
231 }
232 return(0);
233}