etc: Add environment option to run init.d/ashd silently.
[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>
0bde126e 22#include <string.h>
805fef2b
FT
23#include <errno.h>
24#include <err.h>
25#include <sys/poll.h>
26#include <sys/socket.h>
27#include <sys/un.h>
28#include <sys/stat.h>
29#include <sys/wait.h>
30
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34#include <utils.h>
35#include <log.h>
36#include <req.h>
37#include <proc.h>
38
39static void usage(FILE *out)
40{
41 fprintf(out, "usge: htpipe [-h] [-CS] SOCKET-PATH [CHILD ARGS...]\n");
42}
43
44static int clconnect(char *path)
45{
46 int sk;
47 struct sockaddr_un unm;
48
49 if((sk = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
50 return(-1);
51 memset(&unm, 0, sizeof(unm));
52 unm.sun_family = AF_UNIX;
53 strcpy(unm.sun_path, path);
54 if(connect(sk, (struct sockaddr *)&unm, sizeof(unm))) {
55 close(sk);
56 return(-1);
57 }
58 return(sk);
59}
60
61static int mklisten(char *path)
62{
63 int sk;
64 struct sockaddr_un unm;
65 struct stat sb;
66
67 if(!stat(path, &sb) && S_ISSOCK(sb.st_mode))
68 unlink(path);
69 if((sk = socket(AF_UNIX, SOCK_SEQPACKET, 0)) < 0)
70 return(-1);
71 memset(&unm, 0, sizeof(unm));
72 unm.sun_family = AF_UNIX;
73 strcpy(unm.sun_path, path);
74 if(bind(sk, (struct sockaddr *)&unm, sizeof(unm)) || listen(sk, 128)) {
75 close(sk);
76 return(-1);
77 }
78 return(sk);
79}
80
81static void runclient(int sk)
82{
83 int fd;
84 struct hthead *req;
85
86 while(1) {
87 if((fd = recvreq(0, &req)) < 0) {
88 if(errno == 0)
89 break;
90 flog(LOG_ERR, "htpipe: error in recvreq: %s", strerror(errno));
91 exit(1);
92 }
93 if(sendreq(sk, req, fd)) {
94 flog(LOG_ERR, "htpipe: could not pass request across pipe: %s", strerror(errno));
95 exit(1);
96 }
97 freehthead(req);
98 close(fd);
99 }
100}
101
102static void runserver(int lsk, int ch)
103{
104 int i, o, ret, rfd, ncl, *cl, acl;
105 struct hthead *req;
106
107 ncl = 0;
3840f76d 108 cl = NULL;
805fef2b
FT
109 while(1) {
110 struct pollfd pfd[ncl + 1];
111 for(i = 0; i < ncl; i++) {
112 pfd[i].fd = cl[i];
113 pfd[i].events= POLLIN;
114 }
115 pfd[i].fd = lsk;
116 pfd[i].events = POLLIN;
117 if((ret = poll(pfd, ncl + 1, -1)) < 0) {
118 if(errno != EINTR) {
119 flog(LOG_ERR, "htpipe: error in poll: %s", strerror(errno));
120 exit(1);
121 }
122 }
123 for(i = 0; i < ncl; i++) {
124 if(pfd[i].revents & POLLIN) {
125 if((rfd = recvreq(cl[i], &req)) < 0) {
126 if(errno != 0)
127 flog(LOG_ERR, "htpipe: error from client: %s", strerror(errno));
62687b1f 128 close(cl[i]);
805fef2b
FT
129 cl[i] = -1;
130 } else {
131 if(sendreq(ch, req, rfd)) {
132 flog(LOG_ERR, "htpipe: could not pass request to child: %s", strerror(errno));
133 exit(1);
134 }
135 freehthead(req);
136 close(rfd);
137 }
138 }
139 }
140 if(pfd[i].revents & POLLIN) {
141 if((acl = accept(lsk, NULL, 0)) < 0) {
142 flog(LOG_ERR, "htpipe: error in accept: %s", strerror(errno));
143 } else {
144 cl = srealloc(cl, sizeof(*cl) * (ncl + 1));
145 cl[ncl++] = acl;
146 }
147 }
148 for(i = o = 0; i < ncl; i++) {
149 if(cl[i] >= 0)
150 cl[o++] = cl[i];
151 }
152 ncl = o;
153 }
154}
155
156int main(int argc, char **argv)
157{
158 int c, cli, srv, sk, ch, sst;
159 pid_t sproc;
160 char *path, **chspec;
161
162 cli = srv = 0;
163 while((c = getopt(argc, argv, "+hCS")) >= 0) {
164 switch(c) {
165 case 'h':
166 usage(stdout);
167 exit(0);
168 case 'C':
169 cli = 1;
170 break;
171 case 'S':
172 srv = 1;
173 break;
174 }
175 }
176 if(argc - optind < 1) {
177 usage(stderr);
178 exit(1);
179 }
180 path = argv[optind++];
181 chspec = argv + optind;
182 if(cli) {
183 if((sk = clconnect(path)) < 0) {
184 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
185 exit(1);
186 }
187 runclient(sk);
188 } else if(srv) {
189 if(!*chspec) {
190 usage(stderr);
191 exit(1);
192 }
193 if((sk = mklisten(path)) < 0) {
194 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
195 exit(1);
196 }
197 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
198 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
199 exit(1);
200 }
201 runserver(sk, ch);
202 } else {
203 if(!*chspec) {
204 usage(stderr);
205 exit(1);
206 }
207 if((sk = clconnect(path)) < 0) {
208 if((sproc = fork()) < 0)
209 err(1, "fork");
210 if(sproc == 0) {
211 if((sk = mklisten(path)) < 0) {
212 flog(LOG_ERR, "htpipe: %s: %s", path, strerror(errno));
213 exit(1);
214 }
215 if((ch = stdmkchild(chspec, NULL, NULL)) < 0) {
216 flog(LOG_ERR, "htpipe: could not fork child: %s", strerror(errno));
217 exit(1);
218 }
219 daemon(0, 1);
220 runserver(sk, ch);
221 abort();
222 }
223 if((waitpid(sproc, &sst, 0)) != sproc) {
224 flog(LOG_ERR, "htpipe: could not wait for server process: %s", strerror(errno));
225 exit(1);
226 }
227 if((sk = clconnect(path)) < 0) {
228 flog(LOG_ERR, "htpipe: could not connect to newly forked server: %s", strerror(errno));
229 exit(1);
230 }
231 }
232 runclient(sk);
233 }
234 return(0);
235}