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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors