blob: 82c15203eeeb9e62065755a4331e90295f15af38 [file] [log] [blame] [edit]
/*
* libbrlapi - A library providing access to braille terminals for applications.
*
* Copyright (C) 2002-2023 by
* Samuel Thibault <Samuel.Thibault@ens-lyon.org>
* Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>
*
* libbrlapi comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed 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. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
/* api_client.c handles connection with BrlApi */
#ifdef __ANDROID__
#define __ANDROID_API__ 21
#endif /* __ANDROID__ */
#define WIN_ERRNO_STORAGE_CLASS static
#include "prologue.h"
#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <locale.h>
#include <assert.h>
#include <limits.h>
#include <unistd.h>
#ifndef __MINGW32__
#ifdef HAVE_LANGINFO_H
#include <langinfo.h>
#endif /* HAVE_LANGINFO_H */
#endif /* __MINGW32__ */
#ifdef __MINGW32__
#include <ws2tcpip.h>
#include "win_pthread.h"
#include "win_errno.h"
#define syslog(level,fmt,...) fprintf(stderr,#level ": " fmt, ## __VA_ARGS__)
#else /* __MINGW32__ */
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include <syslog.h>
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/semaphore.h>
#else /* semaphore support */
#include <semaphore.h>
#endif /* semaphore support */
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#ifdef HAVE_SYS_POLL_H
#include <poll.h>
#endif /* HAVE_SYS_POLL_H */
#endif /* __MINGW32__ */
#include <sys/time.h>
#include "gettime.h"
#ifdef linux
#include <sys/sysmacros.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/vt.h>
#define MAXIMUM_VIRTUAL_CONSOLE MAX_NR_CONSOLES
#endif /* linux */
#ifdef __OpenBSD__
#define MAXIMUM_VIRTUAL_CONSOLE 16
#endif /* __OpenBSD__ */
#ifndef MAXIMUM_VIRTUAL_CONSOLE
#define MAXIMUM_VIRTUAL_CONSOLE 1
#endif /* MAXIMUM_VIRTUAL_CONSOLE */
#ifdef HAVE_SD_SESSION_GET_VT
#include <systemd/sd-login.h>
#endif /* HAVE_SD_SESSION_GET_VT */
#define BRLAPI_NO_DEPRECATED
#include "brlapi.h"
#include "brlapi_protocol.h"
#define BRLAPI(fun) brlapi_ ## fun
#include "brlapi_common.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b))? (a): (b))
#endif /* MIN */
#ifndef MAX
#define MAX(a, b) (((a) > (b))? (a): (b))
#endif /* MAX */
/* API states */
#define STCONNECTED 1
#define STRAW 2
#define STSUSPEND 4
#define STCONTROLLINGTTY 8
#ifdef WINDOWS
#ifdef __MINGW32__
static WSADATA wsadata;
#endif /* __MINGW32__ */
static void* GetProc(const char *library, const char *fun) {
HMODULE module;
void *ret;
if (!(module = LoadLibrary(library))) return (void*)(-1);
if ((ret = GetProcAddress(module, fun))) return ret;
FreeLibrary(module);
return (void*)(-1);
}
#define CHECKPROC(library, name) \
(name##Proc && name##Proc != (void*)(-1))
#define CHECKGETPROC(library, name) \
(name##Proc != (void*)(-1) && (name##Proc || (name##Proc = GetProc(library,#name)) != (void*)(-1)))
#define WIN_PROC_STUB(name) typeof(name) (*name##Proc);
static WIN_PROC_STUB(GetConsoleWindow);
#ifdef __MINGW32__
static WIN_PROC_STUB(wcslen);
static WIN_PROC_STUB(getaddrinfo);
#define getaddrinfo(host,port,hints,res) getaddrinfoProc(host,port,hints,res)
static WIN_PROC_STUB(freeaddrinfo);
#define freeaddrinfo(res) freeaddrinfoProc(res)
#endif /* __MINGW32__ */
#endif /* WINDOWS */
/* We need to declare these as weak external references to determine
* at runtime whether libpthread is used or not.
* We also can't rely on the functions prototypes.
*/
#if defined(WINDOWS)
#elif defined(__APPLE__)
#pragma weak pthread_key_create
#pragma weak pthread_once
#pragma weak pthread_getspecific
#pragma weak pthread_setspecific
typedef semaphore_t sem_t;
static int
sem_init (sem_t *sem, int shared, int value) {
return semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value);
}
static int
sem_post (sem_t *sem) {
return semaphore_signal(*sem);
}
static int
sem_wait (sem_t *sem) {
return semaphore_wait(*sem);
}
static int
sem_timedwait (sem_t *sem, const struct timespec *abs) {
struct timeval now;
getRealTime(&now);
mach_timespec_t rel = {
.tv_sec = abs->tv_sec - now.tv_sec,
.tv_nsec = abs->tv_nsec - (now.tv_usec * 1000)
};
if (rel.tv_nsec < 0) rel.tv_sec -= 1, rel.tv_nsec += 1000000000;
if (rel.tv_sec < 0) rel.tv_sec = 0, rel.tv_nsec = 0;
return semaphore_timedwait(*sem, rel);
}
static int
sem_trywait (sem_t *sem) {
mach_timespec_t rel = { .tv_sec=0, .tv_nsec=0 };
return semaphore_timedwait(*sem, rel);
}
static int
sem_destroy (sem_t *sem) {
return semaphore_destroy(mach_task_self(), *sem);
}
#elif defined(__GNUC__) || defined(__sun__)
#pragma weak pthread_key_create
#pragma weak pthread_once
#pragma weak pthread_getspecific
#pragma weak pthread_setspecific
#pragma weak sem_init
#pragma weak sem_post
#pragma weak sem_wait
#pragma weak sem_destroy
#endif /* weak external references */
/** key presses buffer size
*
* key presses won't be lost provided no more than BRL_KEYBUF_SIZE key presses
* are done between two calls to brlapi_read* if a call to another function is
* done in the meanwhile (which needs somewhere to put them before being able
* to get responses from the server)
*/
#define BRL_KEYBUF_SIZE 256
struct brlapi_parameterCallback_t {
brlapi_param_t parameter;
brlapi_param_subparam_t subparam;
brlapi_param_flags_t flags;
brlapi_paramCallback_t func;
void *priv;
struct brlapi_parameterCallback_t *prev, *next;
};
struct brlapi_handle_t { /* Connection-specific information */
uint32_t serverVersion;
unsigned int brlx;
unsigned int brly;
brlapi_fileDescriptor fileDescriptor; /* Descriptor of the socket connected to BrlApi */
int addrfamily; /* Address family of the socket */
/* to protect concurrent fd write operations */
pthread_mutex_t fileDescriptor_mutex;
/* to protect concurrent fd requests */
pthread_mutex_t req_mutex;
/* to protect concurrent key reading */
pthread_mutex_t key_mutex;
/* to protect concurrent fd key events and request answers, also protects the
* key buffer */
/* Only two threads might want to take it: one that already got
* brlapi_req_mutex, or one that got key_mutex */
pthread_mutex_t read_mutex;
Packet packet;
/* when someone is already reading (reading==1), put expected type,
* address/size of buffer, and address of return value, then wait on semaphore,
* the buffer gets filled, altRes too */
int reading;
brlapi_packetType_t altExpectedPacketType;
unsigned char *altPacket;
size_t altSize;
ssize_t *altRes;
sem_t *altSem;
int state;
pthread_mutex_t state_mutex;
#ifdef LC_GLOBAL_LOCALE
locale_t default_locale;
#endif /* LC_GLOBAL_LOCALE */
/* key presses buffer, for when key presses are received instead of
* acknowledgements for instance
*
* every function must hence be able to read at least sizeof(brlapi_keyCode_t) */
brlapi_keyCode_t keybuf[BRL_KEYBUF_SIZE];
unsigned keybuf_next;
unsigned keybuf_nb;
union {
brlapi_exceptionHandler_t withoutHandle;
brlapi__exceptionHandler_t withHandle;
} exceptionHandler;
int exception_sync;
int exception_error;
pthread_mutex_t exceptionHandler_mutex;
struct brlapi_parameterCallback_t *parameterCallbacks;
/* This protects the callback list, but also callback calling conventions in
* general:
* callbacks are called in the update order, and not after unregistration */
pthread_mutex_t callbacks_mutex;
/* The callback might want to unregister itself, while we are processing the
* list of callbacks. This records where we are in the list, to skip the
* deleted item. */
struct brlapi_parameterCallback_t *nextCallback;
void *clientData; /* Private client data */
};
/* Function brlapi_getLibraryVersion */
void brlapi_getLibraryVersion(int *major, int *minor, int *revision)
{
*major = BRLAPI_MAJOR;
*minor = BRLAPI_MINOR;
*revision = BRLAPI_REVISION;
}
/* Function brlapi_getHandleSize */
size_t BRLAPI_STDCALL brlapi_getHandleSize(void)
{
return sizeof(brlapi_handle_t);
}
static brlapi_handle_t defaultHandle;
/* brlapi_initializeHandle */
/* Initialize a BrlAPI handle */
static void brlapi_initializeHandle(brlapi_handle_t *handle)
{
handle->brlx = 0;
handle->brly = 0;
handle->fileDescriptor = BRLAPI_INVALID_FILE_DESCRIPTOR;
handle->addrfamily = 0;
pthread_mutex_init(&handle->fileDescriptor_mutex, NULL);
pthread_mutex_init(&handle->req_mutex, NULL);
pthread_mutex_init(&handle->key_mutex, NULL);
pthread_mutex_init(&handle->read_mutex, NULL);
brlapi_initializePacket(&handle->packet);
handle->reading = 0;
handle->altExpectedPacketType = 0;
handle->altPacket = NULL;
handle->altSize = 0;
handle->altRes = NULL;
handle->altSem = NULL;
handle->state = 0;
pthread_mutex_init(&handle->state_mutex, NULL);
#ifdef LC_GLOBAL_LOCALE
handle->default_locale = LC_GLOBAL_LOCALE;
#endif /* LC_GLOBAL_LOCALE */
memset(handle->keybuf, 0, sizeof(handle->keybuf));
handle->keybuf_next = 0;
handle->keybuf_nb = 0;
if (handle == &defaultHandle)
handle->exceptionHandler.withoutHandle = brlapi_defaultExceptionHandler;
else
handle->exceptionHandler.withHandle = brlapi__defaultExceptionHandler;
handle->exception_sync = 0;
handle->exception_error = BRLAPI_ERROR_SUCCESS;
pthread_mutex_init(&handle->exceptionHandler_mutex, NULL);
handle->parameterCallbacks = NULL;
{
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&handle->callbacks_mutex, &mattr);
}
handle->nextCallback = NULL;
handle->clientData = NULL;
}
/* brlapi_doWaitForPacket */
/* Waits for the specified type of packet: must be called with brlapi_req_mutex locked */
/* deadline can be used to stop waiting after a given date, or wait forever (NULL) */
/* If the right packet type arrives, returns its size */
/* Returns -1 if a non-fatal error is encountered */
/* Returns -2 on end of file */
/* Returns -3 if the available packet was not for us */
/* Returns -4 on timeout (if deadline is not NULL) */
/* Calls the exception handler if an exception is encountered */
static ssize_t brlapi__doWaitForPacket(brlapi_handle_t *handle, brlapi_packetType_t expectedPacketType, void *packet, size_t packetSize, struct timeval *deadline)
{
uint32_t *uint32Packet = handle->packet.content;
const brlapi_packet_t *localPacket = (brlapi_packet_t *) uint32Packet;
const brlapi_errorPacket_t *errorPacket = &localPacket->error;
uint32_t size;
brlapi_packetType_t type;
int ret = 0;
struct timeval now;
long delay = 0;
int polled = 0; /* Whether we have polled at least once */
do {
if (deadline) {
getRealTime(&now);
delay = (deadline->tv_sec - now.tv_sec ) * 1000 +
(deadline->tv_usec - now.tv_usec) / 1000;
if (delay < 0) {
if (polled) {
/* The deadline has expired, don't wait more */
return -4;
}
/* Poll at least once */
delay = 0;
}
}
polled = 1;
#ifdef __MINGW32__
DWORD dw;
dw = WaitForSingleObject(handle->packet.overl.hEvent, deadline ? delay : INFINITE);
if (dw == WAIT_FAILED) {
setSystemErrno();
LibcError("waiting for packet");
return -2;
}
if (dw == WAIT_OBJECT_0)
#else /* __MINGW32__ */
#ifdef HAVE_POLL
struct pollfd pollfd;
pollfd.fd = handle->fileDescriptor;
pollfd.events = POLLIN;
pollfd.revents = 0;
if (poll(&pollfd, 1, deadline ? delay : -1) < 0) {
LibcError("waiting for packet");
return -2;
}
if (pollfd.revents & POLLIN)
#else /* HAVE_POLL */
fd_set sockset;
struct timeval timeout, *ptimeout = NULL;
if (deadline) {
timeout.tv_sec = delay / 1000;
timeout.tv_usec = (delay % 1000) * 1000;
ptimeout = &timeout;
}
FD_ZERO(&sockset);
FD_SET(handle->fileDescriptor, &sockset);
if (select(handle->fileDescriptor+1, &sockset, NULL, NULL, ptimeout) < 0) {
LibcError("waiting for packet");
return -2;
}
if (FD_ISSET(handle->fileDescriptor, &sockset))
#endif /* !HAVE_POLL */
#endif /* __MINGW32__ */
{
/* Some data is here, read it */
ret = brlapi__readPacket(&handle->packet, handle->fileDescriptor);
if (ret == -1) {
LibcError("reading packet");
}
if (ret < 0) {
/* error or end of file */
return -2;
}
}
} while (ret == 0);
/* Got a packet, process it. */
size = handle->packet.header.size;
type = handle->packet.header.type;
if (type==expectedPacketType)
{
/* For us, just copy */
memcpy(packet, handle->packet.content, MIN(packetSize, size));
return size;
}
/* Not for us. For alternate reader? */
pthread_mutex_lock(&handle->read_mutex);
if (handle->altSem && type==handle->altExpectedPacketType) {
/* Yes, put packet content there */
memcpy(handle->altPacket, handle->packet.content, MIN(handle->altSize, size));
*handle->altRes = size;
#ifndef WINDOWS
if (sem_post)
#endif /* WINDOWS */
sem_post(handle->altSem);
handle->altSem = NULL;
pthread_mutex_unlock(&handle->read_mutex);
return -3;
}
/* No alternate reader, read it locally... */
if ((type==BRLAPI_PACKET_KEY) && (handle->state & STCONTROLLINGTTY) && (size==sizeof(brlapi_keyCode_t))) {
/* keypress, buffer it */
if (handle->keybuf_nb>=BRL_KEYBUF_SIZE) {
syslog(LOG_WARNING,"lost key: 0X%8lx%8lx\n",(unsigned long)ntohl(uint32Packet[0]),(unsigned long)ntohl(uint32Packet[1]));
} else {
handle->keybuf[(handle->keybuf_next+handle->keybuf_nb++)%BRL_KEYBUF_SIZE]
= brlapi_packetToKeyCode(uint32Packet);
}
pthread_mutex_unlock(&handle->read_mutex);
return -3;
}
if (type==BRLAPI_PACKET_PARAM_UPDATE) {
/* Parameter update, find handler */
brlapi_paramValuePacket_t *value = (void*) handle->packet.content;
brlapi_param_flags_t flags = ntohl(value->flags);
brlapi_param_t param = ntohl(value->param);
brlapi_param_subparam_t subparam = ((brlapi_param_subparam_t)ntohl(value->subparam_hi) << 32) | ntohl(value->subparam_lo);
size_t rlen = size - sizeof(flags) - sizeof(param) - sizeof(subparam);
_brlapi_ntohParameter(param, value, rlen);
pthread_mutex_lock(&handle->callbacks_mutex);
for(handle->nextCallback = handle->parameterCallbacks;
handle->nextCallback; ) {
struct brlapi_parameterCallback_t *callback = handle->nextCallback;
handle->nextCallback = handle->nextCallback->next;
if (callback->parameter == param &&
callback->subparam == subparam &&
(callback->flags & BRLAPI_PARAMF_GLOBAL) == (flags & BRLAPI_PARAMF_GLOBAL)) {
callback->func(param, subparam, callback->flags, callback->priv, value->data, rlen);
/* Note: the callback might have removed this entry */
}
}
handle->nextCallback = NULL;
pthread_mutex_unlock(&handle->callbacks_mutex);
}
pthread_mutex_unlock(&handle->read_mutex);
/* otherwise this is an error */
if (type==BRLAPI_PACKET_ERROR) {
brlapi_errno = ntohl(errorPacket->code);
return -1;
}
if (type==BRLAPI_PACKET_EXCEPTION) {
size_t esize;
int hdrSize = sizeof(errorPacket->code)+sizeof(errorPacket->type);
int err = ntohl(errorPacket->code);
if (size<hdrSize)
esize = 0;
else
esize = size-hdrSize;
pthread_mutex_lock(&handle->exceptionHandler_mutex);
if (handle->exception_sync) {
handle->exception_error = err;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
return -3;
}
if (handle==&defaultHandle)
defaultHandle.exceptionHandler.withoutHandle(err, ntohl(errorPacket->type), &errorPacket->packet, esize);
else
handle->exceptionHandler.withHandle(handle, err, ntohl(errorPacket->type), &errorPacket->packet, esize);
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
pthread_mutex_lock(&handle->fileDescriptor_mutex);
closeFileDescriptor(handle->fileDescriptor);
handle->fileDescriptor = BRLAPI_INVALID_FILE_DESCRIPTOR;
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
return -2;
}
syslog(LOG_ERR,"(brlapi_waitForPacket) Received unexpected packet of type %s and size %ld\n",brlapi_getPacketTypeName(type),(long)size);
return -3;
}
#define TRY_WAIT_FOR_EXPECTED_PACKET 0
#define WAIT_FOR_EXPECTED_PACKET 1
#define POLL 0
#define WAIT_FOREVER (-1)
/* brlapi_waitForPacket */
/* same as brlapi_doWaitForPacket, but sleeps instead of reading if another
* thread is already reading, and timeout_ms expressed as relative ms delay
* instead of absolute deadline.
* timeout_ms set to WAIT_FOREVER means no deadline.
* Never returns -2. If loop is WAIT_FOR_EXPECTED_PACKET, never returns -3.
*/
static ssize_t brlapi__waitForPacket(brlapi_handle_t *handle, brlapi_packetType_t expectedPacketType, void *packet, size_t size, int loop, int timeout_ms) {
int doread;
ssize_t res;
sem_t sem;
struct timeval deadline, *pdeadline = NULL;
if (timeout_ms >= 0) {
getRealTime(&deadline);
deadline.tv_sec += timeout_ms / 1000;
deadline.tv_usec += (timeout_ms % 1000) * 1000;
if (deadline.tv_usec >= 1000000) {
deadline.tv_sec++;
deadline.tv_usec -= 1000000;
}
pdeadline = &deadline;
}
again:
doread = 0;
pthread_mutex_lock(&handle->read_mutex);
if (!handle->reading) {
doread = handle->reading = 1;
} else {
if (
#ifndef WINDOWS
!sem_init || !sem_post || !sem_wait || !sem_destroy ||
#endif /* WINDOWS */
handle->altSem) {
/* This can't happen without threads */
pthread_mutex_unlock(&handle->read_mutex);
syslog(LOG_ERR,"third call to brlapi_waitForPacket !");
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
return -1;
}
handle->altExpectedPacketType = expectedPacketType;
handle->altPacket = packet;
handle->altSize = size;
handle->altRes = &res;
sem_init(&sem, 0, 0);
handle->altSem = &sem;
}
pthread_mutex_unlock(&handle->read_mutex);
if (doread) {
do {
res = brlapi__doWaitForPacket(handle, expectedPacketType, packet, size, pdeadline);
} while (loop && (res == -3 || (res == -1 && brlapi_errno == BRLAPI_ERROR_LIBCERR && (
brlapi_libcerrno == EINTR ||
#ifdef EWOULDBLOCK
brlapi_libcerrno == EWOULDBLOCK ||
#endif /* EWOULDBLOCK */
brlapi_libcerrno == EAGAIN))));
pthread_mutex_lock(&handle->read_mutex);
if (handle->altSem) {
*handle->altRes = -3; /* no packet for him */
#ifndef WINDOWS
if (sem_post)
#endif /* WINDOWS */
sem_post(handle->altSem);
handle->altSem = NULL;
}
handle->reading = 0;
pthread_mutex_unlock(&handle->read_mutex);
} else {
int ret;
if (timeout_ms == 0) {
ret = sem_trywait(&sem);
} else if (timeout_ms > 0) {
struct timespec abs_timeout;
abs_timeout.tv_sec = deadline.tv_sec;
abs_timeout.tv_nsec = deadline.tv_usec * 1000;
ret = sem_timedwait(&sem, &abs_timeout);
} else {
do {
ret = sem_wait(&sem);
} while (ret && errno == EINTR);
}
if (ret) {
/* Timeout, drop our semaphore */
pthread_mutex_lock(&handle->read_mutex);
handle->altSem = NULL;
pthread_mutex_unlock(&handle->read_mutex);
res = -4;
}
sem_destroy(&sem);
if (res == -1 /* He got an error, we want to read it too */
|| (res == -3 && loop)) /* reader hadn't any packet for us */
goto again;
}
if (res==-2) {
res = -1;
brlapi_errno = BRLAPI_ERROR_EOF;
}
return res;
}
/* brlapi_waitForAck */
/* Wait for an acknowledgement, must be called with brlapi_req_mutex locked */
static int brlapi__waitForAck(brlapi_handle_t *handle)
{
return brlapi__waitForPacket(handle, BRLAPI_PACKET_ACK, NULL, 0, WAIT_FOR_EXPECTED_PACKET, WAIT_FOREVER);
}
/* brlapi_writePacketWaitForAck */
/* write a packet and wait for an acknowledgement */
static int brlapi__writePacketWaitForAck(brlapi_handle_t *handle, brlapi_packetType_t type, const void *buf, size_t size)
{
ssize_t res;
pthread_mutex_lock(&handle->req_mutex);
if ((res=brlapi_writePacket(handle->fileDescriptor, type,buf,size))<0) {
pthread_mutex_unlock(&handle->req_mutex);
return res;
}
res=brlapi__waitForAck(handle);
pthread_mutex_unlock(&handle->req_mutex);
return res;
}
/* brlapi__pause */
/* Wait for an event to be received */
int BRLAPI_STDCALL brlapi__pause(brlapi_handle_t *handle, int timeout_ms) {
ssize_t res = brlapi__waitForPacket(handle, 0, NULL, 0, TRY_WAIT_FOR_EXPECTED_PACKET, timeout_ms);
if (res == -3) {
brlapi_libcerrno = EINTR;
brlapi_errno = BRLAPI_ERROR_LIBCERR;
brlapi_errfun = "waitForPacket";
res = -1;
}
if (res == -4) {
/* Timeout */
res = 0;
}
return res;
}
/* brlapi_pause */
int BRLAPI_STDCALL brlapi_pause(int timeout_ms) {
return brlapi__pause(&defaultHandle, timeout_ms);
}
/* brlapi__sync */
int BRLAPI_STDCALL brlapi__sync(brlapi_handle_t *handle) {
int res, error;
pthread_mutex_lock(&handle->exceptionHandler_mutex);
handle->exception_sync++;
error = handle->exception_error;
handle->exception_error = BRLAPI_ERROR_SUCCESS;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
if (error != BRLAPI_ERROR_SUCCESS) {
pthread_mutex_lock(&handle->exceptionHandler_mutex);
handle->exception_sync--;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
brlapi_errno = error;
return -1;
}
res = brlapi__writePacketWaitForAck(handle,BRLAPI_PACKET_SYNCHRONIZE,NULL,0);
if (res == -1) {
pthread_mutex_lock(&handle->exceptionHandler_mutex);
handle->exception_sync--;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
return -1;
}
pthread_mutex_lock(&handle->exceptionHandler_mutex);
handle->exception_sync--;
error = handle->exception_error;
handle->exception_error = BRLAPI_ERROR_SUCCESS;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
if (error != BRLAPI_ERROR_SUCCESS) {
brlapi_errno = error;
return -1;
}
return 0;
}
/* brlapi_sync */
int BRLAPI_STDCALL brlapi_sync(void) {
return brlapi__sync(&defaultHandle);
}
/* Function: tryHost */
/* Tries to connect to the given host. */
static int tryHost(brlapi_handle_t *handle, const char *hostAndPort) {
char *host = NULL;
char *port;
SocketDescriptor sockfd = -1;
#ifdef __MINGW32__
if (WSAStartup(
#ifdef HAVE_GETADDRINFO
MAKEWORD(2,0),
#else /* HAVE_GETADDRINFO */
MAKEWORD(1,1),
#endif /* HAVE_GETADDRINFO */
&wsadata))
return -1;
#endif /* __MINGW32__ */
handle->addrfamily = brlapi_expandHost(hostAndPort,&host,&port);
#if defined(PF_LOCAL)
if (handle->addrfamily == PF_LOCAL) {
int lpath = strlen(BRLAPI_SOCKETPATH),lport;
lport = strlen(port);
#ifdef __MINGW32__
{
HANDLE pipefd;
char path[lpath+lport+1];
memcpy(path, BRLAPI_SOCKETPATH, lpath);
memcpy(path+lpath, port, lport+1);
while ((pipefd = CreateFile(path,GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,SECURITY_IMPERSONATION|FILE_FLAG_OVERLAPPED,NULL))
== INVALID_HANDLE_VALUE) {
if (GetLastError() != ERROR_PIPE_BUSY) {
brlapi_errfun = "CreateFile";
goto outlibc;
}
WaitNamedPipe(path, NMPWAIT_WAIT_FOREVER);
}
sockfd = (SocketDescriptor)pipefd;
}
#else /* __MINGW32__ */
{
struct sockaddr_un sa;
if (lpath+1+lport+1 > sizeof(sa.sun_path)) {
brlapi_libcerrno=ENAMETOOLONG;
brlapi_errfun="path";
brlapi_errno = BRLAPI_ERROR_LIBCERR;
goto out;
}
if ((sockfd = socket(PF_LOCAL, SOCK_STREAM, 0))<0) {
brlapi_errfun="socket";
setSocketErrno();
goto outlibc;
}
#if !defined(HAVE_POLL)
if (sockfd >= FD_SETSIZE) {
/* Will not be able to call select() on this */
closeFileDescriptor(sockfd);
brlapi_errfun="socket";
setErrno(EMFILE);
goto outlibc;
}
#endif /* !HAVE_POLL */
sa.sun_family = AF_LOCAL;
memcpy(sa.sun_path,BRLAPI_SOCKETPATH "/",lpath+1);
memcpy(sa.sun_path+lpath+1,port,lport+1);
if (connect(sockfd, (struct sockaddr *) &sa, sizeof(sa))<0) {
brlapi_errfun="connect";
setSocketErrno();
goto outlibc;
}
}
#endif /* __MINGW32__ */
handle->fileDescriptor = (FileDescriptor) sockfd;
} else {
#else /* PF_LOCAL */
if (0) {} else {
#endif /* PF_LOCAL */
#ifdef __MINGW32__
if (CHECKGETPROC("ws2_32.dll",getaddrinfo)
&& CHECKGETPROC("ws2_32.dll",freeaddrinfo)) {
#endif /* __MINGW32__ */
#if defined(HAVE_GETADDRINFO) || defined(__MINGW32__)
struct addrinfo *res,*cur;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
brlapi_gaierrno = getaddrinfo(host, port, &hints, &res);
if (brlapi_gaierrno) {
brlapi_errno=BRLAPI_ERROR_GAIERR;
brlapi_libcerrno=errno;
goto out;
}
for(cur = res; cur; cur = cur->ai_next) {
sockfd = socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
if (sockfd<0) continue;
#if !defined(__MINGW32__) && !defined(HAVE_POLL)
if (sockfd >= FD_SETSIZE) {
/* Will not be able to call select() on this */
closeFileDescriptor(sockfd);
brlapi_errfun="socket";
setErrno(EMFILE);
goto outlibc;
}
#endif /* !__MINGW32__ && !HAVE_POLL */
if (connect(sockfd, cur->ai_addr, cur->ai_addrlen)<0) {
closeSocketDescriptor(sockfd);
continue;
}
break;
}
freeaddrinfo(res);
if (!cur) {
brlapi_errno=BRLAPI_ERROR_CONNREFUSED;
goto out;
}
#endif /* HAVE_GETADDRINFO */
#ifdef __MINGW32__
} else {
#endif /* __MINGW32__ */
#if !defined(HAVE_GETADDRINFO) || defined(__MINGW32__)
struct sockaddr_in addr;
struct hostent *he;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
if (!port)
addr.sin_port = htons(BRLAPI_SOCKETPORTNUM);
else {
char *c;
addr.sin_port = htons(strtol(port, &c, 0));
if (*c) {
struct servent *se;
if (!(se = getservbyname(port,"tcp"))) {
brlapi_gaierrno = h_errno;
brlapi_errno=BRLAPI_ERROR_GAIERR;
goto out;
}
addr.sin_port = se->s_port;
}
}
if (!host) {
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
} else if ((addr.sin_addr.s_addr = inet_addr(host)) == htonl(INADDR_NONE)) {
if (!(he = gethostbyname(host))) {
brlapi_gaierrno = h_errno;
brlapi_errno = BRLAPI_ERROR_GAIERR;
goto out;
}
if (he->h_addrtype != AF_INET) {
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else /* EAFNOSUPPORT */
errno = EINVAL;
#endif /* EAFNOSUPPORT */
brlapi_errfun = "gethostbyname";
goto outlibc;
}
if (he->h_length > sizeof(addr.sin_addr)) {
errno = EINVAL;
brlapi_errfun = "gethostbyname";
goto outlibc;
}
memcpy(&addr.sin_addr,he->h_addr,he->h_length);
}
sockfd = socket(addr.sin_family, SOCK_STREAM, 0);
if (sockfd<0) {
brlapi_errfun = "socket";
setSocketErrno();
goto outlibc;
}
#if !defined(__MINGW32__) && !defined(HAVE_POLL)
if (sockfd >= FD_SETSIZE) {
/* Will not be able to call select() on this */
closeFileDescriptor(sockfd);
brlapi_errfun="socket";
setErrno(EMFILE);
goto outlibc;
}
#endif /* !__MINGW32__ && !HAVE_POLL */
if (connect(sockfd, (struct sockaddr *) &addr, sizeof(addr))<0) {
brlapi_errfun = "connect";
setSocketErrno();
goto outlibc;
}
#endif /* !HAVE_GETADDRINFO */
#ifdef __MINGW32__
}
#endif /* __MINGW32__ */
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
{
int yes=1;
setsockopt(sockfd,IPPROTO_TCP,TCP_NODELAY,(void*)&yes,sizeof(yes));
}
#endif /* defined(IPPROTO_TCP) && defined(TCP_NODELAY) */
handle->fileDescriptor = (FileDescriptor) sockfd;
}
free(host);
free(port);
return 0;
outlibc:
brlapi_errno = BRLAPI_ERROR_LIBCERR;
brlapi_libcerrno = errno;
if (sockfd>=0)
closeSocketDescriptor(sockfd);
out:
free(host);
free(port);
return -1;
}
/* Function : updateSettings */
/* Updates the content of a brlapi_connectionSettings_t structure according to */
/* another structure of the same type */
static void updateSettings(brlapi_connectionSettings_t *s1, const brlapi_connectionSettings_t *s2)
{
if (s2==NULL) return;
if ((s2->auth) && (*s2->auth))
s1->auth = s2->auth;
if ((s2->host) && (*s2->host))
s1->host = s2->host;
}
/* Function: brlapi_openConnection
* Creates a socket to connect to BrlApi */
brlapi_fileDescriptor BRLAPI_STDCALL brlapi__openConnection(brlapi_handle_t *handle, const brlapi_connectionSettings_t *clientSettings, brlapi_connectionSettings_t *usedSettings)
{
brlapi_packet_t packet;
brlapi_packet_t serverPacket;
brlapi_authClientPacket_t *auth = &packet.authClient;
brlapi_authServerPacket_t *authServer = &serverPacket.authServer;
brlapi_versionPacket_t *version = &serverPacket.version;
uint32_t *type;
int len;
brlapi_connectionSettings_t settings = { BRLAPI_DEFAUTH, ":0" };
brlapi_connectionSettings_t envsettings = { getenv("BRLAPI_AUTH"), getenv("BRLAPI_HOST") };
/* Here update settings with the parameters from misc sources (files, env...) */
updateSettings(&settings, &envsettings);
updateSettings(&settings, clientSettings);
if (usedSettings!=NULL) updateSettings(usedSettings, &settings);
brlapi_initializeHandle(handle);
if (tryHost(handle, settings.host)<0) {
const char *port = strrchr(settings.host, ':');
if (port != settings.host) goto out;
if (!isPortNumber(port+1, NULL)) goto out;
brlapi_error_t originalError = brlapi_error;
size_t portLength = strlen(port);
static const char *const localhostAddressTable[] = {
LOCALHOST_ADDRESS_IPV4,
#ifdef AF_INET6
LOCALHOST_ADDRESS_IPV6,
#endif /* AF_INET6 */
};
const char *const *lha = localhostAddressTable;
const char *const *end = lha + ARRAY_COUNT(localhostAddressTable);
while (lha < end) {
size_t hostLength = strlen(*lha);
char host[hostLength + portLength + 1];
snprintf(host, sizeof(host), "%s%s", *lha, port);
if (tryHost(handle, host) != -1) {
if (usedSettings) usedSettings->host = strdup(host);
break;
}
lha += 1;
}
if (lha == end) {
brlapi_error = originalError;
goto out;
}
}
if ((len = brlapi__waitForPacket(handle, BRLAPI_PACKET_VERSION, &serverPacket, sizeof(serverPacket), WAIT_FOR_EXPECTED_PACKET, WAIT_FOREVER)) < 0)
goto outfd;
handle->serverVersion = ntohl(version->protocolVersion);
if (handle->serverVersion < 8) {
/* We only provide compatibility with version 8 and later. */
brlapi_errno = BRLAPI_ERROR_PROTOCOL_VERSION;
goto outfd;
}
version->protocolVersion = htonl(BRLAPI_PROTOCOL_VERSION);
if (brlapi_writePacket(handle->fileDescriptor, BRLAPI_PACKET_VERSION, version, sizeof(*version)) < 0)
goto outfd;
if ((len = brlapi__waitForPacket(handle, BRLAPI_PACKET_AUTH, &serverPacket, sizeof(serverPacket), WAIT_FOR_EXPECTED_PACKET, WAIT_FOREVER)) < 0)
return BRLAPI_INVALID_FILE_DESCRIPTOR;
for (type = &authServer->type[0]; type < &authServer->type[len / sizeof(authServer->type[0])]; type++) {
auth->type = *type;
switch (ntohl(*type)) {
case BRLAPI_AUTH_NONE:
if (usedSettings) usedSettings->auth = "none";
goto done;
case BRLAPI_AUTH_KEY: {
size_t authKeyLength;
int res;
char *keyfile = brlapi_getKeyFile(settings.auth);
if (!keyfile)
continue;
res = brlapi_loadAuthKey(keyfile, &authKeyLength, (void *) &auth->key);
free(keyfile);
if (res < 0)
continue;
res = brlapi_writePacket(handle->fileDescriptor, BRLAPI_PACKET_AUTH, auth,
sizeof(auth->type)+authKeyLength);
memset(&auth->key, 0, authKeyLength);
if (res < 0)
goto outfd;
if (usedSettings) usedSettings->auth = settings.auth;
break;
}
default:
/* unsupported authorization type */
continue;
}
if ((brlapi__waitForAck(handle)) == 0) goto done;
}
/* no suitable authorization method */
brlapi_errno = BRLAPI_ERROR_AUTHENTICATION;
outfd:
closeFileDescriptor(handle->fileDescriptor);
handle->fileDescriptor = BRLAPI_INVALID_FILE_DESCRIPTOR;
out:
return BRLAPI_INVALID_FILE_DESCRIPTOR;
done:
pthread_mutex_lock(&handle->state_mutex);
handle->state = STCONNECTED;
pthread_mutex_unlock(&handle->state_mutex);
return handle->fileDescriptor;
}
brlapi_fileDescriptor BRLAPI_STDCALL brlapi_openConnection(const brlapi_connectionSettings_t *clientSettings, brlapi_connectionSettings_t *usedSettings)
{
return brlapi__openConnection(&defaultHandle, clientSettings, usedSettings);
}
brlapi_fileDescriptor BRLAPI_STDCALL brlapi__getFileDescriptor(brlapi_handle_t *handle)
{
return handle->fileDescriptor;
}
brlapi_fileDescriptor BRLAPI_STDCALL brlapi_getFileDescriptor(void)
{
return brlapi__getFileDescriptor(&defaultHandle);
}
/* brlapi_closeConnection */
/* Cleanly close the socket */
void BRLAPI_STDCALL brlapi__closeConnection(brlapi_handle_t *handle)
{
pthread_mutex_lock(&handle->state_mutex);
handle->state = 0;
pthread_mutex_unlock(&handle->state_mutex);
pthread_mutex_lock(&handle->fileDescriptor_mutex);
closeFileDescriptor(handle->fileDescriptor);
handle->fileDescriptor = BRLAPI_INVALID_FILE_DESCRIPTOR;
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
#ifdef LC_GLOBAL_LOCALE
if (handle->default_locale != LC_GLOBAL_LOCALE) {
freelocale(handle->default_locale);
}
#endif /* LC_GLOBAL_LOCALE */
#ifdef __MINGW32__
WSACleanup();
#endif /* __MINGW32__ */
}
void BRLAPI_STDCALL brlapi_closeConnection(void)
{
brlapi__closeConnection(&defaultHandle);
}
/* brlapi__setClientData */
void BRLAPI_STDCALL brlapi__setClientData(brlapi_handle_t *handle, void *data)
{
handle->clientData = data;
}
void BRLAPI_STDCALL brlapi_setClientData(void *data)
{
brlapi__setClientData(&defaultHandle, data);
}
/* brlapi__getClientData */
void * BRLAPI_STDCALL brlapi__getClientData(brlapi_handle_t *handle)
{
return handle->clientData;
}
void * BRLAPI_STDCALL brlapi_getClientData(void)
{
return brlapi__getClientData(&defaultHandle);
}
/* brlapi_getDriverSpecific */
/* Switch to device specific mode */
static int brlapi__getDriverSpecific(brlapi_handle_t *handle, const char *driver, brlapi_packetType_t type, int st)
{
int res;
brlapi_packet_t packet;
brlapi_getDriverSpecificModePacket_t *driverPacket = &packet.getDriverSpecificMode;
unsigned int n = strlen(driver);
if (n>BRLAPI_MAXNAMELENGTH) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
pthread_mutex_lock(&defaultHandle.state_mutex);
if ((defaultHandle.state & st)) {
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
res = -1;
goto out;
}
driverPacket->magic = htonl(BRLAPI_DEVICE_MAGIC);
driverPacket->nameLength = n;
memcpy(&driverPacket->name, driver, n);
res = brlapi__writePacketWaitForAck(handle, type, &packet, sizeof(uint32_t)+1+n);
if (res!=-1)
handle->state |= st;
out:
pthread_mutex_unlock(&handle->state_mutex);
return res;
}
/* brlapi_leaveDriverSpecific */
/* Leave device specific mode */
static int brlapi__leaveDriverSpecific(brlapi_handle_t *handle, brlapi_packetType_t type, int st)
{
int res;
pthread_mutex_lock(&handle->state_mutex);
if (!(handle->state & st)) {
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
res = -1;
goto out;
}
res = brlapi__writePacketWaitForAck(handle, type, NULL, 0);
if (!res)
handle->state &= ~st;
out:
pthread_mutex_unlock(&handle->state_mutex);
return res;
}
/* brlapi_enterRawMode */
/* Switch to Raw mode */
int BRLAPI_STDCALL brlapi__enterRawMode(brlapi_handle_t *handle, const char *driver)
{
return brlapi__getDriverSpecific(handle, driver, BRLAPI_PACKET_ENTERRAWMODE, STRAW);
}
int BRLAPI_STDCALL brlapi_enterRawMode(const char *driver)
{
return brlapi__enterRawMode(&defaultHandle, driver);
}
/* brlapi_leaveRawMode */
/* Leave Raw mode */
int BRLAPI_STDCALL brlapi__leaveRawMode(brlapi_handle_t *handle)
{
return brlapi__leaveDriverSpecific(handle, BRLAPI_PACKET_LEAVERAWMODE, STRAW);
}
int BRLAPI_STDCALL brlapi_leaveRawMode(void)
{
return brlapi__leaveRawMode(&defaultHandle);
}
/* brlapi_sendRaw */
/* Send a Raw Packet */
ssize_t BRLAPI_STDCALL brlapi__sendRaw(brlapi_handle_t *handle, const void *buf, size_t size)
{
ssize_t res;
pthread_mutex_lock(&handle->fileDescriptor_mutex);
res=brlapi_writePacket(handle->fileDescriptor, BRLAPI_PACKET_PACKET, buf, size);
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
return res;
}
ssize_t BRLAPI_STDCALL brlapi_sendRaw(const void *buf, size_t size)
{
return brlapi__sendRaw(&defaultHandle, buf, size);
}
/* brlapi_recvRaw */
/* Get a Raw packet */
ssize_t BRLAPI_STDCALL brlapi__recvRaw(brlapi_handle_t *handle, void *buf, size_t size)
{
ssize_t res;
if (!(handle->state & STRAW)) {
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
return -1;
}
res = brlapi__waitForPacket(handle, BRLAPI_PACKET_PACKET, buf, size, TRY_WAIT_FOR_EXPECTED_PACKET, WAIT_FOREVER);
if (res == -3) {
brlapi_libcerrno = EINTR;
brlapi_errno = BRLAPI_ERROR_LIBCERR;
brlapi_errfun = "waitForPacket";
res = -1;
}
return res;
}
ssize_t BRLAPI_STDCALL brlapi_recvRaw(void *buf, size_t size)
{
return brlapi__recvRaw(&defaultHandle, buf, size);
}
/* brlapi_suspendDriver */
int BRLAPI_STDCALL brlapi__suspendDriver(brlapi_handle_t *handle, const char *driver)
{
return brlapi__getDriverSpecific(handle, driver, BRLAPI_PACKET_SUSPENDDRIVER, STSUSPEND);
}
int BRLAPI_STDCALL brlapi_suspendDriver(const char *driver)
{
return brlapi__suspendDriver(&defaultHandle, driver);
}
/* brlapi_resumeDriver */
int BRLAPI_STDCALL brlapi__resumeDriver(brlapi_handle_t *handle)
{
return brlapi__leaveDriverSpecific(handle, BRLAPI_PACKET_RESUMEDRIVER, STSUSPEND);
}
int BRLAPI_STDCALL brlapi_resumeDriver(void)
{
return brlapi__resumeDriver(&defaultHandle);
}
/* Function brlapi_request */
/* Sends a request to the API and waits for the answer */
/* The answer is put in the given packet */
static ssize_t brlapi__request(brlapi_handle_t *handle, brlapi_packetType_t request, void *packet, size_t size)
{
ssize_t res;
pthread_mutex_lock(&handle->req_mutex);
res = brlapi_writePacket(handle->fileDescriptor, request, NULL, 0);
if (res==-1) {
pthread_mutex_unlock(&handle->req_mutex);
return -1;
}
res = brlapi__waitForPacket(handle, request, packet, size, WAIT_FOR_EXPECTED_PACKET, WAIT_FOREVER);
pthread_mutex_unlock(&handle->req_mutex);
return res;
}
/* Function : brlapi_getDriverName */
/* Name of the driver used by brltty */
int BRLAPI_STDCALL brlapi__getDriverName(brlapi_handle_t *handle, char *name, size_t n)
{
ssize_t res = brlapi__request(handle, BRLAPI_PACKET_GETDRIVERNAME, name, n);
if ((res>0) && (res<=n)) name[res-1] = '\0';
return res;
}
int BRLAPI_STDCALL brlapi_getDriverName(char *name, size_t n)
{
return brlapi__getDriverName(&defaultHandle, name, n);
}
/* Function : brlapi_getModelIdentifier */
/* An identifier for the model of the device used by brltty */
int BRLAPI_STDCALL brlapi__getModelIdentifier(brlapi_handle_t *handle, char *identifier, size_t n)
{
ssize_t res = brlapi__request(handle, BRLAPI_PACKET_GETMODELID, identifier, n);
if ((res>0) && (res<=n)) identifier[res-1] = '\0';
return res;
}
int BRLAPI_STDCALL brlapi_getModelIdentifier(char *identifier, size_t n)
{
return brlapi__getModelIdentifier(&defaultHandle, identifier, n);
}
/* Function : brlapi_getDisplaySize */
/* Returns the size of the braille display */
int BRLAPI_STDCALL brlapi__getDisplaySize(brlapi_handle_t *handle, unsigned int *x, unsigned int *y)
{
uint32_t displaySize[2];
ssize_t res;
if (handle->brlx&&handle->brly) { *x = handle->brlx; *y = handle->brly; return 0; }
res = brlapi__request(handle, BRLAPI_PACKET_GETDISPLAYSIZE, displaySize, sizeof(displaySize));
if (res==-1) { return -1; }
handle->brlx = ntohl(displaySize[0]);
handle->brly = ntohl(displaySize[1]);
*x = handle->brlx; *y = handle->brly;
return 0;
}
int BRLAPI_STDCALL brlapi_getDisplaySize(unsigned int *x, unsigned int *y)
{
return brlapi__getDisplaySize(&defaultHandle, x, y);
}
/* Function: brlapi_getParameter */
/* Internal version, returns the reply value packet and the length of the value */
static ssize_t _brlapi__getParameter(brlapi_handle_t *handle, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, brlapi_paramValuePacket_t *reply)
{
brlapi_paramRequestPacket_t request;
int res;
ssize_t rlen;
request.flags = htonl(flags);
request.param = htonl(parameter);
request.subparam_hi = htonl(subparam >> 32);
request.subparam_lo = htonl(subparam & 0xfffffffful);
pthread_mutex_lock(&handle->req_mutex);
res = brlapi_writePacket(handle->fileDescriptor, BRLAPI_PACKET_PARAM_REQUEST, &request, sizeof(request));
if (res < 0) {
pthread_mutex_unlock(&handle->req_mutex);
return -1;
}
if (flags & BRLAPI_PARAMF_GET)
rlen = brlapi__waitForPacket(handle, BRLAPI_PACKET_PARAM_VALUE, reply, sizeof(*reply), 1, -1);
else
rlen = brlapi__waitForAck(handle);
pthread_mutex_unlock(&handle->req_mutex);
if (rlen < 0) {
return -1;
}
if (flags & BRLAPI_PARAMF_GET) {
rlen -= sizeof(brlapi_param_flags_t) + sizeof(brlapi_param_t) + sizeof(brlapi_param_subparam_t);
if (rlen < 0) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
}
return rlen;
}
ssize_t BRLAPI_STDCALL brlapi__getParameter(brlapi_handle_t *handle, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, void* data, size_t len)
{
brlapi_paramValuePacket_t reply;
ssize_t rlen;
if (flags & ~BRLAPI_PARAMF_GLOBAL) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
rlen = _brlapi__getParameter(handle, parameter, subparam, flags | BRLAPI_PARAMF_GET, &reply);
if (rlen < 0)
return -1;
if (rlen < len) {
len = rlen;
}
_brlapi_ntohParameter(parameter, &reply, rlen);
memcpy(data, &reply.data, len);
return rlen;
}
ssize_t BRLAPI_STDCALL brlapi_getParameter(brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, void* data, size_t len)
{
return brlapi__getParameter(&defaultHandle, parameter, subparam, flags, data, len);
}
void* BRLAPI_STDCALL brlapi__getParameterAlloc(brlapi_handle_t *handle, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, size_t *len)
{
brlapi_paramValuePacket_t reply;
ssize_t rlen;
void *data;
if (flags & ~BRLAPI_PARAMF_GLOBAL) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return NULL;
}
rlen = _brlapi__getParameter(handle, parameter, subparam, flags | BRLAPI_PARAMF_GET, &reply);
if (rlen < 0)
return NULL;
data = malloc(rlen + 1);
if (!data)
return NULL;
_brlapi_ntohParameter(parameter, &reply, rlen);
memcpy(data, &reply.data, rlen);
((char*)data)[rlen] = 0;
if (len)
*len = rlen;
return data;
}
void* BRLAPI_STDCALL brlapi_getParameterAlloc(brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, size_t *len)
{
return brlapi__getParameterAlloc(&defaultHandle, parameter, subparam, flags, len);
}
/* Function: brlapi_setParameter */
int BRLAPI_STDCALL brlapi__setParameter(brlapi_handle_t *handle, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void* data, size_t len)
{
brlapi_paramValuePacket_t packet;
int res;
if (flags & ~BRLAPI_PARAMF_GLOBAL) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
if (len > sizeof(packet.data)) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
packet.flags = htonl(flags);
packet.param = htonl(parameter);
packet.subparam_hi = htonl(subparam >> 32);
packet.subparam_lo = htonl(subparam & 0xfffffffful);
memcpy(packet.data, data, len);
_brlapi_htonParameter(parameter, &packet, len);
res = brlapi__writePacketWaitForAck(handle, BRLAPI_PACKET_PARAM_VALUE, &packet, sizeof(packet.flags) + sizeof(parameter) + sizeof(subparam) + len);
return res;
}
int BRLAPI_STDCALL brlapi_setParameter(brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void* data, size_t len)
{
return brlapi__setParameter(&defaultHandle, parameter, subparam, flags, data, len);
}
/* Function: brlapi_watchParameter */
brlapi_paramCallbackDescriptor_t BRLAPI_STDCALL brlapi__watchParameter(brlapi_handle_t *handle, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, brlapi_paramCallback_t func, void *priv, void* data, size_t len)
{
brlapi_paramValuePacket_t reply;
ssize_t rlen;
struct brlapi_parameterCallback_t *callback;
if (flags & ~(BRLAPI_PARAMF_GLOBAL|BRLAPI_PARAMF_SELF)) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return NULL;
}
pthread_mutex_lock(&handle->callbacks_mutex);
rlen = _brlapi__getParameter(handle, parameter, subparam, flags | BRLAPI_PARAMF_GET | BRLAPI_PARAMF_SUBSCRIBE, &reply);
if (rlen < 0) {
pthread_mutex_unlock(&handle->callbacks_mutex);
return NULL;
}
callback = malloc(sizeof(*callback));
if (callback == NULL) {
brlapi_errno = BRLAPI_ERROR_NOMEM;
return NULL;
}
callback->parameter = parameter;
callback->subparam = subparam;
callback->flags = flags;
callback->func = func;
callback->priv = priv;
callback->next = handle->parameterCallbacks;
if (callback->next)
callback->next->prev = callback;
callback->prev = NULL;
handle->parameterCallbacks = callback;
_brlapi_ntohParameter(parameter, &reply, rlen);
if (data) {
if (rlen < len) {
len = rlen;
}
memcpy(data, &reply.data, len);
} else {
func(parameter, subparam, flags, priv, &reply.data, rlen);
}
pthread_mutex_unlock(&handle->callbacks_mutex);
return callback;
}
brlapi_paramCallbackDescriptor_t BRLAPI_STDCALL brlapi_watchParameter(brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, brlapi_paramCallback_t func, void *priv, void* data, size_t len)
{
return brlapi__watchParameter(&defaultHandle, parameter, subparam, flags, func, priv, data, len);
}
/* Function: brlapi_unwatchParameter */
int BRLAPI_STDCALL brlapi__unwatchParameter(brlapi_handle_t *handle, brlapi_paramCallbackDescriptor_t descriptor)
{
brlapi_paramValuePacket_t reply;
ssize_t rlen;
struct brlapi_parameterCallback_t *callback = descriptor;
pthread_mutex_lock(&handle->callbacks_mutex);
rlen = _brlapi__getParameter(handle, callback->parameter, callback->subparam, callback->flags | BRLAPI_PARAMF_UNSUBSCRIBE, &reply);
if (rlen < 0) {
pthread_mutex_unlock(&handle->callbacks_mutex);
return -1;
}
if (handle->nextCallback == callback) {
/* We might be removing an entry which brlapi__doWaitForPacket was planning
* to have a look at next, make it skip it. */
handle->nextCallback = handle->nextCallback->next;
}
if (callback->next) {
callback->next->prev = callback->prev;
}
if (callback->prev) {
callback->prev->next = callback->next;
} else {
handle->parameterCallbacks = callback->next;
}
pthread_mutex_unlock(&handle->callbacks_mutex);
free(callback);
return 0;
}
int BRLAPI_STDCALL brlapi_unwatchParameter(brlapi_paramCallbackDescriptor_t descriptor)
{
return brlapi__unwatchParameter(&defaultHandle, descriptor);
}
/* Function : getControllingTty */
/* Returns the number of the caller's controlling terminal */
/* -1 if error or unknown */
static int getControllingTty(void)
{
int tty;
const char *env;
/*if ((env = getenv("WINDOW")) && sscanf(env, "%u", &tty) == 1) return tty;*/
if ((env = getenv("WINDOWID")) && sscanf(env, "%u", &tty) == 1) return tty;
if ((env = getenv("CONTROLVT")) && sscanf(env, "%u", &tty) == 1) return tty;
if (!getenv("DISPLAY") && !getenv("WAYLAND_DISPLAY"))
if ((env = getenv("XDG_VTNR")) && sscanf(env, "%u", &tty) == 1) return tty;
#ifdef WINDOWS
if (CHECKGETPROC("kernel32.dll", GetConsoleWindow))
/* really good guess */
if ((tty = (LONG_PTR) GetConsoleWindowProc())) return tty;
if ((tty = (LONG_PTR) GetActiveWindow()) || (tty = (LONG_PTR) GetFocus())) {
/* good guess, but need to get back up to parent window */
HWND root = GetDesktopWindow();
HWND tmp = (HWND) (LONG_PTR) tty;
while (1) {
tmp = GetParent(tmp);
if (!tmp || tmp == root) return tty;
tty = (LONG_PTR) tmp;
}
}
/* poor guess: assumes that focus is here */
if ((tty = (LONG_PTR) GetForegroundWindow())) return tty;
#endif /* WINDOWS */
#ifdef linux
do {
int fd = open("/proc/self/stat", O_RDONLY, 0);
if (fd < 0) {
break;
}
char buf[256];
ssize_t read_bytes = read(fd, buf, sizeof(buf) - 1);
close(fd);
if (read_bytes < 0) {
break;
}
/* Append null at the end of buffer to ensure that sscanf doesn't
* overflows.*/
buf[read_bytes] = '\0';
/* The line format is "$pid ($comm) $state ...
* where comm may contain space and `)` as part of its name, ex:
* 12345 (some proc (foo)) R ...
* So to process valid identified after comm, we should skip comm by
* getting the 1st `)` from end.
*/
char *ptr = buf + read_bytes - 1;
while (ptr > buf && *ptr != ')') {
ptr -= 1;
}
if (*ptr != ')') {
break;
}
/* Point to space after ($comm). */
ptr += 1;
int vt;
int ok = sscanf(ptr, " %*c %*d %*d %*d %d", &tty) == 1;
if (ok && (major(tty) == TTY_MAJOR)) {
vt = minor(tty);
if ((vt >= 1) && (vt <= MAXIMUM_VIRTUAL_CONSOLE)) return vt;
}
} while (0);
#endif /* linux */
return -1;
}
/* Function : brlapi_enterTtyMode */
/* Takes control of a tty */
int BRLAPI_STDCALL brlapi__enterTtyMode(brlapi_handle_t *handle, int tty, const char *driverName)
{
/* Determine which tty to take control of */
if (tty<0) tty = getControllingTty();
/* 0 can be a valid screen WINDOW
0xffffffff can not be a valid WINDOWID (top 3 bits guaranteed to be zero) */
if (tty<0) { brlapi_errno=BRLAPI_ERROR_UNKNOWNTTY; return -1; }
if (brlapi__enterTtyModeWithPath(handle, &tty, 1, driverName)) return -1;
return tty;
}
int BRLAPI_STDCALL brlapi_enterTtyMode(int tty, const char *how)
{
return brlapi__enterTtyMode(&defaultHandle, tty, how);
}
/* Function : brlapi_enterTtyModeWithPath */
/* Takes control of a tty path */
int BRLAPI_STDCALL brlapi__enterTtyModeWithPath(brlapi_handle_t *handle, const int *ttys, int nttys, const char *driverName)
{
int res;
void *packet;
unsigned char *p;
uint32_t *nbTtys, *t;
char *ttytreepath,*cur,*next;
int ttypath;
unsigned int n, nttytreepath;
size_t size;
pthread_mutex_lock(&handle->state_mutex);
if ((handle->state & STCONTROLLINGTTY)) {
pthread_mutex_unlock(&handle->state_mutex);
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
return -1;
}
if (brlapi__getDisplaySize(handle, &handle->brlx, &handle->brly)<0){
pthread_mutex_unlock(&handle->state_mutex);
return -1;
}
/* Clear key buffer before taking the tty, just in case... */
pthread_mutex_lock(&handle->read_mutex);
handle->keybuf_next = handle->keybuf_nb = 0;
pthread_mutex_unlock(&handle->read_mutex);
/* OK, Now we know where we are, so get the effective control of the terminal! */
ttytreepath = getenv("WINDOWPATH");
if (!ttytreepath && (getenv("DISPLAY") || getenv("WAYLAND_DISPLAY"))) {
/* Cope with some DMs which are not setting WINDOWPATH (e.g. gdm 3.12) */
ttytreepath = getenv("XDG_VTNR");
#ifdef HAVE_SD_SESSION_GET_VT
if (!ttytreepath) {
/* Fallback to asking logind */
char *id;
unsigned vtnr = 0;
int ret;
ret = sd_pid_get_session(0, &id);
if (ret == 0 && id != NULL) {
sd_session_get_vt(id, &vtnr);
free(id);
} else {
char **sessions;
/* Not even logind knows :/ we are probably logged in from gdm */
int nsessions = sd_uid_get_sessions(getuid(), 0, &sessions);
if (nsessions > 0) {
int i, chosen = -1;
for (i = 0; i < nsessions; i++) {
char *type;
ret = sd_session_get_type(sessions[i], &type);
if (ret == 0) {
if (strcmp(type, "tty") != 0 &&
strcmp(type, "unspecified") != 0) {
/* x11, wayland or mir, so graphical.
* User normally has only one of them */
if (chosen >= 0) {
/* Oops, several sessions? That is not supposed to happen, better
* choose none of them. */
chosen = -1;
break;
}
chosen = i;
}
}
}
if (chosen >= 0) sd_session_get_vt(sessions[i], &vtnr);
for (i = 0; i < nsessions; i++) free(sessions[i]);
free(sessions);
}
}
if (vtnr) {
size_t size = sizeof(vtnr)*3 + 1;
ttytreepath=alloca(size);
snprintf(ttytreepath, size, "%u", vtnr);
}
}
#endif /* HAVE_SD_SESSION_GET_VT */
}
nttytreepath = 0;
if (ttytreepath) {
/* Count ttys from path */
for(cur = ttytreepath; *cur; nttytreepath++, cur = next+1) {
strtol(cur,&next,0);
if (next==cur) {
syslog(LOG_WARNING, "Erroneous window path %s", ttytreepath);
pthread_mutex_unlock(&handle->state_mutex);
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
if (*next==0) { nttytreepath++; break; }
}
}
size = (1 + nttytreepath + nttys) * sizeof(uint32_t);
size += 1 + (driverName ? strlen(driverName) : 0);
packet = alloca(size);
nbTtys = packet;
t = nbTtys+1;
*nbTtys = htonl(nttytreepath + nttys);
if (ttytreepath) {
/* First add ttys from environment path */
for(cur=ttytreepath; *cur; nttytreepath++, cur=next+1) {
ttypath=strtol(cur,&next,0);
*t++ = htonl(ttypath);
if (*next==0) break;
}
}
/* Then add ttys from caller path */
for (n=0; n<nttys; n++) *t++ = htonl(ttys[n]);
p = (unsigned char *) t;
if (driverName==NULL) n = 0; else n = strlen(driverName);
if (n>BRLAPI_MAXNAMELENGTH) {
pthread_mutex_unlock(&handle->state_mutex);
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
*p = n;
p++;
if (n) p = mempcpy(p, driverName, n);
assert(p-(unsigned char *)packet == size);
if ((res=brlapi__writePacketWaitForAck(handle,BRLAPI_PACKET_ENTERTTYMODE,packet,size)) == 0) {
handle->state |= STCONTROLLINGTTY;
}
pthread_mutex_unlock(&handle->state_mutex);
/* Determine default charset if application did not call setlocale. */
#ifdef LC_GLOBAL_LOCALE
const char *locale = setlocale(LC_CTYPE, NULL);
if (!locale || !strcmp(locale, "C")) {
/* Application did not call setlocale, try to load the current locale. */
locale_t default_locale = newlocale(LC_CTYPE_MASK, "", 0);
if (default_locale) handle->default_locale = default_locale;
}
#endif /* LC_GLOBAL_LOCALE */
return res;
}
int BRLAPI_STDCALL brlapi_enterTtyModeWithPath(const int *ttys, int nttys, const char *how)
{
return brlapi__enterTtyModeWithPath(&defaultHandle, ttys, nttys, how);
}
/* Function : brlapi_leaveTtyMode */
/* Gives back control of our tty to brltty */
int BRLAPI_STDCALL brlapi__leaveTtyMode(brlapi_handle_t *handle)
{
int res;
pthread_mutex_lock(&handle->state_mutex);
if (!(handle->state & STCONTROLLINGTTY)) {
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
res = -1;
goto out;
}
handle->brlx = 0; handle->brly = 0;
res = brlapi__writePacketWaitForAck(handle,BRLAPI_PACKET_LEAVETTYMODE,NULL,0);
handle->state &= ~STCONTROLLINGTTY;
out:
pthread_mutex_unlock(&handle->state_mutex);
return res;
}
int BRLAPI_STDCALL brlapi_leaveTtyMode(void)
{
return brlapi__leaveTtyMode(&defaultHandle);
}
/* Function : brlapi_setFocus */
/* sends the current focus to brltty */
int BRLAPI_STDCALL brlapi__setFocus(brlapi_handle_t *handle, int tty)
{
uint32_t utty;
int res;
utty = htonl(tty);
pthread_mutex_lock(&handle->fileDescriptor_mutex);
res = brlapi_writePacket(handle->fileDescriptor, BRLAPI_PACKET_SETFOCUS, &utty, sizeof(utty));
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
return res;
}
int BRLAPI_STDCALL brlapi_setFocus(int tty)
{
return brlapi__setFocus(&defaultHandle, tty);
}
static size_t getCharset(brlapi_handle_t *handle, void *buffer, int wide) {
char *p = buffer;
const char *start = p;
const char *locale = setlocale(LC_CTYPE, NULL);
if (wide
#ifdef __MINGW32__
&& CHECKPROC("ntdll.dll", wcslen)
#endif /* __MINGW32__ */
) {
size_t length = strlen(WCHAR_CHARSET);
*p++ = length;
p = mempcpy(p, WCHAR_CHARSET, length);
} else if ((locale && strcmp(locale, "C"))
#ifdef LC_GLOBAL_LOCALE
|| (handle->default_locale != LC_GLOBAL_LOCALE)
#endif /* LC_GLOBAL_LOCALE */
) {
/* not default locale, tell charset to server */
#ifdef WINDOWS
UINT CP = GetACP();
if (!CP) CP = GetOEMCP();
if (CP) {
size_t length = sprintf(p+1, "CP%u", CP);
*p++ = length;
p += length;
}
#elif defined(HAVE_NL_LANGINFO)
char *language = nl_langinfo(CODESET);
size_t length = strlen(language);
*p++ = length;
p = mempcpy(p, language, length);
#endif /* get non-wide character set */
}
return p-start;
}
/* Function : brlapi_writeText */
/* Writes a string to the braille display */
static int brlapi___writeText(brlapi_handle_t *handle, int cursor, const void *str, int wide)
{
int dispSize = handle->brlx * handle->brly;
brlapi_packet_t packet;
brlapi_writeArgumentsPacket_t *wa = &packet.writeArguments;
unsigned char *p = &wa->data;
int res;
size_t len;
#ifdef LC_GLOBAL_LOCALE
locale_t old_locale = 0;
if (handle->default_locale != LC_GLOBAL_LOCALE) {
/* Temporarily load the default locale. */
old_locale = uselocale(handle->default_locale);
}
#else /* LC_GLOBAL_LOCALE */
setlocale(LC_CTYPE,NULL);
#endif /* LC_GLOBAL_LOCALE */
wa->flags = BRLAPI_WF_REGION;
*((uint32_t *) p) = htonl(1); p += sizeof(uint32_t);
*((uint32_t *) p) = htonl(-dispSize); p += sizeof(uint32_t);
if (str) {
wa->flags |= BRLAPI_WF_TEXT;
#if defined(__MINGW32__)
if (CHECKGETPROC("ntdll.dll", wcslen) && wide)
len = sizeof(wchar_t) * wcslenProc(str);
#else /* __MINGW32__ */
if (wide)
len = sizeof(wchar_t) * wcslen(str);
#endif /* __MINGW32__ */
else
len = strlen(str);
*((uint32_t *) p) = htonl(len); p += sizeof(uint32_t);
memcpy(p, str, len);
p += len;
}
if (cursor!=BRLAPI_CURSOR_LEAVE) {
wa->flags |= BRLAPI_WF_CURSOR;
*((uint32_t *) p) = htonl(cursor);
p += sizeof(uint32_t);
}
if ((len = getCharset(handle, p , wide))) {
wa->flags |= BRLAPI_WF_CHARSET;
p += len;
}
wa->flags = htonl(wa->flags);
pthread_mutex_lock(&handle->fileDescriptor_mutex);
res = brlapi_writePacket(handle->fileDescriptor,BRLAPI_PACKET_WRITE,&packet,sizeof(wa->flags)+(p-&wa->data));
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
#ifdef LC_GLOBAL_LOCALE
if (handle->default_locale != LC_GLOBAL_LOCALE) {
/* Restore application locale */
uselocale(old_locale);
}
#endif /* LC_GLOBAL_LOCALE */
return res;
}
#ifdef WINDOWS
int BRLAPI_STDCALL brlapi__writeTextWin(brlapi_handle_t *handle, int cursor, const void *str, int wide)
{
return brlapi___writeText(handle, cursor, str, wide);
}
int BRLAPI_STDCALL brlapi_writeTextWin(int cursor, const void *str, int wide)
{
return brlapi___writeText(&defaultHandle, cursor, str, wide);
}
#else /* WINDOWS */
int BRLAPI_STDCALL brlapi__writeText(brlapi_handle_t *handle, int cursor, const char *str)
{
return brlapi___writeText(handle, cursor, str, 0);
}
int BRLAPI_STDCALL brlapi__writeWText(brlapi_handle_t *handle, int cursor, const wchar_t *str)
{
return brlapi___writeText(handle, cursor, str, 1);
}
int BRLAPI_STDCALL brlapi_writeText(int cursor, const char *str)
{
return brlapi__writeText(&defaultHandle, cursor, str);
}
int BRLAPI_STDCALL brlapi_writeWText(int cursor, const wchar_t *str)
{
return brlapi__writeWText(&defaultHandle, cursor, str);
}
#endif /* WINDOWS */
/* Function : brlapi_writeDots */
/* Writes dot-matrix to the braille display */
int BRLAPI_STDCALL brlapi__writeDots(brlapi_handle_t *handle, const unsigned char *dots)
{
brlapi_writeArguments_t wa = BRLAPI_WRITEARGUMENTS_INITIALIZER;
unsigned int size = handle->brlx * handle->brly;
if (size == 0) {
brlapi_errno=BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
unsigned char andMask[size];
memset(andMask, 0, size);
wa.andMask = andMask;
unsigned char orMask[size];
memcpy(orMask, dots, size);
wa.orMask = orMask;
char text[(size * 3) + 1];
wa.text = text;
wa.charset = "utf-8";
{
/* Pass a UTF8-encoded string of the braille characters as the text.
*
* The Unicode row for the braille pattern characters is U+2800.
* Each of the eight dots is represented by a bit in the low-order byte:
* Dot1 by 0X01, Dot2 by 0X02, ..., Dot7 by 0X40, and Dot8 by 0X80.
*
* The UTF-8 template for the Unicode braille row is: 0XE2, 0XA0, 0X80.
* Dots 1-6 are the low-order six bits of the last (0X80) byte.
* Dots 7-8 are the low-order two bits of the middle (0XA0) byte.
*/
char *byte = text;
const unsigned char *cell = dots;
const unsigned char *end = cell + size;
while (cell < end) {
*byte++ = 0XE2;
*byte++ = 0XA0 | ((*cell >> 6) & 0X3); // dots 7-8
*byte++ = 0X80 | (*cell & 0X3F); // dots 1-6
cell += 1;
}
*byte = 0;
wa.textSize = byte - text;
}
wa.regionBegin = 1;
wa.regionSize = -size;
wa.cursor = BRLAPI_CURSOR_OFF;
return brlapi__write(handle, &wa);
}
int BRLAPI_STDCALL brlapi_writeDots(const unsigned char *dots)
{
return brlapi__writeDots(&defaultHandle, dots);
}
/* Function : brlapi_write */
/* Extended writes on braille displays */
#ifdef WINDOWS
int BRLAPI_STDCALL brlapi__writeWin(brlapi_handle_t *handle, const brlapi_writeArguments_t *s, int wide)
#else /* WINDOWS */
int brlapi__write(brlapi_handle_t *handle, const brlapi_writeArguments_t *s)
#endif /* WINDOWS */
{
int dispSize = handle->brlx * handle->brly;
unsigned int rbeg, strLen;
int rsiz;
brlapi_packet_t packet;
brlapi_writeArgumentsPacket_t *wa = &packet.writeArguments;
unsigned char *p = &wa->data;
unsigned char *end = (unsigned char*) &packet.data[sizeof(packet)];
int res;
#ifndef WINDOWS
int wide = 0;
#endif /* WINDOWS */
wa->flags = 0;
if (s==NULL) goto send;
rbeg = s->regionBegin;
rsiz = s->regionSize;
if (rbeg || rsiz) {
if (rsiz == 0) return 0;
wa->flags |= BRLAPI_WF_REGION;
*((uint32_t *) p) = htonl(rbeg); p += sizeof(uint32_t);
*((uint32_t *) p) = htonl((int32_t) rsiz); p += sizeof(uint32_t);
} else {
/* DEPRECATED */
rsiz = -dispSize;
}
if (rsiz < 0)
rsiz = -rsiz;
if (s->text) {
if (s->textSize != -1)
strLen = s->textSize;
else
#if defined(__CYGWIN__)
if (wide)
strLen = sizeof(wchar_t) * wcslen((wchar_t *) s->text);
else
#elif defined(__MINGW32__)
if (CHECKGETPROC("ntdll.dll", wcslen) && wide)
strLen = sizeof(wchar_t) * wcslenProc((wchar_t *) s->text);
else
#endif /* windows wide string length */
strLen = strlen(s->text);
*((uint32_t *) p) = htonl(strLen); p += sizeof(uint32_t);
wa->flags |= BRLAPI_WF_TEXT;
if (p + strLen > end) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
memcpy(p, s->text, strLen);
p += strLen;
}
if (s->andMask) {
wa->flags |= BRLAPI_WF_ATTR_AND;
if (p + rsiz > end) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
memcpy(p, s->andMask, rsiz);
p += rsiz;
}
if (s->orMask) {
wa->flags |= BRLAPI_WF_ATTR_OR;
if (p + rsiz > end) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
memcpy(p, s->orMask, rsiz);
p += rsiz;
}
if ((s->cursor>=0) && (s->cursor<=dispSize)) {
wa->flags |= BRLAPI_WF_CURSOR;
if (p + sizeof(uint32_t) > end) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
*((uint32_t *) p) = htonl(s->cursor);
p += sizeof(uint32_t);
} else if (s->cursor != BRLAPI_CURSOR_LEAVE) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
if (s->charset) {
if (!*s->charset) {
#ifdef LC_GLOBAL_LOCALE
locale_t old_locale = 0;
if (handle->default_locale != LC_GLOBAL_LOCALE) {
/* Temporarily load the default locale. */
old_locale = uselocale(handle->default_locale);
}
#endif /* LC_GLOBAL_LOCALE */
if ((strLen = getCharset(handle, p, wide))) {
wa->flags |= BRLAPI_WF_CHARSET;
p += strLen;
}
#ifdef LC_GLOBAL_LOCALE
if (handle->default_locale != LC_GLOBAL_LOCALE) {
/* Restore application locale */
uselocale(old_locale);
}
#endif /* LC_GLOBAL_LOCALE */
} else {
strLen = strlen(s->charset);
*p++ = strLen;
wa->flags |= BRLAPI_WF_CHARSET;
if (p + strLen > end) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
memcpy(p, s->charset, strLen);
p += strLen;
}
}
send:
wa->flags = htonl(wa->flags);
pthread_mutex_lock(&handle->fileDescriptor_mutex);
res = brlapi_writePacket(handle->fileDescriptor,BRLAPI_PACKET_WRITE,&packet,sizeof(wa->flags)+(p-&wa->data));
pthread_mutex_unlock(&handle->fileDescriptor_mutex);
return res;
}
#ifdef WINDOWS
int BRLAPI_STDCALL brlapi_writeWin(const brlapi_writeArguments_t *s, int wide)
{
return brlapi__writeWin(&defaultHandle, s, wide);
}
#else /* WINDOWS */
int brlapi_write(const brlapi_writeArguments_t *s)
{
return brlapi__write(&defaultHandle, s);
}
#endif /* WINDOWS */
/* Function : brlapi_readKey */
/* Reads a key from the braille keyboard */
int BRLAPI_STDCALL brlapi__readKeyWithTimeout(brlapi_handle_t *handle, int timeout_ms, brlapi_keyCode_t *code)
{
ssize_t res;
uint32_t buf[2];
pthread_mutex_lock(&handle->state_mutex);
if (!(handle->state & STCONTROLLINGTTY)) {
pthread_mutex_unlock(&handle->state_mutex);
brlapi_errno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION;
return -1;
}
pthread_mutex_unlock(&handle->state_mutex);
pthread_mutex_lock(&handle->read_mutex);
if (handle->keybuf_nb>0) {
*code=handle->keybuf[handle->keybuf_next];
handle->keybuf_next=(handle->keybuf_next+1)%BRL_KEYBUF_SIZE;
handle->keybuf_nb--;
pthread_mutex_unlock(&handle->read_mutex);
return 1;
}
pthread_mutex_unlock(&handle->read_mutex);
pthread_mutex_lock(&handle->key_mutex);
res = brlapi__waitForPacket(handle,BRLAPI_PACKET_KEY, buf, sizeof(buf), TRY_WAIT_FOR_EXPECTED_PACKET, timeout_ms);
pthread_mutex_unlock(&handle->key_mutex);
if (res == -3) {
if (timeout_ms == 0) return 0;
brlapi_libcerrno = EINTR;
brlapi_errno = BRLAPI_ERROR_LIBCERR;
brlapi_errfun = "waitForPacket";
return -1;
}
if (res == -4) {
/* Timeout */
return 0;
}
if (res < 0) return -1;
*code = brlapi_packetToKeyCode(buf);
return 1;
}
int BRLAPI_STDCALL brlapi_readKeyWithTimeout(int timeout_ms, brlapi_keyCode_t *code)
{
return brlapi__readKeyWithTimeout(&defaultHandle, timeout_ms, code);
}
int BRLAPI_STDCALL brlapi__readKey(brlapi_handle_t *handle, int block, brlapi_keyCode_t *code)
{
return brlapi__readKeyWithTimeout(handle, block ? -1 : 0, code);
}
int BRLAPI_STDCALL brlapi_readKey(int block, brlapi_keyCode_t *code)
{
return brlapi__readKeyWithTimeout(&defaultHandle, block ? -1 : 0, code);
}
typedef struct {
brlapi_keyCode_t code;
const char *name;
} brlapi_keyEntry_t;
static const brlapi_keyEntry_t brlapi_keyTable[] = {
#include "brlapi_keytab.auto.h"
{.code=0X0000U, .name="LATIN1"},
{.code=0X0100U, .name="LATIN2"},
{.code=0X0200U, .name="LATIN3"},
{.code=0X0300U, .name="LATIN4"},
{.code=0X0400U, .name="KATAKANA"},
{.code=0X0500U, .name="ARABIC"},
{.code=0X0600U, .name="CYRILLIC"},
{.code=0X0700U, .name="GREEK"},
{.code=0X0800U, .name="TECHNICAL"},
{.code=0X0900U, .name="SPECIAL"},
{.code=0X0A00U, .name="PUBLISHING"},
{.code=0X0B00U, .name="APL"},
{.code=0X0C00U, .name="HEBREW"},
{.code=0X0D00U, .name="THAI"},
{.code=0X0E00U, .name="KOREAN"},
{.code=0X1200U, .name="LATIN8"},
{.code=0X1300U, .name="LATIN9"},
{.code=0X1400U, .name="ARMENIAN"},
{.code=0X1500U, .name="GEORGIAN"},
{.code=0X1600U, .name="CAUCASUS"},
{.code=0X1E00U, .name="VIETNAMESE"},
{.code=0X2000U, .name="CURRENCY"},
{.code=0XFD00U, .name="3270"},
{.code=0XFE00U, .name="XKB"},
{.code=0XFF00U, .name="MISCELLANY"},
{.code=0X01000000U, .name="UNICODE"},
{.name=NULL}
};
int BRLAPI_STDCALL
brlapi_expandKeyCode (brlapi_keyCode_t keyCode, brlapi_expandedKeyCode_t *ekc) {
int argumentWidth = brlapi_getArgumentWidth(keyCode);
if (argumentWidth != -1) {
unsigned int argumentMask = (1 << argumentWidth) - 1;
brlapi_keyCode_t type = keyCode & BRLAPI_KEY_TYPE_MASK;
brlapi_keyCode_t code = keyCode & BRLAPI_KEY_CODE_MASK;
ekc->type = type;
ekc->command = (code & ~argumentMask);
ekc->argument = code & argumentMask;
ekc->flags = (keyCode & BRLAPI_KEY_FLAGS_MASK) >> BRLAPI_KEY_FLAGS_SHIFT;
return 0;
}
return -1;
}
int BRLAPI_STDCALL
brlapi_describeKeyCode (brlapi_keyCode_t keyCode, brlapi_describedKeyCode_t *dkc) {
brlapi_expandedKeyCode_t ekc;
int result = brlapi_expandKeyCode(keyCode, &ekc);
if (result != -1) {
unsigned int argument = ekc.argument;
unsigned int codeWithoutArgument = ekc.type | ekc.command;
unsigned int codeWithArgument = codeWithoutArgument | argument;
const brlapi_keyEntry_t *keyWithoutArgument = NULL;
const brlapi_keyEntry_t *key = brlapi_keyTable;
while (key->name) {
if (codeWithArgument == key->code) {
argument = 0;
goto found;
}
if (codeWithoutArgument == key->code)
if (!keyWithoutArgument)
keyWithoutArgument = key;
++key;
}
if (keyWithoutArgument) {
key = keyWithoutArgument;
goto found;
}
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
result = -1;
goto done;
found:
dkc->command = key->name;
dkc->argument = argument;
dkc->values = ekc;
switch (ekc.type) {
case BRLAPI_KEY_TYPE_SYM: dkc->type = "SYM"; break;
case BRLAPI_KEY_TYPE_CMD: dkc->type = "CMD"; break;
default: dkc->type = "UNKNOWN"; break;
}
#define FLAG(name) if (keyCode & BRLAPI_KEY_FLG_##name) dkc->flag[dkc->flags++] = #name
dkc->flags = 0;
FLAG(SHIFT);
FLAG(UPPER);
FLAG(CONTROL);
FLAG(META);
FLAG(ALTGR);
FLAG(GUI);
switch (ekc.type) {
case BRLAPI_KEY_TYPE_CMD:
switch (ekc.command & BRLAPI_KEY_CMD_BLK_MASK) {
case BRLAPI_KEY_CMD_PASSDOTS:
break;
case BRLAPI_KEY_CMD_PASSXT:
case BRLAPI_KEY_CMD_PASSAT:
case BRLAPI_KEY_CMD_PASSPS2:
FLAG(KBD_RELEASE);
FLAG(KBD_EMUL0);
FLAG(KBD_EMUL1);
break;
default:
FLAG(TOGGLE_ON);
FLAG(TOGGLE_OFF);
FLAG(MOTION_ROUTE);
FLAG(MOTION_SCALED);
FLAG(MOTION_TOLEFT);
break;
}
break;
}
#undef FLAG
}
done:
return result;
}
/* Function : ignore_accept_key_range */
/* Common tasks for ignoring and unignoring key ranges */
/* what = 0 for ignoring !0 for unignoring */
static int ignore_accept_key_ranges(brlapi_handle_t *handle, int what, const brlapi_range_t ranges[], unsigned int n)
{
uint32_t ints[n][4];
unsigned int i;
for (i=0; i<n; i++) {
ints[i][0] = htonl(ranges[i].first >> 32);
ints[i][1] = htonl(ranges[i].first & 0xffffffff);
ints[i][2] = htonl(ranges[i].last >> 32);
ints[i][3] = htonl(ranges[i].last & 0xffffffff);
};
if (brlapi__writePacketWaitForAck(handle,(what ? BRLAPI_PACKET_ACCEPTKEYRANGES : BRLAPI_PACKET_IGNOREKEYRANGES),ints,n*2*sizeof(brlapi_keyCode_t)))
return -1;
return 0;
}
/* Function : ignore_accept_keys */
/* Common tasks for ignoring and unignoring keys */
/* what = 0 for ignoring !0 for unignoring */
static int ignore_accept_keys(brlapi_handle_t *handle, int what, brlapi_rangeType_t r, const brlapi_keyCode_t *code, unsigned int n)
{
if (!n) {
if (r != brlapi_rangeType_all) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
brlapi_range_t range = { .first = 0, .last = BRLAPI_KEY_MAX };
return ignore_accept_key_ranges(handle, what, &range, 1);
} else {
brlapi_range_t ranges[n];
unsigned int i;
brlapi_keyCode_t mask;
for (i=0; i<n; i++) {
if (brlapi_getKeyrangeMask(r, code[i], &mask))
return -1;
if (code[i] & mask) {
brlapi_errno = BRLAPI_ERROR_INVALID_PARAMETER;
return -1;
}
ranges[i].first = code[i];
ranges[i].last = code[i] | mask;
}
return ignore_accept_key_ranges(handle, what, ranges, n);
}
}
/* Function : brlapi_acceptKeyRanges */
int BRLAPI_STDCALL brlapi__acceptKeyRanges(brlapi_handle_t *handle, const brlapi_range_t ranges[], unsigned int n)
{
return ignore_accept_key_ranges(handle, !0, ranges, n);
}
int BRLAPI_STDCALL brlapi_acceptKeyRanges(const brlapi_range_t ranges[], unsigned int n)
{
return brlapi__acceptKeyRanges(&defaultHandle, ranges, n);
}
/* Function : brlapi_acceptKeys */
int BRLAPI_STDCALL brlapi__acceptKeys(brlapi_handle_t *handle, brlapi_rangeType_t r, const brlapi_keyCode_t *code, unsigned int n)
{
return ignore_accept_keys(handle, !0, r, code, n);
}
int BRLAPI_STDCALL brlapi_acceptKeys(brlapi_rangeType_t r, const brlapi_keyCode_t *code, unsigned int n)
{
return brlapi__acceptKeys(&defaultHandle, r, code, n);
}
/* Function : brlapi_ignoreKeyRanges */
int BRLAPI_STDCALL brlapi__ignoreKeyRanges(brlapi_handle_t *handle, const brlapi_range_t ranges[], unsigned int n)
{
return ignore_accept_key_ranges(handle, 0, ranges, n);
}
int BRLAPI_STDCALL brlapi_ignoreKeyRanges(const brlapi_range_t ranges[], unsigned int n)
{
return brlapi__ignoreKeyRanges(&defaultHandle, ranges, n);
}
/* Function : brlapi_ignoreKeys */
int BRLAPI_STDCALL brlapi__ignoreKeys(brlapi_handle_t *handle, brlapi_rangeType_t r, const brlapi_keyCode_t *code, unsigned int n)
{
return ignore_accept_keys(handle, 0, r, code, n);
}
int BRLAPI_STDCALL brlapi_ignoreKeys(brlapi_rangeType_t r, const brlapi_keyCode_t *code, unsigned int n)
{
return brlapi__ignoreKeys(&defaultHandle, r, code, n);
}
/* Error code handling */
/* brlapi_errlist: error messages */
const char *brlapi_errlist[] = {
[0] = "Success",
[BRLAPI_ERROR_NOMEM] = "Insufficient memory",
[BRLAPI_ERROR_TTYBUSY] = "Tty is busy",
[BRLAPI_ERROR_DEVICEBUSY] = "Device is busy",
[BRLAPI_ERROR_UNKNOWN_INSTRUCTION] = "Unknown instruction",
[BRLAPI_ERROR_ILLEGAL_INSTRUCTION] = "Illegal instruction",
[BRLAPI_ERROR_INVALID_PARAMETER] = "Invalid parameter",
[BRLAPI_ERROR_INVALID_PACKET] = "Invalid packet",
[BRLAPI_ERROR_CONNREFUSED] = "Connection refused",
[BRLAPI_ERROR_OPNOTSUPP] = "Operation not supported",
[BRLAPI_ERROR_GAIERR] = "getaddrinfo() error",
[BRLAPI_ERROR_LIBCERR] = "libc error",
[BRLAPI_ERROR_UNKNOWNTTY] = "Can't determine tty number",
[BRLAPI_ERROR_PROTOCOL_VERSION] = "Bad protocol version",
[BRLAPI_ERROR_EOF] = "Unexpected end of file",
[BRLAPI_ERROR_EMPTYKEY] = "Key file is empty",
[BRLAPI_ERROR_DRIVERERROR] = "Driver error",
[BRLAPI_ERROR_AUTHENTICATION] = "Authentication failed",
[BRLAPI_ERROR_READONLY_PARAMETER] = "Parameter can not be changed",
};
/* brlapi_nerr: number of error codes */
const int brlapi_nerr = (sizeof(brlapi_errlist)/sizeof(char*));
/* _brlapi_strerror_cpy: store constant-string error message */
static size_t _brlapi_strerror_cpy(char *buf, size_t buflen, const char *msg)
{
size_t totsize = strlen(msg);
if (buflen == 0)
return totsize;
if (buflen > totsize+1) {
buflen = totsize+1;
}
memcpy(buf, msg, buflen-1);
buf[buflen-1] = '\0';
return totsize;
}
/* _brlapi_strerror_snprintf: store formatted error message */
static size_t _brlapi_strerror_snprintf(char *buf, size_t buflen, const char *fmt, ...)
{
va_list va;
int totsize;
va_start(va, fmt);
totsize = vsnprintf(buf, buflen, fmt, va);
va_end(va);
return totsize;
}
/* brlapi_strerror_r: store error message */
size_t BRLAPI_STDCALL brlapi_strerror_r(const brlapi_error_t *error, char *buf, size_t buflen)
{
if (error->brlerrno>=brlapi_nerr)
return _brlapi_strerror_cpy(buf, buflen, "Unknown error");
else if (error->brlerrno==BRLAPI_ERROR_GAIERR) {
#if defined(EAI_SYSTEM)
if (error->gaierrno == EAI_SYSTEM)
return _brlapi_strerror_snprintf(buf,buflen,"resolve: %s", strerror(error->libcerrno));
else
#endif /* EAI_SYSTEM */
return _brlapi_strerror_snprintf(buf,buflen,"resolve: "
#if defined(HAVE_GETADDRINFO)
#if defined(HAVE_GAI_STRERROR)
"%s", gai_strerror(error->gaierrno)
#else
"%d", error->gaierrno
#endif
#elif defined(HAVE_HSTRERROR) && !defined(__MINGW32__)
"%s", hstrerror(error->gaierrno)
#else
"%x", error->gaierrno
#endif
);
}
else if (error->brlerrno==BRLAPI_ERROR_LIBCERR) {
return _brlapi_strerror_snprintf(buf,buflen,"%s: %s", error->errfun?error->errfun:"(null)", strerror(error->libcerrno));
} else
return _brlapi_strerror_cpy(buf, buflen, brlapi_errlist[error->brlerrno]);
}
/* brlapi_strerror: return error message */
const char * BRLAPI_STDCALL brlapi_strerror(const brlapi_error_t *error)
{
static char errmsg[128];
brlapi_strerror_r(error, errmsg, sizeof(errmsg));
return errmsg;
}
/* brlapi_perror: error message printing */
void BRLAPI_STDCALL brlapi_perror(const char *s)
{
fprintf(stderr,"%s: %s\n",s,brlapi_strerror(&brlapi_error));
}
/* XXX functions mustn't use brlapi_errno after this since it #undefs it XXX */
#ifdef brlapi_error
#undef brlapi_error
#endif /* brlapi_error */
brlapi_error_t brlapi_error;
static int pthread_error_ok;
/* we need a per-thread errno variable, thanks to pthread_keys */
static pthread_key_t error_key;
/* the key must be created at most once */
static pthread_once_t error_key_once = PTHREAD_ONCE_INIT;
static void error_key_free(void *key)
{
free(key);
}
static void error_key_alloc(void)
{
pthread_error_ok=!pthread_key_create(&error_key, error_key_free);
}
/* how to get per-thread errno variable. This will be called by the macro
* brlapi_errno */
brlapi_error_t * BRLAPI_STDCALL brlapi_error_location(void)
{
brlapi_error_t *errorp;
#ifndef WINDOWS
if (pthread_once && pthread_key_create) {
#endif /* WINDOWS */
pthread_once(&error_key_once, error_key_alloc);
if (pthread_error_ok) {
if ((errorp=(brlapi_error_t *) pthread_getspecific(error_key)))
/* normal case */
return errorp;
else
/* on the first time, must allocate it */
if ((errorp=malloc(sizeof(*errorp))) && !pthread_setspecific(error_key,errorp)) {
memset(errorp,0,sizeof(*errorp));
return errorp;
}
}
#ifndef WINDOWS
}
#endif /* WINDOWS */
/* fall-back: shared errno :/ */
return &brlapi_error;
}
brlapi__exceptionHandler_t BRLAPI_STDCALL brlapi__setExceptionHandler(brlapi_handle_t *handle, brlapi__exceptionHandler_t new)
{
brlapi__exceptionHandler_t tmp;
pthread_mutex_lock(&handle->exceptionHandler_mutex);
tmp = handle->exceptionHandler.withHandle;
if (new!=NULL) handle->exceptionHandler.withHandle = new;
pthread_mutex_unlock(&handle->exceptionHandler_mutex);
return tmp;
}
brlapi_exceptionHandler_t BRLAPI_STDCALL brlapi_setExceptionHandler(brlapi_exceptionHandler_t new)
{
brlapi_exceptionHandler_t tmp;
pthread_mutex_lock(&defaultHandle.exceptionHandler_mutex);
tmp = defaultHandle.exceptionHandler.withoutHandle;
if (new!=NULL) defaultHandle.exceptionHandler.withoutHandle = new;
pthread_mutex_unlock(&defaultHandle.exceptionHandler_mutex);
return tmp;
}
int BRLAPI_STDCALL brlapi__strexception(brlapi_handle_t *handle, char *buf, size_t n, int err, brlapi_packetType_t type, const void *packet, size_t size)
{
int chars = 128; /* Number of bytes to dump */
char hexString[3*chars+1];
int i, nbChars = MIN(chars, size);
char *p = hexString;
brlapi_error_t error = { .brlerrno = err };
for (i=0; i<nbChars; i++)
p += sprintf(p, "%02x ", ((unsigned char *) packet)[i]);
p--; /* Don't keep last space */
*p = '\0';
return snprintf(buf, n, "%s on %s request of size %d (%s)",
brlapi_strerror(&error), brlapi_getPacketTypeName(type), (int)size, hexString);
}
int BRLAPI_STDCALL brlapi_strexception(char *buf, size_t n, int err, brlapi_packetType_t type, const void *packet, size_t size)
{
return brlapi__strexception(&defaultHandle, buf, n, err, type, packet, size);
}
void BRLAPI_STDCALL brlapi__defaultExceptionHandler(brlapi_handle_t *handle, int err, brlapi_packetType_t type, const void *packet, size_t size)
{
char str[0X100];
brlapi_strexception(str,0X100, err, type, packet, size);
fprintf(stderr, "BrlAPI exception: %s\nYou may wish to add the -ldebug option to the brltty command line in order to get additional information in the system log\n", str);
fprintf(stderr, "Crashing the client now. You may want to use brlapi_setExceptionHandler to define your own exception handling.\n");
abort();
}
void BRLAPI_STDCALL brlapi_defaultExceptionHandler(int err, brlapi_packetType_t type, const void *packet, size_t size)
{
brlapi__defaultExceptionHandler(&defaultHandle, err, type, packet, size);
}