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