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