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
34 const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
35 
36 void *
37 Ipc::Mem::Segment::reserve(size_t chunkSize)
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 
50 SBuf
51 Ipc::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 
61 Ipc::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
81 bool
83 {
84  return true;
85 }
86 
87 void
88 Ipc::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 
131 void
132 Ipc::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 
154 bool
155 Ipc::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 
165 void
166 Ipc::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 
189 void
190 Ipc::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 
206 void
207 Ipc::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 
237 void
238 Ipc::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 
248 off_t
249 Ipc::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 
268 String
269 Ipc::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 
301 typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
303 
304 Ipc::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 
319 bool
321 {
322  return !UsingSmp() && IamWorkerProcess();
323 }
324 
325 void
326 Ipc::Mem::Segment::create(const off_t aSize)
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 
343 void
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 
360 void
361 Ipc::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 
374 void
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 
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool shm_portable_segment_name_is_path()
Determines whether segment names are interpreted 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:304
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:37
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:375
POSIX shared memory segment.
Definition: Segment.h:23
void checkSupport(const char *const context)
Definition: Segment.cc:361
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:669
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
static bool Enabled()
Whether shared memory support is available.
Definition: Segment.cc:320
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:344
void create(const off_t aSize)
Create a new shared memory segment. Unlinks the segment on destruction.
Definition: Segment.cc:326
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:301
int xclose(int fd)
POSIX close(2) equivalent.
Definition: unistd.h:43
bool UsingSmp()
Whether there should be more than one worker process running.
Definition: tools.cc:697
static SegmentMap Segments
Definition: Segment.cc:302
#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:691

 

Introduction

Documentation

Support

Miscellaneous