| /* |
| * 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; |
| } |