Squid Web Cache master
Loading...
Searching...
No Matches
unlinkd.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 02 Unlink Daemon */
10
11#include "squid.h"
12
13#if USE_UNLINKD
14#include "compat/select.h"
15#include "compat/unistd.h"
16#include "fd.h"
17#include "fde.h"
18#include "fs_io.h"
19#include "globals.h"
20#include "SquidConfig.h"
21#include "SquidIpc.h"
22#include "StatCounters.h"
23#include "store/Disk.h"
24#include "tools.h"
25#include "unlinkd.h"
26
27#include <chrono>
28#include <thread>
29
30/* This code gets linked to Squid */
31
32static int unlinkd_wfd = -1;
33static int unlinkd_rfd = -1;
34
35static void * hIpc;
36static pid_t pid;
37
38#define UNLINKD_QUEUE_LIMIT 20
39
40void
41unlinkdUnlink(const char *path)
42{
43 char buf[MAXPATHLEN];
44 int l;
45 static int queuelen = 0;
46
47 if (unlinkd_wfd < 0) {
48 debug_trap("unlinkdUnlink: unlinkd_wfd < 0");
49 safeunlink(path, 0);
50 return;
51 }
52
53 /*
54 * If the queue length is greater than our limit, then we pause
55 * for a small amount of time, hoping that unlinkd has some
56 * feedback for us. Maybe it just needs a slice of the CPU's
57 * time.
58 */
59 if (queuelen >= UNLINKD_QUEUE_LIMIT) {
60#if defined(USE_EPOLL) || defined(USE_KQUEUE) || defined(USE_DEVPOLL)
61 /*
62 * DPW 2007-04-23
63 * We can't use fd_set when using epoll() or kqueue(). In
64 * these cases we block for 10 ms.
65 */
66 std::this_thread::sleep_for(std::chrono::milliseconds(10));
67#else
68 /*
69 * DPW 2007-04-23
70 * When we can use select, block for up to 100 ms.
71 */
72 struct timeval to;
73 fd_set R;
74 FD_ZERO(&R);
75 FD_SET(unlinkd_rfd, &R);
76 to.tv_sec = 0;
77 to.tv_usec = 100000;
78 xselect(unlinkd_rfd + 1, &R, nullptr, nullptr, &to);
79#endif
80 }
81
82 /*
83 * If there is at least one outstanding unlink request, then
84 * try to read a response. If there's nothing to read we'll
85 * get an EWOULDBLOCK or whatever. If we get a response, then
86 * decrement the queue size by the number of newlines read.
87 */
88 if (queuelen > 0) {
89 int i;
90 char rbuf[512];
91 const auto bytes_read = xread(unlinkd_rfd, rbuf, 511);
92
93 if (bytes_read > 0) {
94 rbuf[bytes_read] = '\0';
95
96 for (i = 0; i < bytes_read; ++i)
97 if ('\n' == rbuf[i])
98 --queuelen;
99
100 assert(queuelen >= 0);
101 }
102 }
103
104 l = strlen(path);
105 assert(l < MAXPATHLEN);
106 xstrncpy(buf, path, MAXPATHLEN);
107 buf[l] = '\n';
108 ++l;
109 const auto bytes_written = xwrite(unlinkd_wfd, buf, l);
110
111 if (bytes_written < 0) {
112 int xerrno = errno;
113 debugs(2, DBG_IMPORTANT, "ERROR: unlinkdUnlink: write FD " << unlinkd_wfd << " failed: " << xstrerr(xerrno));
114 safeunlink(path, 0);
115 return;
116 } else if (bytes_written != l) {
117 debugs(2, DBG_IMPORTANT, "unlinkdUnlink: FD " << unlinkd_wfd << " only wrote " << bytes_written << " of " << l << " bytes");
118 safeunlink(path, 0);
119 return;
120 }
121
123 /*
124 * Increment this syscalls counter here, even though the syscall
125 * is executed by the helper process. We try to be consistent
126 * in counting unlink operations.
127 */
129 ++queuelen;
130}
131
132void
134#if _SQUID_WINDOWS_
135{
136
137 if (unlinkd_wfd > -1) {
138 debugs(2, DBG_IMPORTANT, "Closing unlinkd pipe on FD " << unlinkd_wfd);
139 shutdown(unlinkd_wfd, SD_BOTH);
141
144
145 unlinkd_wfd = -1;
146
147 unlinkd_rfd = -1;
148 }
149
150 if (hIpc) {
151 if (WaitForSingleObject(hIpc, 5000) != WAIT_OBJECT_0) {
153 debugs(2, DBG_IMPORTANT, "WARNING: unlinkdClose: (unlinkd," << pid << "d) didn't exit in 5 seconds");
154 }
155
156 CloseHandle(hIpc);
157 }
158}
159#else
160{
161
162 if (unlinkd_wfd < 0)
163 return;
164
165 debugs(2, DBG_IMPORTANT, "Closing unlinkd pipe on FD " << unlinkd_wfd);
166
168
171
172 unlinkd_wfd = -1;
173
174 unlinkd_rfd = -1;
175}
176
177#endif
178
179bool
181{
182 // we should start unlinkd if there are any cache_dirs using it
183 for (size_t i = 0; i < Config.cacheSwap.n_configured; ++i) {
185 if (sd->unlinkdUseful())
186 return true;
187 }
188
189 return false;
190}
191
192void
194{
195 if (unlinkd_wfd >= 0)
196 return; // unlinkd already started
197
198 const char *args[2];
199 Ip::Address localhost;
200
201 args[0] = "(unlinkd)";
202 args[1] = nullptr;
203 localhost.setLocalhost();
204
205 pid = ipcCreate(
206#if USE_POLL && _SQUID_OSF_
207 /* pipes and poll() don't get along on DUNIX -DW */
209#elif _SQUID_WINDOWS_
210 /* select() will fail on a pipe */
212#else
213 /* We currently need to use FIFO.. see below */
214 IPC_FIFO,
215#endif
217 args,
218 "unlinkd",
219 localhost,
222 &hIpc);
223
224 if (pid < 0)
225 fatal("Failed to create unlinkd subprocess");
226
227 std::this_thread::sleep_for(std::chrono::milliseconds(250));
228
229 fd_note(unlinkd_wfd, "squid -> unlinkd");
230
231 fd_note(unlinkd_rfd, "unlinkd -> squid");
232
235
236 /*
237 * unlinkd_rfd should already be non-blocking because of
238 * ipcCreate. We change unlinkd_wfd to blocking mode because
239 * we never want to lose an unlink request, and we don't have
240 * code to retry if we get EWOULDBLOCK. Unfortunately, we can
241 * do this only for the IPC_FIFO case.
242 */
243 assert(fd_table[unlinkd_rfd].flags.nonblocking);
244
245 if (FD_PIPE == fd_table[unlinkd_wfd].type)
247
248 debugs(2, DBG_IMPORTANT, "Unlinkd pipe opened on FD " << unlinkd_wfd);
249
250#if _SQUID_WINDOWS_
251
252 debugs(2, 4, "Unlinkd handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid);
253
254#endif
255
256}
257#endif /* USE_UNLINKD */
258
class SquidConfig Config
StatCounters statCounter
#define assert(EX)
Definition assert.h:17
#define USE_POLL
Definition autoconf.h:1560
void setLocalhost()
Definition Address.cc:275
struct SquidConfig::@83 Program
char * unlinkd
Store::DiskConfig cacheSwap
struct StatCounters::@112 syscalls
struct StatCounters::@112::@116 disk
struct StatCounters::@108 unlink
RefCount< SwapDir > * swapDirs
Definition SquidConfig.h:68
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
Definition comm.cc:581
int commUnsetNonBlocking(int fd)
Definition comm.cc:1077
#define comm_close(x)
Definition comm.h:36
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
#define IPC_STREAM
Definition defines.h:104
#define IPC_FIFO
Definition defines.h:91
#define IPC_TCP_SOCKET
Definition defines.h:89
@ FD_PIPE
Definition enums.h:17
void fatal(const char *message)
Definition fatal.cc:28
void fd_note(int fd, const char *s)
Definition fd.cc:211
#define fd_table
Definition fde.h:189
void safeunlink(const char *s, int quiet)
Definition fs_io.cc:432
void file_close(int fd)
Definition fs_io.cc:92
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:63
int xselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
POSIX select(2) equivalent.
Definition select.h:22
#define MAXPATHLEN
Definition stdio.h:62
time_t getCurrentTime() STUB_RETVAL(0) int tvSubUsec(struct timeval
void debug_trap(const char *message)
Definition tools.cc:459
int xread(int fd, void *buf, size_t bufSize)
POSIX read(2) equivalent.
Definition unistd.h:61
int xwrite(int fd, const void *buf, size_t bufSize)
POSIX write(2) equivalent.
Definition unistd.h:67
void unlinkdUnlink(const char *path)
Definition unlinkd.cc:41
bool unlinkdNeeded(void)
Definition unlinkd.cc:180
void unlinkdClose(void)
Definition unlinkd.cc:133
void unlinkdInit(void)
Definition unlinkd.cc:193
static void * hIpc
Definition unlinkd.cc:35
static int unlinkd_rfd
Definition unlinkd.cc:33
static pid_t pid
Definition unlinkd.cc:36
static int unlinkd_wfd
Definition unlinkd.cc:32
#define UNLINKD_QUEUE_LIMIT
Definition unlinkd.cc:38
const char * xstrerr(int error)
Definition xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition xstring.cc:37