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) {
35 #elif APR_USE_SHMEM_SHMGET_ANON
36 if (shmdt(m->base) == -1) {
39 /* This segment will automatically remove itself after all
40 * references have detached. */
45 /* name-based shared memory */
47 #if APR_USE_SHMEM_MMAP_TMP
48 if (munmap(m->base, m->realsize) == -1) {
51 if (access(m->filename, F_OK)) {
55 return apr_file_remove(m->filename, m->pool);
57 #elif APR_USE_SHMEM_MMAP_SHM
58 if (munmap(m->base, m->realsize) == -1) {
61 if (shm_unlink(m->filename) == -1) {
65 #elif APR_USE_SHMEM_SHMGET
66 /* Indicate that the segment is to be destroyed as soon
67 * as all processes have detached. This also disallows any
68 * new attachments to the segment. */
69 if (shmctl(m->shmid, IPC_RMID, NULL) == -1 && errno != EINVAL) {
72 if (shmdt(m->base) == -1) {
75 if (access(m->filename, F_OK)) {
79 return apr_file_remove(m->filename, m->pool);
87 APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m,
94 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
95 struct shmid_ds shmbuf;
99 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
100 APR_USE_SHMEM_MMAP_ZERO
103 #if APR_USE_SHMEM_SHMGET
107 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \
108 APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
109 apr_file_t *file; /* file where metadata is stored */
112 /* Check if they want anonymous or name-based shared memory */
113 if (filename == NULL) {
114 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
115 new_m = apr_palloc(pool, sizeof(apr_shm_t));
117 new_m->reqsize = reqsize;
118 new_m->realsize = reqsize +
119 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
120 new_m->filename = NULL;
122 #if APR_USE_SHMEM_MMAP_ZERO
123 status = apr_file_open(&file, "/dev/zero", APR_READ | APR_WRITE,
124 APR_OS_DEFAULT, pool);
125 if (status != APR_SUCCESS) {
128 status = apr_os_file_get(&tmpfd, file);
129 if (status != APR_SUCCESS) {
133 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
134 MAP_SHARED, tmpfd, 0);
135 if (new_m->base == (void *)MAP_FAILED) {
139 status = apr_file_close(file);
140 if (status != APR_SUCCESS) {
144 /* store the real size in the metadata */
145 *(apr_size_t*)(new_m->base) = new_m->realsize;
146 /* metadata isn't usable */
147 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
149 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
150 apr_pool_cleanup_null);
154 #elif APR_USE_SHMEM_MMAP_ANON
155 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
156 MAP_ANON|MAP_SHARED, -1, 0);
157 if (new_m->base == (void *)MAP_FAILED) {
161 /* store the real size in the metadata */
162 *(apr_size_t*)(new_m->base) = new_m->realsize;
163 /* metadata isn't usable */
164 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
166 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
167 apr_pool_cleanup_null);
171 #endif /* APR_USE_SHMEM_MMAP_ZERO */
172 #elif APR_USE_SHMEM_SHMGET_ANON
173 new_m = apr_palloc(pool, sizeof(apr_shm_t));
175 new_m->reqsize = reqsize;
176 new_m->realsize = reqsize;
177 new_m->filename = NULL;
179 if ((new_m->shmid = shmget(IPC_PRIVATE, new_m->realsize,
180 SHM_R | SHM_W | IPC_CREAT)) < 0) {
184 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
187 new_m->usable = new_m->base;
189 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
192 apr_uid_current(&uid, &gid, pool);
193 shmbuf.shm_perm.uid = uid;
194 shmbuf.shm_perm.gid = gid;
195 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
199 /* Remove the segment once use count hits zero.
200 * We will not attach to this segment again, since it is
201 * anonymous memory, so it is ok to mark it for deletion.
203 if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) {
207 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
208 apr_pool_cleanup_null);
212 /* It is an error if they want anonymous memory but we don't have it. */
213 return APR_ENOTIMPL; /* requested anonymous but we don't have it */
217 /* Name-based shared memory */
219 new_m = apr_palloc(pool, sizeof(apr_shm_t));
221 new_m->reqsize = reqsize;
222 new_m->filename = apr_pstrdup(pool, filename);
224 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
225 new_m->realsize = reqsize +
226 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
227 /* FIXME: Ignore error for now. *
228 * status = apr_file_remove(file, pool);*/
229 status = APR_SUCCESS;
231 #if APR_USE_SHMEM_MMAP_TMP
232 /* FIXME: Is APR_OS_DEFAULT sufficient? */
233 status = apr_file_open(&file, filename,
234 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
235 APR_OS_DEFAULT, pool);
236 if (status != APR_SUCCESS) {
240 status = apr_os_file_get(&tmpfd, file);
241 if (status != APR_SUCCESS) {
242 apr_file_close(file); /* ignore errors, we're failing */
243 apr_file_remove(new_m->filename, new_m->pool);
247 status = apr_file_trunc(file, new_m->realsize);
248 if (status != APR_SUCCESS) {
249 apr_file_close(file); /* ignore errors, we're failing */
250 apr_file_remove(new_m->filename, new_m->pool);
254 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
255 MAP_SHARED, tmpfd, 0);
256 /* FIXME: check for errors */
258 status = apr_file_close(file);
259 if (status != APR_SUCCESS) {
262 #endif /* APR_USE_SHMEM_MMAP_TMP */
263 #if APR_USE_SHMEM_MMAP_SHM
264 tmpfd = shm_open(filename, O_RDWR | O_CREAT | O_EXCL, 0644);
269 status = apr_os_file_put(&file, &tmpfd,
270 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
272 if (status != APR_SUCCESS) {
276 status = apr_file_trunc(file, new_m->realsize);
277 if (status != APR_SUCCESS) {
278 shm_unlink(filename); /* we're failing, remove the object */
281 new_m->base = mmap(NULL, reqsize, PROT_READ | PROT_WRITE,
282 MAP_SHARED, tmpfd, 0);
284 /* FIXME: check for errors */
286 status = apr_file_close(file);
287 if (status != APR_SUCCESS) {
290 #endif /* APR_USE_SHMEM_MMAP_SHM */
292 /* store the real size in the metadata */
293 *(apr_size_t*)(new_m->base) = new_m->realsize;
294 /* metadata isn't usable */
295 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
297 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
298 apr_pool_cleanup_null);
302 #elif APR_USE_SHMEM_SHMGET
303 new_m->realsize = reqsize;
305 /* FIXME: APR_OS_DEFAULT is too permissive, switch to 600 I think. */
306 status = apr_file_open(&file, filename,
307 APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL,
308 APR_OS_DEFAULT, pool);
309 if (status != APR_SUCCESS) {
313 /* ftok() (on solaris at least) requires that the file actually
314 * exist before calling ftok(). */
315 shmkey = ftok(filename, 1);
316 if (shmkey == (key_t)-1) {
320 if ((new_m->shmid = shmget(shmkey, new_m->realsize,
321 SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) {
325 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
328 new_m->usable = new_m->base;
330 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
333 apr_uid_current(&uid, &gid, pool);
334 shmbuf.shm_perm.uid = uid;
335 shmbuf.shm_perm.gid = gid;
336 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
340 nbytes = sizeof(reqsize);
341 status = apr_file_write(file, (const void *)&reqsize,
343 if (status != APR_SUCCESS) {
346 status = apr_file_close(file);
347 if (status != APR_SUCCESS) {
351 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
352 apr_pool_cleanup_null);
362 APR_DECLARE(apr_status_t) apr_shm_remove(const char *filename,
365 #if APR_USE_SHMEM_SHMGET
372 #if APR_USE_SHMEM_MMAP_TMP
373 return apr_file_remove(filename, pool);
374 #elif APR_USE_SHMEM_MMAP_SHM
375 if (shm_unlink(filename) == -1) {
379 #elif APR_USE_SHMEM_SHMGET
380 /* Presume that the file already exists; just open for writing */
381 status = apr_file_open(&file, filename, APR_FOPEN_WRITE,
382 APR_OS_DEFAULT, pool);
387 /* ftok() (on solaris at least) requires that the file actually
388 * exist before calling ftok(). */
389 shmkey = ftok(filename, 1);
390 if (shmkey == (key_t)-1) {
391 goto shm_remove_failed;
394 apr_file_close(file);
396 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) {
397 goto shm_remove_failed;
400 /* Indicate that the segment is to be destroyed as soon
401 * as all processes have detached. This also disallows any
402 * new attachments to the segment. */
403 if (shmctl(shmid, IPC_RMID, NULL) == -1) {
404 goto shm_remove_failed;
406 return apr_file_remove(filename, pool);
410 /* ensure the file has been removed anyway. */
411 apr_file_remove(filename, pool);
415 /* No support for anonymous shm */
420 APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m)
422 return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner);
425 static apr_status_t shm_cleanup_attach(void *m_)
427 apr_shm_t *m = (apr_shm_t *)m_;
429 if (m->filename == NULL) {
430 /* It doesn't make sense to detach from an anonymous memory segment. */
434 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
435 if (munmap(m->base, m->realsize) == -1) {
439 #elif APR_USE_SHMEM_SHMGET
440 if (shmdt(m->base) == -1) {
450 APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
451 const char *filename,
454 if (filename == NULL) {
455 /* It doesn't make sense to attach to a segment if you don't know
460 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
464 apr_file_t *file; /* file where metadata is stored */
467 new_m = apr_palloc(pool, sizeof(apr_shm_t));
469 new_m->filename = apr_pstrdup(pool, filename);
471 status = apr_file_open(&file, filename,
472 APR_READ | APR_WRITE,
473 APR_OS_DEFAULT, pool);
474 if (status != APR_SUCCESS) {
477 status = apr_os_file_get(&tmpfd, file);
478 if (status != APR_SUCCESS) {
482 nbytes = sizeof(new_m->realsize);
483 status = apr_file_read(file, (void *)&(new_m->realsize),
485 if (status != APR_SUCCESS) {
489 status = apr_os_file_get(&tmpfd, file);
490 if (status != APR_SUCCESS) {
491 apr_file_close(file); /* ignore errors, we're failing */
492 apr_file_remove(new_m->filename, new_m->pool);
496 new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
498 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
499 MAP_SHARED, tmpfd, 0);
500 /* FIXME: check for errors */
502 status = apr_file_close(file);
503 if (status != APR_SUCCESS) {
507 /* metadata isn't part of the usable segment */
508 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
510 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
511 apr_pool_cleanup_null);
515 #elif APR_USE_SHMEM_SHMGET
518 apr_file_t *file; /* file where metadata is stored */
522 new_m = apr_palloc(pool, sizeof(apr_shm_t));
524 status = apr_file_open(&file, filename,
525 APR_FOPEN_READ, APR_OS_DEFAULT, pool);
526 if (status != APR_SUCCESS) {
530 nbytes = sizeof(new_m->reqsize);
531 status = apr_file_read(file, (void *)&(new_m->reqsize),
533 if (status != APR_SUCCESS) {
536 status = apr_file_close(file);
537 if (status != APR_SUCCESS) {
541 new_m->filename = apr_pstrdup(pool, filename);
543 shmkey = ftok(filename, 1);
544 if (shmkey == (key_t)-1) {
547 if ((new_m->shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
550 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
553 new_m->usable = new_m->base;
554 new_m->realsize = new_m->reqsize;
556 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
557 apr_pool_cleanup_null);
567 APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m)
569 apr_status_t rv = shm_cleanup_attach(m);
570 apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach);
574 APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m)
579 APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m)
584 APR_POOL_IMPLEMENT_ACCESSOR(shm)
586 APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm,
592 APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m,