]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ipc/mem/Segment.cc
Bug 3383: unhandled exception: theGroupBSize > 0
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
CommitLineData
f5eef98c
DK
1/*
2 * $Id$
3 *
4 * DEBUG: section 54 Interprocess Communication
5 *
6 */
7
8#include "config.h"
e08b0f77 9#include "base/TextException.h"
c975f532 10#include "compat/shm.h"
d8a82533 11#include "ipc/mem/Segment.h"
f5eef98c
DK
12#include "protos.h"
13
14#include <fcntl.h>
15#include <sys/mman.h>
16#include <sys/stat.h>
f5eef98c
DK
17#include <unistd.h>
18
ef8de464
AR
19
20// test cases change this
21const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
22
be9cb908
DK
23void *
24Ipc::Mem::Segment::reserve(size_t chunkSize)
25{
26 Must(theMem);
27 // check for overflows
28 // chunkSize >= 0 may result in warnings on systems where off_t is unsigned
29 assert(!chunkSize || static_cast<off_t>(chunkSize) > 0);
30 assert(static_cast<off_t>(chunkSize) <= theSize);
31 assert(theReserved <= theSize - static_cast<off_t>(chunkSize));
32 void *result = reinterpret_cast<char*>(theMem) + theReserved;
33 theReserved += chunkSize;
34 return result;
35}
36
37#if HAVE_SHM
38
d8a82533 39Ipc::Mem::Segment::Segment(const char *const id):
be9cb908 40 theFD(-1), theName(GenerateName(id)), theMem(NULL),
9199139f 41 theSize(0), theReserved(0), doUnlink(false)
f5eef98c
DK
42{
43}
44
9199139f
AR
45Ipc::Mem::Segment::~Segment()
46{
f5eef98c
DK
47 if (theFD >= 0) {
48 detach();
d8a82533 49 if (close(theFD) != 0)
be1d3736 50 debugs(54, 5, HERE << "close " << theName << ": " << xstrerror());
f5eef98c 51 }
68353d5a
DK
52 if (doUnlink)
53 unlink();
f5eef98c
DK
54}
55
be9cb908 56// fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
c975f532 57bool
9199139f
AR
58Ipc::Mem::Segment::Enabled()
59{
c975f532 60 return true;
c975f532
DK
61}
62
f5eef98c 63void
e08b0f77 64Ipc::Mem::Segment::create(const off_t aSize)
f5eef98c
DK
65{
66 assert(aSize > 0);
67 assert(theFD < 0);
68
e1825c5d 69 theFD = shm_open(theName.termedBuf(), O_CREAT | O_RDWR | O_TRUNC,
f5eef98c
DK
70 S_IRUSR | S_IWUSR);
71 if (theFD < 0) {
be1d3736 72 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
77915f06
AR
73 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
74 theName.termedBuf(), xstrerror());
f5eef98c
DK
75 }
76
77 if (ftruncate(theFD, aSize)) {
be1d3736 78 debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerror());
b46ae525
AR
79 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
80 theName.termedBuf(), xstrerror());
f5eef98c
DK
81 }
82
e08b0f77
AR
83 assert(statSize("Ipc::Mem::Segment::create") == aSize); // paranoid
84
f5eef98c 85 theSize = aSize;
937d0d3f 86 theReserved = 0;
68353d5a 87 doUnlink = true;
f5eef98c 88
446485d5
AR
89 debugs(54, 3, HERE << "created " << theName << " segment: " << theSize);
90
f5eef98c
DK
91 attach();
92}
93
94void
d8a82533 95Ipc::Mem::Segment::open()
f5eef98c
DK
96{
97 assert(theFD < 0);
98
99 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
100 if (theFD < 0) {
be1d3736 101 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
77915f06
AR
102 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
103 theName.termedBuf(), xstrerror());
f5eef98c
DK
104 }
105
e08b0f77 106 theSize = statSize("Ipc::Mem::Segment::open");
f5eef98c 107
446485d5
AR
108 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
109
f5eef98c
DK
110 attach();
111}
112
113/// Map the shared memory segment to the process memory space.
114void
d8a82533 115Ipc::Mem::Segment::attach()
f5eef98c
DK
116{
117 assert(theFD >= 0);
f5eef98c
DK
118 assert(!theMem);
119
3a2ede14
AR
120 // mmap() accepts size_t for the size; we give it off_t which might
121 // be bigger; assert overflows until we support multiple mmap()s?
122 assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
123
f5eef98c
DK
124 void *const p =
125 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
126 if (p == MAP_FAILED) {
be1d3736 127 debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror());
b46ae525
AR
128 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
129 theName.termedBuf(), xstrerror());
f5eef98c
DK
130 }
131 theMem = p;
132}
133
134/// Unmap the shared memory segment from the process memory space.
135void
d8a82533 136Ipc::Mem::Segment::detach()
f5eef98c
DK
137{
138 if (!theMem)
139 return;
140
141 if (munmap(theMem, theSize)) {
be1d3736 142 debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror());
b46ae525
AR
143 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
144 theName.termedBuf(), xstrerror());
f5eef98c
DK
145 }
146 theMem = 0;
147}
148
68353d5a
DK
149void
150Ipc::Mem::Segment::unlink()
151{
152 if (shm_unlink(theName.termedBuf()) != 0)
153 debugs(54, 5, HERE << "shm_unlink(" << theName << "): " << xstrerror());
154 else
155 debugs(54, 3, HERE << "unlinked " << theName << " segment");
156}
157
e08b0f77
AR
158/// determines the size of the underlying "file"
159off_t
160Ipc::Mem::Segment::statSize(const char *context) const
161{
162 Must(theFD >= 0);
163
164 struct stat s;
165 memset(&s, 0, sizeof(s));
166
167 if (fstat(theFD, &s) != 0) {
b46ae525
AR
168 debugs(54, 5, HERE << context << " fstat " << theName << ": " << xstrerror());
169 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
170 context, theName.termedBuf(), xstrerror());
e08b0f77
AR
171 }
172
173 return s.st_size;
174}
175
ef8de464
AR
176/// Generate name for shared memory segment. Starts with a prefix required
177/// for cross-platform portability and replaces all slashes in ID with dots.
f5eef98c 178String
d8a82533 179Ipc::Mem::Segment::GenerateName(const char *id)
f5eef98c 180{
ef8de464
AR
181 assert(BasePath);
182 static const bool nameIsPath = shm_portable_segment_name_is_path();
183 String name(nameIsPath ? BasePath : "/squid-");
184
185 // append id, replacing slashes with dots
9a51593d
DK
186 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
187 if (id != slash) {
188 name.append(id, slash - id);
189 name.append('.');
190 }
191 id = slash + 1;
192 }
f5eef98c 193 name.append(id);
ef8de464
AR
194
195 name.append(".shm"); // to distinguish from non-segments when nameIsPath
f5eef98c
DK
196 return name;
197}
be9cb908
DK
198
199#else // HAVE_SHM
200
201#include <map>
202
203typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
204static SegmentMap Segments;
205
206Ipc::Mem::Segment::Segment(const char *const id):
e29ccb57 207 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
be9cb908
DK
208{
209}
210
211Ipc::Mem::Segment::~Segment()
212{
213 if (doUnlink) {
214 delete [] static_cast<char *>(theMem);
44c64af0
AR
215 theMem = NULL;
216 Segments.erase(theName);
217 debugs(54, 3, HERE << "unlinked " << theName << " fake segment");
be9cb908
DK
218 }
219}
220
221bool
222Ipc::Mem::Segment::Enabled()
223{
224 return !InDaemonMode() || (!UsingSmp() && IamWorkerProcess());
225}
226
227void
228Ipc::Mem::Segment::create(const off_t aSize)
229{
230 assert(aSize > 0);
231 assert(!theMem);
232 checkSupport("Fake segment creation");
233
234 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
235 if (!inserted)
236 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
237
238 theMem = new char[aSize];
239 theSize = aSize;
240 doUnlink = true;
241
242 debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize);
243}
244
245void
246Ipc::Mem::Segment::open()
247{
248 assert(!theMem);
249 checkSupport("Fake segment open");
250
251 const SegmentMap::const_iterator i = Segments.find(theName);
252 if (i == Segments.end())
253 fatalf("Fake segment not found: %s", theName.termedBuf());
254
255 const Segment &segment = *i->second;
256 theMem = segment.theMem;
257 theSize = segment.theSize;
258
259 debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize);
260}
261
262void
263Ipc::Mem::Segment::checkSupport(const char *const context)
264{
265 if (!Enabled()) {
b46ae525
AR
266 debugs(54, 5, HERE << context <<
267 ": True shared memory segments are not supported. "
be9cb908 268 "Cannot fake shared segments in SMP config.");
b46ae525
AR
269 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
270 context);
be9cb908
DK
271 }
272}
273
274#endif // HAVE_SHM
f5480a9e
DK
275
276void
277Ipc::Mem::RegisteredRunner::run(const RunnerRegistry &r)
278{
279 // If Squid is built with real segments, we create() real segments
280 // in the master process only. Otherwise, we create() fake
281 // segments in each worker process. We assume that only workers
282 // need and can work with fake segments.
283#if HAVE_SHM
284 if (IamMasterProcess())
285#else
9e3f96a4 286 if (IamWorkerProcess())
f5480a9e
DK
287#endif
288 create(r);
289
290 // we assume that master process does not need shared segments
291 // unless it is also a worker
292 if (!InDaemonMode() || !IamMasterProcess())
293 open(r);
294}