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"
25 #if APR_USE_SHMEM_MMAP_SHM
27 * For portable use, a shared memory object should be identified by a name of
28 * the form /somename; that is, a null-terminated string of up to NAME_MAX
29 * (i.e., 255) characters consisting of an initial slash, followed by one or
30 * more characters, none of which are slashes.
36 /* See proc_mutex.c and sem_open for the reason for all this! */
37 static unsigned int rshash (const char *p) {
38 /* hash function from Robert Sedgwicks 'Algorithms in C' book */
39 unsigned int b = 378551;
40 unsigned int a = 63689;
41 unsigned int retval = 0;
44 retval = retval * a + (*p);
51 static const char *make_shm_open_safe_name(const char *filename,
57 if (filename == NULL) {
61 flen = strlen(filename);
62 h1 = (apr_hashfunc_default(filename, &flen) & 0xffffffff);
63 h2 = (rshash(filename) & 0xffffffff);
64 return apr_psprintf(pool, "/ShM.%xH%x", h1, h2);
69 #if APR_USE_SHMEM_SHMGET
70 static key_t our_ftok(const char *filename)
72 /* to help avoid collisions while still using
73 * an easily recreated proj_id */
74 apr_ssize_t slen = strlen(filename);
76 (int)apr_hashfunc_default(filename, &slen));
80 static apr_status_t shm_cleanup_owner(void *m_)
82 apr_shm_t *m = (apr_shm_t *)m_;
84 /* anonymous shared memory */
85 if (m->filename == NULL) {
86 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
87 if (munmap(m->base, m->realsize) == -1) {
91 #elif APR_USE_SHMEM_SHMGET_ANON
92 if (shmdt(m->base) == -1) {
95 /* This segment will automatically remove itself after all
96 * references have detached. */
101 /* name-based shared memory */
103 #if APR_USE_SHMEM_MMAP_TMP
104 if (munmap(m->base, m->realsize) == -1) {
107 if (access(m->filename, F_OK)) {
111 return apr_file_remove(m->filename, m->pool);
113 #elif APR_USE_SHMEM_MMAP_SHM
114 if (munmap(m->base, m->realsize) == -1) {
117 if (shm_unlink(make_shm_open_safe_name(m->filename, m->pool)) == -1 && errno != ENOENT) {
121 #elif APR_USE_SHMEM_SHMGET
122 /* Indicate that the segment is to be destroyed as soon
123 * as all processes have detached. This also disallows any
124 * new attachments to the segment. */
125 if (shmctl(m->shmid, IPC_RMID, NULL) == -1 && errno != EINVAL) {
128 if (shmdt(m->base) == -1) {
131 if (access(m->filename, F_OK)) {
135 return apr_file_remove(m->filename, m->pool);
143 APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m,
145 const char *filename,
150 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
151 struct shmid_ds shmbuf;
155 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
156 APR_USE_SHMEM_MMAP_ZERO
159 #if APR_USE_SHMEM_SHMGET
163 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_SHMGET || \
164 APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
165 apr_file_t *file; /* file where metadata is stored */
168 /* Check if they want anonymous or name-based shared memory */
169 if (filename == NULL) {
170 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
171 new_m = apr_palloc(pool, sizeof(apr_shm_t));
173 new_m->reqsize = reqsize;
174 new_m->realsize = reqsize +
175 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
176 new_m->filename = NULL;
178 #if APR_USE_SHMEM_MMAP_ZERO
179 status = apr_file_open(&file, "/dev/zero", APR_READ | APR_WRITE,
180 APR_OS_DEFAULT, pool);
181 if (status != APR_SUCCESS) {
184 status = apr_os_file_get(&tmpfd, file);
185 if (status != APR_SUCCESS) {
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) {
195 status = apr_file_close(file);
196 if (status != APR_SUCCESS) {
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));
205 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
206 apr_pool_cleanup_null);
210 #elif APR_USE_SHMEM_MMAP_ANON
211 new_m->base = mmap(NULL, new_m->realsize, PROT_READ|PROT_WRITE,
212 MAP_ANON|MAP_SHARED, -1, 0);
213 if (new_m->base == (void *)MAP_FAILED) {
217 /* store the real size in the metadata */
218 *(apr_size_t*)(new_m->base) = new_m->realsize;
219 /* metadata isn't usable */
220 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
222 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
223 apr_pool_cleanup_null);
227 #endif /* APR_USE_SHMEM_MMAP_ZERO */
228 #elif APR_USE_SHMEM_SHMGET_ANON
229 new_m = apr_palloc(pool, sizeof(apr_shm_t));
231 new_m->reqsize = reqsize;
232 new_m->realsize = reqsize;
233 new_m->filename = NULL;
235 if ((new_m->shmid = shmget(IPC_PRIVATE, new_m->realsize,
236 SHM_R | SHM_W | IPC_CREAT)) < 0) {
240 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
243 new_m->usable = new_m->base;
245 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
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) {
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.
259 if (shmctl(new_m->shmid, IPC_RMID, NULL) == -1) {
263 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
264 apr_pool_cleanup_null);
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 */
273 /* Name-based shared memory */
275 new_m = apr_palloc(pool, sizeof(apr_shm_t));
277 new_m->reqsize = reqsize;
278 new_m->filename = apr_pstrdup(pool, filename);
279 #if APR_USE_SHMEM_MMAP_SHM
280 const char *shm_name = make_shm_open_safe_name(filename, pool);
282 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
283 new_m->realsize = reqsize +
284 APR_ALIGN_DEFAULT(sizeof(apr_size_t)); /* room for metadata */
285 /* FIXME: Ignore error for now. *
286 * status = apr_file_remove(file, pool);*/
287 status = APR_SUCCESS;
289 #if APR_USE_SHMEM_MMAP_TMP
290 /* FIXME: Is APR_OS_DEFAULT sufficient? */
291 status = apr_file_open(&file, filename,
292 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
293 APR_OS_DEFAULT, pool);
294 if (status != APR_SUCCESS) {
298 status = apr_os_file_get(&tmpfd, file);
299 if (status != APR_SUCCESS) {
300 apr_file_close(file); /* ignore errors, we're failing */
301 apr_file_remove(new_m->filename, new_m->pool);
305 status = apr_file_trunc(file, new_m->realsize);
306 if (status != APR_SUCCESS && status != APR_ESPIPE) {
307 apr_file_close(file); /* ignore errors, we're failing */
308 apr_file_remove(new_m->filename, new_m->pool);
312 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
313 MAP_SHARED, tmpfd, 0);
314 /* FIXME: check for errors */
316 status = apr_file_close(file);
317 if (status != APR_SUCCESS) {
320 #endif /* APR_USE_SHMEM_MMAP_TMP */
321 #if APR_USE_SHMEM_MMAP_SHM
322 /* FIXME: SysV uses 0600... should we? */
323 tmpfd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0644);
328 status = apr_os_file_put(&file, &tmpfd,
329 APR_READ | APR_WRITE | APR_CREATE | APR_EXCL,
331 if (status != APR_SUCCESS) {
335 status = apr_file_trunc(file, new_m->realsize);
336 if (status != APR_SUCCESS && status != APR_ESPIPE) {
337 shm_unlink(shm_name); /* we're failing, remove the object */
340 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
341 MAP_SHARED, tmpfd, 0);
343 /* FIXME: check for errors */
345 status = apr_file_close(file);
346 if (status != APR_SUCCESS) {
349 #endif /* APR_USE_SHMEM_MMAP_SHM */
351 /* store the real size in the metadata */
352 *(apr_size_t*)(new_m->base) = new_m->realsize;
353 /* metadata isn't usable */
354 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
356 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
357 apr_pool_cleanup_null);
361 #elif APR_USE_SHMEM_SHMGET
362 new_m->realsize = reqsize;
364 /* FIXME: APR_OS_DEFAULT is too permissive, switch to 600 I think. */
365 status = apr_file_open(&file, filename,
366 APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_EXCL,
367 APR_OS_DEFAULT, pool);
368 if (status != APR_SUCCESS) {
372 /* ftok() (on solaris at least) requires that the file actually
373 * exist before calling ftok(). */
374 shmkey = our_ftok(filename);
375 if (shmkey == (key_t)-1) {
376 apr_file_close(file);
380 if ((new_m->shmid = shmget(shmkey, new_m->realsize,
381 SHM_R | SHM_W | IPC_CREAT | IPC_EXCL)) < 0) {
382 apr_file_close(file);
386 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
387 apr_file_close(file);
390 new_m->usable = new_m->base;
392 if (shmctl(new_m->shmid, IPC_STAT, &shmbuf) == -1) {
393 apr_file_close(file);
396 apr_uid_current(&uid, &gid, pool);
397 shmbuf.shm_perm.uid = uid;
398 shmbuf.shm_perm.gid = gid;
399 if (shmctl(new_m->shmid, IPC_SET, &shmbuf) == -1) {
400 apr_file_close(file);
404 nbytes = sizeof(reqsize);
405 status = apr_file_write(file, (const void *)&reqsize,
407 if (status != APR_SUCCESS) {
408 apr_file_close(file);
411 status = apr_file_close(file);
412 if (status != APR_SUCCESS) {
416 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_owner,
417 apr_pool_cleanup_null);
427 APR_DECLARE(apr_status_t) apr_shm_create_ex(apr_shm_t **m,
429 const char *filename,
433 return apr_shm_create(m, reqsize, filename, p);
436 APR_DECLARE(apr_status_t) apr_shm_remove(const char *filename,
439 #if APR_USE_SHMEM_SHMGET
446 #if APR_USE_SHMEM_MMAP_TMP
447 return apr_file_remove(filename, pool);
448 #elif APR_USE_SHMEM_MMAP_SHM
449 const char *shm_name = make_shm_open_safe_name(filename, pool);
450 if (shm_unlink(shm_name) == -1) {
454 #elif APR_USE_SHMEM_SHMGET
455 /* Presume that the file already exists; just open for writing */
456 status = apr_file_open(&file, filename, APR_FOPEN_WRITE,
457 APR_OS_DEFAULT, pool);
462 /* ftok() (on solaris at least) requires that the file actually
463 * exist before calling ftok(). */
464 shmkey = our_ftok(filename);
465 if (shmkey == (key_t)-1) {
466 goto shm_remove_failed;
469 apr_file_close(file);
471 if ((shmid = shmget(shmkey, 0, SHM_R | SHM_W)) < 0) {
472 goto shm_remove_failed;
475 /* Indicate that the segment is to be destroyed as soon
476 * as all processes have detached. This also disallows any
477 * new attachments to the segment. */
478 if (shmctl(shmid, IPC_RMID, NULL) == -1) {
479 goto shm_remove_failed;
481 return apr_file_remove(filename, pool);
485 /* ensure the file has been removed anyway. */
486 apr_file_remove(filename, pool);
490 /* No support for anonymous shm */
495 APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m)
497 return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner);
500 static apr_status_t shm_cleanup_attach(void *m_)
502 apr_shm_t *m = (apr_shm_t *)m_;
504 if (m->filename == NULL) {
505 /* It doesn't make sense to detach from an anonymous memory segment. */
509 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
510 if (munmap(m->base, m->realsize) == -1) {
514 #elif APR_USE_SHMEM_SHMGET
515 if (shmdt(m->base) == -1) {
525 APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
526 const char *filename,
529 if (filename == NULL) {
530 /* It doesn't make sense to attach to a segment if you don't know
535 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
539 apr_file_t *file; /* file where metadata is stored */
542 new_m = apr_palloc(pool, sizeof(apr_shm_t));
544 new_m->filename = apr_pstrdup(pool, filename);
545 #if APR_USE_SHMEM_MMAP_SHM
546 const char *shm_name = make_shm_open_safe_name(filename, pool);
548 /* FIXME: SysV uses 0600... should we? */
549 tmpfd = shm_open(shm_name, O_RDWR, 0644);
554 status = apr_os_file_put(&file, &tmpfd,
555 APR_READ | APR_WRITE,
557 if (status != APR_SUCCESS) {
561 #elif APR_USE_SHMEM_MMAP_TMP
562 status = apr_file_open(&file, filename,
563 APR_READ | APR_WRITE,
564 APR_OS_DEFAULT, pool);
565 if (status != APR_SUCCESS) {
568 status = apr_os_file_get(&tmpfd, file);
569 if (status != APR_SUCCESS) {
576 nbytes = sizeof(new_m->realsize);
577 status = apr_file_read(file, (void *)&(new_m->realsize),
579 if (status != APR_SUCCESS) {
583 status = apr_os_file_get(&tmpfd, file);
584 if (status != APR_SUCCESS) {
585 apr_file_close(file); /* ignore errors, we're failing */
586 apr_file_remove(new_m->filename, new_m->pool);
590 new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
592 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
593 MAP_SHARED, tmpfd, 0);
594 /* FIXME: check for errors */
596 status = apr_file_close(file);
597 if (status != APR_SUCCESS) {
601 /* metadata isn't part of the usable segment */
602 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
604 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
605 apr_pool_cleanup_null);
609 #elif APR_USE_SHMEM_SHMGET
612 apr_file_t *file; /* file where metadata is stored */
616 new_m = apr_palloc(pool, sizeof(apr_shm_t));
618 status = apr_file_open(&file, filename,
619 APR_FOPEN_READ, APR_OS_DEFAULT, pool);
620 if (status != APR_SUCCESS) {
624 nbytes = sizeof(new_m->reqsize);
625 status = apr_file_read(file, (void *)&(new_m->reqsize),
627 if (status != APR_SUCCESS) {
630 status = apr_file_close(file);
631 if (status != APR_SUCCESS) {
635 new_m->filename = apr_pstrdup(pool, filename);
637 shmkey = our_ftok(filename);
638 if (shmkey == (key_t)-1) {
641 if ((new_m->shmid = shmget(shmkey, 0, SHM_R | SHM_W)) == -1) {
644 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
647 new_m->usable = new_m->base;
648 new_m->realsize = new_m->reqsize;
650 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
651 apr_pool_cleanup_null);
661 APR_DECLARE(apr_status_t) apr_shm_attach_ex(apr_shm_t **m,
662 const char *filename,
666 return apr_shm_attach(m, filename, pool);
669 APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m)
671 apr_status_t rv = shm_cleanup_attach(m);
672 apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach);
676 APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m)
681 APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m)
686 APR_POOL_IMPLEMENT_ACCESSOR(shm)
688 APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm,
694 APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m,