ipc.cc
Go to the documentation of this file.
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
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,
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,
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) {
219 
220  if (getsockname(pwfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
221  xerrno = errno;
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 
232 
234 
235  if (getsockname(crfd, AI->ai_addr, &AI->ai_addrlen) < 0) {
236  xerrno = errno;
238  debugs(54, DBG_CRITICAL, "ipcCreate: getsockname: " << xstrerr(xerrno));
239  return ipcCloseAllFD(prfd, pwfd, crfd, cwfd);
240  }
241 
242  ChS = *AI;
243 
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 
317  std::this_thread::sleep_for(std::chrono::microseconds(Config.sleep_after_fork));
318 
319  return pid;
320  }
321 
322  /* child */
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 DebugStream() 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(DebugStream()));
403 
404  assert(t1 > 2 && t2 > 2 && t3 > 2);
405 
406  close(crfd);
407 
408  close(cwfd);
409 
410  close(fileno(DebugStream()));
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 
@ 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:58
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:33
static int ipcCloseAllFD(int prfd, int pwfd, int crfd, int cwfd)
Definition: ipc.cc:38
int comm_udp_recv(int fd, void *buf, size_t len, int flags)
Definition: comm.cc:139
int commSetNonBlocking(int fd)
Definition: comm.cc:1054
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:70
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:31
#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:35
#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