]>
Commit | Line | Data |
---|---|---|
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 "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 (size_t 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 |