blob: e8e7565d19734c9de595a1a8b0790d884512d886 [file] [log] [blame]
/* ====================================================================
* Copyright (c) 1999 Ralf S. Engelschall. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. All advertising materials mentioning features or use of this
* software must display the following acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com>."
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by
* Ralf S. Engelschall <rse@engelschall.com>."
*
* THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*/
/*
**
** mm_core.c -- Low-level Shared Memory API
**
*/
#define MM_PRIVATE
#include "mm.h"
/*
* Some global variables
*/
#if defined(MM_SEMT_FCNTL)
/* lock/unlock structures for fcntl() */
static struct flock mm_core_dolock_rd;
static struct flock mm_core_dolock_rw;
static struct flock mm_core_dounlock;
#endif
#if defined(MM_SEMT_IPCSEM)
/* lock/unlock structures for semop() */
static union semun mm_core_semctlarg;
static struct sembuf mm_core_dolock[2];
static struct sembuf mm_core_dounlock[1];
#endif
#if defined(MM_SHMT_MMFILE) || defined(MM_SHMT_MMPOSX)
static size_t mm_core_mapoffset = 0; /* we use own file */
#elif defined(MM_SHMT_MMZERO)
static size_t mm_core_mapoffset = 1024*1024*1; /* we share with other apps */
#endif
static void mm_core_init(void)
{
static int initialized = FALSE;
if (!initialized) {
#if defined(MM_SEMT_FCNTL)
mm_core_dolock_rd.l_whence = SEEK_SET; /* from current point */
mm_core_dolock_rd.l_start = 0; /* -"- */
mm_core_dolock_rd.l_len = 0; /* until end of file */
mm_core_dolock_rd.l_type = F_RDLCK; /* set shard/read lock */
mm_core_dolock_rd.l_pid = 0; /* pid not actually interesting */
mm_core_dolock_rw.l_whence = SEEK_SET; /* from current point */
mm_core_dolock_rw.l_start = 0; /* -"- */
mm_core_dolock_rw.l_len = 0; /* until end of file */
mm_core_dolock_rw.l_type = F_WRLCK; /* set exclusive/read-write lock */
mm_core_dolock_rw.l_pid = 0; /* pid not actually interesting */
mm_core_dounlock.l_whence = SEEK_SET; /* from current point */
mm_core_dounlock.l_start = 0; /* -"- */
mm_core_dounlock.l_len = 0; /* until end of file */
mm_core_dounlock.l_type = F_UNLCK; /* unlock */
mm_core_dounlock.l_pid = 0; /* pid not actually interesting */
#endif
#if defined(MM_SEMT_IPCSEM)
mm_core_dolock[0].sem_num = 0;
mm_core_dolock[0].sem_op = 0;
mm_core_dolock[0].sem_flg = 0;
mm_core_dolock[1].sem_num = 0;
mm_core_dolock[1].sem_op = 1;
mm_core_dolock[1].sem_flg = SEM_UNDO;
mm_core_dounlock[0].sem_num = 0;
mm_core_dounlock[0].sem_op = -1;
mm_core_dounlock[0].sem_flg = SEM_UNDO;
#endif
initialized = TRUE;
}
return;
}
#if defined(MM_SEMT_FLOCK)
/*
* Determine per-process fd for semaphore
* (needed only for flock() based semaphore)
*/
static int mm_core_getfdsem(mem_core *mc)
{
int fd = -1;
pid_t pid;
int i;
pid = getpid();
for (i = 0; i < MM_MAXCHILD &&
mc->mc_fdsem[i].pid != 0 &&
mc->mc_fdsem[i].fd != -1; i++) {
if (mc->mc_fdsem[i].pid == pid) {
fd = mc->mc_fdsem[i].fd;
break;
}
}
if (fd == -1 && i < MM_MAXCHILD) {
fd = open(mc->mc_fnsem, O_WRONLY, MM_CORE_FILEMODE);
mc->mc_fdsem[i].pid = getpid();
mc->mc_fdsem[i].fd = fd;
}
return fd;
}
#endif /* MM_SEMT_FLOCK */
/*
* Determine memory page size of OS
*/
static size_t mm_core_pagesize(void)
{
static int pagesize = 0;
if (pagesize == 0)
#if defined(MM_VMPS_GETPAGESIZE)
pagesize = getpagesize();
#elif defined(MM_VMPS_SYSCONF)
pagesize = sysconf(_SC_PAGESIZE);
#else
pagesize = MM_CORE_DEFAULT_PAGESIZE;
#endif
return pagesize;
}
/*
* Align a size to the next page or word boundary
*/
size_t mm_core_align2page(size_t size)
{
int psize = mm_core_pagesize();
return ((size)%(psize) > 0 ? ((size)/(psize)+1)*(psize) : (size));
}
size_t mm_core_align2word(size_t size)
{
return ((1+((size-1) / SIZEOF_mem_word)) * SIZEOF_mem_word);
}
size_t mm_core_maxsegsize(void)
{
return mm_core_align2page((MM_SHM_MAXSEGSIZE-SIZEOF_mem_core)-mm_core_pagesize());
}
/*
* Create a shared memory area
*/
void *mm_core_create(size_t usersize, const char *file)
{
mem_core *mc;
void *area = ((void *)-1);
int fdmem = 0;
int fdsem = 0;
#if defined(MM_SEMT_IPCSEM)
int fdsem_rd = 0;
#endif
#if defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
char *fnmem;
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
char *fnsem;
#endif
size_t size;
#if defined(MM_SHMT_MMZERO) || defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
int zero = 0;
#endif
#if defined(MM_SHMT_IPCSHM)
struct shmid_ds shmbuf;
#endif
#if defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
char shmfilename[MM_MAXPATH];
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
char semfilename[MM_MAXPATH];
#endif
char filename[MM_MAXPATH];
if (usersize <= 0 || usersize > mm_core_maxsegsize()) {
errno = EINVAL;
return NULL;
}
if (file == NULL) {
sprintf(filename, MM_CORE_DEFAULT_FILE, (int)getpid());
file = filename;
}
mm_core_init();
size = mm_core_align2page(usersize+SIZEOF_mem_core);
#if defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
sprintf(shmfilename, "%s.mem", file);
fnmem = shmfilename;
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
sprintf(semfilename, "%s.sem", file);
fnsem = semfilename;
#endif
#if defined(MM_SHMT_MMANON)
if ((area = (void *)mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0)) == MAP_FAILED)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to memory map anonymous area");
#endif /* MM_SHMT_MMANON */
#if defined(MM_SHMT_MMPOSX)
shm_unlink(fnmem); /* Ok when it fails */
if ((fdmem = shm_open(fnmem, O_RDWR|O_CREAT, MM_CORE_FILEMODE)) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to open tempfile");
if (ftruncate(fdmem, mm_core_mapoffset+size) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to truncate tempfile");
write(fdmem, &zero, sizeof(zero));
if ((area = (void *)mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_SHARED, fdmem, mm_core_mapoffset)) == MAP_FAILED)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to memory map tempfile");
shm_unlink(fnmem);
mm_core_mapoffset += size;
#endif /* MM_SHMT_MMPOSX */
#if defined(MM_SHMT_MMZERO)
if ((fdmem = open("/dev/zero", O_RDWR, MM_CORE_FILEMODE)) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to open /dev/zero");
if (lseek(fdmem, mm_core_mapoffset+size, SEEK_SET) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to seek in /dev/zero");
write(fdmem, &zero, sizeof(zero));
if ((area = (void *)mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_SHARED, fdmem, mm_core_mapoffset)) == MAP_FAILED)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to memory map /dev/zero");
mm_core_mapoffset += size;
#endif /* MM_SHMT_MMZERO */
#if defined(MM_SHMT_MMFILE)
unlink(fnmem);
if ((fdmem = open(fnmem, O_RDWR|O_CREAT, MM_CORE_FILEMODE)) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to open memory file");
if (ftruncate(fdmem, mm_core_mapoffset+size) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to truncate memory file");
write(fdmem, &zero, sizeof(zero));
if ((area = (void *)mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_SHARED, fdmem, mm_core_mapoffset)) == MAP_FAILED)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to memory map memory file");
mm_core_mapoffset += size;
#endif /* MM_SHMT_MMFILE */
#if defined(MM_SHMT_IPCSHM)
if ((fdmem = shmget(IPC_PRIVATE, size, (SHM_R|SHM_W|IPC_CREAT))) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to acquire shared memory segment");
if ((area = (void *)shmat(fdmem, NULL, 0)) == ((void *)-1))
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to attach shared memory");
if (shmctl(fdmem, IPC_STAT, &shmbuf) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to get status of shared memory");
shmbuf.shm_perm.uid = getuid();
shmbuf.shm_perm.gid = getgid();
if (shmctl(fdmem, IPC_SET, &shmbuf) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to set status of shared memory");
if (shmctl(fdmem, IPC_RMID, NULL) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to remove shared memory in advance");
#endif /* MM_SHMT_IPCSHM */
#if defined(MM_SEMT_FLOCK)
unlink(fnsem);
if ((fdsem = open(fnsem, O_RDWR|O_CREAT, MM_CORE_FILEMODE)) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to open semaphore file");
#endif /* MM_SEMT_FLOCK */
#if defined(MM_SEMT_FCNTL)
unlink(fnsem);
if ((fdsem = open(fnsem, O_RDWR|O_CREAT, MM_CORE_FILEMODE)) == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to open semaphore file");
#endif /* MM_SEMT_FCNTL */
#if defined(MM_SEMT_IPCSEM)
fdsem = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
if (fdsem == -1 && errno == EEXIST)
fdsem = semget(IPC_PRIVATE, 1, IPC_EXCL|S_IRUSR|S_IWUSR);
if (fdsem == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to acquire semaphore");
mm_core_semctlarg.val = 0;
semctl(fdsem, 0, SETVAL, mm_core_semctlarg);
fdsem_rd = semget(IPC_PRIVATE, 1, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR);
if (fdsem_rd == -1 && errno == EEXIST)
fdsem_rd = semget(IPC_PRIVATE, 1, IPC_EXCL|S_IRUSR|S_IWUSR);
if (fdsem_rd == -1)
FAIL(MM_ERR_CORE|MM_ERR_SYSTEM, "failed to acquire semaphore");
mm_core_semctlarg.val = 0;
semctl(fdsem_rd, 0, SETVAL, mm_core_semctlarg);
#endif /* MM_SEMT_IPCSEM */
/*
* Configure the memory core parameters
*/
mc = (mem_core *)area;
mc->mc_size = size;
mc->mc_usize = usersize;
mc->mc_pid = getpid();
mc->mc_fdmem = fdmem;
#if defined(MM_SEMT_FLOCK)
mc->mc_fdsem[0].pid = getpid();
mc->mc_fdsem[0].fd = fdsem;
mc->mc_fdsem[1].pid = 0;
mc->mc_fdsem[1].fd = -1;
#else
mc->mc_fdsem = fdsem;
#endif
#if defined(MM_SEMT_IPCSEM)
mc->mc_fdsem_rd = fdsem_rd;
mc->mc_readers = 0;
#endif
#if defined(MM_SHMT_MMFILE)
memcpy(mc->mc_fnmem, fnmem, MM_MAXPATH);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
memcpy(mc->mc_fnsem, fnsem, MM_MAXPATH);
#endif
/*
* Return successfully established core
*/
return ((void *)&(mc->mc_base.mw_cp));
/*
* clean-up sequence (CUS) for error situation
*/
BEGIN_FAILURE
#if defined(MM_SHMT_MMANON) || defined(MM_SHMT_MMZERO) || defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
if (area != ((void *)-1))
munmap((caddr_t)area, size);
#endif
#if defined(MM_SHMT_IPCSHM)
if (area != ((void *)-1))
shmdt(area);
#endif
#if defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMFILE)
if (fdmem != -1)
close(fdmem);
#endif
#if defined(MM_SHMT_IPCSHM)
if (fdmem != -1)
shmctl(fdmem, IPC_RMID, NULL);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
if (fdsem != -1)
close(fdsem);
#endif
#if defined(MM_SEMT_IPCSEM)
if (fdsem != -1)
semctl(fdsem, 0, IPC_RMID, 0);
if (fdsem_rd != -1)
semctl(fdsem_rd, 0, IPC_RMID, 0);
#endif
#if defined(MM_SHMT_MMFILE)
unlink(fnmem);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
unlink(fnsem);
#endif
return NULL;
END_FAILURE
}
int mm_core_permission(void *core, mode_t mode, uid_t owner, gid_t group)
{
int rc;
mem_core *mc;
if (core == NULL)
return -1;
mc = (mem_core *)((char *)core-SIZEOF_mem_core);
rc = 0;
#if defined(MM_SHMT_MMFILE)
if (rc == 0 && chmod(mc->mc_fnmem, mode) < 0)
rc = -1;
if (rc == 0 && chown(mc->mc_fnmem, owner, group) < 0)
rc = -1;
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
if (rc == 0 && chmod(mc->mc_fnsem, mode) < 0)
rc = -1;
if (rc == 0 && chown(mc->mc_fnsem, owner, group) < 0)
rc = -1;
#endif
return rc;
}
void mm_core_delete(void *core)
{
mem_core *mc;
int fdmem;
int fdsem;
#if defined(MM_SEMT_IPCSEM)
int fdsem_rd;
#endif
size_t size;
#if defined(MM_SHMT_MMFILE)
char fnmem[MM_MAXPATH];
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
char fnsem[MM_MAXPATH];
#endif
if (core == NULL)
return;
mc = (mem_core *)((char *)core-SIZEOF_mem_core);
size = mc->mc_size;
fdmem = mc->mc_fdmem;
#if !defined(MM_SEMT_FLOCK)
fdsem = mc->mc_fdsem;
#endif
#if defined(MM_SEMT_IPCSEM)
fdsem_rd = mc->mc_fdsem_rd;
#endif
#if defined(MM_SEMT_FLOCK)
fdsem = mm_core_getfdsem(mc);
#endif
#if defined(MM_SHMT_MMFILE)
memcpy(fnmem, mc->mc_fnmem, MM_MAXPATH);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
memcpy(fnsem, mc->mc_fnsem, MM_MAXPATH);
#endif
#if defined(MM_SHMT_MMANON) || defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMZERO) || defined(MM_SHMT_MMFILE)
munmap((caddr_t)mc, size);
#endif
#if defined(MM_SHMT_IPCSHM)
shmdt((void *)mc);
shmctl(fdmem, IPC_RMID, NULL);
#endif
#if defined(MM_SHMT_MMPOSX) || defined(MM_SHMT_MMZERO) || defined(MM_SHMT_MMFILE)
close(fdmem);
#endif
#if defined(MM_SHMT_MMFILE)
unlink(fnmem);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
close(fdsem);
#endif
#if defined(MM_SEMT_FLOCK) || defined(MM_SEMT_FCNTL)
unlink(fnsem);
#endif
#if defined(MM_SEMT_IPCSEM)
semctl(fdsem, 0, IPC_RMID, 0);
semctl(fdsem_rd, 0, IPC_RMID, 0);
#endif
return;
}
size_t mm_core_size(const void *core)
{
mem_core *mc;
if (core == NULL)
return 0;
mc = (mem_core *)((char *)core-SIZEOF_mem_core);
return (mc->mc_usize);
}
int mm_core_lock(const void *core, mm_lock_mode mode)
{
mem_core *mc;
int rc;
int fdsem;
if (core == NULL)
return FALSE;
mc = (mem_core *)((char *)core-SIZEOF_mem_core);
#if !defined(MM_SEMT_FLOCK)
fdsem = mc->mc_fdsem;
#endif
#if defined(MM_SEMT_FLOCK)
fdsem = mm_core_getfdsem(mc);
#endif
#if defined(MM_SEMT_FCNTL)
if (mode == MM_LOCK_RD)
while (((rc = fcntl(fdsem, F_SETLKW, &mm_core_dolock_rd)) < 0) && (errno == EINTR)) ;
else
while (((rc = fcntl(fdsem, F_SETLKW, &mm_core_dolock_rw)) < 0) && (errno == EINTR)) ;
#endif
#if defined(MM_SEMT_FLOCK)
if (mode == MM_LOCK_RD)
while (((rc = flock(fdsem, LOCK_SH)) < 0) && (errno == EINTR)) ;
else
while (((rc = flock(fdsem, LOCK_EX)) < 0) && (errno == EINTR)) ;
#endif
#if defined(MM_SEMT_IPCSEM)
if (mode == MM_LOCK_RD) {
while (((rc = semop(mc->mc_fdsem_rd, mm_core_dolock, 2)) < 0) && (errno == EINTR)) ;
mc->mc_readers++;
if (mc->mc_readers == 1)
while (((rc = semop(fdsem, mm_core_dolock, 2)) < 0) && (errno == EINTR)) ;
while (((rc = semop(mc->mc_fdsem_rd, mm_core_dounlock, 1)) < 0) && (errno == EINTR)) ;
}
else {
while (((rc = semop(fdsem, mm_core_dolock, 2)) < 0) && (errno == EINTR)) ;
}
mc->mc_lockmode = mode;
#endif
if (rc < 0) {
ERR(MM_ERR_CORE|MM_ERR_SYSTEM, "Failed to lock");
rc = FALSE;
}
else
rc = TRUE;
return rc;
}
int mm_core_unlock(const void *core)
{
mem_core *mc;
int rc;
int fdsem;
if (core == NULL)
return FALSE;
mc = (mem_core *)((char *)core-SIZEOF_mem_core);
#if !defined(MM_SEMT_FLOCK)
fdsem = mc->mc_fdsem;
#endif
#if defined(MM_SEMT_FLOCK)
fdsem = mm_core_getfdsem(mc);
#endif
#if defined(MM_SEMT_FCNTL)
while (((rc = fcntl(fdsem, F_SETLKW, &mm_core_dounlock)) < 0) && (errno == EINTR)) ;
#endif
#if defined(MM_SEMT_FLOCK)
while (((rc = flock(fdsem, LOCK_UN)) < 0) && (errno == EINTR)) ;
#endif
#if defined(MM_SEMT_IPCSEM)
if (mc->mc_lockmode == MM_LOCK_RD) {
while (((rc = semop(mc->mc_fdsem_rd, mm_core_dolock, 2)) < 0) && (errno == EINTR)) ;
mc->mc_readers--;
if (mc->mc_readers == 0)
while (((rc = semop(fdsem, mm_core_dounlock, 1)) < 0) && (errno == EINTR)) ;
while (((rc = semop(mc->mc_fdsem_rd, mm_core_dounlock, 1)) < 0) && (errno == EINTR)) ;
}
else {
while (((rc = semop(fdsem, mm_core_dounlock, 1)) < 0) && (errno == EINTR)) ;
}
#endif
if (rc < 0) {
ERR(MM_ERR_CORE|MM_ERR_SYSTEM, "Failed to unlock");
rc = FALSE;
}
else
rc = TRUE;
return rc;
}
/*EOF*/