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