]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/mem/Segment.cc
Fixed build broken by r14601: Use trunk, not v3.5 ID for Cache-Control.
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
CommitLineData
f5eef98c 1/*
ef57eb7b 2 * Copyright (C) 1996-2016 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"
5bed43d6 18#include "tools.h"
f5eef98c 19
7e851a39 20#if HAVE_FCNTL_H
f5eef98c 21#include <fcntl.h>
7e851a39
AJ
22#endif
23#if HAVE_SYS_MMAN_H
f5eef98c 24#include <sys/mman.h>
7e851a39
AJ
25#endif
26#if HAVE_SYS_STAT_H
f5eef98c 27#include <sys/stat.h>
7e851a39
AJ
28#endif
29#if HAVE_UNISTD_H
f5eef98c 30#include <unistd.h>
7e851a39 31#endif
f5eef98c 32
ef8de464
AR
33// test cases change this
34const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
35
be9cb908
DK
36void *
37Ipc::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
1860fbac
AR
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
be9cb908
DK
59#if HAVE_SHM
60
d8a82533 61Ipc::Mem::Segment::Segment(const char *const id):
f53969cc
SM
62 theFD(-1), theName(GenerateName(id)), theMem(NULL),
63 theSize(0), theReserved(0), doUnlink(false)
f5eef98c
DK
64{
65}
66
9199139f
AR
67Ipc::Mem::Segment::~Segment()
68{
f5eef98c
DK
69 if (theFD >= 0) {
70 detach();
d8a82533 71 if (close(theFD) != 0)
be1d3736 72 debugs(54, 5, HERE << "close " << theName << ": " << xstrerror());
f5eef98c 73 }
68353d5a
DK
74 if (doUnlink)
75 unlink();
f5eef98c
DK
76}
77
be9cb908 78// fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
c975f532 79bool
9199139f
AR
80Ipc::Mem::Segment::Enabled()
81{
c975f532 82 return true;
c975f532
DK
83}
84
f5eef98c 85void
e08b0f77 86Ipc::Mem::Segment::create(const off_t aSize)
f5eef98c
DK
87{
88 assert(aSize > 0);
89 assert(theFD < 0);
90
f58b1eb4
MM
91 // Why a brand new segment? A Squid crash may leave a reusable segment, but
92 // our placement-new code requires an all-0s segment. We could truncate and
93 // resize the old segment, but OS X does not allow using O_TRUNC with
94 // shm_open() and does not support ftruncate() for old segments.
95 if (!createFresh() && errno == EEXIST) {
96 unlink();
97 createFresh();
98 }
99
f5eef98c 100 if (theFD < 0) {
be1d3736 101 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
77915f06
AR
102 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
103 theName.termedBuf(), xstrerror());
f5eef98c
DK
104 }
105
106 if (ftruncate(theFD, aSize)) {
62fa1d73
FC
107 const int savedError = errno;
108 unlink();
109 debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerr(savedError));
b46ae525 110 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
62fa1d73 111 theName.termedBuf(), xstrerr(savedError));
f5eef98c 112 }
62fa1d73
FC
113 // We assume that the shm_open(O_CREAT)+ftruncate() combo zeros the segment.
114
115 theSize = statSize("Ipc::Mem::Segment::create");
f5eef98c 116
62fa1d73
FC
117 // OS X will round up to a full page, so not checking for exact size match.
118 assert(theSize >= aSize);
e08b0f77 119
937d0d3f 120 theReserved = 0;
68353d5a 121 doUnlink = true;
f5eef98c 122
446485d5
AR
123 debugs(54, 3, HERE << "created " << theName << " segment: " << theSize);
124
f5eef98c
DK
125 attach();
126}
127
128void
d8a82533 129Ipc::Mem::Segment::open()
f5eef98c
DK
130{
131 assert(theFD < 0);
132
133 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
134 if (theFD < 0) {
be1d3736 135 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
77915f06
AR
136 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
137 theName.termedBuf(), xstrerror());
f5eef98c
DK
138 }
139
e08b0f77 140 theSize = statSize("Ipc::Mem::Segment::open");
f5eef98c 141
446485d5
AR
142 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
143
f5eef98c
DK
144 attach();
145}
146
f58b1eb4
MM
147/// Creates a brand new shared memory segment and returns true.
148/// Fails and returns false if there exist an old segment with the same name.
149bool
150Ipc::Mem::Segment::createFresh()
151{
152 theFD = shm_open(theName.termedBuf(),
153 O_EXCL | O_CREAT | O_RDWR,
154 S_IRUSR | S_IWUSR);
155 return theFD >= 0;
156}
157
f5eef98c
DK
158/// Map the shared memory segment to the process memory space.
159void
d8a82533 160Ipc::Mem::Segment::attach()
f5eef98c
DK
161{
162 assert(theFD >= 0);
f5eef98c
DK
163 assert(!theMem);
164
3a2ede14
AR
165 // mmap() accepts size_t for the size; we give it off_t which might
166 // be bigger; assert overflows until we support multiple mmap()s?
167 assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
168
f5eef98c
DK
169 void *const p =
170 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
171 if (p == MAP_FAILED) {
be1d3736 172 debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror());
b46ae525
AR
173 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
174 theName.termedBuf(), xstrerror());
f5eef98c
DK
175 }
176 theMem = p;
177}
178
179/// Unmap the shared memory segment from the process memory space.
180void
d8a82533 181Ipc::Mem::Segment::detach()
f5eef98c
DK
182{
183 if (!theMem)
184 return;
185
186 if (munmap(theMem, theSize)) {
be1d3736 187 debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror());
b46ae525
AR
188 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
189 theName.termedBuf(), xstrerror());
f5eef98c
DK
190 }
191 theMem = 0;
192}
193
68353d5a
DK
194void
195Ipc::Mem::Segment::unlink()
196{
197 if (shm_unlink(theName.termedBuf()) != 0)
198 debugs(54, 5, HERE << "shm_unlink(" << theName << "): " << xstrerror());
199 else
200 debugs(54, 3, HERE << "unlinked " << theName << " segment");
201}
202
e08b0f77
AR
203/// determines the size of the underlying "file"
204off_t
205Ipc::Mem::Segment::statSize(const char *context) const
206{
207 Must(theFD >= 0);
208
209 struct stat s;
210 memset(&s, 0, sizeof(s));
211
212 if (fstat(theFD, &s) != 0) {
b46ae525
AR
213 debugs(54, 5, HERE << context << " fstat " << theName << ": " << xstrerror());
214 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
215 context, theName.termedBuf(), xstrerror());
e08b0f77
AR
216 }
217
218 return s.st_size;
219}
220
ef8de464
AR
221/// Generate name for shared memory segment. Starts with a prefix required
222/// for cross-platform portability and replaces all slashes in ID with dots.
f5eef98c 223String
d8a82533 224Ipc::Mem::Segment::GenerateName(const char *id)
f5eef98c 225{
38189e84 226 assert(BasePath && *BasePath);
ef8de464 227 static const bool nameIsPath = shm_portable_segment_name_is_path();
38189e84
AJ
228 String name;
229 if (nameIsPath) {
230 name.append(BasePath);
231 if (name[name.size()-1] != '/')
232 name.append('/');
51d4bfbb
JLG
233 } else {
234 name.append('/');
235 name.append(service_name.c_str());
236 name.append('-');
237 }
ef8de464
AR
238
239 // append id, replacing slashes with dots
9a51593d
DK
240 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
241 if (id != slash) {
242 name.append(id, slash - id);
243 name.append('.');
244 }
245 id = slash + 1;
246 }
f5eef98c 247 name.append(id);
ef8de464
AR
248
249 name.append(".shm"); // to distinguish from non-segments when nameIsPath
f5eef98c
DK
250 return name;
251}
be9cb908
DK
252
253#else // HAVE_SHM
254
255#include <map>
256
257typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
258static SegmentMap Segments;
259
260Ipc::Mem::Segment::Segment(const char *const id):
f53969cc 261 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
be9cb908
DK
262{
263}
264
265Ipc::Mem::Segment::~Segment()
266{
267 if (doUnlink) {
268 delete [] static_cast<char *>(theMem);
44c64af0
AR
269 theMem = NULL;
270 Segments.erase(theName);
271 debugs(54, 3, HERE << "unlinked " << theName << " fake segment");
be9cb908
DK
272 }
273}
274
275bool
276Ipc::Mem::Segment::Enabled()
277{
38a129ef 278 return !UsingSmp() && IamWorkerProcess();
be9cb908
DK
279}
280
281void
282Ipc::Mem::Segment::create(const off_t aSize)
283{
284 assert(aSize > 0);
285 assert(!theMem);
286 checkSupport("Fake segment creation");
287
288 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
289 if (!inserted)
290 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
291
292 theMem = new char[aSize];
293 theSize = aSize;
294 doUnlink = true;
295
296 debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize);
297}
298
299void
300Ipc::Mem::Segment::open()
301{
302 assert(!theMem);
303 checkSupport("Fake segment open");
304
305 const SegmentMap::const_iterator i = Segments.find(theName);
306 if (i == Segments.end())
307 fatalf("Fake segment not found: %s", theName.termedBuf());
308
309 const Segment &segment = *i->second;
310 theMem = segment.theMem;
311 theSize = segment.theSize;
312
313 debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize);
314}
315
316void
317Ipc::Mem::Segment::checkSupport(const char *const context)
318{
319 if (!Enabled()) {
b46ae525
AR
320 debugs(54, 5, HERE << context <<
321 ": True shared memory segments are not supported. "
be9cb908 322 "Cannot fake shared segments in SMP config.");
b46ae525
AR
323 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
324 context);
be9cb908
DK
325 }
326}
327
328#endif // HAVE_SHM
f5480a9e
DK
329
330void
21b7990f 331Ipc::Mem::RegisteredRunner::useConfig()
f5480a9e
DK
332{
333 // If Squid is built with real segments, we create() real segments
334 // in the master process only. Otherwise, we create() fake
335 // segments in each worker process. We assume that only workers
336 // need and can work with fake segments.
337#if HAVE_SHM
338 if (IamMasterProcess())
339#else
9e3f96a4 340 if (IamWorkerProcess())
f5480a9e 341#endif
21b7990f 342 create();
f5480a9e
DK
343
344 // we assume that master process does not need shared segments
345 // unless it is also a worker
346 if (!InDaemonMode() || !IamMasterProcess())
21b7990f 347 open();
f5480a9e 348}
f53969cc 349