]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/mem/Segment.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
CommitLineData
f5eef98c 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
f5eef98c 3 *
bbc27441
AJ
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.
f5eef98c
DK
7 */
8
bbc27441
AJ
9/* DEBUG: section 54 Interprocess Communication */
10
f7f3304a 11#include "squid.h"
e08b0f77 12#include "base/TextException.h"
c975f532 13#include "compat/shm.h"
af69c635
FC
14#include "Debug.h"
15#include "fatal.h"
d8a82533 16#include "ipc/mem/Segment.h"
65e41a45 17#include "sbuf/SBuf.h"
c756d517 18#include "SquidConfig.h"
5bed43d6 19#include "tools.h"
f5eef98c 20
7e851a39 21#if HAVE_FCNTL_H
f5eef98c 22#include <fcntl.h>
7e851a39
AJ
23#endif
24#if HAVE_SYS_MMAN_H
f5eef98c 25#include <sys/mman.h>
7e851a39
AJ
26#endif
27#if HAVE_SYS_STAT_H
f5eef98c 28#include <sys/stat.h>
7e851a39
AJ
29#endif
30#if HAVE_UNISTD_H
f5eef98c 31#include <unistd.h>
7e851a39 32#endif
f5eef98c 33
ef8de464
AR
34// test cases change this
35const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
36
be9cb908
DK
37void *
38Ipc::Mem::Segment::reserve(size_t chunkSize)
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
1860fbac
AR
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
be9cb908
DK
60#if HAVE_SHM
61
d8a82533 62Ipc::Mem::Segment::Segment(const char *const id):
f53969cc
SM
63 theFD(-1), theName(GenerateName(id)), theMem(NULL),
64 theSize(0), theReserved(0), doUnlink(false)
f5eef98c
DK
65{
66}
67
9199139f
AR
68Ipc::Mem::Segment::~Segment()
69{
f5eef98c
DK
70 if (theFD >= 0) {
71 detach();
b69e9ffa
AJ
72 if (close(theFD) != 0) {
73 int xerrno = errno;
74 debugs(54, 5, "close " << theName << ": " << xstrerr(xerrno));
75 }
f5eef98c 76 }
68353d5a
DK
77 if (doUnlink)
78 unlink();
f5eef98c
DK
79}
80
be9cb908 81// fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
c975f532 82bool
9199139f
AR
83Ipc::Mem::Segment::Enabled()
84{
c975f532 85 return true;
c975f532
DK
86}
87
f5eef98c 88void
e08b0f77 89Ipc::Mem::Segment::create(const off_t aSize)
f5eef98c
DK
90{
91 assert(aSize > 0);
92 assert(theFD < 0);
93
45a6b2c8 94 int xerrno = 0;
b69e9ffa 95
f58b1eb4
MM
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.
45a6b2c8 100 if (!createFresh(xerrno) && xerrno == EEXIST) {
f58b1eb4 101 unlink();
45a6b2c8 102 createFresh(xerrno);
f58b1eb4
MM
103 }
104
f5eef98c 105 if (theFD < 0) {
b69e9ffa 106 debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
77915f06 107 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
b69e9ffa 108 theName.termedBuf(), xstrerr(xerrno));
f5eef98c
DK
109 }
110
111 if (ftruncate(theFD, aSize)) {
45a6b2c8 112 xerrno = errno;
62fa1d73 113 unlink();
45a6b2c8 114 debugs(54, 5, "ftruncate " << theName << ": " << xstrerr(xerrno));
b46ae525 115 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
45a6b2c8 116 theName.termedBuf(), xstrerr(xerrno));
f5eef98c 117 }
62fa1d73
FC
118 // We assume that the shm_open(O_CREAT)+ftruncate() combo zeros the segment.
119
120 theSize = statSize("Ipc::Mem::Segment::create");
f5eef98c 121
62fa1d73
FC
122 // OS X will round up to a full page, so not checking for exact size match.
123 assert(theSize >= aSize);
e08b0f77 124
937d0d3f 125 theReserved = 0;
68353d5a 126 doUnlink = true;
f5eef98c 127
45a6b2c8 128 debugs(54, 3, "created " << theName << " segment: " << theSize);
f5eef98c
DK
129 attach();
130}
131
132void
d8a82533 133Ipc::Mem::Segment::open()
f5eef98c
DK
134{
135 assert(theFD < 0);
136
137 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
138 if (theFD < 0) {
b69e9ffa
AJ
139 int xerrno = errno;
140 debugs(54, 5, "shm_open " << theName << ": " << xstrerr(xerrno));
77915f06 141 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
b69e9ffa 142 theName.termedBuf(), xstrerr(xerrno));
f5eef98c
DK
143 }
144
e08b0f77 145 theSize = statSize("Ipc::Mem::Segment::open");
f5eef98c 146
446485d5
AR
147 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
148
f5eef98c
DK
149 attach();
150}
151
f58b1eb4
MM
152/// Creates a brand new shared memory segment and returns true.
153/// Fails and returns false if there exist an old segment with the same name.
154bool
45a6b2c8 155Ipc::Mem::Segment::createFresh(int &xerrno)
f58b1eb4
MM
156{
157 theFD = shm_open(theName.termedBuf(),
158 O_EXCL | O_CREAT | O_RDWR,
159 S_IRUSR | S_IWUSR);
45a6b2c8 160 xerrno = errno;
f58b1eb4
MM
161 return theFD >= 0;
162}
163
f5eef98c
DK
164/// Map the shared memory segment to the process memory space.
165void
d8a82533 166Ipc::Mem::Segment::attach()
f5eef98c
DK
167{
168 assert(theFD >= 0);
f5eef98c
DK
169 assert(!theMem);
170
3a2ede14
AR
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
f5eef98c
DK
175 void *const p =
176 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
177 if (p == MAP_FAILED) {
b69e9ffa
AJ
178 int xerrno = errno;
179 debugs(54, 5, "mmap " << theName << ": " << xstrerr(xerrno));
b46ae525 180 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
b69e9ffa 181 theName.termedBuf(), xstrerr(xerrno));
f5eef98c
DK
182 }
183 theMem = p;
c756d517
AR
184
185 lock();
f5eef98c
DK
186}
187
188/// Unmap the shared memory segment from the process memory space.
189void
d8a82533 190Ipc::Mem::Segment::detach()
f5eef98c
DK
191{
192 if (!theMem)
193 return;
194
195 if (munmap(theMem, theSize)) {
b69e9ffa
AJ
196 int xerrno = errno;
197 debugs(54, 5, "munmap " << theName << ": " << xstrerr(xerrno));
b46ae525 198 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
b69e9ffa 199 theName.termedBuf(), xstrerr(xerrno));
f5eef98c
DK
200 }
201 theMem = 0;
202}
203
c756d517
AR
204/// Lock the segment into RAM, ensuring that the OS has enough RAM for it [now]
205/// and preventing segment bytes from being swapped out to disk later by the OS.
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",
90ab8f20 219 theName.termedBuf(),static_cast<int64_t>(theSize), xstrerr(savedError));
c756d517
AR
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
68353d5a
DK
237void
238Ipc::Mem::Segment::unlink()
239{
b69e9ffa
AJ
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");
68353d5a
DK
245}
246
e08b0f77
AR
247/// determines the size of the underlying "file"
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) {
b69e9ffa
AJ
257 int xerrno = errno;
258 debugs(54, 5, context << " fstat " << theName << ": " << xstrerr(xerrno));
b46ae525 259 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
b69e9ffa 260 context, theName.termedBuf(), xstrerr(xerrno));
e08b0f77
AR
261 }
262
263 return s.st_size;
264}
265
ef8de464
AR
266/// Generate name for shared memory segment. Starts with a prefix required
267/// for cross-platform portability and replaces all slashes in ID with dots.
f5eef98c 268String
d8a82533 269Ipc::Mem::Segment::GenerateName(const char *id)
f5eef98c 270{
38189e84 271 assert(BasePath && *BasePath);
ef8de464 272 static const bool nameIsPath = shm_portable_segment_name_is_path();
38189e84
AJ
273 String name;
274 if (nameIsPath) {
275 name.append(BasePath);
276 if (name[name.size()-1] != '/')
277 name.append('/');
51d4bfbb
JLG
278 } else {
279 name.append('/');
280 name.append(service_name.c_str());
281 name.append('-');
282 }
ef8de464
AR
283
284 // append id, replacing slashes with dots
9a51593d
DK
285 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
286 if (id != slash) {
287 name.append(id, slash - id);
288 name.append('.');
289 }
290 id = slash + 1;
291 }
f5eef98c 292 name.append(id);
ef8de464
AR
293
294 name.append(".shm"); // to distinguish from non-segments when nameIsPath
f5eef98c
DK
295 return name;
296}
be9cb908
DK
297
298#else // HAVE_SHM
299
300#include <map>
301
302typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
303static SegmentMap Segments;
304
305Ipc::Mem::Segment::Segment(const char *const id):
f53969cc 306 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
be9cb908
DK
307{
308}
309
310Ipc::Mem::Segment::~Segment()
311{
312 if (doUnlink) {
313 delete [] static_cast<char *>(theMem);
44c64af0
AR
314 theMem = NULL;
315 Segments.erase(theName);
316 debugs(54, 3, HERE << "unlinked " << theName << " fake segment");
be9cb908
DK
317 }
318}
319
320bool
321Ipc::Mem::Segment::Enabled()
322{
38a129ef 323 return !UsingSmp() && IamWorkerProcess();
be9cb908
DK
324}
325
326void
327Ipc::Mem::Segment::create(const off_t aSize)
328{
329 assert(aSize > 0);
330 assert(!theMem);
331 checkSupport("Fake segment creation");
332
333 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
334 if (!inserted)
335 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
336
337 theMem = new char[aSize];
338 theSize = aSize;
339 doUnlink = true;
340
341 debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize);
342}
343
344void
345Ipc::Mem::Segment::open()
346{
347 assert(!theMem);
348 checkSupport("Fake segment open");
349
350 const SegmentMap::const_iterator i = Segments.find(theName);
351 if (i == Segments.end())
352 fatalf("Fake segment not found: %s", theName.termedBuf());
353
354 const Segment &segment = *i->second;
355 theMem = segment.theMem;
356 theSize = segment.theSize;
357
358 debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize);
359}
360
361void
362Ipc::Mem::Segment::checkSupport(const char *const context)
363{
364 if (!Enabled()) {
b46ae525
AR
365 debugs(54, 5, HERE << context <<
366 ": True shared memory segments are not supported. "
be9cb908 367 "Cannot fake shared segments in SMP config.");
b46ae525
AR
368 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
369 context);
be9cb908
DK
370 }
371}
372
373#endif // HAVE_SHM
f5480a9e
DK
374
375void
21b7990f 376Ipc::Mem::RegisteredRunner::useConfig()
f5480a9e
DK
377{
378 // If Squid is built with real segments, we create() real segments
379 // in the master process only. Otherwise, we create() fake
380 // segments in each worker process. We assume that only workers
381 // need and can work with fake segments.
382#if HAVE_SHM
383 if (IamMasterProcess())
384#else
9e3f96a4 385 if (IamWorkerProcess())
f5480a9e 386#endif
21b7990f 387 create();
f5480a9e
DK
388
389 // we assume that master process does not need shared segments
390 // unless it is also a worker
391 if (!InDaemonMode() || !IamMasterProcess())
21b7990f 392 open();
f5480a9e 393}
f53969cc 394