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 "debug/Stream.h"
15 #include "fatal.h"
16 #include "Instance.h"
17 #include "ipc/mem/Segment.h"
18 #include "sbuf/SBuf.h"
19 #include "SquidConfig.h"
20 #include "tools.h"
21 
22 #if HAVE_FCNTL_H
23 #include <fcntl.h>
24 #endif
25 #if HAVE_SYS_MMAN_H
26 #include <sys/mman.h>
27 #endif
28 #if HAVE_SYS_STAT_H
29 #include <sys/stat.h>
30 #endif
31 #if HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 
35 // test cases change this
36 const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
37 
38 void *
39 Ipc::Mem::Segment::reserve(size_t chunkSize)
40 {
41  Must(theMem);
42  // check for overflows
43  // chunkSize >= 0 may result in warnings on systems where off_t is unsigned
44  assert(!chunkSize || static_cast<off_t>(chunkSize) > 0);
45  assert(static_cast<off_t>(chunkSize) <= theSize);
46  assert(theReserved <= theSize - static_cast<off_t>(chunkSize));
47  void *result = reinterpret_cast<char*>(theMem) + theReserved;
48  theReserved += chunkSize;
49  return result;
50 }
51 
52 SBuf
53 Ipc::Mem::Segment::Name(const SBuf &prefix, const char *suffix)
54 {
55  SBuf result = prefix;
56  result.append("_");
57  result.append(suffix);
58  return result;
59 }
60 
61 #if HAVE_SHM
62 
63 Ipc::Mem::Segment::Segment(const char *const id):
64  theFD(-1), theName(GenerateName(id)), theMem(nullptr),
65  theSize(0), theReserved(0), doUnlink(false)
66 {
67 }
68 
70 {
71  if (theFD >= 0) {
72  detach();
73  if (close(theFD) != 0) {
74  int xerrno = errno;
75  debugs(54, 5, "close " << theName << ": " << xstrerr(xerrno));
76  }
77  }
78  if (doUnlink)
79  unlink();
80 }
81 
82 // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
83 bool
85 {
86  return true;
87 }
88 
89 void
90 Ipc::Mem::Segment::create(const off_t aSize)
91 {
92  assert(aSize > 0);
93  assert(theFD < 0);
94 
95  int xerrno = 0;
96 
97  // Why a brand new segment? A Squid crash may leave a reusable segment, but
98  // our placement-new code requires an all-0s segment. We could truncate and
99  // resize the old segment, but OS X does not allow using O_TRUNC with
100  // shm_open() and does not support ftruncate() for old segments.
101  if (!createFresh(xerrno) && xerrno == EEXIST) {
102  unlink();
103  createFresh(xerrno);
104  }
105 
106  if (theFD < 0) {
107  debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
108  fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
109  theName.termedBuf(), xstrerr(xerrno));
110  }
111 
112  if (ftruncate(theFD, aSize)) {
113  xerrno = errno;
114  unlink();
115  debugs(54, 5, "ftruncate " << theName << ": " << xstrerr(xerrno));
116  fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
117  theName.termedBuf(), xstrerr(xerrno));
118  }
119  // We assume that the shm_open(O_CREAT)+ftruncate() combo zeros the segment.
120 
121  theSize = statSize("Ipc::Mem::Segment::create");
122 
123  // OS X will round up to a full page, so not checking for exact size match.
124  assert(theSize >= aSize);
125 
126  theReserved = 0;
127  doUnlink = true;
128 
129  debugs(54, 3, "created " << theName << " segment: " << theSize);
130  attach();
131 }
132 
133 void
134 Ipc::Mem::Segment::open(const bool unlinkWhenDone)
135 {
136  assert(theFD < 0);
137 
138  theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
139  if (theFD < 0) {
140  int xerrno = errno;
141  debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
142  fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
143  theName.termedBuf(), xstrerr(xerrno));
144  }
145 
146  theSize = statSize("Ipc::Mem::Segment::open");
147  doUnlink = unlinkWhenDone;
148 
149  debugs(54, 3, "opened " << theName << " segment: " << theSize);
150 
151  attach();
152 }
153 
156 bool
157 Ipc::Mem::Segment::createFresh(int &xerrno)
158 {
159  theFD = shm_open(theName.termedBuf(),
160  O_EXCL | O_CREAT | O_RDWR,
161  S_IRUSR | S_IWUSR);
162  xerrno = errno;
163  return theFD >= 0;
164 }
165 
167 void
168 Ipc::Mem::Segment::attach()
169 {
170  assert(theFD >= 0);
171  assert(!theMem);
172 
173  // mmap() accepts size_t for the size; we give it off_t which might
174  // be bigger; assert overflows until we support multiple mmap()s?
175  assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
176 
177  void *const p =
178  mmap(nullptr, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
179  if (p == MAP_FAILED) {
180  int xerrno = errno;
181  debugs(54, 5, "mmap " << theName << ": " << xstrerr(xerrno));
182  fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
183  theName.termedBuf(), xstrerr(xerrno));
184  }
185  theMem = p;
186 
187  lock();
188 }
189 
191 void
192 Ipc::Mem::Segment::detach()
193 {
194  if (!theMem)
195  return;
196 
197  if (munmap(theMem, theSize)) {
198  int xerrno = errno;
199  debugs(54, 5, "munmap " << theName << ": " << xstrerr(xerrno));
200  fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
201  theName.termedBuf(), xstrerr(xerrno));
202  }
203  theMem = nullptr;
204 }
205 
208 void
209 Ipc::Mem::Segment::lock()
210 {
211  if (!Config.shmLocking) {
212  debugs(54, 5, "mlock(2)-ing disabled");
213  return;
214  }
215 
216 #if defined(_POSIX_MEMLOCK_RANGE)
217  debugs(54, 7, "mlock(" << theName << ',' << theSize << ") starts");
218  if (mlock(theMem, theSize) != 0) {
219  const int savedError = errno;
220  fatalf("shared_memory_locking on but failed to mlock(%s, %" PRId64 "): %s\n",
221  theName.termedBuf(),static_cast<int64_t>(theSize), xstrerr(savedError));
222  }
223  // TODO: Warn if it took too long.
224  debugs(54, 7, "mlock(" << theName << ',' << theSize << ") OK");
225 #else
226  debugs(54, 5, "insufficient mlock(2) support");
227  if (Config.shmLocking.configured()) { // set explicitly
228  static bool warnedOnce = false;
229  if (!warnedOnce) {
230  debugs(54, DBG_IMPORTANT, "ERROR: insufficient mlock(2) support prevents " <<
231  "honoring `shared_memory_locking on`. " <<
232  "If you lack RAM, kernel will kill Squid later.");
233  warnedOnce = true;
234  }
235  }
236 #endif
237 }
238 
239 void
240 Ipc::Mem::Segment::unlink()
241 {
242  if (shm_unlink(theName.termedBuf()) != 0) {
243  int xerrno = errno;
244  debugs(54, 5, "shm_unlink(" << theName << "): " << xstrerr(xerrno));
245  } else
246  debugs(54, 3, "unlinked " << theName << " segment");
247 }
248 
250 off_t
251 Ipc::Mem::Segment::statSize(const char *context) const
252 {
253  Must(theFD >= 0);
254 
255  struct stat s;
256  memset(&s, 0, sizeof(s));
257 
258  if (fstat(theFD, &s) != 0) {
259  int xerrno = errno;
260  debugs(54, 5, context << " fstat " << theName << ": " << xstrerr(xerrno));
261  fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
262  context, theName.termedBuf(), xstrerr(xerrno));
263  }
264 
265  return s.st_size;
266 }
267 
270 String
271 Ipc::Mem::Segment::GenerateName(const char *id)
272 {
273  assert(BasePath && *BasePath);
274  static const bool nameIsPath = shm_portable_segment_name_is_path();
275  String name;
276  if (nameIsPath) {
277  name.append(BasePath);
278  if (name[name.size()-1] != '/')
279  name.append('/');
280  } else {
281  name.append(Instance::NamePrefix("/"));
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 
303 typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
305 
306 Ipc::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 
321 bool
323 {
324  return !UsingSmp() && IamWorkerProcess();
325 }
326 
327 void
328 Ipc::Mem::Segment::create(const off_t aSize)
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 
345 void
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 
362 void
363 Ipc::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 
376 void
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 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool shm_portable_segment_name_is_path()
Determines whether segment names are iterpreted as full file paths.
Definition: shm.cc:23
Definition: SBuf.h:93
Segment(const char *const id)
Create a shared memory segment.
Definition: Segment.cc:306
int shm_unlink(const char *)
Definition: shm.h:39
bool IamWorkerProcess()
whether the current process handles HTTP transactions and such
Definition: stub_tools.cc:47
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:39
static const char * BasePath
common path of all segment names in path-based environments
Definition: Segment.h:45
#define NULL
Definition: types.h:145
void append(char const *buf, int len)
Definition: String.cc:131
off_t theSize
shared memory segment size
Definition: Segment.h:77
SBuf NamePrefix(const char *head, const char *tail=nullptr)
Definition: Instance.cc:253
#define assert(EX)
Definition: assert.h:17
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
bool configured() const
Definition: YesNoNone.h:67
void useConfig() override
Definition: Segment.cc:377
POSIX shared memory segment.
Definition: Segment.h:23
void checkSupport(const char *const context)
Definition: Segment.cc:363
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
bool IamMasterProcess()
whether the current process is the parent of all other Squid processes
Definition: tools.cc:668
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:53
static bool Enabled()
Whether shared memory support is available.
Definition: Segment.cc:322
off_t theReserved
the total number of reserve()d bytes
Definition: Segment.h:78
YesNoNone shmLocking
shared_memory_locking
Definition: SquidConfig.h:90
void open(const bool unlinkWhenDone)
Definition: Segment.cc:346
void create(const off_t aSize)
Create a new shared memory segment. Unlinks the segment on destruction.
Definition: Segment.cc:328
size_type size() const
Definition: SquidString.h:74
#define Must(condition)
Definition: TextException.h:75
#define DBG_IMPORTANT
Definition: Stream.h:38
#define PRId64
Definition: types.h:104
std::map< String, Ipc::Mem::Segment * > SegmentMap
Definition: Segment.cc:303
bool UsingSmp()
Whether there should be more than one worker process running.
Definition: tools.cc:696
static SegmentMap Segments
Definition: Segment.cc:304
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
int shm_open(const char *, int, mode_t)
Definition: shm.h:33
class SquidConfig Config
Definition: SquidConfig.cc:12
bool InDaemonMode()
Whether we are running in daemon mode.
Definition: tools.cc:690

 

Introduction

Documentation

Support

Miscellaneous