1312 lines
32 KiB
C
1312 lines
32 KiB
C
/* This module provides a set of abstract shared memory interfaces
|
|
* with support using both System V and POSIX shared memory
|
|
* implementations. The code is divided into three sections:
|
|
*
|
|
* - common (interface-independent) code
|
|
* - POSIX implementation
|
|
* - System V implementation
|
|
* - Windows implementation
|
|
*
|
|
* The implementation used is determined by whether USE_POSIX_SHM was
|
|
* set in the ./configure step.
|
|
*/
|
|
|
|
/*
|
|
Copyright (C) 2001-2003 Paul Davis
|
|
Copyright (C) 2005-2012 Grame
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
#include "JackConstants.h"
|
|
|
|
#ifdef WIN32
|
|
#include <process.h>
|
|
#include <stdio.h>
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/sem.h>
|
|
#include <stdlib.h>
|
|
#include "promiscuous.h"
|
|
|
|
#endif
|
|
|
|
#include "shm.h"
|
|
#include "JackError.h"
|
|
|
|
static int GetUID()
|
|
{
|
|
#ifdef WIN32
|
|
return _getpid();
|
|
//#error "No getuid function available"
|
|
#else
|
|
return geteuid();
|
|
#endif
|
|
}
|
|
|
|
static int GetPID()
|
|
{
|
|
#ifdef WIN32
|
|
return _getpid();
|
|
#else
|
|
return getpid();
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_POSIX_SHM
|
|
static const jack_shmtype_t jack_shmtype = shm_POSIX;
|
|
#elif WIN32
|
|
static const jack_shmtype_t jack_shmtype = shm_WIN32;
|
|
#else
|
|
static const jack_shmtype_t jack_shmtype = shm_SYSV;
|
|
#endif
|
|
|
|
/* interface-dependent forward declarations */
|
|
static int jack_access_registry (jack_shm_info_t *ri);
|
|
static int jack_create_registry (jack_shm_info_t *ri);
|
|
static void jack_remove_shm (const jack_shm_id_t id);
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* common interface-independent section
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* The JACK SHM registry is a chunk of memory for keeping track of the
|
|
* shared memory used by each active JACK server. This allows the
|
|
* server to clean up shared memory when it exits. To avoid memory
|
|
* leakage due to kill -9, crashes or debugger-driven exits, this
|
|
* cleanup is also done when a new instance of that server starts.
|
|
*/
|
|
|
|
/* per-process global data for the SHM interfaces */
|
|
#ifdef USE_POSIX_SHM
|
|
static const jack_shm_id_t registry_id = "/jack-shm-registry";
|
|
#elif WIN32
|
|
static const jack_shm_id_t registry_id = "jack-shm-registry";
|
|
#else
|
|
static jack_shm_id_t registry_id; /* SHM id for the registry */
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
static jack_shm_info_t registry_info = {/* SHM info for the registry */
|
|
JACK_SHM_NULL_INDEX,
|
|
0,
|
|
{ NULL }
|
|
};
|
|
#else
|
|
static jack_shm_info_t registry_info = { /* SHM info for the registry */
|
|
.index = JACK_SHM_NULL_INDEX,
|
|
.ptr.attached_at = MAP_FAILED
|
|
};
|
|
#endif
|
|
|
|
/* pointers to registry header and array */
|
|
static jack_shm_header_t *jack_shm_header = NULL;
|
|
static jack_shm_registry_t *jack_shm_registry = NULL;
|
|
|
|
/* jack_shm_lock_registry() serializes updates to the shared memory
|
|
* segment JACK uses to keep track of the SHM segments allocated to
|
|
* all its processes, including multiple servers.
|
|
*
|
|
* This is not a high-contention lock, but it does need to work across
|
|
* multiple processes. High transaction rates and realtime safety are
|
|
* not required. Any solution needs to at least be portable to POSIX
|
|
* and POSIX-like systems.
|
|
*
|
|
* We must be particularly careful to ensure that the lock be released
|
|
* if the owning process terminates abnormally. Otherwise, a segfault
|
|
* or kill -9 at the wrong moment could prevent JACK from ever running
|
|
* again on that machine until after a reboot.
|
|
*/
|
|
|
|
#define JACK_SEMAPHORE_KEY 0x282929
|
|
#ifndef USE_POSIX_SHM
|
|
#define JACK_SHM_REGISTRY_KEY JACK_SEMAPHORE_KEY
|
|
#endif
|
|
|
|
static int semid = -1;
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <psapi.h>
|
|
#include <lmcons.h>
|
|
|
|
static BOOL check_process_running(DWORD process_id)
|
|
{
|
|
DWORD aProcesses[2048], cbNeeded, cProcesses;
|
|
unsigned int i;
|
|
|
|
// Enumerate all processes
|
|
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Calculate how many process identifiers were returned.
|
|
cProcesses = cbNeeded / sizeof(DWORD);
|
|
|
|
for (i = 0; i < cProcesses; i++) {
|
|
if (aProcesses[i] == process_id) {
|
|
// Process process_id is running...
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static int
|
|
semaphore_init () {return 0;}
|
|
|
|
static int
|
|
semaphore_add (int value) {return 0;}
|
|
|
|
#else
|
|
/* all semaphore errors are fatal -- issue message, but do not return */
|
|
static void
|
|
semaphore_error (char *msg)
|
|
{
|
|
jack_error ("JACK semaphore error: %s (%s)",
|
|
msg, strerror (errno));
|
|
}
|
|
|
|
static int
|
|
semaphore_init ()
|
|
{
|
|
key_t semkey = JACK_SEMAPHORE_KEY;
|
|
struct sembuf sbuf;
|
|
int create_flags = IPC_CREAT | IPC_EXCL
|
|
| S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
|
|
|
/* Get semaphore ID associated with this key. */
|
|
if ((semid = semget(semkey, 0, 0)) == -1) {
|
|
|
|
/* Semaphore does not exist - Create. */
|
|
if ((semid = semget(semkey, 1, create_flags)) != -1) {
|
|
|
|
/* Initialize the semaphore, allow one owner. */
|
|
sbuf.sem_num = 0;
|
|
sbuf.sem_op = 1;
|
|
sbuf.sem_flg = 0;
|
|
if (semop(semid, &sbuf, 1) == -1) {
|
|
semaphore_error ("semop");
|
|
return -1;
|
|
}
|
|
|
|
} else if (errno == EEXIST) {
|
|
if ((semid = semget(semkey, 0, 0)) == -1) {
|
|
semaphore_error ("semget");
|
|
return -1;
|
|
}
|
|
|
|
} else {
|
|
semaphore_error ("semget creation");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int
|
|
semaphore_add (int value)
|
|
{
|
|
struct sembuf sbuf;
|
|
|
|
sbuf.sem_num = 0;
|
|
sbuf.sem_op = value;
|
|
sbuf.sem_flg = SEM_UNDO;
|
|
|
|
if (semop(semid, &sbuf, 1) == -1) {
|
|
semaphore_error ("semop");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
static int
|
|
jack_shm_lock_registry (void)
|
|
{
|
|
if (semid == -1) {
|
|
if (semaphore_init () < 0)
|
|
return -1;
|
|
}
|
|
|
|
return semaphore_add (-1);
|
|
}
|
|
|
|
static void
|
|
jack_shm_unlock_registry (void)
|
|
{
|
|
semaphore_add (1);
|
|
}
|
|
|
|
static void
|
|
jack_shm_init_registry ()
|
|
{
|
|
/* registry must be locked */
|
|
int i;
|
|
|
|
memset (jack_shm_header, 0, JACK_SHM_REGISTRY_SIZE);
|
|
|
|
jack_shm_header->magic = JACK_SHM_MAGIC;
|
|
//jack_shm_header->protocol = JACK_PROTOCOL_VERSION;
|
|
jack_shm_header->type = jack_shmtype;
|
|
jack_shm_header->size = JACK_SHM_REGISTRY_SIZE;
|
|
jack_shm_header->hdr_len = sizeof (jack_shm_header_t);
|
|
jack_shm_header->entry_len = sizeof (jack_shm_registry_t);
|
|
|
|
for (i = 0; i < MAX_SHM_ID; ++i) {
|
|
jack_shm_registry[i].index = i;
|
|
}
|
|
}
|
|
|
|
static int
|
|
jack_shm_validate_registry ()
|
|
{
|
|
/* registry must be locked */
|
|
|
|
if ((jack_shm_header->magic == JACK_SHM_MAGIC)
|
|
//&& (jack_shm_header->protocol == JACK_PROTOCOL_VERSION)
|
|
&& (jack_shm_header->type == jack_shmtype)
|
|
&& (jack_shm_header->size == JACK_SHM_REGISTRY_SIZE)
|
|
&& (jack_shm_header->hdr_len == sizeof (jack_shm_header_t))
|
|
&& (jack_shm_header->entry_len == sizeof (jack_shm_registry_t))) {
|
|
|
|
return 0; /* registry OK */
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* set a unique per-user, per-server shm prefix string
|
|
*
|
|
* According to the POSIX standard:
|
|
*
|
|
* "The name argument conforms to the construction rules for a
|
|
* pathname. If name begins with the slash character, then processes
|
|
* calling shm_open() with the same value of name refer to the same
|
|
* shared memory object, as long as that name has not been
|
|
* removed. If name does not begin with the slash character, the
|
|
* effect is implementation-defined. The interpretation of slash
|
|
* characters other than the leading slash character in name is
|
|
* implementation-defined."
|
|
*
|
|
* Since the Linux implementation does not allow slashes *within* the
|
|
* name, in the interest of portability we use colons instead.
|
|
*/
|
|
static void
|
|
jack_get_server_prefix (const char* const server_name, char* const prefix, const size_t size)
|
|
{
|
|
#ifdef WIN32
|
|
char buffer[UNLEN+1]={0};
|
|
DWORD len = UNLEN+1;
|
|
GetUserName(buffer, &len);
|
|
snprintf (prefix, size,
|
|
"jack-%s:%s:", buffer, server_name);
|
|
#else
|
|
snprintf (prefix, size,
|
|
"jack-%d:%s:", GetUID(), server_name);
|
|
#endif
|
|
}
|
|
|
|
/* gain server addressability to shared memory registration segment
|
|
*
|
|
* returns: 0 if successful
|
|
*/
|
|
static int
|
|
jack_server_initialize_shm (int new_registry)
|
|
{
|
|
int rc;
|
|
|
|
if (jack_shm_header)
|
|
return 0; /* already initialized */
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
rc = jack_access_registry (®istry_info);
|
|
|
|
if (new_registry) {
|
|
jack_remove_shm (registry_id);
|
|
rc = ENOENT;
|
|
}
|
|
|
|
switch (rc) {
|
|
case ENOENT: /* registry does not exist */
|
|
rc = jack_create_registry (®istry_info);
|
|
break;
|
|
case 0: /* existing registry */
|
|
if (jack_shm_validate_registry () == 0)
|
|
break;
|
|
/* else it was invalid, so fall through */
|
|
case EINVAL: /* bad registry */
|
|
/* Apparently, this registry was created by an older
|
|
* JACK version. Delete it so we can try again. */
|
|
jack_release_shm (®istry_info);
|
|
jack_remove_shm (registry_id);
|
|
if ((rc = jack_create_registry (®istry_info)) != 0) {
|
|
jack_error ("incompatible shm registry (%s)",
|
|
strerror (errno));
|
|
#ifndef USE_POSIX_SHM
|
|
jack_error ("to delete, use `ipcrm -M 0x%0.8x'",
|
|
JACK_SHM_REGISTRY_KEY);
|
|
#endif
|
|
}
|
|
break;
|
|
default: /* failure return code */
|
|
break;
|
|
}
|
|
|
|
jack_shm_unlock_registry ();
|
|
return rc;
|
|
}
|
|
|
|
/* gain client addressability to shared memory registration segment
|
|
*
|
|
* NOTE: this function is no longer used for server initialization,
|
|
* instead it calls jack_register_server().
|
|
*
|
|
* returns: 0 if successful
|
|
*/
|
|
int
|
|
jack_initialize_shm (const char *server_name)
|
|
{
|
|
int rc;
|
|
char server_prefix[JACK_SERVER_NAME_SIZE+1];
|
|
|
|
if (jack_shm_header)
|
|
return 0; /* already initialized */
|
|
|
|
jack_get_server_prefix (server_name, server_prefix, sizeof(server_prefix));
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
if ((rc = jack_access_registry (®istry_info)) == 0) {
|
|
if ((rc = jack_shm_validate_registry ()) != 0) {
|
|
jack_error ("Incompatible shm registry, "
|
|
"are jackd and libjack in sync?");
|
|
}
|
|
}
|
|
jack_shm_unlock_registry ();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
char* jack_shm_addr (jack_shm_info_t* si)
|
|
{
|
|
return (char*)si->ptr.attached_at;
|
|
}
|
|
|
|
void
|
|
jack_destroy_shm (jack_shm_info_t* si)
|
|
{
|
|
/* must NOT have the registry locked */
|
|
if (si->index == JACK_SHM_NULL_INDEX)
|
|
return; /* segment not allocated */
|
|
|
|
jack_remove_shm (jack_shm_registry[si->index].id);
|
|
jack_release_shm_info (si->index);
|
|
}
|
|
|
|
jack_shm_registry_t *
|
|
jack_get_free_shm_info ()
|
|
{
|
|
/* registry must be locked */
|
|
jack_shm_registry_t* si = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_SHM_ID; ++i) {
|
|
if (jack_shm_registry[i].size == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < MAX_SHM_ID) {
|
|
si = &jack_shm_registry[i];
|
|
}
|
|
|
|
return si;
|
|
}
|
|
|
|
static void
|
|
jack_release_shm_entry (jack_shm_registry_index_t index)
|
|
{
|
|
/* the registry must be locked */
|
|
jack_shm_registry[index].size = 0;
|
|
jack_shm_registry[index].allocator = 0;
|
|
memset (&jack_shm_registry[index].id, 0,
|
|
sizeof (jack_shm_registry[index].id));
|
|
}
|
|
|
|
int
|
|
jack_release_shm_info (jack_shm_registry_index_t index)
|
|
{
|
|
/* must NOT have the registry locked */
|
|
if (jack_shm_registry[index].allocator == GetPID()) {
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
jack_release_shm_entry (index);
|
|
jack_shm_unlock_registry ();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Claim server_name for this process.
|
|
*
|
|
* returns 0 if successful
|
|
* EEXIST if server_name was already active for this user
|
|
* ENOSPC if server registration limit reached
|
|
* ENOMEM if unable to access shared memory registry
|
|
*/
|
|
int
|
|
jack_register_server (const char *server_name, int new_registry)
|
|
{
|
|
int i, res = 0;
|
|
char server_prefix[JACK_SERVER_NAME_SIZE+1];
|
|
|
|
jack_get_server_prefix (server_name, server_prefix, sizeof(server_prefix));
|
|
|
|
if (jack_server_initialize_shm (new_registry))
|
|
return ENOMEM;
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
/* See if server_name already registered. Since server names
|
|
* are per-user, we register the unique server prefix string.
|
|
*/
|
|
for (i = 0; i < MAX_SERVERS; i++) {
|
|
|
|
if (strncmp (jack_shm_header->server[i].name,
|
|
server_prefix,
|
|
sizeof(server_prefix)) != 0)
|
|
continue; /* no match */
|
|
|
|
if (jack_shm_header->server[i].pid == GetPID()){
|
|
res = 0; /* it's me */
|
|
goto unlock;
|
|
}
|
|
|
|
/* see if server still exists */
|
|
#ifdef WIN32
|
|
if (check_process_running(jack_shm_header->server[i].pid)) {
|
|
res = EEXIST; /* other server running */
|
|
goto unlock;
|
|
}
|
|
#else
|
|
if (kill (jack_shm_header->server[i].pid, 0) == 0) {
|
|
res = EEXIST; /* other server running */
|
|
goto unlock;
|
|
}
|
|
#endif
|
|
|
|
/* it's gone, reclaim this entry */
|
|
memset (&jack_shm_header->server[i], 0,
|
|
sizeof (jack_shm_server_t));
|
|
}
|
|
|
|
/* find a free entry */
|
|
for (i = 0; i < MAX_SERVERS; i++) {
|
|
if (jack_shm_header->server[i].pid == 0)
|
|
break;
|
|
}
|
|
|
|
if (i >= MAX_SERVERS){
|
|
res = ENOSPC; /* out of space */
|
|
goto unlock;
|
|
}
|
|
|
|
/* claim it */
|
|
jack_shm_header->server[i].pid = GetPID();
|
|
strncpy (jack_shm_header->server[i].name,
|
|
server_prefix,
|
|
sizeof(server_prefix));
|
|
|
|
unlock:
|
|
jack_shm_unlock_registry ();
|
|
return res;
|
|
}
|
|
|
|
/* release server_name registration */
|
|
int
|
|
jack_unregister_server (const char *server_name /* unused */)
|
|
{
|
|
int i;
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < MAX_SERVERS; i++) {
|
|
if (jack_shm_header->server[i].pid == GetPID()) {
|
|
memset (&jack_shm_header->server[i], 0,
|
|
sizeof (jack_shm_server_t));
|
|
}
|
|
}
|
|
|
|
jack_shm_unlock_registry ();
|
|
return 0;
|
|
}
|
|
|
|
/* called for server startup and termination */
|
|
int
|
|
jack_cleanup_shm ()
|
|
{
|
|
int i;
|
|
int destroy;
|
|
jack_shm_info_t copy;
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < MAX_SHM_ID; i++) {
|
|
jack_shm_registry_t* r;
|
|
|
|
r = &jack_shm_registry[i];
|
|
memcpy (©, r, sizeof (jack_shm_info_t));
|
|
destroy = FALSE;
|
|
|
|
/* ignore unused entries */
|
|
if (r->allocator == 0)
|
|
continue;
|
|
|
|
/* is this my shm segment? */
|
|
if (r->allocator == GetPID()) {
|
|
|
|
/* allocated by this process, so unattach
|
|
and destroy. */
|
|
jack_release_shm (©);
|
|
destroy = TRUE;
|
|
|
|
} else {
|
|
|
|
/* see if allocator still exists */
|
|
#ifdef WIN32
|
|
//jack_info("TODO: kill API not available !!");
|
|
#else
|
|
if (kill (r->allocator, 0)) {
|
|
if (errno == ESRCH) {
|
|
/* allocator no longer exists,
|
|
* so destroy */
|
|
destroy = TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (destroy) {
|
|
|
|
int index = copy.index;
|
|
|
|
if ((index >= 0) && (index < MAX_SHM_ID)) {
|
|
jack_remove_shm (jack_shm_registry[index].id);
|
|
jack_release_shm_entry (index);
|
|
}
|
|
r->size = 0;
|
|
r->allocator = 0;
|
|
}
|
|
}
|
|
|
|
jack_shm_unlock_registry ();
|
|
return TRUE;
|
|
}
|
|
|
|
/* resize a shared memory segment
|
|
*
|
|
* There is no way to resize a System V shm segment. Resizing is
|
|
* possible with POSIX shm, but not with the non-conformant Mac OS X
|
|
* implementation. Since POSIX shm is mainly used on that platform,
|
|
* it's simpler to treat them both the same.
|
|
*
|
|
* So, we always resize by deleting and reallocating. This is
|
|
* tricky, because the old segment will not disappear until
|
|
* all the clients have released it. We only do what we can
|
|
* from here.
|
|
*
|
|
* This is not done under a single lock. I don't even want to think
|
|
* about all the things that could possibly go wrong if multiple
|
|
* processes tried to resize the same segment concurrently. That
|
|
* probably doesn't happen.
|
|
*/
|
|
int
|
|
jack_resize_shm (jack_shm_info_t* si, jack_shmsize_t size)
|
|
{
|
|
jack_shm_id_t id;
|
|
|
|
/* The underlying type of `id' differs for SYSV and POSIX */
|
|
memcpy (&id, &jack_shm_registry[si->index].id, sizeof (id));
|
|
|
|
jack_release_shm (si);
|
|
jack_destroy_shm (si);
|
|
|
|
if (jack_shmalloc ((char *) id, size, si)) {
|
|
return -1;
|
|
}
|
|
|
|
return jack_attach_shm (si);
|
|
}
|
|
|
|
int
|
|
jack_attach_lib_shm (jack_shm_info_t* si)
|
|
{
|
|
int res = jack_attach_shm(si);
|
|
if (res == 0)
|
|
si->size = jack_shm_registry[si->index].size; // Keep size in si struct
|
|
return res;
|
|
}
|
|
|
|
int
|
|
jack_attach_lib_shm_read (jack_shm_info_t* si)
|
|
{
|
|
int res = jack_attach_shm_read(si);
|
|
if (res == 0)
|
|
si->size = jack_shm_registry[si->index].size; // Keep size in si struct
|
|
return res;
|
|
}
|
|
|
|
#ifdef USE_POSIX_SHM
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* POSIX interface-dependent functions
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* gain addressability to existing SHM registry segment
|
|
*
|
|
* sets up global registry pointers, if successful
|
|
*
|
|
* returns: 0 if existing registry accessed successfully
|
|
* ENOENT if registry does not exist
|
|
* EINVAL if registry exists, but has the wrong size
|
|
*/
|
|
static int
|
|
jack_access_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
int shm_fd;
|
|
|
|
/* try to open an existing segment */
|
|
if ((shm_fd = shm_open (registry_id, O_RDWR, 0666)) < 0) {
|
|
int rc = errno;
|
|
if (errno != ENOENT) {
|
|
jack_error ("Cannot open existing shm registry segment"
|
|
" (%s)", strerror (errno));
|
|
}
|
|
close (shm_fd);
|
|
return rc;
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE,
|
|
PROT_READ|PROT_WRITE,
|
|
MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
|
|
jack_error ("Cannot mmap shm registry segment (%s)",
|
|
strerror (errno));
|
|
close (shm_fd);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
|
|
close (shm_fd);
|
|
return 0;
|
|
}
|
|
|
|
/* create a new SHM registry segment
|
|
*
|
|
* sets up global registry pointers, if successful
|
|
*
|
|
* returns: 0 if registry created successfully
|
|
* nonzero error code if unable to allocate a new registry
|
|
*/
|
|
static int
|
|
jack_create_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
int shm_fd;
|
|
|
|
if ((shm_fd = shm_open (registry_id, O_RDWR|O_CREAT, 0666)) < 0) {
|
|
int rc = errno;
|
|
jack_error ("Cannot create shm registry segment (%s)",
|
|
strerror (errno));
|
|
return rc;
|
|
}
|
|
|
|
/* Previous shm_open result depends of the actual value of umask, force correct file permission here */
|
|
if (fchmod(shm_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) < 0) {
|
|
jack_log("Cannot chmod jack-shm-registry (%s) %d %d", strerror (errno));
|
|
}
|
|
|
|
/* Set the desired segment size. NOTE: the non-conformant Mac
|
|
* OS X POSIX shm only allows ftruncate() on segment creation.
|
|
*/
|
|
if (ftruncate (shm_fd, JACK_SHM_REGISTRY_SIZE) < 0) {
|
|
int rc = errno;
|
|
jack_error ("Cannot set registry size (%s)", strerror (errno));
|
|
jack_remove_shm (registry_id);
|
|
close (shm_fd);
|
|
return rc;
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = mmap (0, JACK_SHM_REGISTRY_SIZE,
|
|
PROT_READ|PROT_WRITE,
|
|
MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
|
|
jack_error ("Cannot mmap shm registry segment (%s)",
|
|
strerror (errno));
|
|
jack_remove_shm (registry_id);
|
|
close (shm_fd);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
|
|
/* initialize registry contents */
|
|
jack_shm_init_registry ();
|
|
close (shm_fd);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
jack_remove_shm (const jack_shm_id_t id)
|
|
{
|
|
/* registry may or may not be locked */
|
|
shm_unlink (id);
|
|
}
|
|
|
|
void
|
|
jack_release_shm (jack_shm_info_t* si)
|
|
{
|
|
/* registry may or may not be locked */
|
|
if (si->ptr.attached_at != MAP_FAILED) {
|
|
munmap (si->ptr.attached_at, jack_shm_registry[si->index].size);
|
|
}
|
|
}
|
|
|
|
void
|
|
jack_release_lib_shm (jack_shm_info_t* si)
|
|
{
|
|
/* registry may or may not be locked */
|
|
if (si->ptr.attached_at != MAP_FAILED) {
|
|
munmap (si->ptr.attached_at, si->size);
|
|
}
|
|
}
|
|
|
|
/* allocate a POSIX shared memory segment */
|
|
int
|
|
jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si)
|
|
{
|
|
jack_shm_registry_t* registry;
|
|
int shm_fd;
|
|
int rc = -1;
|
|
char name[SHM_NAME_MAX+1];
|
|
const char* promiscuous;
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
if ((registry = jack_get_free_shm_info ()) == NULL) {
|
|
jack_error ("shm registry full");
|
|
goto unlock;
|
|
}
|
|
|
|
/* On Mac OS X, the maximum length of a shared memory segment
|
|
* name is SHM_NAME_MAX (instead of NAME_MAX or PATH_MAX as
|
|
* defined by the standard). Unfortunately, Apple sets this
|
|
* value so small (about 31 bytes) that it is useless for
|
|
* actual names. So, we construct a short name from the
|
|
* registry index for uniqueness and ignore the shm_name
|
|
* parameter. Bah!
|
|
*/
|
|
snprintf (name, sizeof (name), "/jack-%d-%d", GetUID(), registry->index);
|
|
|
|
if (strlen (name) >= sizeof (registry->id)) {
|
|
jack_error ("shm segment name too long %s", name);
|
|
goto unlock;
|
|
}
|
|
|
|
if ((shm_fd = shm_open (name, O_RDWR|O_CREAT, 0666)) < 0) {
|
|
jack_error ("Cannot create shm segment %s (%s)",
|
|
name, strerror (errno));
|
|
goto unlock;
|
|
}
|
|
|
|
if (ftruncate (shm_fd, size) < 0) {
|
|
jack_error ("Cannot set size of engine shm "
|
|
"registry 0 (%s)",
|
|
strerror (errno));
|
|
close (shm_fd);
|
|
goto unlock;
|
|
}
|
|
|
|
promiscuous = getenv("JACK_PROMISCUOUS_SERVER");
|
|
if ((promiscuous != NULL) && (jack_promiscuous_perms(shm_fd, name, jack_group2gid(promiscuous)) < 0))
|
|
goto unlock;
|
|
|
|
close (shm_fd);
|
|
registry->size = size;
|
|
strncpy (registry->id, name, sizeof (registry->id));
|
|
registry->allocator = GetPID();
|
|
si->index = registry->index;
|
|
si->ptr.attached_at = MAP_FAILED; /* not attached */
|
|
rc = 0; /* success */
|
|
|
|
unlock:
|
|
jack_shm_unlock_registry ();
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm (jack_shm_info_t* si)
|
|
{
|
|
int shm_fd;
|
|
jack_shm_registry_t *registry = &jack_shm_registry[si->index];
|
|
|
|
if ((shm_fd = shm_open (registry->id,
|
|
O_RDWR, 0666)) < 0) {
|
|
jack_error ("Cannot open shm segment %s (%s)", registry->id,
|
|
strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
if ((si->ptr.attached_at = mmap (0, registry->size, PROT_READ|PROT_WRITE,
|
|
MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
|
|
jack_error ("Cannot mmap shm segment %s (%s)",
|
|
registry->id,
|
|
strerror (errno));
|
|
close (shm_fd);
|
|
return -1;
|
|
}
|
|
|
|
close (shm_fd);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm_read (jack_shm_info_t* si)
|
|
{
|
|
int shm_fd;
|
|
jack_shm_registry_t *registry = &jack_shm_registry[si->index];
|
|
|
|
if ((shm_fd = shm_open (registry->id,
|
|
O_RDONLY, 0666)) < 0) {
|
|
jack_error ("Cannot open shm segment %s (%s)", registry->id,
|
|
strerror (errno));
|
|
return -1;
|
|
}
|
|
|
|
if ((si->ptr.attached_at = mmap (0, registry->size, PROT_READ,
|
|
MAP_SHARED, shm_fd, 0)) == MAP_FAILED) {
|
|
jack_error ("Cannot mmap shm segment %s (%s)",
|
|
registry->id,
|
|
strerror (errno));
|
|
close (shm_fd);
|
|
return -1;
|
|
}
|
|
|
|
close (shm_fd);
|
|
return 0;
|
|
}
|
|
|
|
#elif WIN32
|
|
|
|
static int
|
|
jack_access_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
HANDLE shm_fd;
|
|
|
|
/* try to open an existing segment */
|
|
|
|
if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry_id)) == NULL) {
|
|
int rc = GetLastError();
|
|
if (rc != ERROR_FILE_NOT_FOUND) {
|
|
jack_error ("Cannot open existing shm registry segment (%ld)", rc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) {
|
|
jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError());
|
|
jack_remove_shm (registry_id);
|
|
CloseHandle (shm_fd);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
|
|
//CloseHandle(shm_fd); // TO CHECK
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
jack_create_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
HANDLE shm_fd;
|
|
|
|
if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
0, PAGE_READWRITE,
|
|
0, JACK_SHM_REGISTRY_SIZE,
|
|
registry_id)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) {
|
|
int rc = GetLastError();
|
|
jack_error ("Cannot create shm registry segment (%ld)", rc);
|
|
return rc;
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, JACK_SHM_REGISTRY_SIZE)) == NULL) {
|
|
jack_error ("Cannot mmap shm registry segment (%ld)", GetLastError());
|
|
jack_remove_shm (registry_id);
|
|
CloseHandle (shm_fd);
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
|
|
/* initialize registry contents */
|
|
jack_shm_init_registry ();
|
|
|
|
//CloseHandle(shm_fd); // TO CHECK
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
jack_remove_shm (const jack_shm_id_t id)
|
|
{
|
|
/* nothing to do */
|
|
}
|
|
|
|
void
|
|
jack_release_shm (jack_shm_info_t* si)
|
|
{
|
|
/* registry may or may not be locked */
|
|
if (si->ptr.attached_at != NULL) {
|
|
UnmapViewOfFile (si->ptr.attached_at);
|
|
}
|
|
}
|
|
|
|
void
|
|
jack_release_lib_shm (jack_shm_info_t* si)
|
|
{
|
|
jack_release_shm(si);
|
|
}
|
|
|
|
int
|
|
jack_shmalloc (const char *shm_name, jack_shmsize_t size, jack_shm_info_t* si)
|
|
{
|
|
jack_shm_registry_t* registry;
|
|
HANDLE shm_fd;
|
|
int rc = -1;
|
|
char name[SHM_NAME_MAX+1];
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
if ((registry = jack_get_free_shm_info ()) == NULL) {
|
|
jack_error ("shm registry full");
|
|
goto unlock;
|
|
}
|
|
|
|
snprintf (name, sizeof (name), "jack-%d-%d", GetUID(), registry->index);
|
|
|
|
if (strlen (name) >= sizeof (registry->id)) {
|
|
jack_error ("shm segment name too long %s", name);
|
|
goto unlock;
|
|
}
|
|
|
|
if ((shm_fd = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
0, PAGE_READWRITE,
|
|
0, size,
|
|
name)) == NULL || (shm_fd == INVALID_HANDLE_VALUE)) {
|
|
int rc = GetLastError();
|
|
jack_error ("Cannot create shm segment (%ld)",rc);
|
|
goto unlock;
|
|
}
|
|
|
|
//CloseHandle (shm_fd); // TO CHECK
|
|
|
|
registry->size = size;
|
|
strncpy (registry->id, name, sizeof (registry->id));
|
|
registry->allocator = _getpid();
|
|
si->index = registry->index;
|
|
si->ptr.attached_at = NULL; /* not attached */
|
|
rc = 0; /* success */
|
|
|
|
unlock:
|
|
jack_shm_unlock_registry ();
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm (jack_shm_info_t* si)
|
|
{
|
|
HANDLE shm_fd;
|
|
jack_shm_registry_t *registry = &jack_shm_registry[si->index];
|
|
|
|
if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) {
|
|
int rc = GetLastError();
|
|
jack_error ("Cannot open shm segment (%ld)",rc);
|
|
return -1;
|
|
}
|
|
|
|
if ((si->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_ALL_ACCESS, 0, 0, registry->size)) == NULL) {
|
|
jack_error ("Cannot mmap shm segment (%ld)", GetLastError());
|
|
jack_remove_shm (registry_id);
|
|
CloseHandle (shm_fd);
|
|
return -1;
|
|
}
|
|
|
|
//CloseHandle (shm_fd); // TO CHECK
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm_read (jack_shm_info_t* si)
|
|
{
|
|
HANDLE shm_fd;
|
|
jack_shm_registry_t *registry = &jack_shm_registry[si->index];
|
|
|
|
if ((shm_fd = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, registry->id)) == NULL) {
|
|
int rc = GetLastError();
|
|
jack_error ("Cannot open shm segment (%ld)",rc);
|
|
return -1;
|
|
}
|
|
|
|
if ((si->ptr.attached_at = MapViewOfFile (shm_fd, FILE_MAP_READ, 0, 0, registry->size)) == NULL) {
|
|
jack_error("Cannot mmap shm segment (%ld)", GetLastError());
|
|
jack_remove_shm(registry_id);
|
|
CloseHandle(shm_fd);
|
|
return -1;
|
|
}
|
|
|
|
//CloseHandle (shm_fd); // TO CHECK
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* System V interface-dependent functions
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
|
|
|
/* gain addressability to existing SHM registry segment
|
|
*
|
|
* sets up global registry pointers, if successful
|
|
*
|
|
* returns: 0 if existing registry accessed successfully
|
|
* ENOENT if registry does not exist
|
|
* EINVAL if registry exists, but has the wrong size
|
|
* other nonzero error code if unable to access registry
|
|
*/
|
|
static int
|
|
jack_access_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
|
|
/* try without IPC_CREAT to get existing segment */
|
|
if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY,
|
|
JACK_SHM_REGISTRY_SIZE, 0666)) < 0) {
|
|
|
|
switch (errno) {
|
|
|
|
case ENOENT: /* segment does not exist */
|
|
return ENOENT;
|
|
|
|
case EINVAL: /* segment exists, but too small */
|
|
/* attempt minimum size access */
|
|
registry_id = shmget (JACK_SHM_REGISTRY_KEY, 1, 0666);
|
|
return EINVAL;
|
|
|
|
default: /* or other error */
|
|
jack_error ("unable to access shm registry (%s)",
|
|
strerror (errno));
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = shmat (registry_id, 0, 0)) < 0) {
|
|
jack_error ("Cannot attach shm registry segment (%s)",
|
|
strerror (errno));
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
return 0;
|
|
}
|
|
|
|
/* create a new SHM registry segment
|
|
*
|
|
* sets up global registry pointers, if successful
|
|
*
|
|
* returns: 0 if registry created successfully
|
|
* nonzero error code if unable to allocate a new registry
|
|
*/
|
|
static int
|
|
jack_create_registry (jack_shm_info_t *ri)
|
|
{
|
|
/* registry must be locked */
|
|
if ((registry_id = shmget (JACK_SHM_REGISTRY_KEY,
|
|
JACK_SHM_REGISTRY_SIZE,
|
|
0666|IPC_CREAT)) < 0) {
|
|
jack_error ("Cannot create shm registry segment (%s)",
|
|
strerror (errno));
|
|
return errno;
|
|
}
|
|
|
|
if ((ri->ptr.attached_at = shmat (registry_id, 0, 0)) < 0) {
|
|
jack_error ("Cannot attach shm registry segment (%s)",
|
|
strerror (errno));
|
|
return EINVAL;
|
|
}
|
|
|
|
/* set up global pointers */
|
|
ri->index = JACK_SHM_REGISTRY_INDEX;
|
|
jack_shm_header = ri->ptr.attached_at;
|
|
jack_shm_registry = (jack_shm_registry_t *) (jack_shm_header + 1);
|
|
|
|
/* initialize registry contents */
|
|
jack_shm_init_registry ();
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
jack_remove_shm (jack_shm_id_t id)
|
|
{
|
|
/* registry may or may not be locked */
|
|
shmctl (id, IPC_RMID, NULL);
|
|
}
|
|
|
|
void
|
|
jack_release_shm (jack_shm_info_t* si)
|
|
{
|
|
/* registry may or may not be locked */
|
|
if (si->ptr.attached_at != MAP_FAILED) {
|
|
shmdt (si->ptr.attached_at);
|
|
}
|
|
}
|
|
|
|
void
|
|
jack_release_lib_shm (jack_shm_info_t* si)
|
|
{
|
|
jack_release_shm(si);
|
|
}
|
|
|
|
int
|
|
jack_shmalloc (const char* name_not_used, jack_shmsize_t size,
|
|
jack_shm_info_t* si)
|
|
{
|
|
int shmflags;
|
|
int shmid;
|
|
int rc = -1;
|
|
jack_shm_registry_t* registry;
|
|
|
|
if (jack_shm_lock_registry () < 0) {
|
|
jack_error ("jack_shm_lock_registry fails...");
|
|
return -1;
|
|
}
|
|
|
|
if ((registry = jack_get_free_shm_info ())) {
|
|
|
|
shmflags = 0666 | IPC_CREAT | IPC_EXCL;
|
|
|
|
if ((shmid = shmget (IPC_PRIVATE, size, shmflags)) >= 0) {
|
|
|
|
registry->size = size;
|
|
registry->id = shmid;
|
|
registry->allocator = getpid();
|
|
si->index = registry->index;
|
|
si->ptr.attached_at = MAP_FAILED; /* not attached */
|
|
rc = 0;
|
|
|
|
} else {
|
|
jack_error ("Cannot create shm segment %s (%s)",
|
|
name_not_used, strerror (errno));
|
|
}
|
|
}
|
|
|
|
jack_shm_unlock_registry ();
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm (jack_shm_info_t* si)
|
|
{
|
|
if ((si->ptr.attached_at = shmat (jack_shm_registry[si->index].id, 0, 0)) < 0) {
|
|
jack_error ("Cannot attach shm segment (%s)",
|
|
strerror (errno));
|
|
jack_release_shm_info (si->index);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
jack_attach_shm_read (jack_shm_info_t* si)
|
|
{
|
|
if ((si->ptr.attached_at = shmat (jack_shm_registry[si->index].id, 0, SHM_RDONLY)) < 0) {
|
|
jack_error ("Cannot attach shm segment (%s)",
|
|
strerror (errno));
|
|
jack_release_shm_info (si->index);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#endif /* !USE_POSIX_SHM */
|
|
|