Squid Web Cache master
Loading...
Searching...
No Matches
Segment.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 54 Interprocess Communication */
10
11#include "squid.h"
12#include "base/TextException.h"
13#include "compat/shm.h"
14#include "compat/unistd.h"
15#include "debug/Stream.h"
16#include "fatal.h"
17#include "Instance.h"
18#include "ipc/mem/Segment.h"
19#include "sbuf/SBuf.h"
20#include "SquidConfig.h"
21#include "tools.h"
22
23#if HAVE_FCNTL_H
24#include <fcntl.h>
25#endif
26#if HAVE_SYS_MMAN_H
27#include <sys/mman.h>
28#endif
29#if HAVE_SYS_STAT_H
30#include <sys/stat.h>
31#endif
32
33// test cases change this
34const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
35
36void *
38{
39 Must(theMem);
40 // check for overflows
41 // chunkSize >= 0 may result in warnings on systems where off_t is unsigned
42 assert(!chunkSize || static_cast<off_t>(chunkSize) > 0);
43 assert(static_cast<off_t>(chunkSize) <= theSize);
44 assert(theReserved <= theSize - static_cast<off_t>(chunkSize));
45 void *result = reinterpret_cast<char*>(theMem) + theReserved;
46 theReserved += chunkSize;
47 return result;
48}
49
50SBuf
51Ipc::Mem::Segment::Name(const SBuf &prefix, const char *suffix)
52{
53 SBuf result = prefix;
54 result.append("_");
55 result.append(suffix);
56 return result;
57}
58
59#if HAVE_SHM
60
61Ipc::Mem::Segment::Segment(const char *const id):
62 theFD(-1), theName(GenerateName(id)), theMem(nullptr),
63 theSize(0), theReserved(0), doUnlink(false)
64{
65}
66
68{
69 if (theFD >= 0) {
70 detach();
71 if (xclose(theFD) != 0) {
72 int xerrno = errno;
73 debugs(54, 5, "close " << theName << ": " << xstrerr(xerrno));
74 }
75 }
76 if (doUnlink)
77 unlink();
78}
79
80// fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
81bool
83{
84 return true;
85}
86
87void
88Ipc::Mem::Segment::create(const off_t aSize)
89{
90 assert(aSize > 0);
91 assert(theFD < 0);
92
93 int xerrno = 0;
94
95 // Why a brand new segment? A Squid crash may leave a reusable segment, but
96 // our placement-new code requires an all-0s segment. We could truncate and
97 // resize the old segment, but OS X does not allow using O_TRUNC with
98 // shm_open() and does not support ftruncate() for old segments.
99 if (!createFresh(xerrno) && xerrno == EEXIST) {
100 unlink();
101 createFresh(xerrno);
102 }
103
104 if (theFD < 0) {
105 debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
106 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
107 theName.termedBuf(), xstrerr(xerrno));
108 }
109
110 if (ftruncate(theFD, aSize)) {
111 xerrno = errno;
112 unlink();
113 debugs(54, 5, "ftruncate " << theName << ": " << xstrerr(xerrno));
114 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
115 theName.termedBuf(), xstrerr(xerrno));
116 }
117 // We assume that the shm_open(O_CREAT)+ftruncate() combo zeros the segment.
118
119 theSize = statSize("Ipc::Mem::Segment::create");
120
121 // OS X will round up to a full page, so not checking for exact size match.
122 assert(theSize >= aSize);
123
124 theReserved = 0;
125 doUnlink = true;
126
127 debugs(54, 3, "created " << theName << " segment: " << theSize);
128 attach();
129}
130
131void
132Ipc::Mem::Segment::open(const bool unlinkWhenDone)
133{
134 assert(theFD < 0);
135
136 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
137 if (theFD < 0) {
138 int xerrno = errno;
139 debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
140 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
141 theName.termedBuf(), xstrerr(xerrno));
142 }
143
144 theSize = statSize("Ipc::Mem::Segment::open");
145 doUnlink = unlinkWhenDone;
146
147 debugs(54, 3, "opened " << theName << " segment: " << theSize);
148
149 attach();
150}
151
154bool
155Ipc::Mem::Segment::createFresh(int &xerrno)
156{
157 theFD = shm_open(theName.termedBuf(),
158 O_EXCL | O_CREAT | O_RDWR,
159 S_IRUSR | S_IWUSR);
160 xerrno = errno;
161 return theFD >= 0;
162}
163
165void
166Ipc::Mem::Segment::attach()
167{
168 assert(theFD >= 0);
169 assert(!theMem);
170
171 // mmap() accepts size_t for the size; we give it off_t which might
172 // be bigger; assert overflows until we support multiple mmap()s?
173 assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
174
175 void *const p =
176 mmap(nullptr, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
177 if (p == MAP_FAILED) {
178 int xerrno = errno;
179 debugs(54, 5, "mmap " << theName << ": " << xstrerr(xerrno));
180 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
181 theName.termedBuf(), xstrerr(xerrno));
182 }
183 theMem = p;
184
185 lock();
186}
187
189void
190Ipc::Mem::Segment::detach()
191{
192 if (!theMem)
193 return;
194
195 if (munmap(theMem, theSize)) {
196 int xerrno = errno;
197 debugs(54, 5, "munmap " << theName << ": " << xstrerr(xerrno));
198 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
199 theName.termedBuf(), xstrerr(xerrno));
200 }
201 theMem = nullptr;
202}
203
206void
207Ipc::Mem::Segment::lock()
208{
209 if (!Config.shmLocking) {
210 debugs(54, 5, "mlock(2)-ing disabled");
211 return;
212 }
213
214#if defined(_POSIX_MEMLOCK_RANGE)
215 debugs(54, 7, "mlock(" << theName << ',' << theSize << ") starts");
216 if (mlock(theMem, theSize) != 0) {
217 const int savedError = errno;
218 fatalf("shared_memory_locking on but failed to mlock(%s, %" PRId64 "): %s\n",
219 theName.termedBuf(),static_cast<int64_t>(theSize), xstrerr(savedError));
220 }
221 // TODO: Warn if it took too long.
222 debugs(54, 7, "mlock(" << theName << ',' << theSize << ") OK");
223#else
224 debugs(54, 5, "insufficient mlock(2) support");
225 if (Config.shmLocking.configured()) { // set explicitly
226 static bool warnedOnce = false;
227 if (!warnedOnce) {
228 debugs(54, DBG_IMPORTANT, "ERROR: insufficient mlock(2) support prevents " <<
229 "honoring `shared_memory_locking on`. " <<
230 "If you lack RAM, kernel will kill Squid later.");
231 warnedOnce = true;
232 }
233 }
234#endif
235}
236
237void
238Ipc::Mem::Segment::unlink()
239{
240 if (shm_unlink(theName.termedBuf()) != 0) {
241 int xerrno = errno;
242 debugs(54, 5, "shm_unlink(" << theName << "): " << xstrerr(xerrno));
243 } else
244 debugs(54, 3, "unlinked " << theName << " segment");
245}
246
248off_t
249Ipc::Mem::Segment::statSize(const char *context) const
250{
251 Must(theFD >= 0);
252
253 struct stat s;
254 memset(&s, 0, sizeof(s));
255
256 if (fstat(theFD, &s) != 0) {
257 int xerrno = errno;
258 debugs(54, 5, context << " fstat " << theName << ": " << xstrerr(xerrno));
259 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
260 context, theName.termedBuf(), xstrerr(xerrno));
261 }
262
263 return s.st_size;
264}
265
268String
269Ipc::Mem::Segment::GenerateName(const char *id)
270{
271 assert(BasePath && *BasePath);
272 static const bool nameIsPath = shm_portable_segment_name_is_path();
273 String name;
274 if (nameIsPath) {
275 name.append(BasePath);
276 if (name[name.size()-1] != '/')
277 name.append('/');
278 } else {
279 name.append(Instance::NamePrefix("/"));
280 name.append('-');
281 }
282
283 // append id, replacing slashes with dots
284 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
285 if (id != slash) {
286 name.append(id, slash - id);
287 name.append('.');
288 }
289 id = slash + 1;
290 }
291 name.append(id);
292
293 name.append(".shm"); // to distinguish from non-segments when nameIsPath
294 return name;
295}
296
297#else // HAVE_SHM
298
299#include <map>
300
301typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
303
304Ipc::Mem::Segment::Segment(const char *const id):
305 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
306{
307}
308
310{
311 if (doUnlink) {
312 delete [] static_cast<char *>(theMem);
313 theMem = nullptr;
314 Segments.erase(theName);
315 debugs(54, 3, "unlinked " << theName << " fake segment");
316 }
317}
318
319bool
324
325void
327{
328 assert(aSize > 0);
329 assert(!theMem);
330 checkSupport("Fake segment creation");
331
332 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
333 if (!inserted)
334 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
335
336 theMem = new char[aSize];
337 theSize = aSize;
338 doUnlink = true;
339
340 debugs(54, 3, "created " << theName << " fake segment: " << theSize);
341}
342
343void
345{
346 assert(!theMem);
347 checkSupport("Fake segment open");
348
349 const SegmentMap::const_iterator i = Segments.find(theName);
350 if (i == Segments.end())
351 fatalf("Fake segment not found: %s", theName.termedBuf());
352
353 const Segment &segment = *i->second;
354 theMem = segment.theMem;
355 theSize = segment.theSize;
356
357 debugs(54, 3, "opened " << theName << " fake segment: " << theSize);
358}
359
360void
361Ipc::Mem::Segment::checkSupport(const char *const context)
362{
363 if (!Enabled()) {
364 debugs(54, 5, context <<
365 ": True shared memory segments are not supported. "
366 "Cannot fake shared segments in SMP config.");
367 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
368 context);
369 }
370}
371
372#endif // HAVE_SHM
373
374void
376{
377 // If Squid is built with real segments, we create() real segments
378 // in the master process only. Otherwise, we create() fake
379 // segments in each worker process. We assume that only workers
380 // need and can work with fake segments.
381#if HAVE_SHM
382 if (IamMasterProcess())
383#else
384 if (IamWorkerProcess())
385#endif
386 create();
387
388 // we assume that master process does not need shared segments
389 // unless it is also a worker
390 if (!InDaemonMode() || !IamMasterProcess())
391 open();
392}
393
static SegmentMap Segments
Definition Segment.cc:302
std::map< String, Ipc::Mem::Segment * > SegmentMap
Definition Segment.cc:301
class SquidConfig Config
#define Must(condition)
#define assert(EX)
Definition assert.h:17
void useConfig() override
Definition Segment.cc:375
POSIX shared memory segment.
Definition Segment.h:24
off_t theReserved
the total number of reserve()d bytes
Definition Segment.h:78
off_t theSize
shared memory segment size
Definition Segment.h:77
void open(const bool unlinkWhenDone)
Definition Segment.cc:344
static const char * BasePath
common path of all segment names in path-based environments
Definition Segment.h:45
static bool Enabled()
Whether shared memory support is available.
Definition Segment.cc:320
static SBuf Name(const SBuf &prefix, const char *suffix)
concatenates parts of a name to form a complete name (or its prefix)
Definition Segment.cc:51
void * theMem
pointer to mmapped shared memory segment
Definition Segment.h:76
void * reserve(size_t chunkSize)
reserve and return the next chunk
Definition Segment.cc:37
Segment(const char *const id)
Create a shared memory segment.
Definition Segment.cc:304
void checkSupport(const char *const context)
Definition Segment.cc:361
void create(const off_t aSize)
Create a new shared memory segment. Unlinks the segment on destruction.
Definition Segment.cc:326
Definition SBuf.h:94
SBuf & append(const SBuf &S)
Definition SBuf.cc:185
YesNoNone shmLocking
shared_memory_locking
Definition SquidConfig.h:90
void append(char const *buf, int len)
Definition String.cc:131
size_type size() const
Definition SquidString.h:74
bool configured() const
Definition YesNoNone.h:67
#define DBG_IMPORTANT
Definition Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
void fatalf(const char *fmt,...)
Definition fatal.cc:68
SBuf NamePrefix(const char *head, const char *tail=nullptr)
Definition Instance.cc:253
bool shm_portable_segment_name_is_path()
Determines whether segment names are interpreted as full file paths.
Definition shm.cc:23
int shm_unlink(const char *)
Definition shm.h:39
int shm_open(const char *, int, mode_t)
Definition shm.h:33
bool IamWorkerProcess()
whether the current process handles HTTP transactions and such
Definition stub_tools.cc:47
bool IamMasterProcess()
whether the current process is the parent of all other Squid processes
Definition tools.cc:669
bool InDaemonMode()
Whether we are running in daemon mode.
Definition tools.cc:691
bool UsingSmp()
Whether there should be more than one worker process running.
Definition tools.cc:697
#define NULL
Definition types.h:145
#define PRId64
Definition types.h:104
int xclose(int fd)
POSIX close(2) equivalent.
Definition unistd.h:43
const char * xstrerr(int error)
Definition xstrerror.cc:83