]>
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); | |
215 | debugs(54, 3, HERE << "deleted " << theName << " segment"); | |
216 | } | |
217 | } | |
218 | ||
219 | bool | |
220 | Ipc::Mem::Segment::Enabled() | |
221 | { | |
222 | return !InDaemonMode() || (!UsingSmp() && IamWorkerProcess()); | |
223 | } | |
224 | ||
225 | void | |
226 | Ipc::Mem::Segment::create(const off_t aSize) | |
227 | { | |
228 | assert(aSize > 0); | |
229 | assert(!theMem); | |
230 | checkSupport("Fake segment creation"); | |
231 | ||
232 | const bool inserted = Segments.insert(std::make_pair(theName, this)).second; | |
233 | if (!inserted) | |
234 | fatalf("Duplicate fake segment creation: %s", theName.termedBuf()); | |
235 | ||
236 | theMem = new char[aSize]; | |
237 | theSize = aSize; | |
238 | doUnlink = true; | |
239 | ||
240 | debugs(54, 3, HERE << "created " << theName << " fake segment: " << theSize); | |
241 | } | |
242 | ||
243 | void | |
244 | Ipc::Mem::Segment::open() | |
245 | { | |
246 | assert(!theMem); | |
247 | checkSupport("Fake segment open"); | |
248 | ||
249 | const SegmentMap::const_iterator i = Segments.find(theName); | |
250 | if (i == Segments.end()) | |
251 | fatalf("Fake segment not found: %s", theName.termedBuf()); | |
252 | ||
253 | const Segment &segment = *i->second; | |
254 | theMem = segment.theMem; | |
255 | theSize = segment.theSize; | |
256 | ||
257 | debugs(54, 3, HERE << "opened " << theName << " fake segment: " << theSize); | |
258 | } | |
259 | ||
260 | void | |
261 | Ipc::Mem::Segment::checkSupport(const char *const context) | |
262 | { | |
263 | if (!Enabled()) { | |
b46ae525 AR |
264 | debugs(54, 5, HERE << context << |
265 | ": True shared memory segments are not supported. " | |
be9cb908 | 266 | "Cannot fake shared segments in SMP config."); |
b46ae525 AR |
267 | fatalf("Ipc::Mem::Segment: Cannot fake shared segments in SMP config (%s)\n", |
268 | context); | |
be9cb908 DK |
269 | } |
270 | } | |
271 | ||
272 | #endif // HAVE_SHM | |
f5480a9e DK |
273 | |
274 | void | |
275 | Ipc::Mem::RegisteredRunner::run(const RunnerRegistry &r) | |
276 | { | |
277 | // If Squid is built with real segments, we create() real segments | |
278 | // in the master process only. Otherwise, we create() fake | |
279 | // segments in each worker process. We assume that only workers | |
280 | // need and can work with fake segments. | |
281 | #if HAVE_SHM | |
282 | if (IamMasterProcess()) | |
283 | #else | |
9e3f96a4 | 284 | if (IamWorkerProcess()) |
f5480a9e DK |
285 | #endif |
286 | create(r); | |
287 | ||
288 | // we assume that master process does not need shared segments | |
289 | // unless it is also a worker | |
290 | if (!InDaemonMode() || !IamMasterProcess()) | |
291 | open(r); | |
292 | } |