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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "apr_arch_shm.h"
19 #include "apr_general.h"
20 #include "apr_errno.h"
22 #include "apr_strings.h"
24 static apr_status_t shm_cleanup_owner(void *m_)
26 apr_shm_t *m = (apr_shm_t *)m_;
28 /* anonymous shared memory */
29 if (m->filename == NULL) {
30 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
31 if (munmap(m->base, m->realsize) == -1) {
36 #if APR_USE_SHMEM_SHMGET_ANON
37 if (shmdt(m->base) == -1) {
40 /* This segment will automatically remove itself after all
41 * references have detached. */
46 /* name-based shared memory */
48 #if APR_USE_SHMEM_MMAP_TMP
51 if (munmap(m->base, m->realsize) == -1) {
54 rv = apr_file_remove(m->filename, m->pool);
55 if (rv != APR_SUCCESS) {
60 #if APR_USE_SHMEM_MMAP_SHM
61 if (munmap(m->base, m->realsize) == -1) {
64 if (shm_unlink(m->filename) == -1) {
69 #if APR_USE_SHMEM_SHMGET
72 /* Indicate that the segment is to be destroyed as soon
73 * as all processes have detached. This also disallows any
74 * new attachments to the segment. */
75 if (shmctl(m->shmid, IPC_RMID, NULL) == -1) {
78 if (shmdt(m->base) == -1) {
81 rv = apr_file_remove(m->filename, m->pool);
82 if (rv != APR_SUCCESS) {
92 APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m,
99 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
100 struct shmid_ds shmbuf;
104 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
105 APR_USE_SHMEM_MMAP_ZERO
108 #if APR_USE_SHMEM_SHMGET
112 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \
113 APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
114 apr_file_t *file; /* file where metadata is stored */
117 /* Check if they want anonymous or name-based shared memory */
118 if (filename == NULL) {
119 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
120 new_m = apr_palloc(pool, sizeof(apr_shm_t));
125 new_m->reqsize = reqsize;
126 new_m->realsize = reqsize +
127 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
128 new_m->filename = NULL;
130 #if APR_USE_SHMEM_MMAP_ZERO
131 status = apr_file_open(&file, "/dev/zero", APR_READ | APR_WRITE,
132 APR_OS_DEFAULT, pool);
133 if (status != APR_SUCCESS) {
136 status = apr_os_file_get(&tmpfd, file);
137 if (status != APR_SUCCESS) {
141 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
142 MAP_SHARED, tmpfd, 0);
143 if (new_m->base == (void *)MAP_FAILED) {
147 status = apr_file_close(file);
148 if (status != APR_SUCCESS) {
152 /* store the real size in the metadata */
153 *(apr_size_t*)(new_m->base) = new_m->realsize;
154 /* metadata isn't usable */
155 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
157 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
158 apr_pool_cleanup_null);
162 #elif APR_USE_SHMEM_MMAP_ANON
163 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
164 MAP_ANON|MAP_SHARED, -1, 0);
165 if (new_m->base == (void *)MAP_FAILED) {
169 /* store the real size in the metadata */
170 *(apr_size_t*)(new_m->base) = new_m->realsize;
171 /* metadata isn't usable */
172 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
174 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
175 apr_pool_cleanup_null);
179 #endif /* APR_USE_SHMEM_MMAP_ZERO */
180 #endif /* APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON */
181 #if APR_USE_SHMEM_SHMGET_ANON
183 new_m = apr_palloc(pool, sizeof(apr_shm_t));
188 new_m->reqsize = reqsize;
189 new_m->realsize = reqsize;
190 new_m->filename = NULL;
192 if ((new_m->shmid = shmget(IPC_PRIVATE, new_m->realsize,
193 SHM_R | SHM_W | IPC_CREAT)) < 0) {
197 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
200 new_m->usable = new_m->base;
202 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
205 apr_uid_current(&uid, &gid, pool);
206 shmbuf.shm_perm.uid = uid;
207 shmbuf.shm_perm.gid = gid;
208 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
212 /* Remove the segment once use count hits zero.
213 * We will not attach to this segment again, since it is
214 * anonymous memory, so it is ok to mark it for deletion.
216 if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) {
220 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
221 apr_pool_cleanup_null);
224 #endif /* APR_USE_SHMEM_SHMGET_ANON */
225 /* It is an error if they want anonymous memory but we don't have it. */
226 return APR_ENOTIMPL; /* requested anonymous but we don't have it */
229 /* Name-based shared memory */
231 new_m = apr_palloc(pool, sizeof(apr_shm_t));
236 new_m->reqsize = reqsize;
237 new_m->filename = apr_pstrdup(pool, filename);
239 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
240 new_m->realsize = reqsize +
241 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
242 /* FIXME: Ignore error for now. *
243 * status = apr_file_remove(file, pool);*/
244 status = APR_SUCCESS;
246 #if APR_USE_SHMEM_MMAP_TMP
247 /* FIXME: Is APR_OS_DEFAULT sufficient? */
248 status = apr_file_open(&file, filename,
249 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
250 APR_OS_DEFAULT, pool);
251 if (status != APR_SUCCESS) {
255 status = apr_os_file_get(&tmpfd, file);
256 if (status != APR_SUCCESS) {
257 apr_file_close(file); /* ignore errors, we're failing */
258 apr_file_remove(new_m->filename, new_m->pool);
262 status = apr_file_trunc(file, new_m->realsize);
263 if (status != APR_SUCCESS) {
264 apr_file_close(file); /* ignore errors, we're failing */
265 apr_file_remove(new_m->filename, new_m->pool);
269 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
270 MAP_SHARED, tmpfd, 0);
271 /* FIXME: check for errors */
273 status = apr_file_close(file);
274 if (status != APR_SUCCESS) {
277 #endif /* APR_USE_SHMEM_MMAP_TMP */
278 #if APR_USE_SHMEM_MMAP_SHM
279 /* FIXME: Is APR_OS_DEFAULT sufficient? */
280 tmpfd = shm_open(filename, O_RDWR | O_CREAT | O_EXCL, APR_OS_DEFAULT);
285 status = apr_os_file_put(&file, &tmpfd,
286 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
288 if (status != APR_SUCCESS) {
292 status = apr_file_trunc(file, new_m->realsize);
293 if (status != APR_SUCCESS) {
294 shm_unlink(filename); /* we're failing, remove the object */
297 new_m->base = mmap(NULL, reqsize, PROT_READ | PROT_WRITE,
298 MAP_SHARED, tmpfd, 0);
300 /* FIXME: check for errors */
302 /* FIXME: Is it ok to close this file when using shm_open?? */
303 status = apr_file_close(file);
304 if (status != APR_SUCCESS) {
307 #endif /* APR_USE_SHMEM_MMAP_SHM */
309 /* store the real size in the metadata */
310 *(apr_size_t*)(new_m->base) = new_m->realsize;
311 /* metadata isn't usable */
312 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
314 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
315 apr_pool_cleanup_null);
319 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
321 #if APR_USE_SHMEM_SHMGET
322 new_m->realsize = reqsize;
324 /* FIXME: APR_OS_DEFAULT is too permissive, switch to 600 I think. */
325 status = apr_file_open(&file, filename,
326 APR_WRITE | APR_CREATE | APR_EXCL,
327 APR_OS_DEFAULT, pool);
328 if (status != APR_SUCCESS) {
332 /* ftok() (on solaris at least) requires that the file actually
333 * exist before calling ftok(). */
334 shmkey = ftok(filename, 1);
335 if (shmkey == (key_t)-1) {
339 if ((new_m->shmid = shmget(shmkey, new_m->realsize,
340 SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) {
344 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
347 new_m->usable = new_m->base;
349 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
352 apr_uid_current(&uid, &gid, pool);
353 shmbuf.shm_perm.uid = uid;
354 shmbuf.shm_perm.gid = gid;
355 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
359 nbytes = sizeof(reqsize);
360 status = apr_file_write(file, (const void *)&reqsize,
362 if (status != APR_SUCCESS) {
365 status = apr_file_close(file);
366 if (status != APR_SUCCESS) {
370 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
371 apr_pool_cleanup_null);
375 #endif /* APR_USE_SHMEM_SHMGET */
381 APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m)
383 return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner);
386 static apr_status_t shm_cleanup_attach(void *m_)
388 apr_shm_t *m = (apr_shm_t *)m_;
390 if (m->filename == NULL) {
391 /* It doesn't make sense to detach from an anonymous memory segment. */
395 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
396 if (munmap(m->base, m->realsize) == -1) {
400 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
401 #if APR_USE_SHMEM_SHMGET
402 if (shmdt(m->base) == -1) {
412 APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
413 const char *filename,
416 if (filename == NULL) {
417 /* It doesn't make sense to attach to a segment if you don't know
422 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
426 apr_file_t *file; /* file where metadata is stored */
429 new_m = apr_palloc(pool, sizeof(apr_shm_t));
434 new_m->filename = apr_pstrdup(pool, filename);
436 status = apr_file_open(&file, filename,
437 APR_READ | APR_WRITE,
438 APR_OS_DEFAULT, pool);
439 if (status != APR_SUCCESS) {
442 status = apr_os_file_get(&tmpfd, file);
443 if (status != APR_SUCCESS) {
447 nbytes = sizeof(new_m->realsize);
448 status = apr_file_read(file, (void *)&(new_m->realsize),
450 if (status != APR_SUCCESS) {
454 status = apr_os_file_get(&tmpfd, file);
455 if (status != APR_SUCCESS) {
456 apr_file_close(file); /* ignore errors, we're failing */
457 apr_file_remove(new_m->filename, new_m->pool);
461 new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
463 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
464 MAP_SHARED, tmpfd, 0);
465 /* FIXME: check for errors */
467 status = apr_file_close(file);
468 if (status != APR_SUCCESS) {
472 /* metadata isn't part of the usable segment */
473 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
475 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
476 apr_pool_cleanup_null);
480 #endif /* APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM */
481 #if APR_USE_SHMEM_SHMGET
484 apr_file_t *file; /* file where metadata is stored */
488 new_m = apr_palloc(pool, sizeof(apr_shm_t));
493 /* FIXME: does APR_OS_DEFAULT matter for reading? */
494 status = apr_file_open(&file, filename,
495 APR_READ, APR_OS_DEFAULT, pool);
496 if (status != APR_SUCCESS) {
500 nbytes = sizeof(new_m->reqsize);
501 status = apr_file_read(file, (void *)&(new_m->reqsize),
503 if (status != APR_SUCCESS) {
506 status = apr_file_close(file);
507 if (status != APR_SUCCESS) {
511 new_m->filename = apr_pstrdup(pool, filename);
513 shmkey = ftok(filename, 1);
514 if (shmkey == (key_t)-1) {
517 if ((new_m->shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
520 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
523 new_m->usable = new_m->base;
524 new_m->realsize = new_m->reqsize;
526 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
527 apr_pool_cleanup_null);
531 #endif /* APR_USE_SHMEM_SHMGET */
537 APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m)
539 apr_status_t rv = shm_cleanup_attach(m);
540 apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach);
544 APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m)
549 APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m)
554 APR_POOL_IMPLEMENT_ACCESSOR(shm)
556 APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm,
562 APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m,