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