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