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