]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc.cc
Author: Henrik Nordstrom <henrik@henriknordstrom.net>
[thirdparty/squid.git] / src / ipc.cc
1 /*
2 * DEBUG: section 54 Interprocess Communication
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "comm.h"
35 #include "fde.h"
36 #include "ip/IpAddress.h"
37
38 static const char *hello_string = "hi there\n";
39 #define HELLO_BUF_SZ 32
40 static char hello_buf[HELLO_BUF_SZ];
41
42 static int
43 ipcCloseAllFD(int prfd, int pwfd, int crfd, int cwfd)
44 {
45 if (prfd >= 0)
46 comm_close(prfd);
47
48 if (prfd != pwfd)
49 if (pwfd >= 0)
50 comm_close(pwfd);
51
52 if (crfd >= 0)
53 comm_close(crfd);
54
55 if (crfd != cwfd)
56 if (cwfd >= 0)
57 comm_close(cwfd);
58
59 return -1;
60 }
61
62 static void
63 PutEnvironment()
64 {
65 #if HAVE_PUTENV
66 char *env_str;
67 int tmp_s;
68 env_str = (char *)xcalloc((tmp_s = strlen(Debug::debugOptions) + 32), 1);
69 snprintf(env_str, tmp_s, "SQUID_DEBUG=%s", Debug::debugOptions);
70 putenv(env_str);
71 #endif
72 }
73
74 pid_t
75 ipcCreate(int type, const char *prog, const char *const args[], const char *name, IpAddress &local_addr, int *rfd, int *wfd, void **hIpc)
76 {
77 pid_t pid;
78 IpAddress ChS;
79 IpAddress PaS;
80 struct addrinfo *AI = NULL;
81 int crfd = -1;
82 int prfd = -1;
83 int cwfd = -1;
84 int pwfd = -1;
85 int fd;
86 int t1, t2, t3;
87 int x;
88
89 #if USE_POLL && defined(_SQUID_OSF_)
90
91 assert(type != IPC_FIFO);
92 #endif
93
94 if (rfd)
95 *rfd = -1;
96
97 if (wfd)
98 *wfd = -1;
99
100 if (hIpc)
101 *hIpc = NULL;
102
103 if (type == IPC_TCP_SOCKET) {
104 crfd = cwfd = comm_open(SOCK_STREAM,
105 0,
106 local_addr,
107 COMM_NOCLOEXEC,
108 name);
109 prfd = pwfd = comm_open(SOCK_STREAM,
110 0, /* protocol */
111 local_addr,
112 0, /* blocking */
113 name);
114 } else if (type == IPC_UDP_SOCKET) {
115 crfd = cwfd = comm_open(SOCK_DGRAM,
116 0,
117 local_addr,
118 COMM_NOCLOEXEC,
119 name);
120 prfd = pwfd = comm_open(SOCK_DGRAM,
121 0,
122 local_addr,
123 0,
124 name);
125 } else if (type == IPC_FIFO) {
126 int p2c[2];
127 int c2p[2];
128
129 if (pipe(p2c) < 0) {
130 debugs(54, 0, "ipcCreate: pipe: " << xstrerror());
131 return -1;
132 }
133
134 if (pipe(c2p) < 0) {
135 debugs(54, 0, "ipcCreate: pipe: " << xstrerror());
136 return -1;
137 }
138
139 fd_open(prfd = p2c[0], FD_PIPE, "IPC FIFO Parent Read");
140 fd_open(cwfd = p2c[1], FD_PIPE, "IPC FIFO Child Write");
141 fd_open(crfd = c2p[0], FD_PIPE, "IPC FIFO Child Read");
142 fd_open(pwfd = c2p[1], FD_PIPE, "IPC FIFO Parent Write");
143 #if HAVE_SOCKETPAIR && defined(AF_UNIX)
144
145 } else if (type == IPC_UNIX_STREAM) {
146 int fds[2];
147 int buflen = 32768;
148
149 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
150 debugs(54, 0, "ipcCreate: socketpair: " << xstrerror());
151 return -1;
152 }
153
154 setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
155 setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
156 setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
157 setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
158 fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX STREAM Parent");
159 fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX STREAM Parent");
160 } else if (type == IPC_UNIX_DGRAM) {
161 int fds[2];
162
163 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) < 0) {
164 debugs(54, 0, "ipcCreate: socketpair: " << xstrerror());
165 return -1;
166 }
167
168 fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX DGRAM Parent");
169 fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX DGRAM Parent");
170 #endif
171
172 } else {
173 assert(IPC_NONE);
174 }
175
176 debugs(54, 3, "ipcCreate: prfd FD " << prfd);
177 debugs(54, 3, "ipcCreate: pwfd FD " << pwfd);
178 debugs(54, 3, "ipcCreate: crfd FD " << crfd);
179 debugs(54, 3, "ipcCreate: cwfd FD " << cwfd);
180
181 if (crfd < 0) {
182 debugs(54, 0, "ipcCreate: Failed to create child FD.");
183 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
184 }
185
186 if (pwfd < 0) {
187 debugs(54, 0, "ipcCreate: Failed to create server FD.");
188 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
189 }
190
191 if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
192 PaS.InitAddrInfo(AI);
193
194 if (getsockname(pwfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
195 PaS.FreeAddrInfo(AI);
196 debugs(54, 0, "ipcCreate: getsockname: " << xstrerror());
197 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
198 }
199
200 PaS = *AI;
201
202 debugs(54, 3, "ipcCreate: FD " << pwfd << " sockaddr " << PaS);
203
204 PaS.FreeAddrInfo(AI);
205
206 ChS.InitAddrInfo(AI);
207
208 if (getsockname(crfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
209 ChS.FreeAddrInfo(AI);
210 debugs(54, 0, "ipcCreate: getsockname: " << xstrerror());
211 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
212 }
213
214 ChS = *AI;
215
216 ChS.FreeAddrInfo(AI);
217
218 debugs(54, 3, "ipcCreate: FD " << crfd << " sockaddr " << ChS );
219
220 }
221
222 if (type == IPC_TCP_SOCKET) {
223 if (listen(crfd, 1) < 0) {
224 debugs(54, 1, "ipcCreate: listen FD " << crfd << ": " << xstrerror());
225 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
226 }
227
228 debugs(54, 3, "ipcCreate: FD " << crfd << " listening...");
229 }
230
231 /* flush or else we get dup data if unbuffered_logs is set */
232 logsFlush();
233
234 if ((pid = fork()) < 0) {
235 debugs(54, 1, "ipcCreate: fork: " << xstrerror());
236 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
237 }
238
239 if (pid > 0) { /* parent */
240 /* close shared socket with child */
241 comm_close(crfd);
242
243 if (cwfd != crfd)
244 comm_close(cwfd);
245
246 cwfd = crfd = -1;
247
248 if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
249 if (comm_connect_addr(pwfd, ChS) == COMM_ERROR)
250 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
251 }
252
253 memset(hello_buf, '\0', HELLO_BUF_SZ);
254
255 if (type == IPC_UDP_SOCKET)
256 x = comm_udp_recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
257 else
258 x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
259
260 if (x < 0) {
261 debugs(54, 0, "ipcCreate: PARENT: hello read test failed");
262 debugs(54, 0, "--> read: " << xstrerror());
263 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
264 } else if (strcmp(hello_buf, hello_string)) {
265 debugs(54, 0, "ipcCreate: PARENT: hello read test failed");
266 debugs(54, 0, "--> read returned " << x);
267 debugs(54, 0, "--> got '" << rfc1738_escape(hello_buf) << "'");
268 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
269 }
270
271 commSetTimeout(prfd, -1, NULL, NULL);
272 commSetNonBlocking(prfd);
273 commSetNonBlocking(pwfd);
274
275 if (rfd)
276 *rfd = prfd;
277
278 if (wfd)
279 *wfd = pwfd;
280
281 fd_table[prfd].flags.ipc = 1;
282
283 fd_table[pwfd].flags.ipc = 1;
284
285 if (Config.sleep_after_fork) {
286 /* XXX emulation of usleep() */
287
288 struct timeval sl;
289 sl.tv_sec = Config.sleep_after_fork / 1000000;
290 sl.tv_usec = Config.sleep_after_fork % 1000000;
291 select(0, NULL, NULL, NULL, &sl);
292 }
293
294 return pid;
295 }
296
297 /* child */
298 no_suid(); /* give up extra priviliges */
299
300 /* close shared socket with parent */
301 close(prfd);
302
303 if (pwfd != prfd)
304 close(pwfd);
305
306 pwfd = prfd = -1;
307
308 if (type == IPC_TCP_SOCKET) {
309 debugs(54, 3, "ipcCreate: calling accept on FD " << crfd);
310
311 if ((fd = accept(crfd, NULL, NULL)) < 0) {
312 debugs(54, 0, "ipcCreate: FD " << crfd << " accept: " << xstrerror());
313 _exit(1);
314 }
315
316 debugs(54, 3, "ipcCreate: CHILD accepted new FD " << fd);
317 close(crfd);
318 cwfd = crfd = fd;
319 } else if (type == IPC_UDP_SOCKET) {
320 if (comm_connect_addr(crfd, PaS) == COMM_ERROR)
321 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
322 }
323
324 if (type == IPC_UDP_SOCKET) {
325 x = comm_udp_send(cwfd, hello_string, strlen(hello_string) + 1, 0);
326
327 if (x < 0) {
328 debugs(54, 0, "sendto FD " << cwfd << ": " << xstrerror());
329 debugs(54, 0, "ipcCreate: CHILD: hello write test failed");
330 _exit(1);
331 }
332 } else {
333 if (write(cwfd, hello_string, strlen(hello_string) + 1) < 0) {
334 debugs(54, 0, "write FD " << cwfd << ": " << xstrerror());
335 debugs(54, 0, "ipcCreate: CHILD: hello write test failed");
336 _exit(1);
337 }
338 }
339
340 PutEnvironment();
341 /*
342 * This double-dup stuff avoids problems when one of
343 * crfd, cwfd, or debug_log are in the rage 0-2.
344 */
345
346 do {
347 /* First make sure 0-2 is occupied by something. Gets cleaned up later */
348 x = dup(crfd);
349 assert(x > -1);
350 } while (x < 3 && x > -1);
351
352 close(x);
353
354 t1 = dup(crfd);
355
356 t2 = dup(cwfd);
357
358 t3 = dup(fileno(debug_log));
359
360 assert(t1 > 2 && t2 > 2 && t3 > 2);
361
362 close(crfd);
363
364 close(cwfd);
365
366 close(fileno(debug_log));
367
368 dup2(t1, 0);
369
370 dup2(t2, 1);
371
372 dup2(t3, 2);
373
374 close(t1);
375
376 close(t2);
377
378 close(t3);
379
380 /* Make sure all other filedescriptors are closed */
381 for (x = 3; x < SQUID_MAXFD; x++)
382 close(x);
383
384 #if HAVE_SETSID
385 if (opt_no_daemon)
386 setsid();
387 #endif
388
389 execvp(prog, (char *const *) args);
390
391 debug_log = fdopen(2, "a+");
392
393 debugs(54, 0, "ipcCreate: " << prog << ": " << xstrerror());
394
395 _exit(1);
396
397 return 0;
398 }