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

 

Introduction

Documentation

Support

Miscellaneous