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

 

Introduction

Documentation

Support

Miscellaneous