]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ipc/mem/Segment.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ipc / mem / Segment.cc
1 /*
2 * Copyright (C) 1996-2015 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 "Debug.h"
15 #include "fatal.h"
16 #include "ipc/mem/Segment.h"
17 #include "SBuf.h"
18 #include "tools.h"
19
20 #if HAVE_FCNTL_H
21 #include <fcntl.h>
22 #endif
23 #if HAVE_SYS_MMAN_H
24 #include <sys/mman.h>
25 #endif
26 #if HAVE_SYS_STAT_H
27 #include <sys/stat.h>
28 #endif
29 #if HAVE_UNISTD_H
30 #include <unistd.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(NULL),
63 theSize(0), theReserved(0), doUnlink(false)
64 {
65 }
66
67 Ipc::Mem::Segment::~Segment()
68 {
69 if (theFD >= 0) {
70 detach();
71 if (close(theFD) != 0)
72 debugs(54, 5, HERE << "close " << theName << ": " << xstrerror());
73 }
74 if (doUnlink)
75 unlink();
76 }
77
78 // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective
79 bool
80 Ipc::Mem::Segment::Enabled()
81 {
82 return true;
83 }
84
85 void
86 Ipc::Mem::Segment::create(const off_t aSize)
87 {
88 assert(aSize > 0);
89 assert(theFD < 0);
90
91 theFD = shm_open(theName.termedBuf(), O_CREAT | O_RDWR | O_TRUNC,
92 S_IRUSR | S_IWUSR);
93 if (theFD < 0) {
94 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
95 fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n",
96 theName.termedBuf(), xstrerror());
97 }
98
99 if (ftruncate(theFD, aSize)) {
100 debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerror());
101 fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n",
102 theName.termedBuf(), xstrerror());
103 }
104
105 assert(statSize("Ipc::Mem::Segment::create") == aSize); // paranoid
106
107 theSize = aSize;
108 theReserved = 0;
109 doUnlink = true;
110
111 debugs(54, 3, HERE << "created " << theName << " segment: " << theSize);
112
113 attach();
114 }
115
116 void
117 Ipc::Mem::Segment::open()
118 {
119 assert(theFD < 0);
120
121 theFD = shm_open(theName.termedBuf(), O_RDWR, 0);
122 if (theFD < 0) {
123 debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror());
124 fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n",
125 theName.termedBuf(), xstrerror());
126 }
127
128 theSize = statSize("Ipc::Mem::Segment::open");
129
130 debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize);
131
132 attach();
133 }
134
135 /// Map the shared memory segment to the process memory space.
136 void
137 Ipc::Mem::Segment::attach()
138 {
139 assert(theFD >= 0);
140 assert(!theMem);
141
142 // mmap() accepts size_t for the size; we give it off_t which might
143 // be bigger; assert overflows until we support multiple mmap()s?
144 assert(theSize == static_cast<off_t>(static_cast<size_t>(theSize)));
145
146 void *const p =
147 mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0);
148 if (p == MAP_FAILED) {
149 debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror());
150 fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n",
151 theName.termedBuf(), xstrerror());
152 }
153 theMem = p;
154 }
155
156 /// Unmap the shared memory segment from the process memory space.
157 void
158 Ipc::Mem::Segment::detach()
159 {
160 if (!theMem)
161 return;
162
163 if (munmap(theMem, theSize)) {
164 debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror());
165 fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n",
166 theName.termedBuf(), xstrerror());
167 }
168 theMem = 0;
169 }
170
171 void
172 Ipc::Mem::Segment::unlink()
173 {
174 if (shm_unlink(theName.termedBuf()) != 0)
175 debugs(54, 5, HERE << "shm_unlink(" << theName << "): " << xstrerror());
176 else
177 debugs(54, 3, HERE << "unlinked " << theName << " segment");
178 }
179
180 /// determines the size of the underlying "file"
181 off_t
182 Ipc::Mem::Segment::statSize(const char *context) const
183 {
184 Must(theFD >= 0);
185
186 struct stat s;
187 memset(&s, 0, sizeof(s));
188
189 if (fstat(theFD, &s) != 0) {
190 debugs(54, 5, HERE << context << " fstat " << theName << ": " << xstrerror());
191 fatalf("Ipc::Mem::Segment::statSize: %s failed to fstat(%s): %s\n",
192 context, theName.termedBuf(), xstrerror());
193 }
194
195 return s.st_size;
196 }
197
198 /// Generate name for shared memory segment. Starts with a prefix required
199 /// for cross-platform portability and replaces all slashes in ID with dots.
200 String
201 Ipc::Mem::Segment::GenerateName(const char *id)
202 {
203 assert(BasePath && *BasePath);
204 static const bool nameIsPath = shm_portable_segment_name_is_path();
205 String name;
206 if (nameIsPath) {
207 name.append(BasePath);
208 if (name[name.size()-1] != '/')
209 name.append('/');
210 } else
211 name.append("/squid-");
212
213 // append id, replacing slashes with dots
214 for (const char *slash = strchr(id, '/'); slash; slash = strchr(id, '/')) {
215 if (id != slash) {
216 name.append(id, slash - id);
217 name.append('.');
218 }
219 id = slash + 1;
220 }
221 name.append(id);
222
223 name.append(".shm"); // to distinguish from non-segments when nameIsPath
224 return name;
225 }
226
227 #else // HAVE_SHM
228
229 #include <map>
230
231 typedef std::map<String, Ipc::Mem::Segment *> SegmentMap;
232 static SegmentMap Segments;
233
234 Ipc::Mem::Segment::Segment(const char *const id):
235 theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false)
236 {
237 }
238
239 Ipc::Mem::Segment::~Segment()
240 {
241 if (doUnlink) {
242 delete [] static_cast<char *>(theMem);
243 theMem = NULL;
244 Segments.erase(theName);
245 debugs(54, 3, HERE << "unlinked " << theName << " fake segment");
246 }
247 }
248
249 bool
250 Ipc::Mem::Segment::Enabled()
251 {
252 return !UsingSmp() && IamWorkerProcess();
253 }
254
255 void
256 Ipc::Mem::Segment::create(const off_t aSize)
257 {
258 assert(aSize > 0);
259 assert(!theMem);
260 checkSupport("Fake segment creation");
261
262 const bool inserted = Segments.insert(std::make_pair(theName, this)).second;
263 if (!inserted)
264 fatalf("Duplicate fake segment creation: %s", theName.termedBuf());
265
266 theMem = new char[aSize];
267 theSize = aSize;
268 doUnlink = true;
269
270 debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize);
271 }
272
273 void
274 Ipc::Mem::Segment::open()
275 {
276 assert(!theMem);
277 checkSupport("Fake segment open");
278
279 const SegmentMap::const_iterator i = Segments.find(theName);
280 if (i == Segments.end())
281 fatalf("Fake segment not found: %s", theName.termedBuf());
282
283 const Segment &segment = *i->second;
284 theMem = segment.theMem;
285 theSize = segment.theSize;
286
287 debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize);
288 }
289
290 void
291 Ipc::Mem::Segment::checkSupport(const char *const context)
292 {
293 if (!Enabled()) {
294 debugs(54, 5, HERE << context <<
295 ": True shared memory segments are not supported. "
296 "Cannot fake shared segments in SMP config.");
297 fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n",
298 context);
299 }
300 }
301
302 #endif // HAVE_SHM
303
304 void
305 Ipc::Mem::RegisteredRunner::useConfig()
306 {
307 // If Squid is built with real segments, we create() real segments
308 // in the master process only. Otherwise, we create() fake
309 // segments in each worker process. We assume that only workers
310 // need and can work with fake segments.
311 #if HAVE_SHM
312 if (IamMasterProcess())
313 #else
314 if (IamWorkerProcess())
315 #endif
316 create();
317
318 // we assume that master process does not need shared segments
319 // unless it is also a worker
320 if (!InDaemonMode() || !IamMasterProcess())
321 open();
322 }
323