]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc.cc
Merge from trunk
[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/Address.h"
37 #include "rfc1738.h"
38
39 static const char *hello_string = "hi there\n";
40 #define HELLO_BUF_SZ 32
41 static char hello_buf[HELLO_BUF_SZ];
42
43 static int
44 ipcCloseAllFD(int prfd, int pwfd, int crfd, int cwfd)
45 {
46 if (prfd >= 0)
47 comm_close(prfd);
48
49 if (prfd != pwfd)
50 if (pwfd >= 0)
51 comm_close(pwfd);
52
53 if (crfd >= 0)
54 comm_close(crfd);
55
56 if (crfd != cwfd)
57 if (cwfd >= 0)
58 comm_close(cwfd);
59
60 return -1;
61 }
62
63 static void
64 PutEnvironment()
65 {
66 #if HAVE_PUTENV
67 char *env_str;
68 int tmp_s;
69 env_str = (char *)xcalloc((tmp_s = strlen(Debug::debugOptions) + 32), 1);
70 snprintf(env_str, tmp_s, "SQUID_DEBUG=%s", Debug::debugOptions);
71 putenv(env_str);
72 #endif
73 }
74
75 pid_t
76 ipcCreate(int type, const char *prog, const char *const args[], const char *name, Ip::Address &local_addr, int *rfd, int *wfd, void **hIpc)
77 {
78 pid_t pid;
79 Ip::Address ChS;
80 Ip::Address PaS;
81 struct addrinfo *AI = NULL;
82 int crfd = -1;
83 int prfd = -1;
84 int cwfd = -1;
85 int pwfd = -1;
86 int fd;
87 int t1, t2, t3;
88 int x;
89
90 #if USE_POLL && defined(_SQUID_OSF_)
91
92 assert(type != IPC_FIFO);
93 #endif
94
95 if (rfd)
96 *rfd = -1;
97
98 if (wfd)
99 *wfd = -1;
100
101 if (hIpc)
102 *hIpc = NULL;
103
104 if (type == IPC_TCP_SOCKET) {
105 crfd = cwfd = comm_open(SOCK_STREAM,
106 0,
107 local_addr,
108 COMM_NOCLOEXEC,
109 name);
110 prfd = pwfd = comm_open(SOCK_STREAM,
111 0, /* protocol */
112 local_addr,
113 0, /* blocking */
114 name);
115 } else if (type == IPC_UDP_SOCKET) {
116 crfd = cwfd = comm_open(SOCK_DGRAM,
117 0,
118 local_addr,
119 COMM_NOCLOEXEC,
120 name);
121 prfd = pwfd = comm_open(SOCK_DGRAM,
122 0,
123 local_addr,
124 0,
125 name);
126 } else if (type == IPC_FIFO) {
127 int p2c[2];
128 int c2p[2];
129
130 if (pipe(p2c) < 0) {
131 debugs(54, 0, "ipcCreate: pipe: " << xstrerror());
132 return -1;
133 }
134
135 if (pipe(c2p) < 0) {
136 debugs(54, 0, "ipcCreate: pipe: " << xstrerror());
137 return -1;
138 }
139
140 fd_open(prfd = p2c[0], FD_PIPE, "IPC FIFO Parent Read");
141 fd_open(cwfd = p2c[1], FD_PIPE, "IPC FIFO Child Write");
142 fd_open(crfd = c2p[0], FD_PIPE, "IPC FIFO Child Read");
143 fd_open(pwfd = c2p[1], FD_PIPE, "IPC FIFO Parent Write");
144 #if HAVE_SOCKETPAIR && defined(AF_UNIX)
145
146 } else if (type == IPC_UNIX_STREAM) {
147 int fds[2];
148 int buflen = 32768;
149
150 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
151 debugs(54, 0, "ipcCreate: socketpair: " << xstrerror());
152 return -1;
153 }
154
155 setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
156 setsockopt(fds[0], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
157 setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, (void *) &buflen, sizeof(buflen));
158 setsockopt(fds[1], SOL_SOCKET, SO_RCVBUF, (void *) &buflen, sizeof(buflen));
159 fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX STREAM Parent");
160 fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX STREAM Parent");
161 } else if (type == IPC_UNIX_DGRAM) {
162 int fds[2];
163
164 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) < 0) {
165 debugs(54, 0, "ipcCreate: socketpair: " << xstrerror());
166 return -1;
167 }
168
169 fd_open(prfd = pwfd = fds[0], FD_PIPE, "IPC UNIX DGRAM Parent");
170 fd_open(crfd = cwfd = fds[1], FD_PIPE, "IPC UNIX DGRAM Parent");
171 #endif
172
173 } else {
174 assert(IPC_NONE);
175 }
176
177 debugs(54, 3, "ipcCreate: prfd FD " << prfd);
178 debugs(54, 3, "ipcCreate: pwfd FD " << pwfd);
179 debugs(54, 3, "ipcCreate: crfd FD " << crfd);
180 debugs(54, 3, "ipcCreate: cwfd FD " << cwfd);
181
182 if (crfd < 0) {
183 debugs(54, 0, "ipcCreate: Failed to create child FD.");
184 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
185 }
186
187 if (pwfd < 0) {
188 debugs(54, 0, "ipcCreate: Failed to create server FD.");
189 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
190 }
191
192 if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
193 PaS.InitAddrInfo(AI);
194
195 if (getsockname(pwfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
196 PaS.FreeAddrInfo(AI);
197 debugs(54, 0, "ipcCreate: getsockname: " << xstrerror());
198 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
199 }
200
201 PaS = *AI;
202
203 debugs(54, 3, "ipcCreate: FD " << pwfd << " sockaddr " << PaS);
204
205 PaS.FreeAddrInfo(AI);
206
207 ChS.InitAddrInfo(AI);
208
209 if (getsockname(crfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
210 ChS.FreeAddrInfo(AI);
211 debugs(54, 0, "ipcCreate: getsockname: " << xstrerror());
212 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
213 }
214
215 ChS = *AI;
216
217 ChS.FreeAddrInfo(AI);
218
219 debugs(54, 3, "ipcCreate: FD " << crfd << " sockaddr " << ChS );
220
221 }
222
223 if (type == IPC_TCP_SOCKET) {
224 if (listen(crfd, 1) < 0) {
225 debugs(54, 1, "ipcCreate: listen FD " << crfd << ": " << xstrerror());
226 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
227 }
228
229 debugs(54, 3, "ipcCreate: FD " << crfd << " listening...");
230 }
231
232 /* flush or else we get dup data if unbuffered_logs is set */
233 logsFlush();
234
235 if ((pid = fork()) < 0) {
236 debugs(54, 1, "ipcCreate: fork: " << xstrerror());
237 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
238 }
239
240 if (pid > 0) { /* parent */
241 /* close shared socket with child */
242 comm_close(crfd);
243
244 if (cwfd != crfd)
245 comm_close(cwfd);
246
247 cwfd = crfd = -1;
248
249 if (type == IPC_TCP_SOCKET || type == IPC_UDP_SOCKET) {
250 if (comm_connect_addr(pwfd, ChS) == COMM_ERROR)
251 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
252 }
253
254 memset(hello_buf, '\0', HELLO_BUF_SZ);
255
256 if (type == IPC_UDP_SOCKET)
257 x = comm_udp_recv(prfd, hello_buf, HELLO_BUF_SZ - 1, 0);
258 else
259 x = read(prfd, hello_buf, HELLO_BUF_SZ - 1);
260
261 if (x < 0) {
262 debugs(54, 0, "ipcCreate: PARENT: hello read test failed");
263 debugs(54, 0, "--> read: " << xstrerror());
264 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
265 } else if (strcmp(hello_buf, hello_string)) {
266 debugs(54, 0, "ipcCreate: PARENT: hello read test failed");
267 debugs(54, 0, "--> read returned " << x);
268 debugs(54, 0, "--> got '" << rfc1738_escape(hello_buf) << "'");
269 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
270 }
271
272 commSetTimeout(prfd, -1, NULL, NULL);
273 commSetNonBlocking(prfd);
274 commSetNonBlocking(pwfd);
275
276 if (rfd)
277 *rfd = prfd;
278
279 if (wfd)
280 *wfd = pwfd;
281
282 fd_table[prfd].flags.ipc = 1;
283
284 fd_table[pwfd].flags.ipc = 1;
285
286 if (Config.sleep_after_fork) {
287 /* XXX emulation of usleep() */
288
289 struct timeval sl;
290 sl.tv_sec = Config.sleep_after_fork / 1000000;
291 sl.tv_usec = Config.sleep_after_fork % 1000000;
292 select(0, NULL, NULL, NULL, &sl);
293 }
294
295 return pid;
296 }
297
298 /* child */
299 no_suid(); /* give up extra priviliges */
300
301 /* close shared socket with parent */
302 close(prfd);
303
304 if (pwfd != prfd)
305 close(pwfd);
306
307 pwfd = prfd = -1;
308
309 if (type == IPC_TCP_SOCKET) {
310 debugs(54, 3, "ipcCreate: calling accept on FD " << crfd);
311
312 if ((fd = accept(crfd, NULL, NULL)) < 0) {
313 debugs(54, 0, "ipcCreate: FD " << crfd << " accept: " << xstrerror());
314 _exit(1);
315 }
316
317 debugs(54, 3, "ipcCreate: CHILD accepted new FD " << fd);
318 close(crfd);
319 cwfd = crfd = fd;
320 } else if (type == IPC_UDP_SOCKET) {
321 if (comm_connect_addr(crfd, PaS) == COMM_ERROR)
322 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
323 }
324
325 if (type == IPC_UDP_SOCKET) {
326 x = comm_udp_send(cwfd, hello_string, strlen(hello_string) + 1, 0);
327
328 if (x < 0) {
329 debugs(54, 0, "sendto FD " << cwfd << ": " << xstrerror());
330 debugs(54, 0, "ipcCreate: CHILD: hello write test failed");
331 _exit(1);
332 }
333 } else {
334 if (write(cwfd, hello_string, strlen(hello_string) + 1) < 0) {
335 debugs(54, 0, "write FD " << cwfd << ": " << xstrerror());
336 debugs(54, 0, "ipcCreate: CHILD: hello write test failed");
337 _exit(1);
338 }
339 }
340
341 PutEnvironment();
342 /*
343 * This double-dup stuff avoids problems when one of
344 * crfd, cwfd, or debug_log are in the rage 0-2.
345 */
346
347 do {
348 /* First make sure 0-2 is occupied by something. Gets cleaned up later */
349 x = dup(crfd);
350 assert(x > -1);
351 } while (x < 3 && x > -1);
352
353 close(x);
354
355 t1 = dup(crfd);
356
357 t2 = dup(cwfd);
358
359 t3 = dup(fileno(debug_log));
360
361 assert(t1 > 2 && t2 > 2 && t3 > 2);
362
363 close(crfd);
364
365 close(cwfd);
366
367 close(fileno(debug_log));
368
369 dup2(t1, 0);
370
371 dup2(t2, 1);
372
373 dup2(t3, 2);
374
375 close(t1);
376
377 close(t2);
378
379 close(t3);
380
381 /* Make sure all other filedescriptors are closed */
382 for (x = 3; x < SQUID_MAXFD; x++)
383 close(x);
384
385 #if HAVE_SETSID
386 if (opt_no_daemon)
387 setsid();
388 #endif
389
390 execvp(prog, (char *const *) args);
391
392 debug_log = fdopen(2, "a+");
393
394 debugs(54, 0, "ipcCreate: " << prog << ": " << xstrerror());
395
396 _exit(1);
397
398 return 0;
399 }