]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc.cc
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 54 Interprocess Communication */
12 #include "comm/Connection.h"
16 #include "ip/Address.h"
19 #include "SquidConfig.h"
31 static const char *hello_string
= "hi there\n";
33 #define HELLO_BUF_SZ 32
35 static char hello_buf
[HELLO_BUF_SZ
];
38 ipcCloseAllFD(int prfd
, int pwfd
, int crfd
, int cwfd
)
63 env_str
= (char *)xcalloc((tmp_s
= strlen(Debug::debugOptions
) + 32), 1);
64 snprintf(env_str
, tmp_s
, "SQUID_DEBUG=%s", Debug::debugOptions
);
70 ipcCreate(int type
, const char *prog
, const char *const args
[], const char *name
, Ip::Address
&local_addr
, int *rfd
, int *wfd
, void **hIpc
)
75 struct addrinfo
*AI
= nullptr;
85 #if USE_POLL && _SQUID_OSF_
86 assert(type
!= IPC_FIFO
);
98 // NP: no wrapping around d and c usage since we *want* code expansion
99 #define IPC_CHECK_FAIL(f,d,c) \
101 debugs(54, DBG_CRITICAL, "ERROR: Failed to create helper " d " FD: " << c); \
102 return ipcCloseAllFD(prfd, pwfd, crfd, cwfd); \
105 if (type
== IPC_TCP_SOCKET
) {
106 crfd
= cwfd
= comm_open_listener(SOCK_STREAM
,
111 prfd
= pwfd
= comm_open(SOCK_STREAM
,
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
,
124 prfd
= pwfd
= comm_open(SOCK_DGRAM
,
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
) {
137 debugs(54, DBG_CRITICAL
, "ipcCreate: pipe: " << xstrerr(xerrno
));
138 return -1; // maybe ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
140 fd_open(prfd
= p2c
[0], FD_PIPE
, "IPC FIFO Parent Read");
141 fd_open(cwfd
= p2c
[1], FD_PIPE
, "IPC FIFO Child Write");
145 debugs(54, DBG_CRITICAL
, "ipcCreate: pipe: " << xstrerr(xerrno
));
146 return ipcCloseAllFD(prfd
, pwfd
, crfd
, cwfd
);
148 fd_open(crfd
= c2p
[0], FD_PIPE
, "IPC FIFO Child Read");
149 fd_open(pwfd
= c2p
[1], FD_PIPE
, "IPC FIFO Parent Write");
151 IPC_CHECK_FAIL(crfd
, "child read", "FIFO pipe");
152 IPC_CHECK_FAIL(prfd
, "parent read", "FIFO pipe");
154 #if HAVE_SOCKETPAIR && defined(AF_UNIX)
156 } else if (type
== IPC_UNIX_STREAM
) {
160 if (socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
) < 0) {
162 debugs(54, DBG_CRITICAL
, "ipcCreate: socketpair: " << xstrerr(xerrno
));
167 if (setsockopt(fds
[0], SOL_SOCKET
, SO_SNDBUF
, (void *) &buflen
, sizeof(buflen
)) == -1) {
169 debugs(54, DBG_IMPORTANT
, "ERROR: setsockopt failed: " << xstrerr(xerrno
));
172 if (setsockopt(fds
[0], SOL_SOCKET
, SO_RCVBUF
, (void *) &buflen
, sizeof(buflen
)) == -1) {
174 debugs(54, DBG_IMPORTANT
, "ERROR: setsockopt failed: " << xstrerr(xerrno
));
177 if (setsockopt(fds
[1], SOL_SOCKET
, SO_SNDBUF
, (void *) &buflen
, sizeof(buflen
)) == -1) {
179 debugs(54, DBG_IMPORTANT
, "ERROR: setsockopt failed: " << xstrerr(xerrno
));
182 if (setsockopt(fds
[1], SOL_SOCKET
, SO_RCVBUF
, (void *) &buflen
, sizeof(buflen
)) == -1) {
184 debugs(54, DBG_IMPORTANT
, "ERROR: setsockopt failed: " << xstrerr(xerrno
));
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");
192 } else if (type
== IPC_UNIX_DGRAM
) {
195 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, fds
) < 0) {
197 debugs(54, DBG_CRITICAL
, "ipcCreate: socketpair: " << xstrerr(xerrno
));
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");
204 IPC_CHECK_FAIL(crfd
, "child read", "UDS datagram");
205 IPC_CHECK_FAIL(prfd
, "parent read", "UDS datagram");
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
);
217 if (type
== IPC_TCP_SOCKET
|| type
== IPC_UDP_SOCKET
) {
218 Ip::Address::InitAddr(AI
);
220 if (getsockname(pwfd
, AI
->ai_addr
, &AI
->ai_addrlen
) < 0) {
222 Ip::Address::FreeAddr(AI
);
223 debugs(54, DBG_CRITICAL
, "ipcCreate: getsockname: " << xstrerr(xerrno
));
224 return ipcCloseAllFD(prfd
, pwfd
, crfd
, cwfd
);
229 debugs(54, 3, "ipcCreate: FD " << pwfd
<< " sockaddr " << PaS
);
231 Ip::Address::FreeAddr(AI
);
233 Ip::Address::InitAddr(AI
);
235 if (getsockname(crfd
, AI
->ai_addr
, &AI
->ai_addrlen
) < 0) {
237 Ip::Address::FreeAddr(AI
);
238 debugs(54, DBG_CRITICAL
, "ipcCreate: getsockname: " << xstrerr(xerrno
));
239 return ipcCloseAllFD(prfd
, pwfd
, crfd
, cwfd
);
244 Ip::Address::FreeAddr(AI
);
246 debugs(54, 3, "ipcCreate: FD " << crfd
<< " sockaddr " << ChS
);
250 if (type
== IPC_TCP_SOCKET
) {
251 if (listen(crfd
, 1) < 0) {
253 debugs(54, DBG_IMPORTANT
, "ipcCreate: listen FD " << crfd
<< ": " << xstrerr(xerrno
));
254 return ipcCloseAllFD(prfd
, pwfd
, crfd
, cwfd
);
257 debugs(54, 3, "ipcCreate: FD " << crfd
<< " listening...");
260 /* flush or else we get dup data if unbuffered_logs is set */
263 if ((pid
= fork()) < 0) {
265 debugs(54, DBG_IMPORTANT
, "ipcCreate: fork: " << xstrerr(xerrno
));
266 return ipcCloseAllFD(prfd
, pwfd
, crfd
, cwfd
);
269 if (pid
> 0) { /* parent */
270 /* close shared socket with child */
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
);
283 if (type
== IPC_UDP_SOCKET
)
284 x
= comm_udp_recv(prfd
, hello_buf
, sizeof(hello_buf
)-1, 0);
286 x
= read(prfd
, hello_buf
, sizeof(hello_buf
)-1);
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
);
302 commUnsetFdTimeout(prfd
);
303 commSetNonBlocking(prfd
);
304 commSetNonBlocking(pwfd
);
312 fd_table
[prfd
].flags
.ipc
= 1;
314 fd_table
[pwfd
].flags
.ipc
= 1;
316 if (Config
.sleep_after_fork
)
317 std::this_thread::sleep_for(std::chrono::microseconds(Config
.sleep_after_fork
));
323 TheProcessKind
= pkHelper
;
324 no_suid(); /* give up extra privileges */
326 /* close shared socket with parent */
334 if (type
== IPC_TCP_SOCKET
) {
335 debugs(54, 3, "ipcCreate: calling accept on FD " << crfd
);
337 if ((fd
= accept(crfd
, nullptr, nullptr)) < 0) {
339 debugs(54, DBG_CRITICAL
, "ipcCreate: FD " << crfd
<< " accept: " << xstrerr(xerrno
));
343 debugs(54, 3, "ipcCreate: CHILD accepted new FD " << 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
);
351 if (type
== IPC_UDP_SOCKET
) {
352 x
= comm_udp_send(cwfd
, hello_string
, strlen(hello_string
) + 1, 0);
356 debugs(54, DBG_CRITICAL
, "sendto FD " << cwfd
<< ": " << xstrerr(xerrno
));
357 debugs(54, DBG_CRITICAL
, "ERROR: ipcCreate: CHILD: hello write test failed");
361 if (write(cwfd
, hello_string
, strlen(hello_string
) + 1) < 0) {
363 debugs(54, DBG_CRITICAL
, "write FD " << cwfd
<< ": " << xstrerr(xerrno
));
364 debugs(54, DBG_CRITICAL
, "ERROR: ipcCreate: CHILD: hello write test failed");
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
);
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
));
387 * This double-dup stuff avoids problems when one of
388 * crfd, cwfd, or debug_log are in the rage 0-2.
392 /* First make sure 0-2 is occupied by something. Gets cleaned up later */
398 t1
= dupOrExit(crfd
);
400 t2
= dupOrExit(cwfd
);
402 t3
= dupOrExit(fileno(debug_log
));
404 assert(t1
> 2 && t2
> 2 && t3
> 2);
410 close(fileno(debug_log
));
424 /* Make sure all other filedescriptors are closed */
425 for (x
= 3; x
< SQUID_MAXFD
; ++x
)
433 execvp(prog
, (char *const *) args
);
436 ResyncDebugLog(fdopen(2, "a+"));
438 debugs(54, DBG_CRITICAL
, "ipcCreate: " << prog
<< ": " << xstrerr(xerrno
));