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