/src/httpd/srclib/apr/shmem/unix/shm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Licensed to the Apache Software Foundation (ASF) under one or more |
2 | | * contributor license agreements. See the NOTICE file distributed with |
3 | | * this work for additional information regarding copyright ownership. |
4 | | * The ASF licenses this file to You under the Apache License, Version 2.0 |
5 | | * (the "License"); you may not use this file except in compliance with |
6 | | * the License. You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | |
17 | | #include "apr_arch_shm.h" |
18 | | #include "apr_arch_file_io.h" |
19 | | |
20 | | #include "apr_general.h" |
21 | | #include "apr_errno.h" |
22 | | #include "apr_user.h" |
23 | | #include "apr_strings.h" |
24 | | #include "apr_hash.h" |
25 | | |
26 | | #if APR_USE_SHMEM_MMAP_SHM |
27 | | /* |
28 | | * For portable use, a shared memory object should be identified by a name of |
29 | | * the form /somename; that is, a null-terminated string of up to NAME_MAX |
30 | | * (i.e., 255) characters consisting of an initial slash, followed by one or |
31 | | * more characters, none of which are slashes. |
32 | | */ |
33 | | #ifndef NAME_MAX |
34 | | #define NAME_MAX 255 |
35 | | #endif |
36 | | |
37 | | /* See proc_mutex.c and sem_open for the reason for all this! */ |
38 | 0 | static unsigned int rshash (const char *p) { |
39 | | /* hash function from Robert Sedgwicks 'Algorithms in C' book */ |
40 | 0 | unsigned int b = 378551; |
41 | 0 | unsigned int a = 63689; |
42 | 0 | unsigned int retval = 0; |
43 | |
|
44 | 0 | for( ; *p; p++) { |
45 | 0 | retval = retval * a + (*p); |
46 | 0 | a *= b; |
47 | 0 | } |
48 | |
|
49 | 0 | return retval; |
50 | 0 | } |
51 | | |
52 | | static const char *make_shm_open_safe_name(const char *filename, |
53 | | apr_pool_t *pool) |
54 | 0 | { |
55 | 0 | apr_ssize_t flen; |
56 | 0 | unsigned int h1, h2; |
57 | |
|
58 | 0 | if (filename == NULL) { |
59 | 0 | return NULL; |
60 | 0 | } |
61 | | |
62 | 0 | flen = strlen(filename); |
63 | 0 | h1 = (apr_hashfunc_default(filename, &flen) & 0xffffffff); |
64 | 0 | h2 = (rshash(filename) & 0xffffffff); |
65 | 0 | return apr_psprintf(pool, "/ShM.%xH%x", h1, h2); |
66 | |
|
67 | 0 | } |
68 | | #endif |
69 | | |
70 | | #if APR_USE_SHMEM_SHMGET |
71 | | static key_t our_ftok(const char *filename) |
72 | | { |
73 | | /* to help avoid collisions while still using |
74 | | * an easily recreated proj_id */ |
75 | | apr_ssize_t slen = strlen(filename); |
76 | | return ftok(filename, |
77 | | (int)apr_hashfunc_default(filename, &slen)); |
78 | | } |
79 | | #endif |
80 | | |
81 | | static apr_status_t shm_cleanup_owner(void *m_) |
82 | 0 | { |
83 | 0 | apr_shm_t *m = (apr_shm_t *)m_; |
84 | | |
85 | | /* anonymous shared memory */ |
86 | 0 | if (m->filename == NULL) { |
87 | 0 | #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON |
88 | 0 | if (munmap(m->base, m->realsize) == -1) { |
89 | 0 | return errno; |
90 | 0 | } |
91 | 0 | return APR_SUCCESS; |
92 | | #elif APR_USE_SHMEM_SHMGET_ANON |
93 | | if (shmdt(m->base) == -1) { |
94 | | return errno; |
95 | | } |
96 | | /* This segment will automatically remove itself after all |
97 | | * references have detached. */ |
98 | | return APR_SUCCESS; |
99 | | #endif |
100 | 0 | } |
101 | | |
102 | | /* name-based shared memory */ |
103 | 0 | else { |
104 | | #if APR_USE_SHMEM_MMAP_TMP |
105 | | if (munmap(m->base, m->realsize) == -1) { |
106 | | return errno; |
107 | | } |
108 | | if (access(m->filename, F_OK)) { |
109 | | return APR_SUCCESS; |
110 | | } |
111 | | else { |
112 | | return apr_file_remove(m->filename, m->pool); |
113 | | } |
114 | | #elif APR_USE_SHMEM_MMAP_SHM |
115 | 0 | if (munmap(m->base, m->realsize) == -1) { |
116 | 0 | return errno; |
117 | 0 | } |
118 | 0 | if (shm_unlink(make_shm_open_safe_name(m->filename, m->pool)) == -1 && errno != ENOENT) { |
119 | 0 | return errno; |
120 | 0 | } |
121 | 0 | return APR_SUCCESS; |
122 | | #elif APR_USE_SHMEM_SHMGET |
123 | | /* Indicate that the segment is to be destroyed as soon |
124 | | * as all processes have detached. This also disallows any |
125 | | * new attachments to the segment. */ |
126 | | if (shmctl(m->shmid, IPC_RMID, NULL) == -1 && errno != EINVAL) { |
127 | | return errno; |
128 | | } |
129 | | if (shmdt(m->base) == -1) { |
130 | | return errno; |
131 | | } |
132 | | if (access(m->filename, F_OK)) { |
133 | | return APR_SUCCESS; |
134 | | } |
135 | | else { |
136 | | return apr_file_remove(m->filename, m->pool); |
137 | | } |
138 | | #else |
139 | | return APR_ENOTIMPL; |
140 | | #endif |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m, |
145 | | apr_size_t reqsize, |
146 | | const char *filename, |
147 | | apr_pool_t *pool) |
148 | 0 | { |
149 | 0 | apr_shm_t *new_m; |
150 | 0 | apr_status_t status; |
151 | | #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON |
152 | | struct shmid_ds shmbuf; |
153 | | apr_uid_t uid; |
154 | | apr_gid_t gid; |
155 | | #endif |
156 | 0 | #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \ |
157 | 0 | APR_USE_SHMEM_MMAP_ZERO |
158 | 0 | int tmpfd; |
159 | 0 | #endif |
160 | | #if APR_USE_SHMEM_SHMGET |
161 | | apr_size_t nbytes; |
162 | | #endif |
163 | | #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \ |
164 | | APR_USE_SHMEM_MMAP_TMP |
165 | | apr_file_t *file; /* file where metadata is stored */ |
166 | | #endif |
167 | | |
168 | | /* Check if they want anonymous or name-based shared memory */ |
169 | 0 | if (filename == NULL) { |
170 | 0 | #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON |
171 | 0 | new_m = apr_palloc(pool, sizeof(apr_shm_t)); |
172 | 0 | new_m->pool = pool; |
173 | 0 | new_m->reqsize = reqsize; |
174 | 0 | new_m->realsize = reqsize + |
175 | 0 | APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */ |
176 | 0 | new_m->filename = NULL; |
177 | |
|
178 | | #if APR_USE_SHMEM_MMAP_ZERO |
179 | | status = apr_file_open(&file, "/dev/zero", APR_FOPEN_READ | APR_FOPEN_WRITE, |
180 | | APR_FPROT_OS_DEFAULT, pool); |
181 | | if (status != APR_SUCCESS) { |
182 | | return status; |
183 | | } |
184 | | status = apr_os_file_get(&tmpfd, file); |
185 | | if (status != APR_SUCCESS) { |
186 | | return status; |
187 | | } |
188 | | |
189 | | new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE, |
190 | | MAP_SHARED, tmpfd, 0); |
191 | | if (new_m->base == (void *)MAP_FAILED) { |
192 | | return errno; |
193 | | } |
194 | | |
195 | | status = apr_file_close(file); |
196 | | if (status != APR_SUCCESS) { |
197 | | return status; |
198 | | } |
199 | | |
200 | | /* store the real size in the metadata */ |
201 | | *(apr_size_t*)(new_m->base) = new_m->realsize; |
202 | | /* metadata isn't usable */ |
203 | | new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t)); |
204 | | |
205 | | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner, |
206 | | apr_pool_cleanup_null); |
207 | | *m = new_m; |
208 | | return APR_SUCCESS; |
209 | | |
210 | | #elif APR_USE_SHMEM_MMAP_ANON |
211 | | new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE, |
212 | 0 | MAP_ANON|MAP_SHARED, -1, 0); |
213 | 0 | if (new_m->base == (void *)MAP_FAILED) { |
214 | 0 | return errno; |
215 | 0 | } |
216 | | |
217 | | /* store the real size in the metadata */ |
218 | 0 | *(apr_size_t*)(new_m->base) = new_m->realsize; |
219 | | /* metadata isn't usable */ |
220 | 0 | new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t)); |
221 | |
|
222 | 0 | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner, |
223 | 0 | apr_pool_cleanup_null); |
224 | 0 | *m = new_m; |
225 | 0 | return APR_SUCCESS; |
226 | |
|
227 | 0 | #endif /* APR_USE_SHMEM_MMAP_ZERO */ |
228 | | #elif APR_USE_SHMEM_SHMGET_ANON |
229 | | new_m = apr_palloc(pool, sizeof(apr_shm_t)); |
230 | | new_m->pool = pool; |
231 | | new_m->reqsize = reqsize; |
232 | | new_m->realsize = reqsize; |
233 | | new_m->filename = NULL; |
234 | | new_m->shmkey = IPC_PRIVATE; |
235 | | if ((new_m->shmid = shmget(new_m->shmkey, new_m->realsize, |
236 | | SHM_R | SHM_W | IPC_CREAT)) < 0) { |
237 | | return errno; |
238 | | } |
239 | | |
240 | | if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) { |
241 | | return errno; |
242 | | } |
243 | | new_m->usable = new_m->base; |
244 | | |
245 | | if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) { |
246 | | return errno; |
247 | | } |
248 | | apr_uid_current(&uid, &gid, pool); |
249 | | shmbuf.shm_perm.uid = uid; |
250 | | shmbuf.shm_perm.gid = gid; |
251 | | if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) { |
252 | | return errno; |
253 | | } |
254 | | |
255 | | /* Remove the segment once use count hits zero. |
256 | | * We will not attach to this segment again, since it is |
257 | | * anonymous memory, so it is ok to mark it for deletion. |
258 | | */ |
259 | | if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) { |
260 | | return errno; |
261 | | } |
262 | | |
263 | | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner, |
264 | | apr_pool_cleanup_null); |
265 | | *m = new_m; |
266 | | return APR_SUCCESS; |
267 | | #else |
268 | | /* It is an error if they want anonymous memory but we don't have it. */ |
269 | | return APR_ENOTIMPL; /* requested anonymous but we don't have it */ |
270 | | #endif |
271 | 0 | } |
272 | | |
273 | | /* Name-based shared memory */ |
274 | 0 | else { |
275 | 0 | new_m = apr_palloc(pool, sizeof(apr_shm_t)); |
276 | 0 | new_m->pool = pool; |
277 | 0 | new_m->reqsize = reqsize; |
278 | 0 | new_m->filename = apr_pstrdup(pool, filename); |
279 | 0 | #if APR_USE_SHMEM_MMAP_SHM |
280 | 0 | const char *shm_name = make_shm_open_safe_name(filename, pool); |
281 | 0 | #endif |
282 | 0 | #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM |
283 | 0 | new_m->realsize = reqsize + |
284 | 0 | APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */ |
285 | | /* FIXME: Ignore error for now. * |
286 | | * status = apr_file_remove(file, pool);*/ |
287 | 0 | status = APR_SUCCESS; |
288 | |
|
289 | | #if APR_USE_SHMEM_MMAP_TMP |
290 | | status = apr_file_open(&file, filename, |
291 | | APR_FOPEN_READ | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL, |
292 | | APR_FPROT_UREAD | APR_FPROT_UWRITE, pool); |
293 | | if (status != APR_SUCCESS) { |
294 | | return status; |
295 | | } |
296 | | |
297 | | status = apr_os_file_get(&tmpfd, file); |
298 | | if (status != APR_SUCCESS) { |
299 | | apr_file_close(file); /* ignore errors, we're failing */ |
300 | | apr_file_remove(new_m->filename, new_m->pool); |
301 | | return status; |
302 | | } |
303 | | |
304 | | status = apr_file_trunc(file, new_m->realsize); |
305 | | if (status != APR_SUCCESS && status != APR_ESPIPE) { |
306 | | apr_file_close(file); /* ignore errors, we're failing */ |
307 | | apr_file_remove(new_m->filename, new_m->pool); |
308 | | return status; |
309 | | } |
310 | | |
311 | | new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE, |
312 | | MAP_SHARED, tmpfd, 0); |
313 | | /* FIXME: check for errors */ |
314 | | |
315 | | status = apr_file_close(file); |
316 | | if (status != APR_SUCCESS) { |
317 | | return status; |
318 | | } |
319 | | #endif /* APR_USE_SHMEM_MMAP_TMP */ |
320 | 0 | #if APR_USE_SHMEM_MMAP_SHM |
321 | 0 | tmpfd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600); |
322 | 0 | if (tmpfd == -1) { |
323 | 0 | return errno; |
324 | 0 | } |
325 | | |
326 | | /* Note that apr_file_trunc() also calls lseek() so wrapping |
327 | | * the fd into an apr_file_t and doing this indirectly is |
328 | | * undesirable, see PR 66435. */ |
329 | 0 | status = ftruncate(tmpfd, new_m->realsize) < 0 ? errno : APR_SUCCESS; |
330 | 0 | if (status != APR_SUCCESS && status != APR_ESPIPE) { |
331 | 0 | shm_unlink(shm_name); /* we're failing, remove the object */ |
332 | 0 | close(tmpfd); |
333 | 0 | return status; |
334 | 0 | } |
335 | 0 | new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE, |
336 | 0 | MAP_SHARED, tmpfd, 0); |
337 | 0 | status = (new_m->base == (void *)-1) ? errno : APR_SUCCESS; |
338 | | /* fd no longer needed once the memory is mapped. */ |
339 | 0 | close(tmpfd); |
340 | 0 | if (status) { |
341 | 0 | shm_unlink(shm_name); |
342 | 0 | return status; |
343 | 0 | } |
344 | 0 | #endif /* APR_USE_SHMEM_MMAP_SHM */ |
345 | | |
346 | | /* store the real size in the metadata */ |
347 | 0 | *(apr_size_t*)(new_m->base) = new_m->realsize; |
348 | | /* metadata isn't usable */ |
349 | 0 | new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t)); |
350 | |
|
351 | 0 | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner, |
352 | 0 | apr_pool_cleanup_null); |
353 | 0 | *m = new_m; |
354 | 0 | return APR_SUCCESS; |
355 | |
|
356 | | #elif APR_USE_SHMEM_SHMGET |
357 | | new_m->realsize = reqsize; |
358 | | |
359 | | status = apr_file_open(&file, filename, |
360 | | APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL, |
361 | | APR_FPROT_UREAD | APR_FPROT_UWRITE, pool); |
362 | | if (status != APR_SUCCESS) { |
363 | | return status; |
364 | | } |
365 | | |
366 | | /* ftok() (on solaris at least) requires that the file actually |
367 | | * exist before calling ftok(). */ |
368 | | new_m->shmkey = our_ftok(filename); |
369 | | if (new_m->shmkey == (key_t)-1) { |
370 | | apr_file_close(file); |
371 | | return errno; |
372 | | } |
373 | | |
374 | | if ((new_m->shmid = shmget(new_m->shmkey, new_m->realsize, |
375 | | SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) { |
376 | | apr_file_close(file); |
377 | | return errno; |
378 | | } |
379 | | |
380 | | if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) { |
381 | | apr_file_close(file); |
382 | | return errno; |
383 | | } |
384 | | new_m->usable = new_m->base; |
385 | | |
386 | | if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) { |
387 | | apr_file_close(file); |
388 | | return errno; |
389 | | } |
390 | | apr_uid_current(&uid, &gid, pool); |
391 | | shmbuf.shm_perm.uid = uid; |
392 | | shmbuf.shm_perm.gid = gid; |
393 | | if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) { |
394 | | apr_file_close(file); |
395 | | return errno; |
396 | | } |
397 | | |
398 | | nbytes = sizeof(reqsize); |
399 | | status = apr_file_write(file, (const void *)&reqsize, |
400 | | &nbytes); |
401 | | if (status != APR_SUCCESS) { |
402 | | apr_file_close(file); |
403 | | return status; |
404 | | } |
405 | | status = apr_file_close(file); |
406 | | if (status != APR_SUCCESS) { |
407 | | return status; |
408 | | } |
409 | | |
410 | | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner, |
411 | | apr_pool_cleanup_null); |
412 | | *m = new_m; |
413 | | return APR_SUCCESS; |
414 | | |
415 | | #else |
416 | | return APR_ENOTIMPL; |
417 | | #endif |
418 | 0 | } |
419 | 0 | } |
420 | | |
421 | | APR_DECLARE(apr_status_t) apr_shm_create_ex(apr_shm_t **m, |
422 | | apr_size_t reqsize, |
423 | | const char *filename, |
424 | | apr_pool_t *p, |
425 | | apr_int32_t flags) |
426 | 0 | { |
427 | 0 | return apr_shm_create(m, reqsize, filename, p); |
428 | 0 | } |
429 | | |
430 | | APR_DECLARE(apr_status_t) apr_shm_remove(const char *filename, |
431 | | apr_pool_t *pool) |
432 | 0 | { |
433 | | #if APR_USE_SHMEM_SHMGET |
434 | | apr_status_t status; |
435 | | apr_file_t *file; |
436 | | key_t shmkey; |
437 | | int shmid; |
438 | | #endif |
439 | |
|
440 | | #if APR_USE_SHMEM_MMAP_TMP |
441 | | return apr_file_remove(filename, pool); |
442 | | #elif APR_USE_SHMEM_MMAP_SHM |
443 | | const char *shm_name = make_shm_open_safe_name(filename, pool); |
444 | 0 | if (shm_unlink(shm_name) == -1) { |
445 | 0 | return errno; |
446 | 0 | } |
447 | 0 | return APR_SUCCESS; |
448 | | #elif APR_USE_SHMEM_SHMGET |
449 | | /* Presume that the file already exists; just open for writing */ |
450 | | status = apr_file_open(&file, filename, APR_FOPEN_WRITE, |
451 | | APR_FPROT_OS_DEFAULT, pool); |
452 | | if (status) { |
453 | | return status; |
454 | | } |
455 | | |
456 | | /* ftok() (on solaris at least) requires that the file actually |
457 | | * exist before calling ftok(). */ |
458 | | shmkey = our_ftok(filename); |
459 | | if (shmkey == (key_t)-1) { |
460 | | goto shm_remove_failed; |
461 | | } |
462 | | |
463 | | apr_file_close(file); |
464 | | |
465 | | if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) { |
466 | | goto shm_remove_failed; |
467 | | } |
468 | | |
469 | | /* Indicate that the segment is to be destroyed as soon |
470 | | * as all processes have detached. This also disallows any |
471 | | * new attachments to the segment. */ |
472 | | if (shmctl(shmid, IPC_RMID, NULL) == -1) { |
473 | | goto shm_remove_failed; |
474 | | } |
475 | | return apr_file_remove(filename, pool); |
476 | | |
477 | | shm_remove_failed: |
478 | | status = errno; |
479 | | /* ensure the file has been removed anyway. */ |
480 | | apr_file_remove(filename, pool); |
481 | | return status; |
482 | | #else |
483 | | |
484 | | /* No support for anonymous shm */ |
485 | | return APR_ENOTIMPL; |
486 | | #endif |
487 | 0 | } |
488 | | |
489 | | APR_DECLARE(apr_status_t) apr_shm_delete(apr_shm_t *m) |
490 | 0 | { |
491 | 0 | if (m->filename) { |
492 | 0 | return apr_shm_remove(m->filename, m->pool); |
493 | 0 | } |
494 | 0 | else { |
495 | 0 | return APR_ENOTIMPL; |
496 | 0 | } |
497 | 0 | } |
498 | | |
499 | | APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m) |
500 | 0 | { |
501 | 0 | return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner); |
502 | 0 | } |
503 | | |
504 | | static apr_status_t shm_cleanup_attach(void *m_) |
505 | 0 | { |
506 | 0 | apr_shm_t *m = (apr_shm_t *)m_; |
507 | |
|
508 | 0 | if (m->filename == NULL) { |
509 | | /* It doesn't make sense to detach from an anonymous memory segment. */ |
510 | 0 | return APR_EINVAL; |
511 | 0 | } |
512 | 0 | else { |
513 | 0 | #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM |
514 | 0 | if (munmap(m->base, m->realsize) == -1) { |
515 | 0 | return errno; |
516 | 0 | } |
517 | 0 | return APR_SUCCESS; |
518 | | #elif APR_USE_SHMEM_SHMGET |
519 | | if (shmdt(m->base) == -1) { |
520 | | return errno; |
521 | | } |
522 | | return APR_SUCCESS; |
523 | | #else |
524 | | return APR_ENOTIMPL; |
525 | | #endif |
526 | 0 | } |
527 | 0 | } |
528 | | |
529 | | APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m, |
530 | | const char *filename, |
531 | | apr_pool_t *pool) |
532 | 0 | { |
533 | 0 | if (filename == NULL) { |
534 | | /* It doesn't make sense to attach to a segment if you don't know |
535 | | * the filename. */ |
536 | 0 | return APR_EINVAL; |
537 | 0 | } |
538 | 0 | else { |
539 | 0 | #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM |
540 | 0 | apr_shm_t *new_m; |
541 | 0 | apr_status_t status; |
542 | 0 | int tmpfd; |
543 | 0 | apr_file_t *file; /* file where metadata is stored */ |
544 | 0 | apr_size_t nbytes; |
545 | |
|
546 | 0 | new_m = apr_palloc(pool, sizeof(apr_shm_t)); |
547 | 0 | new_m->pool = pool; |
548 | 0 | new_m->filename = apr_pstrdup(pool, filename); |
549 | 0 | #if APR_USE_SHMEM_MMAP_SHM |
550 | 0 | const char *shm_name = make_shm_open_safe_name(filename, pool); |
551 | |
|
552 | 0 | tmpfd = shm_open(shm_name, O_RDWR, 0600); |
553 | 0 | if (tmpfd == -1) { |
554 | 0 | return errno; |
555 | 0 | } |
556 | | |
557 | 0 | status = apr_os_file_put(&file, &tmpfd, |
558 | 0 | APR_READ | APR_WRITE, |
559 | 0 | pool); |
560 | 0 | if (status != APR_SUCCESS) { |
561 | 0 | return status; |
562 | 0 | } |
563 | | |
564 | | #elif APR_USE_SHMEM_MMAP_TMP |
565 | | status = apr_file_open(&file, filename, |
566 | | APR_FOPEN_READ | APR_FOPEN_WRITE, |
567 | | APR_FPROT_OS_DEFAULT, pool); |
568 | | if (status != APR_SUCCESS) { |
569 | | return status; |
570 | | } |
571 | | status = apr_os_file_get(&tmpfd, file); |
572 | | if (status != APR_SUCCESS) { |
573 | | return status; |
574 | | } |
575 | | #else |
576 | | return APR_ENOTIMPL; |
577 | | #endif |
578 | | |
579 | 0 | nbytes = sizeof(new_m->realsize); |
580 | 0 | status = apr_file_read(file, (void *)&(new_m->realsize), |
581 | 0 | &nbytes); |
582 | 0 | if (status != APR_SUCCESS) { |
583 | 0 | return status; |
584 | 0 | } |
585 | | |
586 | 0 | status = apr_os_file_get(&tmpfd, file); |
587 | 0 | if (status != APR_SUCCESS) { |
588 | 0 | apr_file_close(file); /* ignore errors, we're failing */ |
589 | 0 | apr_file_remove(new_m->filename, new_m->pool); |
590 | 0 | return status; |
591 | 0 | } |
592 | | |
593 | 0 | new_m->reqsize = new_m->realsize - sizeof(apr_size_t); |
594 | |
|
595 | 0 | new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE, |
596 | 0 | MAP_SHARED, tmpfd, 0); |
597 | | /* FIXME: check for errors */ |
598 | |
|
599 | 0 | status = apr_file_close(file); |
600 | 0 | if (status != APR_SUCCESS) { |
601 | 0 | return status; |
602 | 0 | } |
603 | | |
604 | | /* metadata isn't part of the usable segment */ |
605 | 0 | new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t)); |
606 | |
|
607 | 0 | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach, |
608 | 0 | apr_pool_cleanup_null); |
609 | 0 | *m = new_m; |
610 | 0 | return APR_SUCCESS; |
611 | |
|
612 | | #elif APR_USE_SHMEM_SHMGET |
613 | | apr_shm_t *new_m; |
614 | | apr_status_t status; |
615 | | apr_file_t *file; /* file where metadata is stored */ |
616 | | apr_size_t nbytes; |
617 | | |
618 | | new_m = apr_palloc(pool, sizeof(apr_shm_t)); |
619 | | |
620 | | status = apr_file_open(&file, filename, |
621 | | APR_FOPEN_READ, APR_FPROT_OS_DEFAULT, pool); |
622 | | if (status != APR_SUCCESS) { |
623 | | return status; |
624 | | } |
625 | | |
626 | | nbytes = sizeof(new_m->reqsize); |
627 | | status = apr_file_read(file, (void *)&(new_m->reqsize), |
628 | | &nbytes); |
629 | | if (status != APR_SUCCESS) { |
630 | | return status; |
631 | | } |
632 | | status = apr_file_close(file); |
633 | | if (status != APR_SUCCESS) { |
634 | | return status; |
635 | | } |
636 | | |
637 | | new_m->filename = apr_pstrdup(pool, filename); |
638 | | new_m->pool = pool; |
639 | | new_m->shmkey = our_ftok(filename); |
640 | | if (new_m->shmkey == (key_t)-1) { |
641 | | return errno; |
642 | | } |
643 | | if ((new_m->shmid = shmget(new_m->shmkey, 0, SHM_R | SHM_W)) == -1) { |
644 | | return errno; |
645 | | } |
646 | | if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) { |
647 | | return errno; |
648 | | } |
649 | | new_m->usable = new_m->base; |
650 | | new_m->realsize = new_m->reqsize; |
651 | | |
652 | | apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach, |
653 | | apr_pool_cleanup_null); |
654 | | *m = new_m; |
655 | | return APR_SUCCESS; |
656 | | |
657 | | #else |
658 | | return APR_ENOTIMPL; |
659 | | #endif |
660 | 0 | } |
661 | 0 | } |
662 | | |
663 | | APR_DECLARE(apr_status_t) apr_shm_attach_ex(apr_shm_t **m, |
664 | | const char *filename, |
665 | | apr_pool_t *pool, |
666 | | apr_int32_t flags) |
667 | 0 | { |
668 | 0 | return apr_shm_attach(m, filename, pool); |
669 | 0 | } |
670 | | |
671 | | APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m) |
672 | 0 | { |
673 | 0 | apr_status_t rv = shm_cleanup_attach(m); |
674 | 0 | apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach); |
675 | 0 | return rv; |
676 | 0 | } |
677 | | |
678 | | APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m) |
679 | 0 | { |
680 | 0 | return m->usable; |
681 | 0 | } |
682 | | |
683 | | APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m) |
684 | 0 | { |
685 | 0 | return m->reqsize; |
686 | 0 | } |
687 | | |
688 | | APR_PERMS_SET_IMPLEMENT(shm) |
689 | 0 | { |
690 | | #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON |
691 | | struct shmid_ds shmbuf; |
692 | | int shmid; |
693 | | apr_shm_t *m = (apr_shm_t *)theshm; |
694 | | |
695 | | if ((shmid = shmget(m->shmkey, 0, SHM_R | SHM_W)) == -1) { |
696 | | return errno; |
697 | | } |
698 | | shmbuf.shm_perm.uid = uid; |
699 | | shmbuf.shm_perm.gid = gid; |
700 | | shmbuf.shm_perm.mode = apr_unix_perms2mode(perms); |
701 | | if (shmctl(shmid, IPC_SET, &shmbuf) == -1) { |
702 | | return errno; |
703 | | } |
704 | | return APR_SUCCESS; |
705 | | #elif APR_USE_SHMEM_MMAP_SHM && !defined(DARWIN) |
706 | | /* ### This hangs or fails on MacOS, so skipping this for the |
707 | | * ENOTIMPL case there - unclear why or if that's fixable. */ |
708 | 0 | apr_shm_t *shm = (apr_shm_t *)theshm; |
709 | 0 | const char *shm_name; |
710 | 0 | int fd; |
711 | 0 | apr_status_t rv; |
712 | |
|
713 | 0 | if (!shm->filename) |
714 | 0 | return APR_ENOTIMPL; |
715 | | |
716 | 0 | shm_name = make_shm_open_safe_name(shm->filename, shm->pool); |
717 | |
|
718 | 0 | fd = shm_open(shm_name, O_RDWR, 0); |
719 | 0 | if (fd == -1) |
720 | 0 | return errno; |
721 | | |
722 | 0 | if (fchown(fd, uid, gid)) { |
723 | 0 | rv = errno; |
724 | 0 | close(fd); |
725 | 0 | return rv; |
726 | 0 | } |
727 | | |
728 | 0 | if (fchmod(fd, apr_unix_perms2mode(perms))) { |
729 | 0 | rv = errno; |
730 | 0 | close(fd); |
731 | 0 | return rv; |
732 | 0 | } |
733 | 0 | close(fd); |
734 | 0 | return APR_SUCCESS; |
735 | | #elif APR_USE_SHMEM_MMAP_TMP |
736 | | apr_shm_t *shm = (apr_shm_t *)theshm; |
737 | | |
738 | | if (!shm->filename) |
739 | | return APR_ENOTIMPL; |
740 | | |
741 | | if (chown(shm->filename, uid, gid)) |
742 | | return errno; |
743 | | |
744 | | if (chmod(shm->filename, apr_unix_perms2mode(perms))) |
745 | | return errno; |
746 | | |
747 | | return APR_SUCCESS; |
748 | | #else |
749 | | return APR_ENOTIMPL; |
750 | | #endif |
751 | 0 | } |
752 | | |
753 | | APR_POOL_IMPLEMENT_ACCESSOR(shm) |
754 | | |
755 | | APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm, |
756 | | apr_shm_t *shm) |
757 | 0 | { |
758 | 0 | return APR_ENOTIMPL; |
759 | 0 | } |
760 | | |
761 | | APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m, |
762 | | apr_os_shm_t *osshm, |
763 | | apr_pool_t *pool) |
764 | 0 | { |
765 | 0 | return APR_ENOTIMPL; |
766 | 0 | } |
767 | | |