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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors