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