]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/mem/Segment.cc
Simplified code. No functionality changes expected.
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 54 Interprocess Communication
5 *
6 */
7
8 #include "config.h"
9 #include "base/TextException.h"
10 #include "compat/shm.h"
11 #include "ipc/mem/Segment.h"
12 #include "protos.h"
13
14 #include <fcntl.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19
20 // test cases change this
21 const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR;
22
23 void *
24 Ipc::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
39 Ipc::Mem::Segment::Segment(const char *const id):
40 theFD(-1), theName(GenerateName(id)), theMem(NULL),
41 theSize(0), theReserved(0), doUnlink(false)
42 {
43 }
44
45 Ipc::Mem::Segment::~Segment()
46 {
47 if (theFD >= 0) {
48 detach();
49 if (close(theFD) != 0)
50 debugs(54, 5, HERE << "close " << theName << ": " << xstrerror());
51 }
52 if (doUnlink)
53 unlink();
54 }
55
56 // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
57 bool
58 Ipc::Mem::Segment::Enabled()
59 {
60 return true;
61 }
62
63 void
64 Ipc::Mem::Segment::create(const off_t aSize)
65 {
66 assert(aSize > 0);
67 assert(theFD < 0);
68
69 theFD = shm_open(theName.termedBuf(), O_CREAT | O_RDWR | O_TRUNC,
70 S_IRUSR | S_IWUSR);
71 if (theFD < 0) {
72 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
73 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
74 theName.termedBuf(), xstrerror());
75 }
76
77 if (ftruncate(theFD, aSize)) {
78 debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerror());
79 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
80 theName.termedBuf(), xstrerror());
81 }
82
83 assert(statSize("Ipc::Mem::Segment::create") == aSize); // paranoid
84
85 theSize = aSize;
86 theReserved = 0;
87 doUnlink = true;
88
89 debugs(54, 3, HERE << "created " << theName << " segment: " << theSize);
90
91 attach();
92 }
93
94 void
95 Ipc::Mem::Segment::open()
96 {
97 assert(theFD < 0);
98
99 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
100 if (theFD < 0) {
101 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
102 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
103 theName.termedBuf(), xstrerror());
104 }
105
106 theSize = statSize("Ipc::Mem::Segment::open");
107
108 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
109
110 attach();
111 }
112
113 /// Map the shared memory segment to the process memory space.
114 void
115 Ipc::Mem::Segment::attach()
116 {
117 assert(theFD >= 0);
118 assert(!theMem);
119
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
124 void *const p =
125 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
126 if (p == MAP_FAILED) {
127 debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror());
128 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
129 theName.termedBuf(), xstrerror());
130 }
131 theMem = p;
132 }
133
134 /// Unmap the shared memory segment from the process memory space.
135 void
136 Ipc::Mem::Segment::detach()
137 {
138 if (!theMem)
139 return;
140
141 if (munmap(theMem, theSize)) {
142 debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror());
143 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
144 theName.termedBuf(), xstrerror());
145 }
146 theMem = 0;
147 }
148
149 void
150 Ipc::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
158 /// determines the size of the underlying "file"
159 off_t
160 Ipc::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) {
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());
171 }
172
173 return s.st_size;
174 }
175
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.
178 String
179 Ipc::Mem::Segment::GenerateName(const char *id)
180 {
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
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 }
193 name.append(id);
194
195 name.append(".shm"); // to distinguish from non-segments when nameIsPath
196 return name;
197 }
198
199 #else // HAVE_SHM
200
201 #include <map>
202
203 typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
204 static SegmentMap Segments;
205
206 Ipc::Mem::Segment::Segment(const char *const id):
207 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
208 {
209 }
210
211 Ipc::Mem::Segment::~Segment()
212 {
213 if (doUnlink) {
214 delete [] static_cast<char *>(theMem);
215 theMem = NULL;
216 Segments.erase(theName);
217 debugs(54, 3, HERE << "unlinked " << theName << " fake segment");
218 }
219 }
220
221 bool
222 Ipc::Mem::Segment::Enabled()
223 {
224 return !UsingSmp() && IamWorkerProcess();
225 }
226
227 void
228 Ipc::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
245 void
246 Ipc::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
262 void
263 Ipc::Mem::Segment::checkSupport(const char *const context)
264 {
265 if (!Enabled()) {
266 debugs(54, 5, HERE << context <<
267 ": True shared memory segments are not supported. "
268 "Cannot fake shared segments in SMP config.");
269 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
270 context);
271 }
272 }
273
274 #endif // HAVE_SHM
275
276 void
277 Ipc::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
286 if (IamWorkerProcess())
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 }