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