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"
18 #include "apr_arch_file_io.h"
20 #include "apr_general.h"
21 #include "apr_errno.h"
23 #include "apr_strings.h"
26 #if APR_USE_SHMEM_MMAP_SHM
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.
37 /* See proc_mutex.c and sem_open for the reason for all this! */
38 static unsigned int rshash (const char *p) {
39 /* hash function from Robert Sedgwicks 'Algorithms in C' book */
40 unsigned int b = 378551;
41 unsigned int a = 63689;
42 unsigned int retval = 0;
45 retval = retval * a + (*p);
52 static const char *make_shm_open_safe_name(const char *filename,
58 if (filename == NULL) {
62 flen = strlen(filename);
63 h1 = (apr_hashfunc_default(filename, &flen) & 0xffffffff);
64 h2 = (rshash(filename) & 0xffffffff);
65 return apr_psprintf(pool, "/ShM.%xH%x", h1, h2);
70 #if APR_USE_SHMEM_SHMGET
71 static key_t our_ftok(const char *filename)
73 /* to help avoid collisions while still using
74 * an easily recreated proj_id */
75 apr_ssize_t slen = strlen(filename);
77 (int)apr_hashfunc_default(filename, &slen));
81 static apr_status_t shm_cleanup_owner(void *m_)
83 apr_shm_t *m = (apr_shm_t *)m_;
85 /* anonymous shared memory */
86 if (m->filename == NULL) {
87 #if APR_USE_SHMEM_MMAP_ZERO || APR_USE_SHMEM_MMAP_ANON
88 if (munmap(m->base, m->realsize) == -1) {
92 #elif APR_USE_SHMEM_SHMGET_ANON
93 if (shmdt(m->base) == -1) {
96 /* This segment will automatically remove itself after all
97 * references have detached. */
102 /* name-based shared memory */
104 #if APR_USE_SHMEM_MMAP_TMP
105 if (munmap(m->base, m->realsize) == -1) {
108 if (access(m->filename, F_OK)) {
112 return apr_file_remove(m->filename, m->pool);
114 #elif APR_USE_SHMEM_MMAP_SHM
115 if (munmap(m->base, m->realsize) == -1) {
118 if (shm_unlink(make_shm_open_safe_name(m->filename, m->pool)) == -1 && errno != ENOENT) {
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) {
129 if (shmdt(m->base) == -1) {
132 if (access(m->filename, F_OK)) {
136 return apr_file_remove(m->filename, m->pool);
144 APR_DECLARE(apr_status_t) apr_shm_create(apr_shm_t **m,
146 const char *filename,
151 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
152 struct shmid_ds shmbuf;
156 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM || \
157 APR_USE_SHMEM_MMAP_ZERO
160 #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;
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) {
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 new_m->shmkey = our_ftok(filename);
375 if (new_m->shmkey == (key_t)-1) {
376 apr_file_close(file);
380 if ((new_m->shmid = shmget(new_m->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_delete(apr_shm_t *m)
498 return apr_shm_remove(m->filename, m->pool);
505 APR_DECLARE(apr_status_t) apr_shm_destroy(apr_shm_t *m)
507 return apr_pool_cleanup_run(m->pool, m, shm_cleanup_owner);
510 static apr_status_t shm_cleanup_attach(void *m_)
512 apr_shm_t *m = (apr_shm_t *)m_;
514 if (m->filename == NULL) {
515 /* It doesn't make sense to detach from an anonymous memory segment. */
519 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
520 if (munmap(m->base, m->realsize) == -1) {
524 #elif APR_USE_SHMEM_SHMGET
525 if (shmdt(m->base) == -1) {
535 APR_DECLARE(apr_status_t) apr_shm_attach(apr_shm_t **m,
536 const char *filename,
539 if (filename == NULL) {
540 /* It doesn't make sense to attach to a segment if you don't know
545 #if APR_USE_SHMEM_MMAP_TMP || APR_USE_SHMEM_MMAP_SHM
549 apr_file_t *file; /* file where metadata is stored */
552 new_m = apr_palloc(pool, sizeof(apr_shm_t));
554 new_m->filename = apr_pstrdup(pool, filename);
555 #if APR_USE_SHMEM_MMAP_SHM
556 const char *shm_name = make_shm_open_safe_name(filename, pool);
558 /* FIXME: SysV uses 0600... should we? */
559 tmpfd = shm_open(shm_name, O_RDWR, 0644);
564 status = apr_os_file_put(&file, &tmpfd,
565 APR_READ | APR_WRITE,
567 if (status != APR_SUCCESS) {
571 #elif APR_USE_SHMEM_MMAP_TMP
572 status = apr_file_open(&file, filename,
573 APR_READ | APR_WRITE,
574 APR_OS_DEFAULT, pool);
575 if (status != APR_SUCCESS) {
578 status = apr_os_file_get(&tmpfd, file);
579 if (status != APR_SUCCESS) {
586 nbytes = sizeof(new_m->realsize);
587 status = apr_file_read(file, (void *)&(new_m->realsize),
589 if (status != APR_SUCCESS) {
593 status = apr_os_file_get(&tmpfd, file);
594 if (status != APR_SUCCESS) {
595 apr_file_close(file); /* ignore errors, we're failing */
596 apr_file_remove(new_m->filename, new_m->pool);
600 new_m->reqsize = new_m->realsize - sizeof(apr_size_t);
602 new_m->base = mmap(NULL, new_m->realsize, PROT_READ | PROT_WRITE,
603 MAP_SHARED, tmpfd, 0);
604 /* FIXME: check for errors */
606 status = apr_file_close(file);
607 if (status != APR_SUCCESS) {
611 /* metadata isn't part of the usable segment */
612 new_m->usable = (char *)new_m->base + APR_ALIGN_DEFAULT(sizeof(apr_size_t));
614 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
615 apr_pool_cleanup_null);
619 #elif APR_USE_SHMEM_SHMGET
622 apr_file_t *file; /* file where metadata is stored */
625 new_m = apr_palloc(pool, sizeof(apr_shm_t));
627 status = apr_file_open(&file, filename,
628 APR_FOPEN_READ, APR_OS_DEFAULT, pool);
629 if (status != APR_SUCCESS) {
633 nbytes = sizeof(new_m->reqsize);
634 status = apr_file_read(file, (void *)&(new_m->reqsize),
636 if (status != APR_SUCCESS) {
639 status = apr_file_close(file);
640 if (status != APR_SUCCESS) {
644 new_m->filename = apr_pstrdup(pool, filename);
646 new_m->shmkey = our_ftok(filename);
647 if (new_m->shmkey == (key_t)-1) {
650 if ((new_m->shmid = shmget(new_m->shmkey, 0, SHM_R | SHM_W)) == -1) {
653 if ((new_m->base = shmat(new_m->shmid, NULL, 0)) == (void *)-1) {
656 new_m->usable = new_m->base;
657 new_m->realsize = new_m->reqsize;
659 apr_pool_cleanup_register(new_m->pool, new_m, shm_cleanup_attach,
660 apr_pool_cleanup_null);
670 APR_DECLARE(apr_status_t) apr_shm_attach_ex(apr_shm_t **m,
671 const char *filename,
675 return apr_shm_attach(m, filename, pool);
678 APR_DECLARE(apr_status_t) apr_shm_detach(apr_shm_t *m)
680 apr_status_t rv = shm_cleanup_attach(m);
681 apr_pool_cleanup_kill(m->pool, m, shm_cleanup_attach);
685 APR_DECLARE(void *) apr_shm_baseaddr_get(const apr_shm_t *m)
690 APR_DECLARE(apr_size_t) apr_shm_size_get(const apr_shm_t *m)
695 APR_PERMS_SET_IMPLEMENT(shm)
697 #if APR_USE_SHMEM_SHMGET || APR_USE_SHMEM_SHMGET_ANON
698 struct shmid_ds shmbuf;
700 apr_shm_t *m = (apr_shm_t *)theshm;
702 if ((shmid = shmget(m->shmkey, 0, SHM_R | SHM_W)) == -1) {
705 shmbuf.shm_perm.uid = uid;
706 shmbuf.shm_perm.gid = gid;
707 shmbuf.shm_perm.mode = apr_unix_perms2mode(perms);
708 if (shmctl(shmid, IPC_SET, &shmbuf) == -1) {
717 APR_POOL_IMPLEMENT_ACCESSOR(shm)
719 APR_DECLARE(apr_status_t) apr_os_shm_get(apr_os_shm_t *osshm,
725 APR_DECLARE(apr_status_t) apr_os_shm_put(apr_shm_t **m,