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