unlinkd.cc
Go to the documentation of this file.
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
30static int unlinkd_wfd = -1;
31static int unlinkd_rfd = -1;
32
33static void * hIpc;
34static pid_t pid;
35
36#define UNLINKD_QUEUE_LIMIT 20
37
38void
39unlinkdUnlink(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, NULL, NULL, &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
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 (int 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
Definition: SquidConfig.cc:12
StatCounters statCounter
Definition: StatCounters.cc:12
#define assert(EX)
Definition: assert.h:17
void setLocalhost()
Definition: Address.cc:255
char * unlinkd
Definition: SquidConfig.h:203
struct SquidConfig::@99 Program
Store::DiskConfig cacheSwap
Definition: SquidConfig.h:421
struct StatCounters::@127 unlink
struct StatCounters::@131 syscalls
struct StatCounters::@131::@135 disk
RefCount< SwapDir > * swapDirs
Definition: SquidConfig.h:66
void commUnsetFdTimeout(int fd)
clear a timeout handler by FD number
Definition: comm.cc:582
int commUnsetNonBlocking(int fd)
Definition: comm.cc:1099
#define comm_close(x)
Definition: comm.h:27
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define IPC_STREAM
Definition: defines.h:106
#define IPC_FIFO
Definition: defines.h:93
#define IPC_TCP_SOCKET
Definition: defines.h:91
@ 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:216
#define fd_table
Definition: fde.h:189
void safeunlink(const char *s, int quiet)
Definition: fs_io.cc:469
void file_close(int fd)
Definition: fs_io.cc:73
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:65
#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:458
#define NULL
Definition: types.h:160
void unlinkdUnlink(const char *path)
Definition: unlinkd.cc:39
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:33
static int unlinkd_rfd
Definition: unlinkd.cc:31
static pid_t pid
Definition: unlinkd.cc:34
static int unlinkd_wfd
Definition: unlinkd.cc:30
#define UNLINKD_QUEUE_LIMIT
Definition: unlinkd.cc:36
const char * xstrerr(int error)
Definition: xstrerror.cc:83
char * xstrncpy(char *dst, const char *src, size_t n)
Definition: xstring.cc:37

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors