]>
Commit | Line | Data |
---|---|---|
f5eef98c DK |
1 | /* |
2 | * $Id$ | |
3 | * | |
4 | * DEBUG: section 54 Interprocess Communication | |
5 | * | |
6 | */ | |
7 | ||
8 | #include "config.h" | |
e08b0f77 | 9 | #include "base/TextException.h" |
c975f532 | 10 | #include "compat/shm.h" |
d8a82533 | 11 | #include "ipc/mem/Segment.h" |
f5eef98c DK |
12 | #include "protos.h" |
13 | ||
14 | #include <fcntl.h> | |
15 | #include <sys/mman.h> | |
16 | #include <sys/stat.h> | |
f5eef98c DK |
17 | #include <unistd.h> |
18 | ||
ef8de464 AR |
19 | |
20 | // test cases change this | |
21 | const char *Ipc::Mem::Segment::BasePath = DEFAULT_STATEDIR; | |
22 | ||
be9cb908 DK |
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 | ||
d8a82533 | 39 | Ipc::Mem::Segment::Segment(const char *const id): |
be9cb908 | 40 | theFD(-1), theName(GenerateName(id)), theMem(NULL), |
9199139f | 41 | theSize(0), theReserved(0), doUnlink(false) |
f5eef98c DK |
42 | { |
43 | } | |
44 | ||
9199139f AR |
45 | Ipc::Mem::Segment::~Segment() |
46 | { | |
f5eef98c DK |
47 | if (theFD >= 0) { |
48 | detach(); | |
d8a82533 | 49 | if (close(theFD) != 0) |
be1d3736 | 50 | debugs(54, 5, HERE << "close " << theName << ": " << xstrerror()); |
f5eef98c | 51 | } |
68353d5a DK |
52 | if (doUnlink) |
53 | unlink(); | |
f5eef98c DK |
54 | } |
55 | ||
be9cb908 | 56 | // fake Ipc::Mem::Segment::Enabled (!HAVE_SHM) is more selective |
c975f532 | 57 | bool |
9199139f AR |
58 | Ipc::Mem::Segment::Enabled() |
59 | { | |
c975f532 | 60 | return true; |
c975f532 DK |
61 | } |
62 | ||
f5eef98c | 63 | void |
e08b0f77 | 64 | Ipc::Mem::Segment::create(const off_t aSize) |
f5eef98c DK |
65 | { |
66 | assert(aSize > 0); | |
67 | assert(theFD < 0); | |
68 | ||
e1825c5d | 69 | theFD = shm_open(theName.termedBuf(), O_CREAT | O_RDWR | O_TRUNC, |
f5eef98c DK |
70 | S_IRUSR | S_IWUSR); |
71 | if (theFD < 0) { | |
be1d3736 | 72 | debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror()); |
77915f06 AR |
73 | fatalf("Ipc::Mem::Segment::create failed to shm_open(%s): %s\n", |
74 | theName.termedBuf(), xstrerror()); | |
f5eef98c DK |
75 | } |
76 | ||
77 | if (ftruncate(theFD, aSize)) { | |
be1d3736 | 78 | debugs(54, 5, HERE << "ftruncate " << theName << ": " << xstrerror()); |
b46ae525 AR |
79 | fatalf("Ipc::Mem::Segment::create failed to ftruncate(%s): %s\n", |
80 | theName.termedBuf(), xstrerror()); | |
f5eef98c DK |
81 | } |
82 | ||
e08b0f77 AR |
83 | assert(statSize("Ipc::Mem::Segment::create") == aSize); // paranoid |
84 | ||
f5eef98c | 85 | theSize = aSize; |
937d0d3f | 86 | theReserved = 0; |
68353d5a | 87 | doUnlink = true; |
f5eef98c | 88 | |
446485d5 AR |
89 | debugs(54, 3, HERE << "created " << theName << " segment: " << theSize); |
90 | ||
f5eef98c DK |
91 | attach(); |
92 | } | |
93 | ||
94 | void | |
d8a82533 | 95 | Ipc::Mem::Segment::open() |
f5eef98c DK |
96 | { |
97 | assert(theFD < 0); | |
98 | ||
99 | theFD = shm_open(theName.termedBuf(), O_RDWR, 0); | |
100 | if (theFD < 0) { | |
be1d3736 | 101 | debugs(54, 5, HERE << "shm_open " << theName << ": " << xstrerror()); |
77915f06 AR |
102 | fatalf("Ipc::Mem::Segment::open failed to shm_open(%s): %s\n", |
103 | theName.termedBuf(), xstrerror()); | |
f5eef98c DK |
104 | } |
105 | ||
e08b0f77 | 106 | theSize = statSize("Ipc::Mem::Segment::open"); |
f5eef98c | 107 | |
446485d5 AR |
108 | debugs(54, 3, HERE << "opened " << theName << " segment: " << theSize); |
109 | ||
f5eef98c DK |
110 | attach(); |
111 | } | |
112 | ||
113 | /// Map the shared memory segment to the process memory space. | |
114 | void | |
d8a82533 | 115 | Ipc::Mem::Segment::attach() |
f5eef98c DK |
116 | { |
117 | assert(theFD >= 0); | |
f5eef98c DK |
118 | assert(!theMem); |
119 | ||
3a2ede14 AR |
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 | ||
f5eef98c DK |
124 | void *const p = |
125 | mmap(NULL, theSize, PROT_READ | PROT_WRITE, MAP_SHARED, theFD, 0); | |
126 | if (p == MAP_FAILED) { | |
be1d3736 | 127 | debugs(54, 5, HERE << "mmap " << theName << ": " << xstrerror()); |
b46ae525 AR |
128 | fatalf("Ipc::Mem::Segment::attach failed to mmap(%s): %s\n", |
129 | theName.termedBuf(), xstrerror()); | |
f5eef98c DK |
130 | } |
131 | theMem = p; | |
132 | } | |
133 | ||
134 | /// Unmap the shared memory segment from the process memory space. | |
135 | void | |
d8a82533 | 136 | Ipc::Mem::Segment::detach() |
f5eef98c DK |
137 | { |
138 | if (!theMem) | |
139 | return; | |
140 | ||
141 | if (munmap(theMem, theSize)) { | |
be1d3736 | 142 | debugs(54, 5, HERE << "munmap " << theName << ": " << xstrerror()); |
b46ae525 AR |
143 | fatalf("Ipc::Mem::Segment::detach failed to munmap(%s): %s\n", |
144 | theName.termedBuf(), xstrerror()); | |
f5eef98c DK |
145 | } |
146 | theMem = 0; | |
147 | } | |
148 | ||
68353d5a DK |
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 | ||
e08b0f77 AR |
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) { | |
b46ae525 AR |
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()); | |
e08b0f77 AR |
171 | } |
172 | ||
173 | return s.st_size; | |
174 | } | |
175 | ||
ef8de464 AR |
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. | |
f5eef98c | 178 | String |
d8a82533 | 179 | Ipc::Mem::Segment::GenerateName(const char *id) |
f5eef98c | 180 | { |
ef8de464 AR |
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 | |
9a51593d DK |
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 | } | |
f5eef98c | 193 | name.append(id); |
ef8de464 AR |
194 | |
195 | name.append(".shm"); // to distinguish from non-segments when nameIsPath | |
f5eef98c DK |
196 | return name; |
197 | } | |
be9cb908 DK |
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): | |
e29ccb57 | 207 | theName(id), theMem(NULL), theSize(0), theReserved(0), doUnlink(false) |
be9cb908 DK |
208 | { |
209 | } | |
210 | ||
211 | Ipc::Mem::Segment::~Segment() | |
212 | { | |
213 | if (doUnlink) { | |
214 | delete [] static_cast<char *>(theMem); | |
44c64af0 AR |
215 | theMem = NULL; |
216 | Segments.erase(theName); | |
217 | debugs(54, 3, HERE << "unlinked " << theName << " fake segment"); | |
be9cb908 DK |
218 | } |
219 | } | |
220 | ||
221 | bool | |
222 | Ipc::Mem::Segment::Enabled() | |
223 | { | |
224 | return !InDaemonMode() || (!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()) { | |
b46ae525 AR |
266 | debugs(54, 5, HERE << context << |
267 | ": True shared memory segments are not supported. " | |
be9cb908 | 268 | "Cannot fake shared segments in SMP config."); |
b46ae525 AR |
269 | fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n", |
270 | context); | |
be9cb908 DK |
271 | } |
272 | } | |
273 | ||
274 | #endif // HAVE_SHM | |
f5480a9e DK |
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 | |
9e3f96a4 | 286 | if (IamWorkerProcess()) |
f5480a9e DK |
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 | } |