]> git.ipfire.org Git - thirdparty/squid.git/blob - src/unlinkd.cc
Merged from trunk (r13356).
[thirdparty/squid.git] / src / unlinkd.cc
1 /*
2 * DEBUG: section 02 Unlink Daemon
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34
35 #if USE_UNLINKD
36 #include "disk.h"
37 #include "fd.h"
38 #include "fde.h"
39 #include "globals.h"
40 #include "SquidIpc.h"
41 #include "SquidTime.h"
42 #include "StatCounters.h"
43 #include "SwapDir.h"
44 #include "tools.h"
45 #include "xusleep.h"
46
47 /* This code gets linked to Squid */
48
49 static int unlinkd_wfd = -1;
50 static int unlinkd_rfd = -1;
51
52 static void * hIpc;
53 static pid_t pid;
54
55 #define UNLINKD_QUEUE_LIMIT 20
56
57 void
58 unlinkdUnlink(const char *path)
59 {
60 char buf[MAXPATHLEN];
61 int l;
62 int bytes_written;
63 static int queuelen = 0;
64
65 if (unlinkd_wfd < 0) {
66 debug_trap("unlinkdUnlink: unlinkd_wfd < 0");
67 safeunlink(path, 0);
68 return;
69 }
70
71 /*
72 * If the queue length is greater than our limit, then we pause
73 * for a small amount of time, hoping that unlinkd has some
74 * feedback for us. Maybe it just needs a slice of the CPU's
75 * time.
76 */
77 if (queuelen >= UNLINKD_QUEUE_LIMIT) {
78 #if defined(USE_EPOLL) || defined(USE_KQUEUE) || defined(USE_DEVPOLL)
79 /*
80 * DPW 2007-04-23
81 * We can't use fd_set when using epoll() or kqueue(). In
82 * these cases we block for 10 ms.
83 */
84 xusleep(10000);
85 #else
86 /*
87 * DPW 2007-04-23
88 * When we can use select, block for up to 100 ms.
89 */
90 struct timeval to;
91 fd_set R;
92 FD_ZERO(&R);
93 FD_SET(unlinkd_rfd, &R);
94 to.tv_sec = 0;
95 to.tv_usec = 100000;
96 select(unlinkd_rfd + 1, &R, NULL, NULL, &to);
97 #endif
98 }
99
100 /*
101 * If there is at least one outstanding unlink request, then
102 * try to read a response. If there's nothing to read we'll
103 * get an EWOULDBLOCK or whatever. If we get a response, then
104 * decrement the queue size by the number of newlines read.
105 */
106 if (queuelen > 0) {
107 int bytes_read;
108 int i;
109 char rbuf[512];
110 bytes_read = read(unlinkd_rfd, rbuf, 511);
111
112 if (bytes_read > 0) {
113 rbuf[bytes_read] = '\0';
114
115 for (i = 0; i < bytes_read; ++i)
116 if ('\n' == rbuf[i])
117 --queuelen;
118
119 assert(queuelen >= 0);
120 }
121 }
122
123 l = strlen(path);
124 assert(l < MAXPATHLEN);
125 xstrncpy(buf, path, MAXPATHLEN);
126 buf[l] = '\n';
127 ++l;
128 bytes_written = write(unlinkd_wfd, buf, l);
129
130 if (bytes_written < 0) {
131 debugs(2, DBG_IMPORTANT, "unlinkdUnlink: write FD " << unlinkd_wfd << " failed: " << xstrerror());
132 safeunlink(path, 0);
133 return;
134 } else if (bytes_written != l) {
135 debugs(2, DBG_IMPORTANT, "unlinkdUnlink: FD " << unlinkd_wfd << " only wrote " << bytes_written << " of " << l << " bytes");
136 safeunlink(path, 0);
137 return;
138 }
139
140 ++statCounter.unlink.requests;
141 /*
142 * Increment this syscalls counter here, even though the syscall
143 * is executed by the helper process. We try to be consistent
144 * in counting unlink operations.
145 */
146 ++statCounter.syscalls.disk.unlinks;
147 ++queuelen;
148 }
149
150 void
151 unlinkdClose(void)
152 #if _SQUID_WINDOWS_
153 {
154
155 if (unlinkd_wfd > -1) {
156 debugs(2, DBG_IMPORTANT, "Closing unlinkd pipe on FD " << unlinkd_wfd);
157 shutdown(unlinkd_wfd, SD_BOTH);
158 comm_close(unlinkd_wfd);
159
160 if (unlinkd_wfd != unlinkd_rfd)
161 comm_close(unlinkd_rfd);
162
163 unlinkd_wfd = -1;
164
165 unlinkd_rfd = -1;
166 }
167
168 if (hIpc) {
169 if (WaitForSingleObject(hIpc, 5000) != WAIT_OBJECT_0) {
170 getCurrentTime();
171 debugs(2, DBG_IMPORTANT, "unlinkdClose: WARNING: (unlinkd," << pid << "d) didn't exit in 5 seconds");
172 }
173
174 CloseHandle(hIpc);
175 }
176 }
177 #else
178 {
179
180 if (unlinkd_wfd < 0)
181 return;
182
183 debugs(2, DBG_IMPORTANT, "Closing unlinkd pipe on FD " << unlinkd_wfd);
184
185 file_close(unlinkd_wfd);
186
187 if (unlinkd_wfd != unlinkd_rfd)
188 file_close(unlinkd_rfd);
189
190 unlinkd_wfd = -1;
191
192 unlinkd_rfd = -1;
193 }
194
195 #endif
196
197 bool
198 unlinkdNeeded(void)
199 {
200 // we should start unlinkd if there are any cache_dirs using it
201 for (int i = 0; i < Config.cacheSwap.n_configured; ++i) {
202 const RefCount<SwapDir> sd = Config.cacheSwap.swapDirs[i];
203 if (sd->unlinkdUseful())
204 return true;
205 }
206
207 return false;
208 }
209
210 void
211 unlinkdInit(void)
212 {
213 if (unlinkd_wfd >= 0)
214 return; // unlinkd already started
215
216 const char *args[2];
217 Ip::Address localhost;
218
219 args[0] = "(unlinkd)";
220 args[1] = NULL;
221 localhost.setLocalhost();
222
223 pid = ipcCreate(
224 #if USE_POLL && _SQUID_OSF_
225 /* pipes and poll() don't get along on DUNIX -DW */
226 IPC_STREAM,
227 #elif _SQUID_WINDOWS_
228 /* select() will fail on a pipe */
229 IPC_TCP_SOCKET,
230 #else
231 /* We currently need to use FIFO.. see below */
232 IPC_FIFO,
233 #endif
234 Config.Program.unlinkd,
235 args,
236 "unlinkd",
237 localhost,
238 &unlinkd_rfd,
239 &unlinkd_wfd,
240 &hIpc);
241
242 if (pid < 0)
243 fatal("Failed to create unlinkd subprocess");
244
245 xusleep(250000);
246
247 fd_note(unlinkd_wfd, "squid -> unlinkd");
248
249 fd_note(unlinkd_rfd, "unlinkd -> squid");
250
251 commUnsetFdTimeout(unlinkd_rfd);
252 commUnsetFdTimeout(unlinkd_wfd);
253
254 /*
255 * unlinkd_rfd should already be non-blocking because of
256 * ipcCreate. We change unlinkd_wfd to blocking mode because
257 * we never want to lose an unlink request, and we don't have
258 * code to retry if we get EWOULDBLOCK. Unfortunately, we can
259 * do this only for the IPC_FIFO case.
260 */
261 assert(fd_table[unlinkd_rfd].flags.nonblocking);
262
263 if (FD_PIPE == fd_table[unlinkd_wfd].type)
264 commUnsetNonBlocking(unlinkd_wfd);
265
266 debugs(2, DBG_IMPORTANT, "Unlinkd pipe opened on FD " << unlinkd_wfd);
267
268 #if _SQUID_WINDOWS_
269
270 debugs(2, 4, "Unlinkd handle: 0x" << std::hex << hIpc << std::dec << ", PID: " << pid);
271
272 #endif
273
274 }
275 #endif /* USE_UNLINKD */