]> git.ipfire.org Git - thirdparty/squid.git/blob - src/unlinkd.cc
Maintenance: replace most NULL with nullptr (#1402)
[thirdparty/squid.git] / src / unlinkd.cc
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 02 Unlink Daemon */
10
11 #include "squid.h"
12
13 #if USE_UNLINKD
14 #include "fd.h"
15 #include "fde.h"
16 #include "fs_io.h"
17 #include "globals.h"
18 #include "SquidConfig.h"
19 #include "SquidIpc.h"
20 #include "StatCounters.h"
21 #include "store/Disk.h"
22 #include "tools.h"
23 #include "unlinkd.h"
24
25 #include <chrono>
26 #include <thread>
27
28 /* This code gets linked to Squid */
29
30 static int unlinkd_wfd = -1;
31 static int unlinkd_rfd = -1;
32
33 static void * hIpc;
34 static pid_t pid;
35
36 #define UNLINKD_QUEUE_LIMIT 20
37
38 void
39 unlinkdUnlink(const char *path)
40 {
41 char buf[MAXPATHLEN];
42 int l;
43 int bytes_written;
44 static int queuelen = 0;
45
46 if (unlinkd_wfd < 0) {
47 debug_trap("unlinkdUnlink: unlinkd_wfd < 0");
48 safeunlink(path, 0);
49 return;
50 }
51
52 /*
53 * If the queue length is greater than our limit, then we pause
54 * for a small amount of time, hoping that unlinkd has some
55 * feedback for us. Maybe it just needs a slice of the CPU's
56 * time.
57 */
58 if (queuelen >= UNLINKD_QUEUE_LIMIT) {
59 #if defined(USE_EPOLL) || defined(USE_KQUEUE) || defined(USE_DEVPOLL)
60 /*
61 * DPW 2007-04-23
62 * We can't use fd_set when using epoll() or kqueue(). In
63 * these cases we block for 10 ms.
64 */
65 std::this_thread::sleep_for(std::chrono::milliseconds(10));
66 #else
67 /*
68 * DPW 2007-04-23
69 * When we can use select, block for up to 100 ms.
70 */
71 struct timeval to;
72 fd_set R;
73 FD_ZERO(&R);
74 FD_SET(unlinkd_rfd, &R);
75 to.tv_sec = 0;
76 to.tv_usec = 100000;
77 select(unlinkd_rfd + 1, &R, nullptr, nullptr, &to);
78 #endif
79 }
80
81 /*
82 * If there is at least one outstanding unlink request, then
83 * try to read a response. If there's nothing to read we'll
84 * get an EWOULDBLOCK or whatever. If we get a response, then
85 * decrement the queue size by the number of newlines read.
86 */
87 if (queuelen > 0) {
88 int bytes_read;
89 int i;
90 char rbuf[512];
91 bytes_read = read(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 bytes_written = write(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
122 ++statCounter.unlink.requests;
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 */
128 ++statCounter.syscalls.disk.unlinks;
129 ++queuelen;
130 }
131
132 void
133 unlinkdClose(void)
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);
140 comm_close(unlinkd_wfd);
141
142 if (unlinkd_wfd != unlinkd_rfd)
143 comm_close(unlinkd_rfd);
144
145 unlinkd_wfd = -1;
146
147 unlinkd_rfd = -1;
148 }
149
150 if (hIpc) {
151 if (WaitForSingleObject(hIpc, 5000) != WAIT_OBJECT_0) {
152 getCurrentTime();
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
167 file_close(unlinkd_wfd);
168
169 if (unlinkd_wfd != unlinkd_rfd)
170 file_close(unlinkd_rfd);
171
172 unlinkd_wfd = -1;
173
174 unlinkd_rfd = -1;
175 }
176
177 #endif
178
179 bool
180 unlinkdNeeded(void)
181 {
182 // we should start unlinkd if there are any cache_dirs using it
183 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
184 const RefCount<SwapDir> sd = Config.cacheSwap.swapDirs[i];
185 if (sd->unlinkdUseful())
186 return true;
187 }
188
189 return false;
190 }
191
192 void
193 unlinkdInit(void)
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 */
208 IPC_STREAM,
209 #elif _SQUID_WINDOWS_
210 /* select() will fail on a pipe */
211 IPC_TCP_SOCKET,
212 #else
213 /* We currently need to use FIFO.. see below */
214 IPC_FIFO,
215 #endif
216 Config.Program.unlinkd,
217 args,
218 "unlinkd",
219 localhost,
220 &unlinkd_rfd,
221 &unlinkd_wfd,
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
233 commUnsetFdTimeout(unlinkd_rfd);
234 commUnsetFdTimeout(unlinkd_wfd);
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)
246 commUnsetNonBlocking(unlinkd_wfd);
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