Added the `htpipe' program.
[ashd.git] / src / htpipe.c
CommitLineData
805fef2b
FT
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 while(1) {
108 struct pollfd pfd[ncl + 1];
109 for(i = 0; i < ncl; i++) {
110 pfd[i].fd = cl[i];
111 pfd[i].events= POLLIN;
112 }
113 pfd[i].fd = lsk;
114 pfd[i].events = POLLIN;
115 if((ret = poll(pfd, ncl + 1, -1)) < 0) {
116 if(errno != EINTR) {
117 flog(LOG_ERR, "htpipe: error in poll: %s", strerror(errno));
118 exit(1);
119 }
120 }
121 for(i = 0; i < ncl; i++) {
122 if(pfd[i].revents & POLLIN) {
123 if((rfd = recvreq(cl[i], &req)) < 0) {
124 if(errno != 0)
125 flog(LOG_ERR, "htpipe: error from client: %s", strerror(errno));
126 cl[i] = -1;
127 } else {
128 if(sendreq(ch, req, rfd)) {
129 flog(LOG_ERR, "htpipe: could not pass request to child: %s", strerror(errno));
130 exit(1);
131 }
132 freehthead(req);
133 close(rfd);
134 }
135 }
136 }
137 if(pfd[i].revents & POLLIN) {
138 if((acl = accept(lsk, NULL, 0)) < 0) {
139 flog(LOG_ERR, "htpipe: error in accept: %s", strerror(errno));
140 } else {
141 cl = srealloc(cl, sizeof(*cl) * (ncl + 1));
142 cl[ncl++] = acl;
143 }
144 }
145 for(i = o = 0; i < ncl; i++) {
146 if(cl[i] >= 0)
147 cl[o++] = cl[i];
148 }
149 ncl = o;
150 }
151}
152
153int main(int argc, char **argv)
154{
155 int c, cli, srv, sk, ch, sst;
156 pid_t sproc;
157 char *path, **chspec;
158
159 cli = srv = 0;
160 while((c = getopt(argc, argv, "+hCS")) >= 0) {
161 switch(c) {
162 case 'h':
163 usage(stdout);
164 exit(0);
165 case 'C':
166 cli = 1;
167 break;
168 case 'S':
169 srv = 1;
170 break;
171 }
172 }
173 if(argc - optind < 1) {
174 usage(stderr);
175 exit(1);
176 }
177 path = argv[optind++];
178 chspec = argv + optind;
179 if(cli) {
180 if((sk = clconnect(path)) < 0) {
181 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
182 exit(1);
183 }
184 runclient(sk);
185 } else if(srv) {
186 if(!*chspec) {
187 usage(stderr);
188 exit(1);
189 }
190 if((sk = mklisten(path)) < 0) {
191 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
192 exit(1);
193 }
194 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
195 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
196 exit(1);
197 }
198 runserver(sk, ch);
199 } else {
200 if(!*chspec) {
201 usage(stderr);
202 exit(1);
203 }
204 if((sk = clconnect(path)) < 0) {
205 if((sproc = fork()) < 0)
206 err(1, "fork");
207 if(sproc == 0) {
208 if((sk = mklisten(path)) < 0) {
209 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
210 exit(1);
211 }
212 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
213 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
214 exit(1);
215 }
216 daemon(0, 1);
217 runserver(sk, ch);
218 abort();
219 }
220 if((waitpid(sproc, &sst, 0)) != sproc) {
221 flog(LOG_ERR, "htpipe: could not wait for server process: %s", strerror(errno));
222 exit(1);
223 }
224 if((sk = clconnect(path)) < 0) {
225 flog(LOG_ERR, "htpipe: could not connect to newly forked server: %s", strerror(errno));
226 exit(1);
227 }
228 }
229 runclient(sk);
230 }
231 return(0);
232}