diff --git a/Makefile b/Makefile deleted file mode 100644 index 09f22432e..000000000 --- a/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -all: - ninja-build -C build - -install: - ninja-build -C build install - -clean: - ninja-build -C build clean - -run: - SPA_PLUGIN_DIR=build/spa/plugins \ - PIPEWIRE_MODULE_DIR=build \ - PIPEWIRE_CONFIG_FILE=build/pipewire/daemon/pipewire.conf \ - build/pipewire/daemon/pipewire - -monitor: - SPA_PLUGIN_DIR=build/spa/plugins \ - PIPEWIRE_MODULE_DIR=build/pipewire/modules/ \ - build/pipewire/tools/pipewire-monitor - -dist: - git archive --prefix=pipewire-0.1.0/ -o pipewire-0.1.0.tar.gz HEAD - -rpm: dist - rpmbuild -ta pipewire-0.1.0.tar.gz diff --git a/autogen.sh b/autogen.sh index 5becaea53..97970dfa0 100755 --- a/autogen.sh +++ b/autogen.sh @@ -20,3 +20,4 @@ rm -rf ./build mkdir build meson build $@ +ln -s build/Makefile Makefile diff --git a/meson.build b/meson.build index f787858dd..12777b95c 100644 --- a/meson.build +++ b/meson.build @@ -122,6 +122,18 @@ configure_file(input : 'config.h.meson', configinc = include_directories('.') +makedata = configuration_data() + makedata.set('VERSION', pipewire_version) +if version_arr.length() == 4 + makedata.set('TAG', 'HEAD') +else + makedata.set('TAG', pipewire_version) +endif + +configure_file(input : 'Makefile.in', + output : 'Makefile', + configuration : makedata) + # Find dependencies glib_dep = dependency('glib-2.0', version : '>=2.32.0') gobject_dep = dependency('gobject-2.0') diff --git a/pipewire.spec b/pipewire.spec index 2490747c0..0e3af932f 100644 --- a/pipewire.spec +++ b/pipewire.spec @@ -31,6 +31,7 @@ Source0: http://freedesktop.org/software/pipewire/releases/pipewire-%{ver BuildRequires: meson >= 0.35.0 BuildRequires: pkgconfig +BuildRequires: pkgconfig(dbus-1) BuildRequires: pkgconfig(glib-2.0) >= 2.32 BuildRequires: pkgconfig(gio-unix-2.0) >= 2.32 BuildRequires: pkgconfig(gstreamer-1.0) >= 1.10.0 @@ -57,11 +58,11 @@ License: LGPLv2+ This package contains the runtime libraries for any application that wishes to interface with a PipeWire media server. -%package libs-devel +%package devel Summary: Headers and libraries for PipeWire client development License: LGPLv2+ Requires: %{name}-libs%{?_isa} = %{version}-%{release} -%description libs-devel +%description devel Headers and libraries for developing applications that can communicate with a PipeWire media server. diff --git a/pipewire/daemon/pipewire.conf.in b/pipewire/daemon/pipewire.conf.in index a6365c7d1..71b1e130b 100644 --- a/pipewire/daemon/pipewire.conf.in +++ b/pipewire/daemon/pipewire.conf.in @@ -8,3 +8,4 @@ load-module libpipewire-module-autolink #load-module libpipewire-module-mixer load-module libpipewire-module-client-node load-module libpipewire-module-flatpak +#load-module libpipewire-module-jack diff --git a/pipewire/modules/meson.build b/pipewire/modules/meson.build index 6f9fab488..4061a68f9 100644 --- a/pipewire/modules/meson.build +++ b/pipewire/modules/meson.build @@ -66,6 +66,17 @@ pipewire_module_protocol_native = shared_library('pipewire-module-protocol-nativ dependencies : [mathlib, dl_lib, pipewire_dep, pipewirecore_dep], ) +pipewire_module_jack = shared_library('pipewire-module-jack', + [ 'module-jack.c', + 'module-jack/shm.c' ], + c_args : pipewire_module_c_args, + include_directories : [configinc, spa_inc], + link_with : spalib, + install : true, + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep, pipewirecore_dep], +) + pipewire_module_suspend_on_idle = shared_library('pipewire-module-suspend-on-idle', [ 'module-suspend-on-idle.c' ], c_args : pipewire_module_c_args, include_directories : [configinc, spa_inc], diff --git a/pipewire/modules/module-jack.c b/pipewire/modules/module-jack.c new file mode 100644 index 000000000..4f8bf0a63 --- /dev/null +++ b/pipewire/modules/module-jack.c @@ -0,0 +1,701 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "config.h" + +#include "pipewire/client/pipewire.h" +#include "pipewire/client/log.h" +#include "pipewire/client/interfaces.h" + +#include "pipewire/server/core.h" +#include "pipewire/server/node.h" +#include "pipewire/server/module.h" +#include "pipewire/server/client.h" +#include "pipewire/server/resource.h" +#include "pipewire/server/link.h" +#include "pipewire/server/node-factory.h" +#include "pipewire/server/data-loop.h" +#include "pipewire/server/main-loop.h" + +#include "pipewire/modules/module-jack/defs.h" +#include "pipewire/modules/module-jack/shm.h" +#include "pipewire/modules/module-jack/shared.h" +#include "pipewire/modules/module-jack/synchro.h" +#include "pipewire/modules/module-jack/server.h" + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +#define LOCK_SUFFIX ".lock" +#define LOCK_SUFFIXLEN 5 + +static int segment_num = 0; + +typedef bool(*demarshal_func_t) (void *object, void *data, size_t size); + +struct socket { + int fd; + struct sockaddr_un addr; + char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN]; + + struct pw_loop *loop; + struct spa_source *source; + char *core_name; + struct spa_list link; +}; + +struct impl { + struct pw_core *core; + struct spa_list link; + + struct pw_properties *properties; + + struct spa_list socket_list; + struct spa_list client_list; + + struct spa_loop_control_hooks hooks; + + struct jack_server server; +}; + +struct client { + struct impl *impl; + struct spa_list link; + struct pw_client *client; + int fd; + struct spa_source *source; + struct pw_listener busy_changed; +}; + +static int process_messages(struct client *client); + +static void client_destroy(void *data) +{ + struct pw_client *client = data; + struct client *this = client->user_data; + + pw_loop_destroy_source(this->impl->core->main_loop->loop, this->source); + spa_list_remove(&this->link); + + close(this->fd); +} + +static int +handle_register_port(struct client *client) +{ + int result = 0; + int ref_num; + char name[JACK_PORT_NAME_SIZE + 1]; + char port_type[JACK_PORT_TYPE_SIZE + 1]; + unsigned int flags; + unsigned int buffer_size; + static jack_port_id_t port_index = 0; + + CheckSize(kRegisterPort_size); + CheckRead(&ref_num, sizeof(int)); + CheckRead(name, sizeof(name)); + CheckRead(port_type, sizeof(port_type)); + CheckRead(&flags, sizeof(unsigned int)); + CheckRead(&buffer_size, sizeof(unsigned int)); + + pw_log_error("protocol-jack %p: kRegisterPort %d %s %s %u %u", client->impl, + ref_num, name, port_type, flags, buffer_size); + port_index++; + + CheckWrite(&result, sizeof(int)); + CheckWrite(&port_index, sizeof(jack_port_id_t)); + return 0; +} + +static int +handle_activate_client(struct client *client) +{ + int result = 0; + int ref_num; + int is_real_time; + + CheckSize(kActivateClient_size); + CheckRead(&ref_num, sizeof(int)); + CheckRead(&is_real_time, sizeof(int)); + + pw_log_error("protocol-jack %p: kActivateClient %d %d", client->impl, + ref_num, is_real_time); + + CheckWrite(&result, sizeof(int)); + return 0; +} + +static int +handle_deactivate_client(struct client *client) +{ + int result = 0; + int ref_num; + + CheckSize(kDeactivateClient_size); + CheckRead(&ref_num, sizeof(int)); + + pw_log_error("protocol-jack %p: kDeactivateClient %d", client->impl, + ref_num); + + CheckWrite(&result, sizeof(int)); + return 0; +} + +static int +handle_client_check(struct client *client) +{ + char name[JACK_CLIENT_NAME_SIZE+1]; + int protocol; + int options; + int UUID; + int open; + int result = 0; + int status; + + CheckSize(kClientCheck_size); + CheckRead(name, sizeof(name)); + CheckRead(&protocol, sizeof(int)); + CheckRead(&options, sizeof(int)); + CheckRead(&UUID, sizeof(int)); + CheckRead(&open, sizeof(int)); + + pw_log_error("protocol-jack %p: kClientCheck %s %d %d %d %d", client->impl, + name, protocol, options, UUID, open); + + status = 0; + if (protocol != JACK_PROTOCOL_VERSION) { + status |= (JackFailure | JackVersionError); + pw_log_error("protocol-jack: protocol mismatch (%d vs %d)", protocol, JACK_PROTOCOL_VERSION); + result = -1; + goto reply; + } + /* TODO check client name and uuid */ + + reply: + CheckWrite(&result, sizeof(int)); + CheckWrite(name, sizeof(name)); + CheckWrite(&status, sizeof(int)); + + if (open) + return process_messages(client); + + return 0; +} + +static int +handle_client_open(struct client *client) +{ + struct impl *impl = client->impl; + struct jack_server *server = &impl->server; + int PID, UUID; + char name[JACK_CLIENT_NAME_SIZE+1]; + int result, ref_num, shared_engine, shared_client, shared_graph; + struct jack_client *jc; + size_t size; + jack_shm_info_t info; + + CheckSize(kClientOpen_size); + CheckRead(&PID, sizeof(int)); + CheckRead(&UUID, sizeof(int)); + CheckRead(name, sizeof(name)); + + ref_num = jack_server_allocate_ref_num(server); + if (ref_num == -1) { + result = -1; + goto reply; + } + + jc = calloc(1,sizeof(struct jack_client)); + jc->owner = client; + jc->ref_num = ref_num; + + if (jack_synchro_alloc(&server->synchro_table[ref_num], + name, + server->engine_control->server_name, + 0, + false, + server->promiscuous) < 0) { + result = -1; + goto reply; + } + + size = sizeof(struct jack_client_control); + + if (jack_shm_alloc(size, &info, segment_num++) < 0) { + result = -1; + goto reply; + } + + jc->control = (struct jack_client_control *)jack_shm_addr(&info); + jc->control->info = info; + + server->client_table[ref_num] = jc; + + result = 0; + shared_engine = impl->server.engine_control->info.index; + shared_client = jc->control->info.index; + shared_graph = impl->server.graph_manager->info.index; + + reply: + CheckWrite(&result, sizeof(int)); + CheckWrite(&shared_engine, sizeof(int)); + CheckWrite(&shared_client, sizeof(int)); + CheckWrite(&shared_graph, sizeof(int)); + + return 0; +} + +static int +handle_client_close(struct client *client) +{ + int ref_num; + CheckSize(kClientClose_size); + CheckRead(&ref_num, sizeof(int)); + int result = 0; + + CheckWrite(&result, sizeof(int)); + return 0; +} + +static int +handle_connect_name_ports(struct client *client) +{ + int ref_num; + char src[REAL_JACK_PORT_NAME_SIZE+1]; + char dst[REAL_JACK_PORT_NAME_SIZE+1]; + int result = 0; + + CheckSize(kConnectNamePorts_size); + CheckRead(&ref_num, sizeof(int)); + CheckRead(src, sizeof(src)); + CheckRead(dst, sizeof(dst)); + + CheckWrite(&result, sizeof(int)); + return 0; +} + +static int +handle_get_UUID_by_client(struct client *client) +{ + char name[JACK_CLIENT_NAME_SIZE+1]; + char UUID[JACK_UUID_SIZE]; + int result = 0; + + CheckSize(kGetUUIDByClient_size); + CheckRead(name, sizeof(name)); + + CheckWrite(&result, sizeof(int)); + CheckWrite(UUID, sizeof(UUID)); + + return 0; +} + +static int +process_messages(struct client *client) +{ + struct pw_client *c = client->client; + int type, res = -1; + + if (read(client->fd, &type, sizeof(enum jack_request_type)) != sizeof(enum jack_request_type)) { + pw_log_error("protocol-jack %p: failed to read type", client->impl); + goto error; + } + pw_log_info("protocol-jack %p: got type %d", client->impl, type); + + switch(type) { + case jack_request_RegisterPort: + res = handle_register_port(client); + break; + case jack_request_UnRegisterPort: + case jack_request_ConnectPorts: + case jack_request_DisconnectPorts: + case jack_request_SetTimeBaseClient: + case jack_request_ActivateClient: + res = handle_activate_client(client); + break; + case jack_request_DeactivateClient: + res = handle_deactivate_client(client); + break; + case jack_request_DisconnectPort: + case jack_request_SetClientCapabilities: + case jack_request_GetPortConnections: + case jack_request_GetPortNConnections: + case jack_request_ReleaseTimebase: + case jack_request_SetTimebaseCallback: + case jack_request_SetBufferSize: + case jack_request_SetFreeWheel: + break; + case jack_request_ClientCheck: + res = handle_client_check(client); + break; + case jack_request_ClientOpen: + res = handle_client_open(client); + break; + case jack_request_ClientClose: + res = handle_client_close(client); + break; + case jack_request_ConnectNamePorts: + res = handle_connect_name_ports(client); + break; + case jack_request_DisconnectNamePorts: + case jack_request_GetInternalClientName: + case jack_request_InternalClientHandle: + case jack_request_InternalClientLoad: + case jack_request_InternalClientUnload: + case jack_request_PortRename: + case jack_request_Notification: + case jack_request_SessionNotify: + case jack_request_SessionReply: + case jack_request_GetClientByUUID: + case jack_request_ReserveClientName: + break; + case jack_request_GetUUIDByClient: + res = handle_get_UUID_by_client(client); + break; + case jack_request_ClientHasSessionCallback: + case jack_request_ComputeTotalLatencies: + break; + default: + pw_log_error("protocol-jack %p: invalid type %d", client->impl, type); + goto error; + } + if (res != 0) + goto error; + + return res; + + error: + pw_log_error("protocol-jack %p: error handling type %d", client->impl, type); + pw_client_destroy(c); + return -1; + +} + +static void +on_busy_changed(struct pw_listener *listener, + struct pw_client *client) +{ + struct client *c = SPA_CONTAINER_OF(listener, struct client, busy_changed); + enum spa_io mask = SPA_IO_ERR | SPA_IO_HUP; + + if (!client->busy) + mask |= SPA_IO_IN; + + pw_loop_update_io(c->impl->core->main_loop->loop, c->source, mask); + + if (!client->busy) + process_messages(c); +} + +static void +connection_data(struct spa_loop_utils *utils, + struct spa_source *source, int fd, enum spa_io mask, void *data) +{ + struct client *client = data; + + if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { + pw_log_error("protocol-native %p: got connection error", client->impl); + pw_client_destroy(client->client); + return; + } + + if (mask & SPA_IO_IN) + process_messages(client); +} + +static struct client *client_new(struct impl *impl, int fd) +{ + struct client *this; + struct pw_client *client; + socklen_t len; + struct ucred ucred, *ucredp; + + len = sizeof(ucred); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) < 0) { + pw_log_error("no peercred: %m"); + ucredp = NULL; + } else { + ucredp = &ucred; + } + + client = pw_client_new(impl->core, ucredp, NULL, sizeof(struct client)); + if (client == NULL) + goto no_client; + + client->destroy = client_destroy; + + this = client->user_data; + this->impl = impl; + this->fd = fd; + this->source = pw_loop_add_io(impl->core->main_loop->loop, + this->fd, + SPA_IO_ERR | SPA_IO_HUP, false, connection_data, this); + if (this->source == NULL) + goto no_source; + + this->client = client; + + spa_list_insert(impl->client_list.prev, &this->link); + + pw_signal_add(&client->busy_changed, &this->busy_changed, on_busy_changed); + + pw_log_error("module-jack %p: added new client", impl); + + return this; + + no_source: + free(this); + no_client: + return NULL; +} + +static struct socket *create_socket(void) +{ + struct socket *s; + + if ((s = calloc(1, sizeof(struct socket))) == NULL) + return NULL; + + s->fd = -1; + return s; +} + +static void destroy_socket(struct socket *s) +{ + if (s->source) + pw_loop_destroy_source(s->loop, s->source); + if (s->addr.sun_path[0]) + unlink(s->addr.sun_path); + if (s->fd >= 0) + close(s->fd); + if (s->lock_addr[0]) + unlink(s->lock_addr); + free(s); +} + +static bool init_socket_name(struct socket *s, const char *name, bool promiscuous, int which) +{ + int name_size; + const char *runtime_dir; + + runtime_dir = JACK_SOCKET_DIR; + + s->addr.sun_family = AF_UNIX; + if (promiscuous) { + name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path), + "%s/jack_%s_%d", runtime_dir, name, which) + 1; + } else { + name_size = snprintf(s->addr.sun_path, sizeof(s->addr.sun_path), + "%s/jack_%s_%d_%d", runtime_dir, name, getuid(), which) + 1; + } + + s->core_name = (s->addr.sun_path + name_size - 1) - strlen(name); + + if (name_size > (int) sizeof(s->addr.sun_path)) { + pw_log_error("socket path \"%s/%s\" plus null terminator exceeds 108 bytes", + runtime_dir, name); + *s->addr.sun_path = 0; + return false; + } + return true; +} + +static void +socket_data(struct spa_loop_utils *utils, + struct spa_source *source, int fd, enum spa_io mask, void *data) +{ + struct impl *impl = data; + struct client *client; + struct sockaddr_un name; + socklen_t length; + int client_fd; + + length = sizeof(name); + client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC); + if (client_fd < 0) { + pw_log_error("failed to accept: %m"); + return; + } + + client = client_new(impl, client_fd); + if (client == NULL) { + pw_log_error("failed to create client"); + close(client_fd); + return; + } + + pw_loop_update_io(impl->core->main_loop->loop, + client->source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); +} + +static bool add_socket(struct impl *impl, struct socket *s) +{ + socklen_t size; + + if ((s->fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) < 0) + return false; + + size = offsetof(struct sockaddr_un, sun_path) + strlen(s->addr.sun_path); + if (bind(s->fd, (struct sockaddr *) &s->addr, size) < 0) { + pw_log_error("bind() failed with error: %m"); + return false; + } + + if (listen(s->fd, 100) < 0) { + pw_log_error("listen() failed with error: %m"); + return false; + } + + s->loop = impl->core->main_loop->loop; + s->source = pw_loop_add_io(s->loop, s->fd, SPA_IO_IN, false, socket_data, impl); + if (s->source == NULL) + return false; + + spa_list_insert(impl->socket_list.prev, &s->link); + + return true; +} + +static int init_server(struct impl *impl, const char *name, bool promiscuous) +{ + struct jack_server *server = &impl->server; + jack_shm_info_t info; + size_t size; + int i; + struct socket *s; + + pthread_mutex_init(&server->lock, NULL); + + if (jack_register_server(name, 1) != 0) + return -1; + + jack_cleanup_shm(); + + /* graph manager */ + size = sizeof(struct jack_graph_manager) + 2048 * sizeof(struct jack_port); + + if (jack_shm_alloc(size, &info, segment_num++) < 0) + return -1; + + server->graph_manager = (struct jack_graph_manager *)jack_shm_addr(&info); + server->graph_manager->info = info; + + /* engine control */ + size = sizeof(struct jack_engine_control); + + if (jack_shm_alloc(size, &info, segment_num++) < 0) + return -1; + + server->engine_control = (struct jack_engine_control *)jack_shm_addr(&info); + server->engine_control->info = info; + + strcpy(server->engine_control->server_name, name); + + for (i = 0; i < CLIENT_NUM; i++) + server->synchro_table[i] = JACK_SYNCHRO_INIT; + + s = create_socket(); + + if (!init_socket_name(s, name, promiscuous, 0)) + goto error; + + if (!add_socket(impl, s)) + goto error; + + return 0; + + error: + destroy_socket(s); + return -1; +} + + +static struct impl *module_new(struct pw_core *core, struct pw_properties *properties) +{ + struct impl *impl; + const char *name, *str; + bool promiscuous; + + impl = calloc(1, sizeof(struct impl)); + pw_log_debug("protocol-jack %p: new", impl); + + impl->core = core; + impl->properties = properties; + + spa_list_init(&impl->socket_list); + spa_list_init(&impl->client_list); + + str = NULL; + if (impl->properties) + str = pw_properties_get(impl->properties, "jack.default.server"); + if (str == NULL) + str = getenv("JACK_DEFAULT_SERVER"); + + name = str ? str : JACK_DEFAULT_SERVER_NAME; + + str = NULL; + if (impl->properties) + str = pw_properties_get(impl->properties, "jack.promiscuous.server"); + if (str == NULL) + str = getenv("JACK_PROMISCUOUS_SERVER"); + + promiscuous = str ? atoi(str) != 0 : false; + + if (init_server(impl, name, promiscuous) < 0) + goto error; + + return impl; + + error: + free(impl); + return NULL; +} + +#if 0 +static void module_destroy(struct impl *impl) +{ + struct impl *object, *tmp; + + pw_log_debug("module %p: destroy", impl); + + free(impl); +} +#endif + +bool pipewire__module_init(struct pw_module *module, const char *args) +{ + module_new(module->core, NULL); + return true; +} diff --git a/pipewire/modules/module-jack/defs.h b/pipewire/modules/module-jack/defs.h new file mode 100644 index 000000000..ed42e0555 --- /dev/null +++ b/pipewire/modules/module-jack/defs.h @@ -0,0 +1,136 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "pipewire/client/log.h" + +#define USE_POSIX_SHM +#undef JACK_MONITOR + +#define JACK_DEFAULT_SERVER_NAME "default" +#define JACK_SOCKET_DIR "/dev/shm" +#define JACK_SHM_DIR "/dev/shm" +#define JACK_SERVER_NAME_SIZE 256 +#define JACK_CLIENT_NAME_SIZE 64 +#define JACK_PORT_NAME_SIZE 256 +#define JACK_PORT_TYPE_SIZE 32 +#define JACK_PROTOCOL_VERSION 8 + +#define PORT_NUM_MAX 4096 +#define PORT_NUM_FOR_CLIENT 2048 +#define CONNECTION_NUM_FOR_PORT PORT_NUM_FOR_CLIENT + +#define REAL_JACK_PORT_NAME_SIZE JACK_CLIENT_NAME_SIZE + JACK_PORT_NAME_SIZE + +#define BUFFER_SIZE_MAX 8192 + +#define CLIENT_NUM 256 + +#define JACK_ENGINE_ROLLING_COUNT 32 + +#define TIME_POINTS 100000 +#define FAILURE_TIME_POINTS 10000 +#define FAILURE_WINDOW 10 +#define MEASURED_CLIENTS 32 + +#define SYNC_MAX_NAME_SIZE 256 + +#define JACK_UUID_SIZE 36 +#define JACK_UUID_STRING_SIZE (JACK_UUID_SIZE+1) + +#define JACK_SESSION_COMMAND_SIZE 256 + +typedef enum { + JACK_TIMER_SYSTEM_CLOCK, + JACK_TIMER_HPET, +} jack_timer_type_t; + +enum jack_request_type { + jack_request_RegisterPort = 1, + jack_request_UnRegisterPort = 2, + jack_request_ConnectPorts = 3, + jack_request_DisconnectPorts = 4, + jack_request_SetTimeBaseClient = 5, + jack_request_ActivateClient = 6, + jack_request_DeactivateClient = 7, + jack_request_DisconnectPort = 8, + jack_request_SetClientCapabilities = 9, + jack_request_GetPortConnections = 10, + jack_request_GetPortNConnections = 11, + jack_request_ReleaseTimebase = 12, + jack_request_SetTimebaseCallback = 13, + jack_request_SetBufferSize = 20, + jack_request_SetFreeWheel = 21, + jack_request_ClientCheck = 22, + jack_request_ClientOpen = 23, + jack_request_ClientClose = 24, + jack_request_ConnectNamePorts = 25, + jack_request_DisconnectNamePorts = 26, + jack_request_GetInternalClientName = 27, + jack_request_InternalClientHandle = 28, + jack_request_InternalClientLoad = 29, + jack_request_InternalClientUnload = 30, + jack_request_PortRename = 31, + jack_request_Notification = 32, + jack_request_SessionNotify = 33, + jack_request_SessionReply = 34, + jack_request_GetClientByUUID = 35, + jack_request_ReserveClientName = 36, + jack_request_GetUUIDByClient = 37, + jack_request_ClientHasSessionCallback = 38, + jack_request_ComputeTotalLatencies = 39 +}; + +enum jack_notification_type { + jack_notify_AddClient = 0, + jack_notify_RemoveClient = 1, + jack_notify_ActivateClient = 2, + jack_notify_XRunCallback = 3, + jack_notify_GraphOrderCallback = 4, + jack_notify_BufferSizeCallback = 5, + jack_notify_SampleRateCallback = 6, + jack_notify_StartFreewheelCallback = 7, + jack_notify_StopFreewheelCallback = 8, + jack_notify_PortRegistrationOnCallback = 9, + jack_notify_PortRegistrationOffCallback = 10, + jack_notify_PortConnectCallback = 11, + jack_notify_PortDisconnectCallback = 12, + jack_notify_PortRenameCallback = 13, + jack_notify_RealTimeCallback = 14, + jack_notify_ShutDownCallback = 15, + jack_notify_QUIT = 16, + jack_notify_SessionCallback = 17, + jack_notify_LatencyCallback = 18, + jack_notify_max = 64 // To keep some room in JackClientControl fCallback table +}; + +#define kActivateClient_size (2*sizeof(int)) +#define kDeactivateClient_size (sizeof(int)) +#define kRegisterPort_size (sizeof(int) + JACK_PORT_NAME_SIZE+1 + JACK_PORT_TYPE_SIZE+1 + 2*sizeof(unsigned int)) +#define kClientCheck_size (JACK_CLIENT_NAME_SIZE+1 + 4 * sizeof(int)) +#define kClientOpen_size (JACK_CLIENT_NAME_SIZE+1 + 2 * sizeof(int)) +#define kClientClose_size (sizeof(int)) +#define kConnectNamePorts_size (sizeof(int) + REAL_JACK_PORT_NAME_SIZE+1 + REAL_JACK_PORT_NAME_SIZE+1) +#define kGetUUIDByClient_size (JACK_CLIENT_NAME_SIZE+1) + +#define CheckRead(var,size) if(read(client->fd,var,size)!=size) {pw_log_error("read error"); return -1; } +#define CheckWrite(var,size) if(write(client->fd,var,size)!=size) {pw_log_error("write error"); return -1; } +#define CheckSize(expected) { int __size; CheckRead(&__size, sizeof(int)); if (__size != expected) { pw_log_error("CheckSize error size %d != %d", __size, (int)expected); return -1; } } + +#define jack_error pw_log_error +#define jack_log pw_log_info diff --git a/pipewire/modules/module-jack/server.h b/pipewire/modules/module-jack/server.h new file mode 100644 index 000000000..16f83ba4c --- /dev/null +++ b/pipewire/modules/module-jack/server.h @@ -0,0 +1,53 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +struct jack_client { + int ref_num; + struct client *owner; + struct jack_client_control *control; +}; + +struct jack_server { + pthread_mutex_t lock; + + bool promiscuous; + + struct jack_graph_manager *graph_manager; + struct jack_engine_control *engine_control; + + struct jack_client* client_table[CLIENT_NUM]; + struct jack_synchro synchro_table[CLIENT_NUM]; +}; + +static inline int +jack_server_allocate_ref_num(struct jack_server *server) +{ + int i; + + for (i = 0; i < CLIENT_NUM; i++) + if (server->client_table[i] == NULL) + return i; + return -1; +} + +static inline void +jack_server_free_ref_num(struct jack_server *server, int ref_num) +{ + server->client_table[ref_num] = NULL; +} diff --git a/pipewire/modules/module-jack/shared.h b/pipewire/modules/module-jack/shared.h new file mode 100644 index 000000000..0883a2a90 --- /dev/null +++ b/pipewire/modules/module-jack/shared.h @@ -0,0 +1,304 @@ +/* PipeWire + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +typedef uint16_t jack_int_t; // Internal type for ports and refnum + +typedef enum { + NotTriggered, + Triggered, + Running, + Finished, +} jack_client_state_t; + +PRE_PACKED_STRUCTURE +struct jack_client_timing { + jack_time_t signaled_at; + jack_time_t awake_at; + jack_time_t finished_at; + jack_client_state_t status; +} POST_PACKED_STRUCTURE; + +#define JACK_CLIENT_TIMING_INIT (struct jack_client_timing) { 0, 0, 0, NotTriggered } + +PRE_PACKED_STRUCTURE +struct jack_port { + int type_id; + enum JackPortFlags flags; + char name[REAL_JACK_PORT_NAME_SIZE]; + char alias1[REAL_JACK_PORT_NAME_SIZE]; + char alias2[REAL_JACK_PORT_NAME_SIZE]; + int ref_num; + + jack_nframes_t latency; + jack_nframes_t total_latency; + jack_latency_range_t playback_latency; + jack_latency_range_t capture_latency; + uint8_t monitor_requests; + + bool in_use; + jack_port_id_t tied; + jack_default_audio_sample_t buffer[BUFFER_SIZE_MAX + 8]; +} POST_PACKED_STRUCTURE; + +#define MAKE_FIXED_ARRAY(size) \ +PRE_PACKED_STRUCTURE \ +struct { \ + jack_int_t table[size]; \ + uint32_t counter; \ +} POST_PACKED_STRUCTURE + +#define MAKE_FIXED_ARRAY1(size) \ +PRE_PACKED_STRUCTURE \ +struct { \ + MAKE_FIXED_ARRAY(size) array; \ + bool used; \ +} POST_PACKED_STRUCTURE + +#define MAKE_FIXED_MATRIX(size) \ +PRE_PACKED_STRUCTURE \ +struct { \ + jack_int_t table[size][size]; \ +} POST_PACKED_STRUCTURE + +PRE_PACKED_STRUCTURE +struct jack_activation_count { + int32_t value; + int32_t count; +} POST_PACKED_STRUCTURE; + +#define MAKE_LOOP_FEEDBACK(size) \ +PRE_PACKED_STRUCTURE \ +struct { \ + int table[size][3]; \ +} POST_PACKED_STRUCTURE + +PRE_PACKED_STRUCTURE +struct jack_connection_manager { + MAKE_FIXED_ARRAY(CONNECTION_NUM_FOR_PORT) connections[PORT_NUM_MAX]; + MAKE_FIXED_ARRAY1(PORT_NUM_FOR_CLIENT) input_port[CLIENT_NUM]; + MAKE_FIXED_ARRAY(PORT_NUM_FOR_CLIENT) output_port[CLIENT_NUM]; + MAKE_FIXED_MATRIX(CLIENT_NUM) connection_ref; + struct jack_activation_count input_counter[CLIENT_NUM]; + MAKE_LOOP_FEEDBACK(CONNECTION_NUM_FOR_PORT) loop_feedback; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_atomic_counter { + union { + struct { + uint16_t short_val1; // Cur + uint16_t short_val2; // Next + } scounter; + uint32_t long_val; + } info; +} POST_PACKED_STRUCTURE; + +#define MAKE_ATOMIC_STATE(type) \ +PRE_PACKED_STRUCTURE \ +struct { \ + type state[2]; \ + volatile struct jack_atomic_counter counter; \ + int32_t call_write_counter; \ +} POST_PACKED_STRUCTURE + +PRE_PACKED_STRUCTURE +struct jack_atomic_array_counter { + union { + struct { + unsigned char byte_val[4]; + } scounter; + uint32_t long_val; + } info; +} POST_PACKED_STRUCTURE; + +#define MAKE_ATOMIC_ARRAY_STATE(type) \ +PRE_PACKED_STRUCTURE \ +struct { \ + type state[3]; \ + volatile struct jack_atomic_array_counter counter; \ +} POST_PACKED_STRUCTURE + +PRE_PACKED_STRUCTURE +struct jack_graph_manager { + jack_shm_info_t info; + MAKE_ATOMIC_STATE(struct jack_connection_manager) state; + unsigned int port_max; + struct jack_client_timing client_timing[CLIENT_NUM]; + struct jack_port port_array[0]; +} POST_PACKED_STRUCTURE; + +typedef enum { + TransportCommandNone = 0, + TransportCommandStart = 1, + TransportCommandStop = 2, +} transport_command_t; + +PRE_PACKED_STRUCTURE +struct jack_transport_engine { + MAKE_ATOMIC_ARRAY_STATE(jack_position_t) state; + jack_transport_state_t transport_state; + volatile transport_command_t transport_cmd; + transport_command_t previous_cmd; /* previous transport_cmd */ + jack_time_t sync_timeout; + int sync_time_left; + int time_base_master; + bool pending_pos; + bool network_sync; + bool conditionnal; + int32_t write_counter; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_timer { + jack_nframes_t frames; + jack_time_t current_wakeup; + jack_time_t current_callback; + jack_time_t next_wakeup; + float period_usecs; + float filter_omega; /* set once, never altered */ + bool initialized; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_frame_timer { + MAKE_ATOMIC_STATE(struct jack_timer) state; + bool first_wakeup; +} POST_PACKED_STRUCTURE; + +#ifdef JACK_MONITOR +PRE_PACKED_STRUCTURE +struct jack_timing_measure_client { + int ref_num; + jack_time_t signaled_at; + jack_time_t awake_at; + jack_time_t finished_at; + jack_client_state_t status; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_timing_client_interval { + int ref_num; + char name[JACK_CLIENT_NAME_SIZE+1]; + int begin_interval; + int end_interval; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_timing_measure { + unsigned int audio_cycle; + jack_time_t period_usecs; + jack_time_t cur_cycle_begin; + jack_time_t prev_cycle_end; + struct jack_timing_measure_client client_table[CLIENT_NUM]; +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_engine_profiling { + struct jack_timing_measure profile_table[TIME_POINTS]; + struct jack_timing_client_interval interval_table[MEASURED_CLIENTS]; + + unsigned int audio_cycle; + unsigned int measured_client; +} POST_PACKED_STRUCTURE; +#endif + +PRE_PACKED_STRUCTURE +struct jack_engine_control { + jack_shm_info_t info; + jack_nframes_t buffer_size; + jack_nframes_t sample_rate; + bool sync_node; + bool temporary; + jack_time_t period_usecs; + jack_time_t timeout_usecs; + float max_delayed_usecs; + float xrun_delayed_usecs; + bool timeout; + bool real_time; + bool saved_real_time; + int server_priority; + int client_priority; + int max_client_priority; + char server_name[JACK_SERVER_NAME_SIZE]; + struct jack_transport_engine transport; + jack_timer_type_t clock_source; + int driver_num; + bool verbose; + + // CPU Load + jack_time_t prev_cycle_time; + jack_time_t cur_cycle_time; + jack_time_t spare_usecs; + jack_time_t max_usecs; + jack_time_t rolling_client_usecs[JACK_ENGINE_ROLLING_COUNT]; + unsigned int rolling_client_usecs_cnt; + int rolling_client_usecs_index; + int rolling_interval; + float CPU_load; + + // For OSX thread + uint64_t period; + uint64_t computation; + uint64_t constraint; + + // Timer + struct jack_frame_timer frame_timer; + +#ifdef JACK_MONITOR + struct jack_engine_profiling profiler; +#endif +} POST_PACKED_STRUCTURE; + +PRE_PACKED_STRUCTURE +struct jack_client_control { + jack_shm_info_t info; + char name[JACK_CLIENT_NAME_SIZE+1]; + bool callback[jack_notify_max]; + volatile jack_transport_state_t transport_state; + volatile bool transport_sync; + volatile bool transport_timebase; + int ref_num; + int PID; + bool active; + + int session_ID; + char session_command[JACK_SESSION_COMMAND_SIZE]; + jack_session_flags_t session_flags; +} POST_PACKED_STRUCTURE; + + +static inline int jack_shm_alloc(size_t size, jack_shm_info_t *info, int num) +{ + char name[64]; + + snprintf(name, sizeof(name), "/jack_shared%d", num); + + if (jack_shmalloc(name, size, info)) { + pw_log_error("Cannot create shared memory segment of size = %zd (%s)", size, strerror(errno)); + return -1; + } + + if (jack_attach_shm(info)) { + jack_error("Cannot attach shared memory segment name = %s err = %s", name, strerror(errno)); + jack_destroy_shm(info); + return -1; + } + info->size = size; + return 0; +} diff --git a/pipewire/modules/module-jack/shm.c b/pipewire/modules/module-jack/shm.c new file mode 100644 index 000000000..c3989bcf9 --- /dev/null +++ b/pipewire/modules/module-jack/shm.c @@ -0,0 +1,1303 @@ +/* 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 +#include +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +#include "defs.h" +#include "shm.h" + +static int GetUID() +{ +#ifdef WIN32 + return _getpid(); + //#error "No getuid function available" +#else + return getuid(); +#endif +} + +static int GetPID() +{ +#ifdef WIN32 + return _getpid(); +#else + return getpid(); +#endif +} + +#ifdef USE_POSIX_SHM +static jack_shmtype_t jack_shmtype = shm_POSIX; +#elif WIN32 +static jack_shmtype_t jack_shmtype = shm_WIN32; +#else +static 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 (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 */ +static jack_shm_id_t registry_id; /* SHM id for the registry */ + +#ifdef WIN32 +static jack_shm_info_t registry_info = {/* SHM info for the registry */ + JACK_SHM_NULL_INDEX, + 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; +static char jack_shm_server_prefix[JACK_SERVER_NAME_SIZE+1] = ""; + +/* 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 +#include + +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_set_server_prefix (const char *server_name) +{ +#ifdef WIN32 + char buffer[UNLEN+1]={0}; + DWORD len = UNLEN+1; + GetUserName(buffer, &len); + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), + "jack-%s:%s:", buffer, server_name); +#else + snprintf (jack_shm_server_prefix, sizeof (jack_shm_server_prefix), + "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 (®istry_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 (®istry_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%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; + + if (jack_shm_header) + return 0; /* already initialized */ + + jack_set_server_prefix (server_name); + + 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; + + jack_set_server_prefix (server_name); + + 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, + jack_shm_server_prefix, + JACK_SERVER_NAME_SIZE) != 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, + jack_shm_server_prefix, + JACK_SERVER_NAME_SIZE); + + 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 multple + * 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; + + strncpy (registry_id, "/jack-shm-registry", sizeof (registry_id)); + + /* 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; + + strncpy (registry_id, "/jack-shm-registry", sizeof (registry_id)); + + 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 permisssion 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)", 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 (®istry_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 (®istry_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 (jack_shm_id_t *id) +{ + /* registry may or may not be locked */ + shm_unlink ((char *) 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]; + + 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; + } + + 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; + strncpy (registry_id, "jack-shm-registry", sizeof (registry_id)); + + /* 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 (®istry_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; + + strncpy (registry_id, "jack-shm-registry", sizeof (registry_id)); + + 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 (®istry_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 (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 (®istry_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(®istry_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 */ diff --git a/pipewire/modules/module-jack/shm.h b/pipewire/modules/module-jack/shm.h new file mode 100644 index 000000000..90336eeaa --- /dev/null +++ b/pipewire/modules/module-jack/shm.h @@ -0,0 +1,216 @@ +/* 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. + + */ + +#ifndef __jack_shm_h__ +#define __jack_shm_h__ + +#include +#include + +#include + +#define TRUE 1 +#define FALSE 0 + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define MAX_SERVERS 8 /* maximum concurrent servers */ +#define MAX_SHM_ID 256 /* generally about 16 per server */ +#define JACK_SHM_MAGIC 0x4a41434b /* shm magic number: "JACK" */ +#define JACK_SHM_NULL_INDEX -1 /* NULL SHM index */ +#define JACK_SHM_REGISTRY_INDEX -2 /* pseudo SHM index for registry */ + + + /* On Mac OS X, SHM_NAME_MAX is the maximum length of a shared memory + * segment name (instead of NAME_MAX or PATH_MAX as defined by the + * standard). + */ +#ifdef USE_POSIX_SHM + +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +#ifndef SHM_NAME_MAX +#define SHM_NAME_MAX NAME_MAX +#endif + typedef char shm_name_t[SHM_NAME_MAX]; + typedef shm_name_t jack_shm_id_t; + +#elif WIN32 +#define NAME_MAX 255 +#ifndef SHM_NAME_MAX +#define SHM_NAME_MAX NAME_MAX +#endif + typedef char shm_name_t[SHM_NAME_MAX]; + typedef shm_name_t jack_shm_id_t; + +#elif __ANDROID__ + +#ifndef NAME_MAX +#define NAME_MAX 255 +#endif + +#ifndef SHM_NAME_MAX +#define SHM_NAME_MAX NAME_MAX +#endif + typedef char shm_name_t[SHM_NAME_MAX]; + typedef shm_name_t jack_shm_id_t; + typedef int jack_shm_fd_t; + +#else + /* System V SHM */ + typedef int jack_shm_id_t; +#endif /* SHM type */ + + /* shared memory type */ + typedef enum { + shm_POSIX = 1, /* POSIX shared memory */ + shm_SYSV = 2, /* System V shared memory */ + shm_WIN32 = 3, /* Windows 32 shared memory */ + shm_ANDROID = 4 /* Android shared memory */ + } jack_shmtype_t; + + typedef int16_t jack_shm_registry_index_t; + + /** + * A structure holding information about shared memory allocated by + * JACK. this persists across invocations of JACK, and can be used by + * multiple JACK servers. It contains no pointers and is valid across + * address spaces. + * + * The registry consists of two parts: a header including an array of + * server names, followed by an array of segment registry entries. + */ + typedef struct _jack_shm_server { +#ifdef WIN32 + int pid; /* process ID */ +#else + pid_t pid; /* process ID */ +#endif + + char name[JACK_SERVER_NAME_SIZE]; + } + jack_shm_server_t; + + typedef struct _jack_shm_header { + uint32_t magic; /* magic number */ + uint16_t protocol; /* JACK protocol version */ + jack_shmtype_t type; /* shm type */ + jack_shmsize_t size; /* total registry segment size */ + jack_shmsize_t hdr_len; /* size of header */ + jack_shmsize_t entry_len; /* size of registry entry */ + jack_shm_server_t server[MAX_SERVERS]; /* current server array */ + } + jack_shm_header_t; + + typedef struct _jack_shm_registry { + jack_shm_registry_index_t index; /* offset into the registry */ + +#ifdef WIN32 + int allocator; /* PID that created shm segment */ +#else + pid_t allocator; /* PID that created shm segment */ +#endif + + jack_shmsize_t size; /* for POSIX unattach */ + jack_shm_id_t id; /* API specific, see above */ +#ifdef __ANDROID__ + jack_shm_fd_t fd; +#endif + } + jack_shm_registry_t; + +#define JACK_SHM_REGISTRY_SIZE (sizeof (jack_shm_header_t) \ + + sizeof (jack_shm_registry_t) * MAX_SHM_ID) + + /** + * a structure holding information about shared memory + * allocated by JACK. this version is valid only + * for a given address space. It contains a pointer + * indicating where the shared memory has been + * attached to the address space. + */ + + PRE_PACKED_STRUCTURE + struct _jack_shm_info { + jack_shm_registry_index_t index; /* offset into the registry */ + uint32_t size; +#ifdef __ANDROID__ + jack_shm_fd_t fd; +#endif + union { + void *attached_at; /* address where attached */ + char ptr_size[8]; + } ptr; /* a "pointer" that has the same 8 bytes size when compling in 32 or 64 bits */ + } POST_PACKED_STRUCTURE; + + typedef struct _jack_shm_info jack_shm_info_t; + + /* utility functions used only within JACK */ + + void jack_shm_copy_from_registry (jack_shm_info_t*, + jack_shm_registry_index_t); + void jack_shm_copy_to_registry (jack_shm_info_t*, + jack_shm_registry_index_t*); + int jack_release_shm_info (jack_shm_registry_index_t); + char* jack_shm_addr (jack_shm_info_t* si); + + // here begin the API + int jack_register_server (const char *server_name, int new_registry); + int jack_unregister_server (const char *server_name); + + int jack_initialize_shm (const char *server_name); + int jack_initialize_shm_server (void); + int jack_initialize_shm_client (void); + int jack_cleanup_shm (void); + + int jack_shmalloc (const char *shm_name, jack_shmsize_t size, + jack_shm_info_t* result); + void jack_release_shm (jack_shm_info_t*); + void jack_release_lib_shm (jack_shm_info_t*); + void jack_destroy_shm (jack_shm_info_t*); + int jack_attach_shm (jack_shm_info_t*); + int jack_attach_lib_shm (jack_shm_info_t*); + int jack_attach_shm_read (jack_shm_info_t*); + int jack_attach_lib_shm_read (jack_shm_info_t*); + int jack_resize_shm (jack_shm_info_t*, jack_shmsize_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* __jack_shm_h__ */ diff --git a/pipewire/modules/module-jack/synchro.h b/pipewire/modules/module-jack/synchro.h new file mode 100644 index 000000000..c0563f26e --- /dev/null +++ b/pipewire/modules/module-jack/synchro.h @@ -0,0 +1,47 @@ +/* PipeWire + * Copyright (C) 2017 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +struct jack_synchro { + char name[SYNC_MAX_NAME_SIZE]; + bool flush; + sem_t *semaphore; +}; + +#define JACK_SYNCHRO_INIT (struct jack_synchro) { { 0, }, false, NULL } + +static inline int +jack_synchro_alloc(struct jack_synchro *synchro, + const char *client_name, + const char *server_name, + int value, bool internal, + bool promiscuous) +{ + if (promiscuous) + snprintf(synchro->name, sizeof(synchro->name), + "jack_sem.%s_%s", server_name, client_name); + else + snprintf(synchro->name, sizeof(synchro->name), + "jack_sem.%d_%s_%s", getuid(), server_name, client_name); + + if ((synchro->semaphore = sem_open(synchro->name, O_CREAT | O_RDWR, 0777, value)) == (sem_t*)SEM_FAILED) { + pw_log_error("can't check in named semaphore name = %s err = %s", synchro->name, strerror(errno)); + return -1; + } + return true; +} diff --git a/pkgconfig/libpipewire.pc.in b/pkgconfig/libpipewire.pc.in index b7de4e2f6..7087f53a2 100644 --- a/pkgconfig/libpipewire.pc.in +++ b/pkgconfig/libpipewire.pc.in @@ -6,6 +6,6 @@ moduledir=@moduledir@ Name: libpipewire Description: PipeWire Client Interface -Version: @PACKAGE_VERSION@ +Version: @VERSION@ Libs: -L${libdir} -lpipewire-@PIPEWIRE_API_VERSION@ Cflags: -I${includedir} -D_REENTRANT diff --git a/pkgconfig/libpipewirecore.pc.in b/pkgconfig/libpipewirecore.pc.in index 55a2067ab..f16198c2b 100644 --- a/pkgconfig/libpipewirecore.pc.in +++ b/pkgconfig/libpipewirecore.pc.in @@ -6,6 +6,6 @@ moduledir=@moduledir@ Name: libpipewirecore Description: PipeWire Core -Version: @PACKAGE_VERSION@ +Version: @VERSION@ Libs: -L${libdir} -lpipewirecore-@PIPEWIRE_API_VERSION@ Cflags: -I${includedir} -D_REENTRANT diff --git a/pkgconfig/libspa.pc.in b/pkgconfig/libspa.pc.in index afdb65812..b327cf010 100644 --- a/pkgconfig/libspa.pc.in +++ b/pkgconfig/libspa.pc.in @@ -5,6 +5,6 @@ includedir=@includedir@/ Name: libspa Description: Simple Plugin API -Version: @PACKAGE_VERSION@ +Version: @VERSION@ Libs: -L${libdir} -lspa-lib Cflags: -I${includedir} -D_REENTRANT