blob: 109e09cc8375a003a1e4b080d74d85d5a9b1f911 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***************************************************************************
* Description: Shared Memory support *
* Author: Mladen Turk <mturk@jboss.com> *
* Author: Rainer Jung <rjung@apache.org> *
* Version: $Revision$ *
***************************************************************************/
#include "jk_global.h"
#include "jk_pool.h"
#include "jk_util.h"
#include "jk_mt.h"
#include "jk_shm.h"
/** jk shm header core data structure */
struct jk_shm_header_data
{
/* Shared memory magic JK_SHM_MAGIC */
char magic[JK_SHM_MAGIC_SIZ];
size_t size;
size_t pos;
unsigned int childs;
unsigned int workers;
time_t modified;
};
typedef struct jk_shm_header_data jk_shm_header_data_t;
/** jk shm header record structure */
struct jk_shm_header
{
union {
jk_shm_header_data_t data;
char alignbuf[JK_SHM_ALIGN(sizeof(jk_shm_header_data_t))];
} h;
char buf[1];
};
typedef struct jk_shm_header jk_shm_header_t;
/** jk shm structure */
struct jk_shm
{
size_t size;
char *filename;
char *lockname;
int fd;
int fd_lock;
int attached;
jk_shm_header_t *hdr;
JK_CRIT_SEC cs;
};
typedef struct jk_shm jk_shm_t;
static const char shm_signature[] = { JK_SHM_MAGIC };
static jk_shm_t jk_shmem = { 0, NULL, NULL, -1, -1, 0, NULL};
static time_t jk_workers_modified_time = 0;
static time_t jk_workers_access_time = 0;
#if defined (WIN32)
static HANDLE jk_shm_map = NULL;
#endif
#if defined (WIN32) || defined(NETWARE)
/* Use plain memory */
int jk_shm_open(const char *fname, size_t sz, jk_logger_t *l)
{
int rc;
int attached = 0;
JK_TRACE_ENTER(l);
if (jk_shmem.hdr) {
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG, "Shared memory is already opened");
JK_TRACE_EXIT(l);
return 0;
}
jk_shmem.size = JK_SHM_ALIGN(sizeof(jk_shm_header_t) + sz);
#if defined (WIN32)
if (fname) {
jk_shm_map = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
(DWORD)(sizeof(jk_shm_header_t) + sz),
fname);
if (GetLastError() == ERROR_ALREADY_EXISTS) {
attached = 1;
if (jk_shm_map == NULL || jk_shm_map == INVALID_HANDLE_VALUE) {
jk_shm_map = OpenFileMapping(PAGE_READWRITE, FALSE, fname);
}
}
if (jk_shm_map == NULL || jk_shm_map == INVALID_HANDLE_VALUE) {
JK_TRACE_EXIT(l);
return -1;
}
jk_shmem.hdr = (jk_shm_header_t *)MapViewOfFile(jk_shm_map,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
}
else
#endif
jk_shmem.hdr = (jk_shm_header_t *)calloc(1, jk_shmem.size);
if (!jk_shmem.hdr) {
#if defined (WIN32)
if (jk_shm_map) {
CloseHandle(jk_shm_map);
jk_shm_map = NULL;
}
#endif
JK_TRACE_EXIT(l);
return -1;
}
if (!jk_shmem.filename) {
if (fname)
jk_shmem.filename = strdup(fname);
else
jk_shmem.filename = strdup("memory");
}
jk_shmem.fd = 0;
jk_shmem.attached = attached;
if (!attached) {
memcpy(jk_shmem.hdr->h.data.magic, shm_signature,
JK_SHM_MAGIC_SIZ);
jk_shmem.hdr->h.data.size = sz;
jk_shmem.hdr->h.data.childs = 1;
}
else {
jk_shmem.hdr->h.data.childs++;
/*
* Reset the shared memory so that
* alloc works even for attached memory.
* XXX: This might break already used memory
* if the number of workers change between
* open and attach or between two attach operations.
*/
if (jk_shmem.hdr->h.data.childs > 1) {
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG,
"Reseting the shared memory for child %d",
jk_shmem.hdr->h.data.childs);
}
}
jk_shmem.hdr->h.data.pos = 0;
jk_shmem.hdr->h.data.workers = 0;
}
JK_INIT_CS(&(jk_shmem.cs), rc);
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"%s shared memory size=%u free=%u addr=%#lx",
attached ? "Attached" : "Initialized",
jk_shmem.size, jk_shmem.hdr->h.data.size, jk_shmem.hdr);
JK_TRACE_EXIT(l);
return 0;
}
int jk_shm_attach(const char *fname, size_t sz, jk_logger_t *l)
{
JK_TRACE_ENTER(l);
if (!jk_shm_open(fname, sz, l)) {
if (!jk_shmem.attached) {
jk_shmem.attached = 1;
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG,
"Attached shared memory [%d] size=%u free=%u addr=%#lx",
jk_shmem.hdr->h.data.childs, jk_shmem.hdr->h.data.size,
jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos,
jk_shmem.hdr);
}
}
JK_TRACE_EXIT(l);
return 0;
}
else {
JK_TRACE_EXIT(l);
return -1;
}
}
void jk_shm_close()
{
if (jk_shmem.hdr) {
int rc;
#if defined (WIN32)
if (jk_shm_map) {
--jk_shmem.hdr->h.data.childs;
UnmapViewOfFile(jk_shmem.hdr);
CloseHandle(jk_shm_map);
jk_shm_map = NULL;
}
else
#endif
free(jk_shmem.hdr);
JK_DELETE_CS(&(jk_shmem.cs), rc);
}
jk_shmem.hdr = NULL;
if (jk_shmem.filename) {
free(jk_shmem.filename);
jk_shmem.filename = NULL;
}
}
#else
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/uio.h>
#ifndef MAP_FAILED
#define MAP_FAILED (-1)
#endif
#ifndef MAP_FILE
#define MAP_FILE (0)
#endif
static int do_shm_open_lock(const char *fname, int attached, jk_logger_t *l)
{
int rc;
char flkname[256];
JK_TRACE_ENTER(l);
if (attached && jk_shmem.lockname) {
#ifdef JK_SHM_LOCK_REOPEN
jk_shmem.fd_lock = open(jk_shmem.lockname, O_RDWR, 0666);
#else
errno = EINVAL;
#endif
if (jk_shmem.fd_lock == -1) {
rc = errno;
JK_TRACE_EXIT(l);
return rc;
}
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Duplicated shared memory lock %s", jk_shmem.lockname);
JK_TRACE_EXIT(l);
return 0;
}
if (!jk_shmem.lockname) {
#ifdef JK_SHM_LOCK_REOPEN
int i;
jk_shmem.fd_lock = -1;
mode_t mask = umask(0);
for (i = 0; i < 8; i++) {
strcpy(flkname, "/tmp/jkshmlock.XXXXXX");
if (mktemp(flkname)) {
jk_shmem.fd_lock = open(flkname, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (jk_shmem.fd_lock >= 0)
break;
}
}
umask(mask);
#else
strcpy(flkname, fname);
strcat(flkname, ".lock");
jk_shmem.fd_lock = open(flkname, O_RDWR|O_CREAT|O_TRUNC, 0666);
#endif
if (jk_shmem.fd_lock == -1) {
rc = errno;
JK_TRACE_EXIT(l);
return rc;
}
jk_shmem.lockname = strdup(flkname);
}
else {
/* Nothing to do */
JK_TRACE_EXIT(l);
return 0;
}
if (ftruncate(jk_shmem.fd_lock, 1)) {
rc = errno;
close(jk_shmem.fd_lock);
jk_shmem.fd_lock = -1;
JK_TRACE_EXIT(l);
return rc;
}
if (lseek(jk_shmem.fd_lock, 0, SEEK_SET) != 0) {
rc = errno;
close(jk_shmem.fd_lock);
jk_shmem.fd_lock = -1;
return rc;
}
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Opened shared memory lock %s", jk_shmem.lockname);
JK_TRACE_EXIT(l);
return 0;
}
static int do_shm_open(const char *fname, int attached,
size_t sz, jk_logger_t *l)
{
int rc;
int fd;
void *base;
JK_TRACE_ENTER(l);
if (jk_shmem.hdr) {
/* Probably a call from vhost */
if (!attached)
attached = 1;
}
else if (attached) {
/* We should already have a header
* Use memory if we don't
*/
JK_TRACE_EXIT(l);
return 0;
}
jk_shmem.size = JK_SHM_ALIGN(sizeof(jk_shm_header_t) + sz);
if (!fname) {
/* Use plain memory in case there is no file name */
if (!jk_shmem.filename)
jk_shmem.filename = strdup("memory");
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Using process memory as shared memory");
JK_TRACE_EXIT(l);
return 0;
}
if (!jk_shmem.filename) {
jk_shmem.filename = (char *)malloc(strlen(fname) + 32);
sprintf(jk_shmem.filename, "%s.%d", fname, (int)getpid());
}
if (!attached) {
size_t size;
jk_shmem.attached = 0;
fd = open(jk_shmem.filename, O_RDWR|O_CREAT|O_TRUNC, 0666);
if (fd == -1) {
jk_shmem.size = 0;
JK_TRACE_EXIT(l);
return errno;
}
size = lseek(fd, 0, SEEK_END);
if (size < jk_shmem.size) {
size = jk_shmem.size;
if (ftruncate(fd, jk_shmem.size)) {
rc = errno;
close(fd);
unlink(jk_shmem.filename);
jk_shmem.size = 0;
JK_TRACE_EXIT(l);
return rc;
}
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Truncated shared memory to %u", size);
}
if (lseek(fd, 0, SEEK_SET) != 0) {
rc = errno;
close(fd);
unlink(jk_shmem.filename);
jk_shmem.size = 0;
JK_TRACE_EXIT(l);
return rc;
}
base = mmap((caddr_t)0, jk_shmem.size,
PROT_READ | PROT_WRITE,
MAP_FILE | MAP_SHARED,
fd, 0);
if (base == (caddr_t)MAP_FAILED || base == (caddr_t)0) {
rc = errno;
close(fd);
unlink(jk_shmem.filename);
jk_shmem.size = 0;
JK_TRACE_EXIT(l);
return rc;
}
jk_shmem.hdr = base;
jk_shmem.fd = fd;
memset(jk_shmem.hdr, 0, jk_shmem.size);
memcpy(jk_shmem.hdr->h.data.magic, shm_signature, JK_SHM_MAGIC_SIZ);
jk_shmem.hdr->h.data.size = sz;
jk_shmem.hdr->h.data.childs = 1;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Initialized shared memory size=%u free=%u addr=%#lx",
jk_shmem.size, jk_shmem.hdr->h.data.size, jk_shmem.hdr);
}
else {
unsigned int nchild;
jk_shmem.hdr->h.data.childs++;
jk_shmem.attached = (int)getpid();
nchild = jk_shmem.hdr->h.data.childs;
if (JK_IS_DEBUG_LEVEL(l))
jk_log(l, JK_LOG_DEBUG,
"Attached shared memory [%d] size=%u free=%u addr=%#lx",
nchild, jk_shmem.hdr->h.data.size,
jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos,
jk_shmem.hdr);
/*
* Reset the shared memory so that
* alloc works even for attached memory.
* XXX: This might break already used memory
* if the number of workers change between
* open and attach or between two attach operations.
*/
if (nchild > 1) {
if (JK_IS_DEBUG_LEVEL(l)) {
jk_log(l, JK_LOG_DEBUG,
"Reseting the shared memory for child %d",
nchild);
}
}
jk_shmem.hdr->h.data.pos = 0;
jk_shmem.hdr->h.data.workers = 0;
}
JK_INIT_CS(&(jk_shmem.cs), rc);
if ((rc = do_shm_open_lock(jk_shmem.filename, attached, l))) {
if (!attached) {
munmap((void *)jk_shmem.hdr, jk_shmem.size);
close(jk_shmem.fd);
unlink(jk_shmem.filename);
}
jk_shmem.hdr = NULL;
jk_shmem.fd = -1;
JK_TRACE_EXIT(l);
return rc;
}
JK_TRACE_EXIT(l);
return 0;
}
int jk_shm_open(const char *fname, size_t sz, jk_logger_t *l)
{
return do_shm_open(fname, 0, sz, l);
}
int jk_shm_attach(const char *fname, size_t sz, jk_logger_t *l)
{
return do_shm_open(fname, 1, sz, l);
}
void jk_shm_close()
{
int rc;
if (jk_shmem.hdr) {
--jk_shmem.hdr->h.data.childs;
#ifdef JK_SHM_LOCK_REOPEN
if (jk_shmem.fd_lock >= 0) {
close(jk_shmem.fd_lock);
jk_shmem.fd_lock = -1;
}
#endif
JK_DELETE_CS(&(jk_shmem.cs), rc);
if (jk_shmem.attached) {
int p = (int)getpid();
if (p == jk_shmem.attached) {
/* In case this is a forked child
* do not close the shared memory.
* It will be closed by the parent.
*/
jk_shmem.size = 0;
jk_shmem.hdr = NULL;
jk_shmem.fd = -1;
return;
}
}
if (jk_shmem.fd >= 0) {
munmap((void *)jk_shmem.hdr, jk_shmem.size);
close(jk_shmem.fd);
}
if (jk_shmem.fd_lock >= 0)
close(jk_shmem.fd_lock);
if (jk_shmem.lockname) {
unlink(jk_shmem.lockname);
free(jk_shmem.lockname);
jk_shmem.lockname = NULL;
}
if (jk_shmem.filename) {
unlink(jk_shmem.filename);
free(jk_shmem.filename);
jk_shmem.filename = NULL;
}
}
jk_shmem.size = 0;
jk_shmem.hdr = NULL;
jk_shmem.fd = -1;
jk_shmem.fd_lock = -1;
}
#endif
void *jk_shm_alloc(jk_pool_t *p, size_t size)
{
void *rc = NULL;
if (jk_shmem.hdr) {
size = JK_ALIGN_DEFAULT(size);
if ((jk_shmem.hdr->h.data.size - jk_shmem.hdr->h.data.pos) >= size) {
rc = &(jk_shmem.hdr->buf[jk_shmem.hdr->h.data.pos]);
jk_shmem.hdr->h.data.pos += size;
}
}
else if (p)
rc = jk_pool_alloc(p, size);
return rc;
}
const char *jk_shm_name()
{
return jk_shmem.filename;
}
time_t jk_shm_get_workers_time()
{
if (jk_shmem.hdr)
return jk_shmem.hdr->h.data.modified;
else
return jk_workers_modified_time;
}
void jk_shm_set_workers_time(time_t t)
{
if (jk_shmem.hdr)
jk_shmem.hdr->h.data.modified = t;
else
jk_workers_modified_time = t;
jk_workers_access_time = t;
}
int jk_shm_is_modified()
{
time_t m = jk_shm_get_workers_time();
if (m != jk_workers_access_time)
return 1;
else
return 0;
}
void jk_shm_sync_access_time()
{
jk_workers_access_time = jk_shm_get_workers_time();
}
int jk_shm_lock()
{
int rc;
JK_ENTER_CS(&(jk_shmem.cs), rc);
if (rc == JK_TRUE && jk_shmem.fd_lock != -1) {
JK_ENTER_LOCK(jk_shmem.fd_lock, rc);
}
return rc;
}
int jk_shm_unlock()
{
int rc;
JK_LEAVE_CS(&(jk_shmem.cs), rc);
if (rc == JK_TRUE && jk_shmem.fd_lock != -1) {
JK_LEAVE_LOCK(jk_shmem.fd_lock, rc);
}
return rc;
}
jk_shm_worker_t *jk_shm_alloc_worker(jk_pool_t *p)
{
jk_shm_worker_t *w = (jk_shm_worker_t *)jk_shm_alloc(p, sizeof(jk_shm_worker_t));
if (w) {
memset(w, 0, sizeof(jk_shm_worker_t));
if (jk_shmem.hdr) {
jk_shmem.hdr->h.data.workers++;
w->id = jk_shmem.hdr->h.data.workers;
}
else
w->id = -1;
}
return w;
}