/* Copyright (C) 2003 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { shm_name_t name; #ifdef USE_POSIX_SHM char *address; #else int shmid; #endif } jack_shm_registry_entry_t; static jack_shm_registry_entry_t *jack_shm_registry; static int jack_shm_id_cnt; void jack_register_shm (char *shm_name, char *addr, int id) { if (jack_shm_id_cnt < MAX_SHM_ID) { snprintf (jack_shm_registry[jack_shm_id_cnt++].name, sizeof (shm_name_t), "%s", shm_name); #ifdef USE_POSIX_SHM jack_shm_registry[jack_shm_id_cnt].address = addr; #else jack_shm_registry[jack_shm_id_cnt].shmid = id; #endif } } int jack_initialize_shm () { void *addr; int id; int perm; #ifdef USE_POSIX_SHM fprintf (stderr, "JACK compiled with POSIX SHM support\n"); #else fprintf (stderr, "JACK compiled with System V SHM support\n"); #endif if (jack_shm_registry != NULL) { return 0; } /* grab a chunk of memory to store shm ids in. this is to allow our parent to clean up all such ids when if we exit. otherwise, they can get lost in crash or debugger driven exits. */ #if defined(__APPLE__) && defined(__POWERPC__) /* using O_TRUNC option does not work on Darwin */ perm = O_RDWR|O_CREAT; #else perm = O_RDWR|O_CREAT|O_TRUNC; #endif if ((addr = jack_get_shm ("/jack-shm-registry", sizeof (jack_shm_registry_entry_t) * MAX_SHM_ID, perm, 0600, PROT_READ|PROT_WRITE, &id)) == MAP_FAILED) { return -1; } jack_shm_registry = (jack_shm_registry_entry_t *) addr; jack_shm_id_cnt = 0; jack_register_shm ("/jack-shm-registry", addr, id); return 0; } void jack_cleanup_shm () { #if ! USE_POSIX_SHM char path[PATH_MAX+1]; DIR *dir; struct dirent *dirent; #endif int i; for (i = 0; i < jack_shm_id_cnt; i++) { jack_destroy_shm (jack_shm_registry[i].name); } #if ! USE_POSIX_SHM snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); if ((dir = opendir (path)) == NULL) { if (errno != ENOENT) { jack_error ("cannot open jack shm directory (%s)", strerror (errno)); } } else { while ((dirent = readdir (dir)) != NULL) { char fullpath[PATH_MAX+1]; snprintf (fullpath, sizeof (fullpath), "%s/jack/shm/%s", jack_server_dir, dirent->d_name); unlink (fullpath); } } closedir (dir); snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); if (rmdir (path)) { if (errno != ENOENT) { jack_error ("cannot remove JACK shm directory (%s)", strerror (errno)); } } snprintf (path, sizeof(path), "%s/jack", jack_server_dir); if (rmdir (path)) { if (errno != ENOENT) { jack_error ("cannot remove JACK directory (%s)", strerror (errno)); } } #endif } #if USE_POSIX_SHM void jack_destroy_shm (const char *shm_name) { shm_unlink (shm_name); } void jack_release_shm (char *addr, size_t size) { munmap (addr, size); } char * jack_get_shm (const char *shm_name, size_t size, int perm, int mode, int prot, int *not_really_used) { int shm_fd; char *addr; if ((shm_fd = shm_open (shm_name, perm, mode)) < 0) { jack_error ("cannot create shm segment %s (%s)", shm_name, strerror (errno)); return MAP_FAILED; } if (perm & O_CREAT) { if (ftruncate (shm_fd, size) < 0) { jack_error ("cannot set size of engine shm registry (%s)", strerror (errno)); return MAP_FAILED; } } if ((addr = mmap (0, size, prot, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("cannot mmap shm segment %s (%s)", shm_name, strerror (errno)); shm_unlink (shm_name); close (shm_fd); return MAP_FAILED; } close (shm_fd); *not_really_used = 0; return addr; } char * jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, int prot) { int i; int shm_fd; char *addr; struct stat statbuf; for (i = 0; i < jack_shm_id_cnt; ++i) { if (strcmp (jack_shm_registry[i].name, shm_name) == 0) { break; } } if (i == jack_shm_id_cnt) { jack_error ("attempt to resize unknown shm segment \"%s\"", shm_name); return MAP_FAILED; } if ((shm_fd = shm_open (shm_name, perm, mode)) < 0) { jack_error ("cannot create shm segment %s (%s)", shm_name, strerror (errno)); return MAP_FAILED; } fstat (shm_fd, &statbuf); munmap (jack_shm_registry[i].address, statbuf.st_size); if (perm & O_CREAT) { if (ftruncate (shm_fd, size) < 0) { jack_error ("cannot set size of engine shm registry (%s)", strerror (errno)); return MAP_FAILED; } } if ((addr = mmap (0, size, prot, MAP_SHARED, shm_fd, 0)) == MAP_FAILED) { jack_error ("cannot mmap shm segment %s (%s)", shm_name, strerror (errno)); shm_unlink (shm_name); close (shm_fd); return MAP_FAILED; } close (shm_fd); return addr; } #else /* USE_POSIX_SHM */ int jack_get_shmid (const char *name) { int i; /* **** NOT THREAD SAFE *****/ for (i = 0; i < jack_shm_id_cnt; ++i) { if (strcmp (jack_shm_registry[i].name, name) == 0) { return jack_shm_registry[i].shmid; } } return -1; } void jack_destroy_shm (const char *shm_name) { int shmid = jack_get_shmid (shm_name); if (shmid >= 0) { shmctl (IPC_RMID, shmid, NULL); } } void jack_release_shm (char *addr, size_t size) { shmdt (addr); } char * jack_get_shm (const char *shm_name, size_t size, int perm, int mode, int prot, int* shmid) { char *addr; key_t key; int shmflags; char path[PATH_MAX+1]; struct stat statbuf; int status; /* note: no trailing '/' on basic path because we expect shm_name to begin with one (as per POSIX shm API). */ snprintf (path, sizeof(path), "%s/jack", jack_server_dir); if (mkdir (path, 0775)) { if (errno != EEXIST) { jack_error ("cannot create JACK directory (%s)", strerror (errno)); return MAP_FAILED; } } snprintf (path, sizeof(path), "%s/jack/shm", jack_server_dir); if (mkdir (path, 0775)) { if (errno != EEXIST) { jack_error ("cannot create JACK shm directory (%s)", strerror (errno)); return MAP_FAILED; } } snprintf (path, sizeof(path), "%s/jack/shm%s", jack_server_dir, shm_name); if ((status = stat (path, &statbuf)) < 0) { int fd; if ((fd = open (path, O_RDWR|O_CREAT, 0775)) < 0) { jack_error ("cannot create shm file node for %s (%s)", path, strerror (errno)); return MAP_FAILED; } close (fd); } if ((key = ftok (path, 'j')) < 0) { jack_error ("cannot generate IPC key for shm segment %s (%s)", path, strerror (errno)); unlink (path); return MAP_FAILED; } /* XXX need to figure out how to do this without causing the inode reallocation the next time this function is called resulting in ftok() returning non-unique keys. */ /* unlink (path); */ shmflags = mode; if (perm & O_CREAT) { shmflags |= IPC_CREAT; } if (perm & O_TRUNC) { shmflags |= IPC_EXCL; } if ((*shmid = shmget (key, size, shmflags)) < 0) { if (errno == EEXIST && (shmflags & IPC_EXCL)) { shmflags &= ~IPC_EXCL; if ((*shmid = shmget (key, size, shmflags)) < 0) { jack_error ("cannot get existing shm segment for %s (%s)", shm_name, strerror (errno)); return MAP_FAILED; } } else { jack_error ("cannot create shm segment %s (%s)", shm_name, strerror (errno)); return MAP_FAILED; } } if ((addr = shmat (*shmid, 0, 0)) < 0) { jack_error ("cannot attach shm segment %s (%s)", shm_name, strerror (errno)); return MAP_FAILED; } return addr; } char * jack_resize_shm (const char *shm_name, size_t size, int perm, int mode, int prot) { jack_error ("jack_resize_shm() is not implemented for the System V shared memory API"); return 0; } #endif /* USE_POSIX_SHM */