blob: 4958025617e7e057c1928a90708d1b4e5a245774 [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY 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_server.c : Main file for BrlApi server */
#define SERVER_SOCKET_LIMIT 4
#define SERVER_SELECT_TIMEOUT 1
#define UNAUTH_LIMIT 5
#define UNAUTH_TIMEOUT 30
#define RELEASE "BrlAPI Server: release " BRLAPI_RELEASE
#define COPYRIGHT " Copyright (C) 2002-2023 by Sébastien Hinderer <Sebastien.Hinderer@ens-lyon.org>, \
Samuel Thibault <samuel.thibault@ens-lyon.org>"
#include "prologue.h"
#include <stdio.h>
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <locale.h>
#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif /* HAVE_ICONV_H */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif /* HAVE_ALLOCA_H */
#ifdef __MINGW32__
#include "system_windows.h"
#include "win_pthread.h"
#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>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#else /* HAVE_SYS_SELECT_H */
#include <sys/time.h>
#endif /* HAVE_SYS_SELECT_H */
#endif /* __MINGW32__ */
#define BRLAPI_NO_DEPRECATED
#include "brlapi.h"
#include "brlapi_protocol.h"
#include "brlapi_keyranges.h"
#include "cmd_brlapi.h"
#include "brl_cmds.h"
#include "brl_utils.h"
#include "embed.h"
#include "clipboard.h"
#include "ttb.h"
#include "core.h"
#include "api_server.h"
#include "report.h"
#include "log.h"
#include "addresses.h"
#include "prefs.h"
#include "file.h"
#include "parse.h"
#include "timing.h"
#include "auth.h"
#include "io_generic.h"
#include "io_misc.h"
#include "scr.h"
#include "charset.h"
#include "async_signal.h"
#include "thread.h"
#include "blink.h"
#ifdef __MINGW32__
#define LogSocketError(msg) logWindowsSocketError(msg)
#else /* __MINGW32__ */
#define LogSocketError(msg) logSystemError(msg)
#endif /* __MINGW32__ */
#ifdef __CYGWIN__
#undef PF_LOCAL
#endif /* __CYGWIN__ */
typedef enum {
PARM_AUTH,
PARM_HOST,
#ifdef ENABLE_API_FUZZING
PARM_FUZZ,
PARM_FUZZSEED,
PARM_FUZZHEAD,
PARM_FUZZWRITE,
PARM_FUZZWRITEUTF8,
PARM_CRASH,
#endif
} Parameters;
const char *const api_serverParameters[] = {
"auth", "host",
#ifdef ENABLE_API_FUZZING
"fuzz", "fuzzseed", "fuzzhead", "fuzzwrite", "fuzzwriteutf8", "crash",
#endif /* ENABLE_API_FUZZING */
NULL
};
#ifdef ENABLE_API_FUZZING
static int fuzz_runs;
static int fuzz_seed;
static unsigned int fuzz_head;
static unsigned int fuzz_write;
static unsigned int fuzz_writeutf8;
#endif /* ENABLE_API_FUZZING */
#define WERR(x, y, ...) do { \
logMessage(LOG_ERR, "writing error %d to %"PRIfd, y, x); \
logMessage(LOG_ERR, __VA_ARGS__); \
writeError(x, y); \
} while(0)
#define WEXC(fd, err, type, packet, size, ...) do { \
logMessage(LOG_ERR, "writing exception %d to fd %"PRIfd, err, fd); \
logMessage(LOG_ERR, __VA_ARGS__); \
writeException(fd, err, type, packet, size); \
} while(0)
/* These CHECK* macros check whether a condition is true, and, if not, */
/* send back either a non-fatal error, or an exception */
#define CHECKERR(condition, error, msg, ...) \
if (!( condition )) { \
WERR(c->fd, error, "%s not met: " msg, #condition, ## __VA_ARGS__); \
return 0; \
} else { }
#define CHECKEXC(condition, error, msg, ...) \
if (!( condition )) { \
WEXC(c->fd, error, type, packet, size, "%s not met: " msg, #condition, ## __VA_ARGS__); \
return 0; \
} else { }
#ifdef brlapi_error
#undef brlapi_error
#endif
static brlapi_error_t brlapiserver_error;
#define brlapi_error brlapiserver_error
#define BRLAPI(fun) brlapiserver_ ## fun
#include "brlapi_common.h"
/** ask for \e brltty commands */
#define BRL_COMMANDS 0
/** ask for raw driver keycodes */
#define BRL_KEYCODES 1
/****************************************************************************/
/** GLOBAL TYPES AND VARIABLES */
/****************************************************************************/
extern char *opt_brailleParameters;
extern char *cfg_brailleParameters;
typedef struct {
unsigned int cursor;
wchar_t *text;
unsigned char *andAttr;
unsigned char *orAttr;
} BrailleWindow;
typedef enum { TODISPLAY, EMPTY } BrlBufState;
typedef struct Subscription {
brlapi_param_t parameter;
brlapi_param_subparam_t subparam;
brlapi_param_flags_t flags;
struct Subscription *prev, *next;
} Subscription;
typedef struct Connection {
uint32_t clientVersion;
struct Connection *prev, *next;
FileDescriptor fd;
int auth;
struct Tty *tty;
brlapi_param_clientPriority_t client_priority;
int raw, suspend;
unsigned int how; /* how keys must be delivered to clients */
uint8_t retainDots; /* whether client wants dots instead of translating to chars */
BrailleWindow brailleWindow;
BrlBufState brlbufstate;
pthread_mutex_t brailleWindowMutex;
KeyrangeList *acceptedKeys;
pthread_mutex_t acceptedKeysMutex;
time_t upTime;
Packet packet;
struct Subscription subscriptions;
} Connection;
typedef struct Tty {
int focus;
int number;
struct Connection *connections;
struct Tty *father; /* father */
struct Tty **prevnext,*next; /* siblings */
struct Tty *subttys; /* children */
} Tty;
typedef struct {
unsigned local_subscriptions;
unsigned global_subscriptions;
} ParamState;
static ParamState paramState[BRLAPI_PARAM_COUNT];
/* Pointer to the connection accepter thread */
static pthread_t serverThread; /* server */
#ifdef ENABLE_API_FUZZING
static pthread_t fuzzerThread; /* fuzzer */
static pthread_t crasherThread; /* crash reproducer */
#endif /* ENABLE_API_FUZZING */
static pthread_t socketThreads[SERVER_SOCKET_LIMIT]; /* socket binding threads */
static int running; /* should threads be running? */
static char **socketHosts = NULL; /* socket local hosts */
static struct socketInfo {
int addrfamily;
FileDescriptor fd;
char *host;
char *port;
#ifdef __MINGW32__
OVERLAPPED overl;
#endif /* __MINGW32__ */
} socketInfo[SERVER_SOCKET_LIMIT]; /* information for cleaning sockets */
static int serverSocketCount; /* number of sockets */
static int serverSocketsPending; /* number of sockets not opened yet */
pthread_mutex_t apiSocketsMutex;
/* Protects from connection addition / remove from the server thread */
pthread_mutex_t apiConnectionsMutex;
/* Protects the real driver's functions */
pthread_mutex_t apiDriverMutex;
/* Which connection currently has raw mode or requested device suspension */
pthread_mutex_t apiRawMutex;
static Connection *rawConnection = NULL;
static Connection *suspendConnection = NULL;
pthread_mutex_t apiParamMutex;
/* Which connection is currently modifying a parameter */
static Connection *paramUpdateConnection;
/* mutex lock order is as follows:
* 1. apiParamMutex
* 2. apiConnectionsMutex
* 3. apiRawMutex
* 4. acceptedKeysMutex or brailleWindowMutex
* 5. apiDriverMutex
*/
static Tty notty;
static Tty ttys;
static unsigned int unauthConnections;
static unsigned int unauthConnLog = 0;
/*
* API states are
* - stopped: No thread is running (hence no connection allowed).
* started: The server thread is running, accepting connections.
* - unlinked: TrueBraille == &noBraille: API has no control on the driver.
* linked: TrueBraille != &noBraille: API controls the driver.
* - core suspended: The core asked to keep the device closed.
* core active: The core asked has opened the device.
* - device closed: API keeps the device closed.
* device opened: API has really opened the device.
*
* Combinations can be:
* - initial: API stopped, unlinked, core suspended and device closed.
* - started: API started, unlinked, core suspended and device closed.
* - normal: API started, linked, core active and device opened.
* - core suspend: API started, linked, core suspended but device opened.
* (BrlAPI-only output).
* - full suspend: API started, linked, core suspended and device closed.
* - brltty control: API started, core active and device opened, but unlinked.
*
* Other states don't make sense, since
* - api needs to be started before being linked,
* - the device can't remain closed if core is active,
* - the core must resume before unlinking api (so as to let the api re-open
* the driver if necessary)
*/
/* Pointer to subroutines of the real braille driver, &noBraille when API is
* unlinked */
static const BrailleDriver *trueBraille;
static BrailleDriver ApiBraille;
/* Identication of the REAL braille driver currently used */
/* The following variable contains the size of the braille display */
/* stored as a pair of _network_-formatted integers */
static uint32_t displayDimensions[2] = { 0, 0 };
static unsigned int displaySize = 0;
static BrailleDisplay *disp; /* Parameter to pass to braille drivers */
static int coreActive; /* Whether core is active */
static int offline; /* Whether device is offline */
static int driverConstructed; /* Whether device is really opened, protected by apiDriverMutex */
static int driverConstructing; /* Whether device being constructed, protected by apiDriverMutex */
static wchar_t *coreWindowText; /* Last text written by the core */
static unsigned char *coreWindowDots; /* Last dots written by the core */
static int coreWindowCursor; /* Last cursor position set by the core */
pthread_mutex_t apiSuspendMutex; /* Protects use of driverConstructed state */
static const char *auth = BRLAPI_DEFAUTH;
static AuthDescriptor *authDescriptor;
#ifdef __MINGW32__
static WSADATA wsadata;
#endif /* __MINGW32__ */
static unsigned char cursorOverlay = 0;
/****************************************************************************/
/** SOME PROTOTYPES **/
/****************************************************************************/
extern void processParameters(char ***values, const char *const *names, const char *description, char *optionParameters, char *configuredParameters, const char *environmentVariable);
static int initializeAcceptedKeys(Connection *c, int how);
static void brlResize(BrailleDisplay *brl);
static void handleParamUpdate(Connection *source, Connection *dest, brlapi_param_t param, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void *data, size_t size);
/****************************************************************************/
/** DRIVER CAPABILITIES **/
/****************************************************************************/
/* Function : isRawCapable */
/* Returns !0 if the specified driver is raw capable, 0 if it is not. */
static int isRawCapable(const BrailleDriver *brl)
{
return ((brl->readPacket!=NULL) && (brl->writePacket!=NULL) && (brl->reset!=NULL));
}
/* Function : isKeyCapable */
/* Returns !0 if driver can return specific keycodes, 0 if not. */
static int isKeyCapable(const BrailleDriver *brl)
{
int ret;
lockMutex(&apiDriverMutex);
ret = (disp && (disp->keyNames != NULL));
unlockMutex(&apiDriverMutex);
return ret;
}
/* Function : suspendBrailleDriver */
/* Really suspend the braille driver. Assumes that apiDriverMutex is locked */
static void suspendBrailleDriver(void) {
if (trueBraille == &noBraille) return; /* core unlinked api */
logMessage(LOG_CATEGORY(SERVER_EVENTS), "driver suspended");
lockMutex(&apiSuspendMutex);
driverConstructed = 0;
destructBrailleDriver();
unlockMutex(&apiSuspendMutex);
}
CORE_TASK_CALLBACK(apiCoreTask_destructBrailleDriver) {
lockMutex(&apiDriverMutex);
if (driverConstructed) {
suspendBrailleDriver();
}
unlockMutex(&apiDriverMutex);
}
/* Function : suspendDriver */
/* Requests suspending the driver */
static void suspendDriver(void) {
runCoreTask(apiCoreTask_destructBrailleDriver, NULL, 1);
}
/* Function : resumeBrailleDriver */
/* Really resume the braille driver. Assumes that apiDriverMutex is locked */
static int resumeBrailleDriver(BrailleDisplay *brl) {
if (trueBraille == &noBraille) return 0; /* core unlinked api */
driverConstructing = 1;
lockMutex(&apiSuspendMutex);
driverConstructed = constructBrailleDriver();
if (driverConstructed) {
disp = brl;
}
unlockMutex(&apiSuspendMutex);
if (driverConstructed) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "driver resumed");
handleParamUpdate(NULL, NULL, BRLAPI_PARAM_DRIVER_NAME, 0, BRLAPI_PARAMF_GLOBAL, braille->definition.name, strlen(braille->definition.name));
handleParamUpdate(NULL, NULL, BRLAPI_PARAM_DRIVER_CODE, 0, BRLAPI_PARAMF_GLOBAL, braille->definition.code, strlen(braille->definition.code));
handleParamUpdate(NULL, NULL, BRLAPI_PARAM_DRIVER_VERSION, 0, BRLAPI_PARAMF_GLOBAL, braille->definition.version, strlen(braille->definition.version));
handleParamUpdate(NULL, NULL, BRLAPI_PARAM_DEVICE_MODEL, 0, BRLAPI_PARAMF_GLOBAL, disp->keyBindings, strlen(disp->keyBindings));
brlResize(brl);
}
driverConstructing = 0;
return driverConstructed;
}
typedef struct {
unsigned resumed:1;
} CoreTaskData_resumeBrailleDriver;
CORE_TASK_CALLBACK(apiCoreTask_resumeBrailleDriver) {
CoreTaskData_resumeBrailleDriver *rbd = data;
lockMutex(&apiDriverMutex);
if (!disp) {
rbd->resumed = 0;
} else if (driverConstructed || driverConstructing) {
rbd->resumed = 1;
} else {
rbd->resumed = resumeBrailleDriver(disp);
}
unlockMutex(&apiDriverMutex);
}
/* Function : resumeDriver */
/* Requests resuming the driver */
static int resumeDriver(void) {
CoreTaskData_resumeBrailleDriver rbd = {
.resumed = 0
};
runCoreTask(apiCoreTask_resumeBrailleDriver, &rbd, 1);
return rbd.resumed;
}
/* Function : resetBrailleDriver */
/* Really reset the braille driver. Assumes that apiDriverMutex is locked */
static void resetBrailleDevice(void) {
logMessage(LOG_WARNING, "trying to reset the braille device");
if (!trueBraille->reset || !disp || !trueBraille->reset(disp)) {
if (trueBraille->reset) {
logMessage(LOG_WARNING, "reset failed - restarting the braille driver");
}
restartBrailleDriver();
}
}
CORE_TASK_CALLBACK(apiCoreTask_resetBrailleDevice) {
lockMutex(&apiDriverMutex);
resetBrailleDevice();
unlockMutex(&apiDriverMutex);
}
/* Function : resetDriver */
/* Requests resetting the driver */
static void resetDevice(void) {
runCoreTask(apiCoreTask_resetBrailleDevice, NULL, 1);
}
static int flushBrailleOutput(BrailleDisplay *brl) {
int flushed = api_flushOutput(brl);
if (!flushed) brl->hasFailed = 1;
resetAllBlinkDescriptors();
return flushed;
}
typedef struct {
unsigned flushed:1;
} CoreTaskData_flushBrailleOutput;
CORE_TASK_CALLBACK(apiCoreTask_flushBrailleOutput) {
CoreTaskData_flushBrailleOutput *fbo = data;
fbo->flushed = flushBrailleOutput(&brl);
}
static int flushOutput(void) {
CoreTaskData_flushBrailleOutput fbo = {
.flushed = 0
};
runCoreTask(apiCoreTask_flushBrailleOutput, &fbo, 1);
return fbo.flushed;
}
/****************************************************************************/
/** PACKET HANDLING **/
/****************************************************************************/
/* Function : writeAck */
/* Sends an acknowledgement on the given socket */
static inline void writeAck(FileDescriptor fd)
{
brlapiserver_writePacket(fd,BRLAPI_PACKET_ACK,NULL,0);
}
/* Function : writeError */
/* Sends the given non-fatal error on the given socket */
static void writeError(FileDescriptor fd, unsigned int err)
{
uint32_t code = htonl(err);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "error %u on fd %"PRIfd, err, fd);
brlapiserver_writePacket(fd,BRLAPI_PACKET_ERROR,&code,sizeof(code));
}
/* Function : writeException */
/* Sends the given error code on the given socket */
static void writeException(FileDescriptor fd, unsigned int err, brlapi_packetType_t type, const brlapi_packet_t *packet, size_t size)
{
int hdrsize, esize;
brlapi_packet_t epacket;
brlapi_errorPacket_t * errorPacket = &epacket.error;
logMessage(LOG_CATEGORY(SERVER_EVENTS), "exception %u for packet type %lu on fd %"PRIfd, err, (unsigned long)type, fd);
hdrsize = sizeof(errorPacket->code)+sizeof(errorPacket->type);
errorPacket->code = htonl(err);
errorPacket->type = htonl(type);
esize = MIN(size, BRLAPI_MAXPACKETSIZE-hdrsize);
if ((packet!=NULL) && (size!=0)) memcpy(&errorPacket->packet, &packet->data, esize);
brlapiserver_writePacket(fd,BRLAPI_PACKET_EXCEPTION,&epacket.data, hdrsize+esize);
}
static void writeKey(FileDescriptor fd, brlapi_keyCode_t key) {
uint32_t buf[2];
buf[0] = htonl(key >> 32);
buf[1] = htonl(key & 0xffffffff);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "writing key %08"PRIx32" %08"PRIx32" to fd %"PRIfd,buf[0],buf[1],fd);
brlapiserver_writePacket(fd,BRLAPI_PACKET_KEY,&buf,sizeof(buf));
}
typedef int(*PacketHandler)(Connection *, brlapi_packetType_t, brlapi_packet_t *, size_t);
typedef struct { /* packet handlers */
PacketHandler getDriverName;
PacketHandler getModelIdentifier;
PacketHandler getDisplaySize;
PacketHandler enterTtyMode;
PacketHandler setFocus;
PacketHandler leaveTtyMode;
PacketHandler ignoreKeyRanges;
PacketHandler acceptKeyRanges;
PacketHandler write;
PacketHandler enterRawMode;
PacketHandler leaveRawMode;
PacketHandler packet;
PacketHandler suspendDriver;
PacketHandler resumeDriver;
PacketHandler parameterValue;
PacketHandler parameterRequest;
PacketHandler sync;
} PacketHandlers;
/****************************************************************************/
/** BRAILLE WINDOWS MANAGING **/
/****************************************************************************/
/* Function : allocBrailleWindow */
/* Allocates and initializes the members of a BrailleWindow structure */
/* Uses displaySize to determine size of allocated buffers */
/* Returns to report success, -1 on errors */
static int allocBrailleWindow(BrailleWindow *brailleWindow)
{
if (!(brailleWindow->text = malloc(displaySize*sizeof(wchar_t)))) goto out;
if (!(brailleWindow->andAttr = malloc(displaySize))) goto outText;
if (!(brailleWindow->orAttr = malloc(displaySize))) goto outAnd;
wmemset(brailleWindow->text, WC_C(' '), displaySize);
memset(brailleWindow->andAttr, 0xFF, displaySize);
memset(brailleWindow->orAttr, 0x00, displaySize);
brailleWindow->cursor = 0;
return 0;
outAnd:
free(brailleWindow->andAttr);
outText:
free(brailleWindow->text);
out:
return -1;
}
/* Function: freeBrailleWindow */
/* Frees the fields of a BrailleWindow structure */
static void freeBrailleWindow(BrailleWindow *brailleWindow)
{
free(brailleWindow->text); brailleWindow->text = NULL;
free(brailleWindow->andAttr); brailleWindow->andAttr = NULL;
free(brailleWindow->orAttr); brailleWindow->orAttr = NULL;
}
static unsigned char
getCursorOverlay (BrailleDisplay *brl) {
if (prefs.showScreenCursor && !brl->hideCursor) {
BlinkDescriptor *blink = &screenCursorBlinkDescriptor;
requireBlinkDescriptor(blink);
if (isBlinkVisible(blink)) {
return mapCursorDots(getScreenCursorDots());
}
}
return 0;
}
/* Function: getDots */
/* Returns the braille dots corresponding to a BrailleWindow structure */
/* No allocation of buf is performed */
static void getDots(const BrailleWindow *brailleWindow, unsigned char *buf)
{
int i;
unsigned char c;
for (i=0; i<displaySize; i++) {
c = convertCharacterToDots(textTable, brailleWindow->text[i]);
buf[i] = (c & brailleWindow->andAttr[i]) | brailleWindow->orAttr[i];
}
if (brailleWindow->cursor) {
buf[brailleWindow->cursor-1] |= cursorOverlay;
}
}
/****************************************************************************/
/** CONNECTIONS MANAGING **/
/****************************************************************************/
/* Function : createConnection */
/* Creates a connection */
static Connection *createConnection(FileDescriptor fd, time_t currentTime)
{
Connection *c = malloc(sizeof(Connection));
if (c==NULL) goto out;
c->auth = -1;
c->fd = fd;
c->tty = NULL;
c->client_priority = BRLAPI_PARAM_CLIENT_PRIORITY_DEFAULT;
c->raw = 0;
c->suspend = 0;
c->brlbufstate = EMPTY;
{
pthread_mutexattr_t mattr;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&c->brailleWindowMutex,&mattr);
setAddressName(&c->brailleWindowMutex, "apiBrailleWindowMutex[" PRIfd "]", fd);
pthread_mutex_init(&c->acceptedKeysMutex,&mattr);
setAddressName(&c->acceptedKeysMutex, "apiAcceptedKeysMutex[" PRIfd "]", fd);
}
c->how = 0;
c->retainDots = 1;
c->acceptedKeys = NULL;
c->upTime = currentTime;
c->brailleWindow.text = NULL;
c->brailleWindow.andAttr = NULL;
c->brailleWindow.orAttr = NULL;
if (brlapi_initializePacket(&c->packet))
goto outmalloc;
c->subscriptions.next = &c->subscriptions;
c->subscriptions.prev = &c->subscriptions;
return c;
outmalloc:
free(c);
out:
if (fd != INVALID_FILE_DESCRIPTOR) {
writeError(fd,BRLAPI_ERROR_NOMEM);
closeFileDescriptor(fd);
}
return NULL;
}
/* Function : freeConnection */
/* Frees all resources associated to a connection */
static void freeConnection(Connection *c)
{
struct Subscription *s, *next;
if (c->fd != INVALID_FILE_DESCRIPTOR) {
lockMutex(&apiParamMutex);
for (s=c->subscriptions.next; s!=&c->subscriptions; s=next) {
if (s->flags & BRLAPI_PARAMF_GLOBAL)
paramState[s->parameter].global_subscriptions--;
else
paramState[s->parameter].local_subscriptions--;
next = s->next;
free(s);
}
unlockMutex(&apiParamMutex);
if (c->auth != 1) unauthConnections--;
closeFileDescriptor(c->fd);
}
pthread_mutex_destroy(&c->brailleWindowMutex);
unsetAddressName(&c->brailleWindowMutex);
pthread_mutex_destroy(&c->acceptedKeysMutex);
unsetAddressName(&c->acceptedKeysMutex);
freeBrailleWindow(&c->brailleWindow);
freeKeyrangeList(&c->acceptedKeys);
free(c);
}
/* Function : addConnection */
/* Creates a connection and adds it to the connection list */
static void __addConnection(Connection *c, Connection *connections)
{
c->next = connections->next;
c->prev = connections;
connections->next->prev = c;
connections->next = c;
}
static void __addConnectionSorted(Connection *c, Connection *head)
{
Connection *cur = head;
while (cur->next != head && cur->next->client_priority > c->client_priority)
cur = cur->next;
__addConnection(c, cur);
}
static void addConnection(Connection *c, Connection *connections)
{
lockMutex(&apiConnectionsMutex);
__addConnection(c,connections);
unlockMutex(&apiConnectionsMutex);
}
/* Function : removeConnection */
/* Removes the connection from the list */
static void __removeConnection(Connection *c)
{
c->prev->next = c->next;
c->next->prev = c->prev;
}
static void removeConnection(Connection *c)
{
lockMutex(&apiConnectionsMutex);
__removeConnection(c);
unlockMutex(&apiConnectionsMutex);
}
/* Function: removeFreeConnection */
/* Removes the connection from the list and frees its resources */
static void removeFreeConnection(Connection *c)
{
removeConnection(c);
freeConnection(c);
}
/****************************************************************************/
/** TTYs MANAGING **/
/****************************************************************************/
/* Function: newTty */
/* creates a new tty and inserts it in the hierarchy */
static inline Tty *newTty(Tty *father, int number)
{
Tty *tty;
if (!(tty = calloc(1,sizeof(*tty)))) goto out;
if (!(tty->connections = createConnection(INVALID_FILE_DESCRIPTOR,0))) goto outtty;
tty->connections->next = tty->connections->prev = tty->connections;
tty->number = number;
tty->focus = SCR_NO_VT;
tty->father = father;
tty->prevnext = &father->subttys;
if ((tty->next = father->subttys))
tty->next->prevnext = &tty->next;
father->subttys = tty;
return tty;
outtty:
free(tty);
out:
return NULL;
}
/* Function: removeTty */
/* removes an unused tty from the hierarchy */
static inline void removeTty(Tty *toremove)
{
if (toremove->next)
toremove->next->prevnext = toremove->prevnext;
*(toremove->prevnext) = toremove->next;
}
/* Function: freeTty */
/* frees a tty */
static inline void freeTty(Tty *tty)
{
freeConnection(tty->connections);
free(tty);
}
/****************************************************************************/
/** COMMUNICATION PROTOCOL HANDLING **/
/****************************************************************************/
/* Function logRequest */
/* Logs the given request */
static inline void logRequest(brlapi_packetType_t type, FileDescriptor fd)
{
logMessage(LOG_CATEGORY(SERVER_EVENTS), "received %s request on fd %"PRIfd, brlapiserver_getPacketTypeName(type), fd);
}
static int handleGetDriver(Connection *c, brlapi_packetType_t type, size_t size, const char *str)
{
int len = strlen(str);
CHECKERR(size==0,BRLAPI_ERROR_INVALID_PACKET,"packet should be empty");
CHECKERR(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
brlapiserver_writePacket(c->fd, type, str, len+1);
return 0;
}
static int handleGetDriverName(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
return handleGetDriver(c, type, size, braille->definition.name);
}
static int handleGetModelIdentifier(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
return handleGetDriver(c, type, size, disp && disp->keyBindings ? disp->keyBindings : "");
}
static int handleGetDisplaySize(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKERR(size==0,BRLAPI_ERROR_INVALID_PACKET,"packet should be empty");
CHECKERR(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
brlapiserver_writePacket(c->fd,BRLAPI_PACKET_GETDISPLAYSIZE,&displayDimensions[0],sizeof(displayDimensions));
return 0;
}
static int handleEnterTtyMode(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
uint32_t * ints = &packet->uint32;
uint32_t nbTtys;
int how;
unsigned int n;
unsigned char *p = packet->data;
char name[BRLAPI_MAXNAMELENGTH+1];
Tty *tty,*tty2,*tty3;
uint32_t *ptty;
size_t remaining = size;
CHECKERR((!c->raw),BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
CHECKERR(remaining>=sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small");
p += sizeof(uint32_t); remaining -= sizeof(uint32_t);
nbTtys = ntohl(ints[0]);
CHECKERR(remaining>=nbTtys*sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small for provided number of ttys");
p += nbTtys*sizeof(uint32_t); remaining -= nbTtys*sizeof(uint32_t);
CHECKERR(*p<=BRLAPI_MAXNAMELENGTH, BRLAPI_ERROR_INVALID_PARAMETER, "driver name too long");
n = *p; p++; remaining--;
CHECKERR(remaining==n, BRLAPI_ERROR_INVALID_PACKET,"packet size doesn't match format");
memcpy(name, p, n);
name[n] = '\0';
if (!*name) how = BRL_COMMANDS; else {
CHECKERR(!strcmp(name, trueBraille->definition.name), BRLAPI_ERROR_INVALID_PARAMETER, "wrong driver name");
CHECKERR(isKeyCapable(trueBraille), BRLAPI_ERROR_OPNOTSUPP, "driver doesn't support raw keycodes");
how = BRL_KEYCODES;
}
freeBrailleWindow(&c->brailleWindow); /* In case of multiple enterTtyMode requests */
if ((initializeAcceptedKeys(c, how)==-1) || (allocBrailleWindow(&c->brailleWindow)==-1)) {
logMessage(LOG_WARNING,"Failed to allocate some resources");
freeKeyrangeList(&c->acceptedKeys);
WERR(c->fd,BRLAPI_ERROR_NOMEM, "no memory for accepted keys");
return 0;
}
lockMutex(&apiConnectionsMutex);
tty = tty2 = &ttys;
for (ptty=ints+1; ptty<=ints+nbTtys; ptty++) {
for (tty2=tty->subttys; tty2; tty2=tty2->next) {
if (tty2->number == ntohl(*ptty)) break;
}
if (!tty2) break;
tty = tty2;
logMessage(LOG_CATEGORY(SERVER_EVENTS), "tty %#010lx ok",(unsigned long)ntohl(*ptty));
}
if (!tty2) {
/* we were stopped at some point because the path doesn't exist yet */
if (c->tty) {
/* uhu, we already got a tty, but not this one, since the path
* doesn't exist yet. This is forbidden. */
unlockMutex(&apiConnectionsMutex);
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "already having another tty");
return 0;
}
/* ok, allocate path */
/* we lock the entire subtree for easier cleanup */
if (!(tty2 = newTty(tty,ntohl(*ptty)))) {
unlockMutex(&apiConnectionsMutex);
WERR(c->fd,BRLAPI_ERROR_NOMEM, "no memory for new tty");
freeBrailleWindow(&c->brailleWindow);
return 0;
}
ptty++;
logMessage(LOG_CATEGORY(SERVER_EVENTS), "allocated tty %#010lx",(unsigned long)ntohl(*(ptty-1)));
for (; ptty<=ints+nbTtys; ptty++) {
if (!(tty2 = newTty(tty2,ntohl(*ptty)))) {
/* gasp, couldn't allocate :/, clean tree */
for (tty2 = tty->subttys; tty2; tty2 = tty3) {
tty3 = tty2->subttys;
freeTty(tty2);
}
unlockMutex(&apiConnectionsMutex);
WERR(c->fd,BRLAPI_ERROR_NOMEM, "no memory for new tty");
freeBrailleWindow(&c->brailleWindow);
return 0;
}
logMessage(LOG_CATEGORY(SERVER_EVENTS), "allocated tty %#010lx",(unsigned long)ntohl(*ptty));
}
tty = tty2;
}
if (c->tty) {
unlockMutex(&apiConnectionsMutex);
if (c->tty == tty) {
if (c->how==how) {
WERR(c->fd, BRLAPI_ERROR_ILLEGAL_INSTRUCTION, "already controlling tty %#010x", c->tty->number);
} else {
/* Here one is in the case where the client tries to change */
/* from BRL_KEYCODES to BRL_COMMANDS, or something like that */
/* For the moment this operation is not supported */
/* A client that wants to do that should first LeaveTty() */
/* and then get it again, risking to lose it */
WERR(c->fd,BRLAPI_ERROR_OPNOTSUPP, "Switching from BRL_KEYCODES to BRL_COMMANDS not supported yet");
}
return 0;
} else {
/* uhu, we already got a tty, but not this one: this is forbidden. */
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "already having a tty");
return 0;
}
}
c->tty = tty;
c->how = how;
__removeConnection(c);
__addConnectionSorted(c,tty->connections);
unlockMutex(&apiConnectionsMutex);
writeAck(c->fd);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "fd %"PRIfd" taking control of tty %#010x (how=%d)",c->fd,tty->number,how);
return 0;
}
static int handleSetFocus(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
uint32_t * ints = &packet->uint32;
CHECKEXC(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
CHECKEXC(c->tty,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of tty mode");
c->tty->focus = ntohl(ints[0]);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "focus on window %#010x from fd%"PRIfd,c->tty->focus,c->fd);
flushOutput();
return 0;
}
/* Function doLeaveTty */
/* handles a connection leaving its tty */
static void doLeaveTty(Connection *c)
{
Tty *tty = c->tty;
logMessage(LOG_CATEGORY(SERVER_EVENTS), "fd %"PRIfd" releasing tty %#010x",c->fd,tty->number);
c->tty = NULL;
lockMutex(&apiConnectionsMutex);
__removeConnection(c);
__addConnection(c,notty.connections);
unlockMutex(&apiConnectionsMutex);
freeKeyrangeList(&c->acceptedKeys);
freeBrailleWindow(&c->brailleWindow);
}
static int handleLeaveTtyMode(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKERR(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
CHECKERR(c->tty,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of tty mode");
doLeaveTty(c);
writeAck(c->fd);
return 0;
}
static int handleKeyRanges(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
int res = 0;
brlapi_keyCode_t x,y;
uint32_t (*ints)[4] = (uint32_t (*)[4]) &packet->uint32;
unsigned int i;
CHECKERR(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
CHECKERR(c->tty,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of tty mode");
CHECKERR(!(size%(2*sizeof(brlapi_keyCode_t))),BRLAPI_ERROR_INVALID_PACKET,"wrong packet size");
lockMutex(&c->acceptedKeysMutex);
for (i=0; i<size/(2*sizeof(brlapi_keyCode_t)); i++) {
x = brlapiserver_packetToKeyCode(&ints[i][0]);
y = brlapiserver_packetToKeyCode(&ints[i][2]);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "fd %"PRIfd" range: [%016"BRLAPI_PRIxKEYCODE"..%016"BRLAPI_PRIxKEYCODE"]",c->fd,x,y);
if (type==BRLAPI_PACKET_IGNOREKEYRANGES) res = removeKeyrange(x,y,&c->acceptedKeys);
else res = addKeyrange(x,y,&c->acceptedKeys);
if (res==-1) {
/* XXX: humf, in the middle of keycode updates :( */
WERR(c->fd,BRLAPI_ERROR_NOMEM,"no memory for key range");
break;
}
}
unlockMutex(&c->acceptedKeysMutex);
if (!res) writeAck(c->fd);
return 0;
}
static void
logConversionDecision (Connection *connection, const char *charset, const char *decision) {
logMessage(LOG_CATEGORY(SERVER_EVENTS),
"fd %"PRIfd" charset %s %s",
connection->fd, charset, decision
);
}
static void
logConversionResult (Connection *connection, unsigned int chars, unsigned int bytes) {
logMessage(LOG_CATEGORY(SERVER_EVENTS),
"fd %"PRIfd" wrote %d characters %d bytes",
connection->fd, chars, bytes
);
}
static void
convertFromLatin1 (Connection *c, unsigned int rbeg, unsigned int rsiz, const unsigned char *text) {
for (int i=0; i<rsiz; i+=1) {
c->brailleWindow.text[rbeg-1+i] = text[i];
}
logConversionResult(c, rsiz, rsiz);
}
static int
convertFromUTF8 (Connection *c, wchar_t *outBuff, size_t *outLeft, const char *inBuff, size_t *inLeft) {
wchar_t *out = outBuff;
const char *in = inBuff;
while ((*outLeft > 0) && (*inLeft > 0)) {
wint_t wc = convertUtf8ToWchar(&in, inLeft);
if (wc == WEOF) return 0;
*out++ = wc;
*outLeft -= 1;
}
logConversionResult(c, (out - outBuff), (in - inBuff));
return 1;
}
static int handleWrite(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
brlapi_writeArgumentsPacket_t *wa = &packet->writeArguments;
unsigned char *text = NULL, *orAttr = NULL, *andAttr = NULL;
unsigned int rbeg, rsiz, textLen = 0;
bool fill;
int cursor = -1;
unsigned char *p = &wa->data;
int remaining = size;
char *charset = NULL;
unsigned int charsetLen = 0;
CHECKEXC(remaining>=sizeof(wa->flags), BRLAPI_ERROR_INVALID_PACKET, "packet too small for flags");
CHECKEXC(!c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
CHECKEXC(c->tty,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of tty mode");
wa->flags = ntohl(wa->flags);
if ((remaining==sizeof(wa->flags))&&(wa->flags==0)) {
c->brlbufstate = EMPTY;
return 0;
}
remaining -= sizeof(wa->flags); /* flags */
CHECKEXC((wa->flags & BRLAPI_WF_DISPLAYNUMBER)==0, BRLAPI_ERROR_OPNOTSUPP, "display number not yet supported");
if (wa->flags & BRLAPI_WF_REGION) {
int regionSize;
CHECKEXC(remaining>2*sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small for region");
rbeg = ntohl( *((uint32_t *) p) );
p += sizeof(uint32_t); remaining -= sizeof(uint32_t); /* region begin */
regionSize = (int32_t) ntohl( *((uint32_t *) p) );
p += sizeof(uint32_t); remaining -= sizeof(uint32_t); /* region size */
if (regionSize < 0) {
CHECKEXC(regionSize != INT32_MIN, BRLAPI_ERROR_INVALID_PARAMETER, "invalid region size");
rsiz = -regionSize;
fill = true;
} else {
rsiz = regionSize;
fill = false;
}
CHECKEXC(
(rbeg >= 1) && (rbeg <= displaySize),
BRLAPI_ERROR_INVALID_PARAMETER, "invalid region start"
);
CHECKEXC(
(rsiz >= 1) && (rsiz <= displaySize),
BRLAPI_ERROR_INVALID_PARAMETER, "invalid region size"
);
CHECKEXC(
(rbeg + rsiz - 1 <= displaySize),
BRLAPI_ERROR_INVALID_PARAMETER, "invalid region"
);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "warning: fd %"PRIfd" uses deprecated regionBegin=0 and regionSize = 0",c->fd);
rbeg = 1;
rsiz = displaySize;
fill = true;
}
if (wa->flags & BRLAPI_WF_TEXT) {
CHECKEXC(remaining>=sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small for text length");
textLen = ntohl( *((uint32_t *) p) );
p += sizeof(uint32_t); remaining -= sizeof(uint32_t); /* text size */
CHECKEXC(remaining>=textLen, BRLAPI_ERROR_INVALID_PACKET, "packet too small for text");
text = p;
p += textLen; remaining -= textLen; /* text */
}
if (wa->flags & BRLAPI_WF_ATTR_AND) {
CHECKEXC(remaining>=rsiz, BRLAPI_ERROR_INVALID_PACKET, "packet too small for And mask");
andAttr = p;
p += rsiz; remaining -= rsiz; /* and attributes */
}
if (wa->flags & BRLAPI_WF_ATTR_OR) {
CHECKEXC(remaining>=rsiz, BRLAPI_ERROR_INVALID_PACKET, "packet too small for Or mask");
orAttr = p;
p += rsiz; remaining -= rsiz; /* or attributes */
}
if (wa->flags & BRLAPI_WF_CURSOR) {
uint32_t u32;
CHECKEXC(remaining>=sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small for cursor");
memcpy(&u32, p, sizeof(uint32_t));
cursor = ntohl(u32);
p += sizeof(uint32_t); remaining -= sizeof(uint32_t); /* cursor */
CHECKEXC(cursor<=displaySize, BRLAPI_ERROR_INVALID_PACKET, "wrong cursor");
}
if (wa->flags & BRLAPI_WF_CHARSET) {
CHECKEXC(wa->flags & BRLAPI_WF_TEXT, BRLAPI_ERROR_INVALID_PACKET, "charset requires text");
CHECKEXC(remaining>=1, BRLAPI_ERROR_INVALID_PACKET, "packet too small for charset length");
charsetLen = *p++; remaining--; /* charset length */
CHECKEXC(remaining>=charsetLen, BRLAPI_ERROR_INVALID_PACKET, "packet too small for charset");
charset = (char *) p;
p += charsetLen; remaining -= charsetLen; /* charset name */
}
CHECKEXC(remaining==0, BRLAPI_ERROR_INVALID_PACKET, "packet too big");
/* Here the whole packet has been checked */
unsigned int rsiz_filled = fill ? displaySize - rbeg + 1 : rsiz;
if (text) {
int isUTF8 = 0;
int isLatin1 = 0;
#ifndef HAVE_ALLOCA_H
char charsetBuffer[0X20];
#endif /* HAVE_ALLOCA_H */
if (charset) {
charset[charsetLen] = 0; /* we have room for this */
} else {
lockCharset(0);
const char *name = getCharset();
if (name) {
size_t length = strlen(name);
#ifdef HAVE_ALLOCA_H
size_t size = length + 1;
charset = alloca(size);
strcpy(charset, name);
#else /* HAVE_ALLOCA_H */
if (length < sizeof(charsetBuffer)) {
strcpy(charsetBuffer, name);
charset = charsetBuffer;
}
#endif /* HAVE_ALLOCA_H */
if (charset) charsetLen = length;
}
unlockCharset();
}
if (charset) {
if (isCharsetUTF8(charset)) {
isUTF8 = 1;
} else if (isCharsetLatin1(charset)) {
isLatin1 = 1;
} else {
#ifndef HAVE_ICONV_H
CHECKEXC(0, BRLAPI_ERROR_OPNOTSUPP, "charset conversion not supported (enable iconv?)");
#endif /* !HAVE_ICONV_H */
}
}
size_t end;
if (isUTF8) {
logConversionDecision(c, "UTF-8", "internal conversion");
size_t outLeft = rsiz_filled;
wchar_t outBuff[outLeft];
size_t inLeft = textLen;
const char *inBuff = (char *)text;
CHECKEXC(
convertFromUTF8(c, outBuff, &outLeft, inBuff, &inLeft),
BRLAPI_ERROR_INVALID_PACKET, "invalid charset conversion"
);
if (!fill) {
CHECKEXC(!inLeft, BRLAPI_ERROR_INVALID_PACKET, "text too big");
CHECKEXC(!outLeft, BRLAPI_ERROR_INVALID_PACKET, "text too small");
} else {
CHECKEXC(inLeft || (!andAttr && !orAttr) || rsiz_filled - outLeft == rsiz, BRLAPI_ERROR_INVALID_PACKET, "text length does not match and/or mask length");
}
lockMutex(&c->brailleWindowMutex);
wmemcpy(c->brailleWindow.text+rbeg-1, outBuff, rsiz_filled - outLeft);
end = rbeg-1 + rsiz_filled - outLeft;
}
#ifdef HAVE_ICONV_H
else if (charset && !isLatin1) {
logConversionDecision(c, charset, "iconv conversion");
size_t len = fill ? textLen : rsiz;
wchar_t textBuf[len ? len : 1];
char *in = (char *) text, *out = (char *) textBuf;
size_t sin = textLen, sout = len * sizeof(wchar_t);
iconv_t conv = iconv_open(getWcharCharset(), charset);
CHECKEXC((conv != (iconv_t)(-1)), BRLAPI_ERROR_INVALID_PACKET, "invalid charset");
size_t res = iconv(conv,&in,&sin,&out,&sout);
iconv_close(conv);
CHECKEXC((res != (size_t)-1), BRLAPI_ERROR_INVALID_PACKET, "invalid charset conversion");
if (!fill) {
CHECKEXC(!sin, BRLAPI_ERROR_INVALID_PACKET, "text too big");
CHECKEXC(!sout, BRLAPI_ERROR_INVALID_PACKET, "text too small");
} else {
CHECKEXC(sin || (!andAttr && !orAttr) || len - sout / sizeof(wchar_t) == rsiz, BRLAPI_ERROR_INVALID_PACKET, "text length does not match and/or mask length");
}
logConversionResult(c, rsiz, textLen);
len -= sout / sizeof(wchar_t);
if (len > displaySize - rbeg + 1)
len = displaySize - rbeg + 1;
lockMutex(&c->brailleWindowMutex);
wmemcpy(c->brailleWindow.text+rbeg-1, textBuf, len);
end = rbeg-1 + len;
}
#endif /* HAVE_ICONV_H */
else {
logConversionDecision(c, "ISO_8859-1", isLatin1 ? "internal conversion" : "assumed");
size_t len = textLen;
if (!fill) {
CHECKEXC(len <= rsiz, BRLAPI_ERROR_INVALID_PACKET, "text too big");
CHECKEXC(len >= rsiz, BRLAPI_ERROR_INVALID_PACKET, "text too small");
} else {
if (len > rsiz_filled)
len = rsiz_filled;
else
CHECKEXC((!andAttr && !orAttr) || len == rsiz, BRLAPI_ERROR_INVALID_PACKET, "text length does not match and/or mask length");
}
lockMutex(&c->brailleWindowMutex);
convertFromLatin1(c, rbeg, len, text);
end = rbeg-1 + len;
}
if (fill)
wmemset(c->brailleWindow.text+end, L' ', displaySize - end);
// Forget the charset in case it's pointing to a local buffer.
// This occurs when getCharset() is saved and alloca() isn't available.
charset = NULL;
charsetLen = 0;
if (!andAttr) memset(c->brailleWindow.andAttr+rbeg-1,0xFF,rsiz_filled);
if (!orAttr) memset(c->brailleWindow.orAttr+rbeg-1,0x00,rsiz_filled);
if (fill) memset(c->brailleWindow.andAttr+rbeg-1+rsiz,0x00,rsiz_filled-rsiz);
} else {
lockMutex(&c->brailleWindowMutex);
}
if (andAttr) {
memcpy(c->brailleWindow.andAttr+rbeg-1,andAttr,rsiz);
memset(c->brailleWindow.andAttr+rbeg-1+rsiz,0X00,rsiz_filled-rsiz);
}
if (orAttr) {
memcpy(c->brailleWindow.orAttr+rbeg-1,orAttr,rsiz);
memset(c->brailleWindow.orAttr+rbeg-1+rsiz,0X00,rsiz_filled-rsiz);
}
if (cursor >= 0) c->brailleWindow.cursor = cursor;
c->brlbufstate = TODISPLAY;
unlockMutex(&c->brailleWindowMutex);
flushOutput();
return 0;
}
static int checkDriverSpecificModePacket(Connection *c, brlapi_packet_t *packet, size_t size)
{
brlapi_getDriverSpecificModePacket_t *getDevicePacket = &packet->getDriverSpecificMode;
int remaining = size;
CHECKERR(remaining>sizeof(uint32_t), BRLAPI_ERROR_INVALID_PACKET, "packet too small");
remaining -= sizeof(uint32_t);
CHECKERR(ntohl(getDevicePacket->magic)==BRLAPI_DEVICE_MAGIC,BRLAPI_ERROR_INVALID_PARAMETER, "wrong magic number");
remaining--;
CHECKERR(getDevicePacket->nameLength<=BRLAPI_MAXNAMELENGTH && getDevicePacket->nameLength == strlen(trueBraille->definition.name), BRLAPI_ERROR_INVALID_PARAMETER, "wrong driver length");
CHECKERR(remaining==getDevicePacket->nameLength, BRLAPI_ERROR_INVALID_PACKET, "wrong packet size");
CHECKERR(((!strncmp(&getDevicePacket->name, trueBraille->definition.name, remaining))), BRLAPI_ERROR_INVALID_PARAMETER, "wrong driver name");
return 1;
}
static int handleEnterRawMode(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKERR(!c->raw, BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed in raw mode");
if (!checkDriverSpecificModePacket(c, packet, size)) return 0;
CHECKERR(isRawCapable(trueBraille), BRLAPI_ERROR_OPNOTSUPP, "driver doesn't support Raw mode");
lockMutex(&apiRawMutex);
if (rawConnection || suspendConnection) {
WERR(c->fd,BRLAPI_ERROR_DEVICEBUSY,"driver busy (%s)", rawConnection?"raw":"suspend");
unlockMutex(&apiRawMutex);
return 0;
}
rawConnection = c;
unlockMutex(&apiRawMutex);
if (!resumeDriver()) {
WERR(c->fd, BRLAPI_ERROR_DRIVERERROR,"driver resume error");
return 0;
}
c->raw = 1;
writeAck(c->fd);
return 0;
}
static int handleLeaveRawMode(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKERR(c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of raw mode");
logMessage(LOG_CATEGORY(SERVER_EVENTS), "fd %"PRIfd" going out of raw mode",c->fd);
c->raw = 0;
lockMutex(&apiRawMutex);
rawConnection = NULL;
unlockMutex(&apiRawMutex);
writeAck(c->fd);
return 0;
}
static int handlePacket(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKEXC(c->raw,BRLAPI_ERROR_ILLEGAL_INSTRUCTION,"not allowed out of raw mode");
lockMutex(&apiDriverMutex);
trueBraille->writePacket(disp,&packet->data,size);
unlockMutex(&apiDriverMutex);
return 0;
}
static int handleSuspendDriver(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
if (!checkDriverSpecificModePacket(c, packet, size)) return 0;
CHECKERR(!c->suspend,BRLAPI_ERROR_ILLEGAL_INSTRUCTION, "not allowed in suspend mode");
lockMutex(&apiRawMutex);
if (suspendConnection || rawConnection) {
WERR(c->fd, BRLAPI_ERROR_DEVICEBUSY,"driver busy (%s)", rawConnection?"raw":"suspend");
unlockMutex(&apiRawMutex);
return 0;
}
suspendConnection = c;
unlockMutex(&apiRawMutex);
c->suspend = 1;
suspendDriver();
writeAck(c->fd);
return 0;
}
static int handleResumeDriver(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
CHECKERR(c->suspend,BRLAPI_ERROR_ILLEGAL_INSTRUCTION, "not allowed out of suspend mode");
c->suspend = 0;
lockMutex(&apiRawMutex);
suspendConnection = NULL;
unlockMutex(&apiRawMutex);
resumeDriver();
writeAck(c->fd);
return 0;
}
static brlapi_keyCode_t makeDriverKeyCode (KeyGroup group, KeyNumber number, int press)
{
brlapi_keyCode_t code =
((group << BRLAPI_DRV_KEY_GROUP_SHIFT) & BRLAPI_DRV_KEY_GROUP_MASK) |
((number << BRLAPI_DRV_KEY_NUMBER_SHIFT) & BRLAPI_DRV_KEY_NUMBER_MASK);
if (press) code |= BRLAPI_DRV_KEY_PRESS;
return code;
}
/* On success, this should fill 'data', adjust 'size', and return NULL.
* On failure, this should return a non-allocated error message string. */
#define PARAM_READER_DECLARATION(name) const char *name (Connection *c, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, void *data, size_t *size)
typedef PARAM_READER_DECLARATION(ParamReader);
#define PARAM_READER(name) static PARAM_READER_DECLARATION(param_ ## name ## _read)
/* On success, this should return NULL.
* On failure, this should return a non-allocated error message string. */
#define PARAM_WRITER_DECLARATION(name) const char *name (Connection *c, brlapi_param_t parameter, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void *data, size_t size)
typedef PARAM_WRITER_DECLARATION(ParamWriter);
#define PARAM_WRITER(name) static PARAM_WRITER_DECLARATION(param_ ## name ## _write)
#define PARAM_ASSERT(condition) \
do { \
if (!(condition)) return "assertion failed: " #condition; \
} while (0)
#define PARAM_ASSERT_SIZE(pointer) PARAM_ASSERT(size == sizeof(*pointer))
static void param_readString(const char *string, void *data, size_t *size)
{
if (!string) string = "";
size_t length = strlen(string);
if (length < *size) *size = length;
memcpy(data, string, *size);
}
static const char *param_writeString(int (*handler) (const char *string), const void *data, size_t size)
{
char string[size + 1];
memcpy(string, data, size);
string[size] = 0;
if (handler(string)) return NULL;
return "set string parameter failed";
}
/* BRLAPI_PARAM_SERVER_VERSION */
PARAM_READER(serverVersion)
{
brlapi_param_serverVersion_t *serverVersion = data;
*size = sizeof(*serverVersion);
*serverVersion = BRLAPI_PROTOCOL_VERSION;
return NULL;
}
/* BRLAPI_PARAM_CLIENT_PRIORITY */
PARAM_READER(clientPriority)
{
brlapi_param_clientPriority_t *clientPriority = data;
*size = sizeof(*clientPriority);
*clientPriority = c->client_priority;
return NULL;
}
PARAM_WRITER(clientPriority)
{
const brlapi_param_clientPriority_t *clientPriority = data;
PARAM_ASSERT_SIZE(clientPriority);
lockMutex(&apiConnectionsMutex);
c->client_priority = *clientPriority;
if (c->tty) {
__removeConnection(c);
__addConnectionSorted(c,c->tty->connections);
}
unlockMutex(&apiConnectionsMutex);
return NULL;
}
/* BRLAPI_PARAM_DRIVER_NAME */
PARAM_READER(driverName)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
param_readString(braille->definition.name, data, size);
} else {
*size = 0;
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DRIVER_CODE */
PARAM_READER(driverCode)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
param_readString(braille->definition.code, data, size);
} else {
*size = 0;
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DRIVER_VERSION */
PARAM_READER(driverVersion)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
param_readString(braille->definition.version, data, size);
} else {
*size = 0;
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DEVICE_MODEL */
PARAM_READER(deviceModel)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
const char *deviceModel = brl.keyBindings;
if (deviceModel) {
param_readString(deviceModel, data, size);
goto unlock;
}
}
*size = 0;
unlock:
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DEVICE_CELL_SIZE */
PARAM_READER(deviceCellSize)
{
brlapi_param_deviceCellSize_t *deviceCellSize = data;
*size = sizeof(*deviceCellSize);
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
*deviceCellSize = brl.cellSize;
} else {
*deviceCellSize = 0;
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DISPLAY_SIZE */
PARAM_READER(displaySize)
{
brlapi_param_displaySize_t *displaySize = data;
*size = sizeof(*displaySize);
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
displaySize->columns = brl.textColumns;
displaySize->rows = brl.textRows;
} else {
displaySize->columns = 0;
displaySize->rows = 0;
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DEVICE_IDENTIFIER */
PARAM_READER(deviceIdentifier)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
GioEndpoint *endpoint = brl.gioEndpoint;
if (endpoint) {
gioMakeResourceIdentifier(endpoint, data, *size);
*size = strlen(data);
goto unlock;
}
}
*size = 0;
unlock:
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DEVICE_SPEED */
PARAM_READER(deviceSpeed)
{
brlapi_param_deviceSpeed_t *deviceSpeed = data;
*size = sizeof(*deviceSpeed);
*deviceSpeed = 0;
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
GioEndpoint *endpoint = brl.gioEndpoint;
if (endpoint) {
*deviceSpeed = gioGetBytesPerSecond(endpoint);
}
}
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DEVICE_ONLINE */
PARAM_READER(deviceOnline)
{
brlapi_param_deviceOnline_t *deviceOnline = data;
*size = sizeof(*deviceOnline);
lockBrailleDriver();
*deviceOnline = isBrailleOnline();
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_RETAIN_DOTS */
PARAM_READER(retainDots)
{
brlapi_param_retainDots_t *retainDots = data;
*size = sizeof(*retainDots);
*retainDots = c->retainDots;
return NULL;
}
PARAM_WRITER(retainDots)
{
const brlapi_param_retainDots_t *retainDots = data;
PARAM_ASSERT_SIZE(retainDots);
c->retainDots = *retainDots;
return NULL;
}
/* BRLAPI_PARAM_COMPUTER_BRAILLE_CELL_SIZE */
PARAM_READER(computerBrailleCellSize)
{
brlapi_param_computerBrailleCellSize_t *computerBrailleCellSize = data;
*size = sizeof(*computerBrailleCellSize);
*computerBrailleCellSize = isSixDotComputerBraille()? 6: 8;
return NULL;
}
PARAM_WRITER(computerBrailleCellSize)
{
const brlapi_param_computerBrailleCellSize_t *computerBrailleCellSize = data;
PARAM_ASSERT_SIZE(computerBrailleCellSize);
setSixDotComputerBraille(*computerBrailleCellSize <= 6);
return NULL;
}
/* BRLAPI_PARAM_LITERARY_BRAILLE */
PARAM_READER(literaryBraille)
{
brlapi_param_literaryBraille_t *literaryBraille = data;
*size = sizeof(*literaryBraille);
*literaryBraille = isContractedBraille();
return NULL;
}
PARAM_WRITER(literaryBraille)
{
const brlapi_param_literaryBraille_t *literaryBraille = data;
PARAM_ASSERT_SIZE(literaryBraille);
setContractedBraille(*literaryBraille);
return NULL;
}
/* BRLAPI_PARAM_CURSOR_DOTS */
PARAM_READER(cursorDots)
{
brlapi_param_cursorDots_t *cursorDots = data;
*size = sizeof(*cursorDots);
*cursorDots = getScreenCursorDots();
return NULL;
}
PARAM_WRITER(cursorDots)
{
const brlapi_param_cursorDots_t *cursorDots = data;
PARAM_ASSERT_SIZE(cursorDots);
if (!setScreenCursorDots(*cursorDots)) return "unsupported cursor style";
return NULL;
}
/* BRLAPI_PARAM_CURSOR_BLINK_PERIOD */
PARAM_READER(cursorBlinkPeriod)
{
brlapi_param_cursorBlinkPeriod_t *cursorBlinkPeriod = data;
*size = sizeof(*cursorBlinkPeriod);
*cursorBlinkPeriod = getBlinkPeriod(&screenCursorBlinkDescriptor);
return NULL;
}
PARAM_WRITER(cursorBlinkPeriod)
{
const brlapi_param_cursorBlinkPeriod_t *cursorBlinkPeriod = data;
PARAM_ASSERT_SIZE(cursorBlinkPeriod);
if (!setBlinkPeriod(&screenCursorBlinkDescriptor, *cursorBlinkPeriod)) return "unsupported cursor blink period";
return NULL;
}
/* BRLAPI_PARAM_CURSOR_BLINK_PERCENTAGE */
PARAM_READER(cursorBlinkPercentage)
{
brlapi_param_cursorBlinkPercentage_t *cursorBlinkPercentage = data;
*size = sizeof(*cursorBlinkPercentage);
*cursorBlinkPercentage = getBlinkPercentVisible(&screenCursorBlinkDescriptor);
return NULL;
}
PARAM_WRITER(cursorBlinkPercentage)
{
const brlapi_param_cursorBlinkPercentage_t *cursorBlinkPercentage = data;
PARAM_ASSERT_SIZE(cursorBlinkPercentage);
if (!setBlinkPercentVisible(&screenCursorBlinkDescriptor, *cursorBlinkPercentage)) return "unsupported cursor blink percentage";
return NULL;
}
/* BRLAPI_PARAM_RENDERED_CELLS */
PARAM_READER(renderedCells)
{
lockMutex(&apiDriverMutex);
if (disp && c->brailleWindow.text) {
unsigned char buffer[displaySize];
getDots(&c->brailleWindow, buffer);
if (displaySize < *size) *size = displaySize;
memcpy(data, buffer, *size);
} else {
*size = 0;
}
unlockMutex(&apiDriverMutex);
return NULL;
}
/* BRLAPI_PARAM_SKIP_IDENTICAL_LINES */
PARAM_READER(skipIdenticalLines)
{
brlapi_param_skipIdenticalLines_t *skipIdenticalLines = data;
*size = sizeof(*skipIdenticalLines);
*skipIdenticalLines = !!prefs.skipIdenticalLines;
return NULL;
}
PARAM_WRITER(skipIdenticalLines)
{
const brlapi_param_skipIdenticalLines_t *skipIdenticalLines = data;
PARAM_ASSERT_SIZE(skipIdenticalLines);
prefs.skipIdenticalLines = !!*skipIdenticalLines;
api_updateParameter(BRLAPI_PARAM_SKIP_IDENTICAL_LINES, 0);
return NULL;
}
/* BRLAPI_PARAM_AUDIBLE_ALERTS */
PARAM_READER(audibleAlerts)
{
brlapi_param_audibleAlerts_t *audibleAlerts = data;
*size = sizeof(*audibleAlerts);
*audibleAlerts = !!prefs.alertTunes;
return NULL;
}
PARAM_WRITER(audibleAlerts)
{
const brlapi_param_audibleAlerts_t *audibleAlerts = data;
PARAM_ASSERT_SIZE(audibleAlerts);
prefs.alertTunes = !!*audibleAlerts;
api_updateParameter(BRLAPI_PARAM_AUDIBLE_ALERTS, 0);
return NULL;
}
/* BRLAPI_PARAM_CLIPBOARD_CONTENT */
PARAM_READER(clipboardContent)
{
char *content = getMainClipboardContent();
if (!content) return "no memory";
param_readString(content, data, size);
free(content);
return NULL;
}
PARAM_WRITER(clipboardContent)
{
return param_writeString(setMainClipboardContent, data, size);
}
/* BRLAPI_PARAM_BOUND_COMMAND_KEYCODES */
PARAM_READER(boundCommandKeycodes)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
KeyTable *table = brl.keyTable;
if (table) {
unsigned int count;
int *commands = getBoundCommands(table, &count);
if (commands) {
*size /= sizeof(brlapi_param_commandKeycode_t);
*size *= sizeof(brlapi_param_commandKeycode_t);
void *next = data;
const void *end = next + *size;
for (unsigned int i=0; i<count; i+=1) {
if (next == end) break;
brlapi_keyCode_t keyCode;
if (cmdBrlttyToBrlapi(&keyCode, commands[i], c->retainDots)) {
brlapi_param_commandKeycode_t *commandCode = next;
*commandCode = keyCode;
next += sizeof(*commandCode);
}
}
*size = next - data;
free(commands);
goto unlock;
}
}
}
*size = 0;
unlock:
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_COMMAND_KEYCODE_NAME */
PARAM_READER(commandKeycodeName)
{
int command = cmdBrlapiToBrltty(subparam);
if (command == EOF) {
*size = 0;
} else {
describeCommand(data, *size, command, CDO_IncludeName);
char *colon = strchr(data, ':');
if (colon) *colon = 0;
*size = strlen(data);
}
return NULL;
}
/* BRLAPI_PARAM_COMMAND_KEYCODE_SUMMARY */
PARAM_READER(commandKeycodeSummary)
{
int command = cmdBrlapiToBrltty(subparam);
if (command == EOF) {
*size = 0;
} else {
describeCommand(data, *size, command, 0);
*size = strlen(data);
}
return NULL;
}
/* BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES */
typedef struct {
void *next;
const void *end;
} param_addKeyCode_t;
static int param_addKeyCode (const KeyNameEntry *kne, void *data)
{
if (kne) {
param_addKeyCode_t *akc = data;
if (akc->next < akc->end) {
brlapi_param_driverKeycode_t *key = akc->next;
*key = makeDriverKeyCode(kne->value.group, kne->value.number, 0);
akc->next += sizeof(*key);
}
}
return 1;
}
PARAM_READER(definedDriverKeycodes)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
KEY_NAME_TABLES_REFERENCE keys = brl.keyNames;
if (keys) {
*size /= sizeof(brlapi_param_driverKeycode_t);
*size *= sizeof(brlapi_param_driverKeycode_t);
param_addKeyCode_t akc = {
.next = data,
.end = data + *size
};
forEachKeyName(keys, param_addKeyCode, &akc);
*size = akc.next - data;
goto unlock;
}
}
*size = 0;
unlock:
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DRIVER_KEYCODE_NAME */
PARAM_READER(driverKeycodeName)
{
lockBrailleDriver();
if (isBrailleDriverConstructed()) {
KeyTable *table = brl.keyTable;
if (table) {
if (subparam <= UINT16_MAX) {
const KeyValue keyValue = {
.group = subparam >> 8,
.number = subparam & 0XFF
};
const KeyNameEntry *kne = findKeyNameEntry(table, &keyValue);
if (kne) {
param_readString(kne->name, data, size);
goto unlock;
}
}
}
}
*size = 0;
unlock:
unlockBrailleDriver();
return NULL;
}
/* BRLAPI_PARAM_DRIVER_KEYCODE_SUMMARY */
PARAM_READER(driverKeycodeSummary)
{
*size = 0;
return NULL;
}
/* BRLAPI_PARAM_COMPUTER_BRAILLE_ROWS_MASK */
PARAM_READER(computerBrailleRowsMask)
{
lockTextTable();
*size = getTextTableRowsMask(textTable, data, *size);
unlockTextTable();
return NULL;
}
/* BRLAPI_PARAM_COMPUTER_BRAILLE_ROW_CELLS */
PARAM_READER(computerBrailleRowCells)
{
brlapi_param_computerBrailleRowCells_t *computerBrailleRowCells = data;
int rowDefined;
lockTextTable();
rowDefined = getTextTableRowCells(
textTable, subparam,
computerBrailleRowCells->cells,
computerBrailleRowCells->defined
);
unlockTextTable();
if (rowDefined) {
*size = sizeof(*computerBrailleRowCells);
} else {
*size = 0;
}
return NULL;
}
/* BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE */
PARAM_READER(computerBrailleTable)
{
param_readString(opt_textTable, data, size);
return NULL;
}
PARAM_WRITER(computerBrailleTable)
{
return param_writeString(changeTextTable, data, size);
}
/* BRLAPI_PARAM_LITERARY_BRAILLE_TABLE */
PARAM_READER(literaryBrailleTable)
{
param_readString(opt_contractionTable, data, size);
return NULL;
}
PARAM_WRITER(literaryBrailleTable)
{
return param_writeString(changeContractionTable, data, size);
}
/* BRLAPI_PARAM_MESSAGE_LOCALE */
PARAM_READER(messageLocale)
{
param_readString(setlocale(LC_ALL, NULL), data, size);
return NULL;
}
PARAM_WRITER(messageLocale)
{
return param_writeString(changeMessageLocale, data, size);
}
typedef struct {
unsigned local:1;
unsigned global:1;
brlapi_param_t rootParameter;
ParamReader *read;
ParamWriter *write;
} ParamDispatch;
static const ParamDispatch paramDispatch[BRLAPI_PARAM_COUNT] = {
//Connection Parameters
[BRLAPI_PARAM_SERVER_VERSION] = {
.global = 1,
.read = param_serverVersion_read,
},
[BRLAPI_PARAM_CLIENT_PRIORITY] = {
.local = 1,
.read = param_clientPriority_read,
.write = param_clientPriority_write,
},
//Device Parameters
[BRLAPI_PARAM_DRIVER_NAME] = {
.global = 1,
.read = param_driverName_read,
},
[BRLAPI_PARAM_DRIVER_CODE] = {
.global = 1,
.read = param_driverCode_read,
},
[BRLAPI_PARAM_DRIVER_VERSION] = {
.global = 1,
.read = param_driverVersion_read,
},
[BRLAPI_PARAM_DEVICE_MODEL] = {
.global = 1,
.read = param_deviceModel_read,
},
[BRLAPI_PARAM_DEVICE_CELL_SIZE] = {
.global = 1,
.read = param_deviceCellSize_read,
},
[BRLAPI_PARAM_DISPLAY_SIZE] = {
.global = 1,
.read = param_displaySize_read,
},
[BRLAPI_PARAM_DEVICE_IDENTIFIER] = {
.global = 1,
.read = param_deviceIdentifier_read,
},
[BRLAPI_PARAM_DEVICE_SPEED] = {
.global = 1,
.read = param_deviceSpeed_read,
},
[BRLAPI_PARAM_DEVICE_ONLINE] = {
.global = 1,
.read = param_deviceOnline_read,
},
//Input Parameters
[BRLAPI_PARAM_RETAIN_DOTS] = {
.local = 1,
.read = param_retainDots_read,
.write = param_retainDots_write,
},
//Braille Rendering Parameters
[BRLAPI_PARAM_COMPUTER_BRAILLE_CELL_SIZE] = {
.global = 1,
.read = param_computerBrailleCellSize_read,
.write = param_computerBrailleCellSize_write,
},
[BRLAPI_PARAM_LITERARY_BRAILLE] = {
.global = 1,
.read = param_literaryBraille_read,
.write = param_literaryBraille_write,
},
[BRLAPI_PARAM_CURSOR_DOTS] = {
.global = 1,
.read = param_cursorDots_read,
.write = param_cursorDots_write,
},
[BRLAPI_PARAM_CURSOR_BLINK_PERIOD] = {
.global = 1,
.read = param_cursorBlinkPeriod_read,
.write = param_cursorBlinkPeriod_write,
},
[BRLAPI_PARAM_CURSOR_BLINK_PERCENTAGE] = {
.global = 1,
.read = param_cursorBlinkPercentage_read,
.write = param_cursorBlinkPercentage_write,
},
[BRLAPI_PARAM_RENDERED_CELLS] = {
.local = 1,
.read = param_renderedCells_read,
},
//Navigation Parameters
[BRLAPI_PARAM_SKIP_IDENTICAL_LINES] = {
.global = 1,
.read = param_skipIdenticalLines_read,
.write = param_skipIdenticalLines_write,
},
[BRLAPI_PARAM_AUDIBLE_ALERTS] = {
.global = 1,
.read = param_audibleAlerts_read,
.write = param_audibleAlerts_write,
},
//Clipboard Parameters
[BRLAPI_PARAM_CLIPBOARD_CONTENT] = {
.global = 1,
.read = param_clipboardContent_read,
.write = param_clipboardContent_write,
},
//TTY Mode Parameters
[BRLAPI_PARAM_BOUND_COMMAND_KEYCODES] = {
.global = 1,
.read = param_boundCommandKeycodes_read,
},
[BRLAPI_PARAM_COMMAND_KEYCODE_NAME] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_BOUND_COMMAND_KEYCODES,
.read = param_commandKeycodeName_read,
},
[BRLAPI_PARAM_COMMAND_KEYCODE_SUMMARY] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_BOUND_COMMAND_KEYCODES,
.read = param_commandKeycodeSummary_read,
},
[BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES] = {
.global = 1,
.read = param_definedDriverKeycodes_read,
},
[BRLAPI_PARAM_DRIVER_KEYCODE_NAME] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES,
.read = param_driverKeycodeName_read,
},
[BRLAPI_PARAM_DRIVER_KEYCODE_SUMMARY] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_DEFINED_DRIVER_KEYCODES,
.read = param_driverKeycodeSummary_read,
},
//Braille Translation Parameters
[BRLAPI_PARAM_COMPUTER_BRAILLE_ROWS_MASK] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE,
.read = param_computerBrailleRowsMask_read,
},
[BRLAPI_PARAM_COMPUTER_BRAILLE_ROW_CELLS] = {
.global = 1,
.rootParameter = BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE,
.read = param_computerBrailleRowCells_read,
},
[BRLAPI_PARAM_COMPUTER_BRAILLE_TABLE] = {
.global = 1,
.read = param_computerBrailleTable_read,
.write = param_computerBrailleTable_write,
},
[BRLAPI_PARAM_LITERARY_BRAILLE_TABLE] = {
.global = 1,
.read = param_literaryBrailleTable_read,
.write = param_literaryBrailleTable_write,
},
[BRLAPI_PARAM_MESSAGE_LOCALE] = {
.global = 1,
.read = param_messageLocale_read,
.write = param_messageLocale_write,
},
};
static inline const ParamDispatch *param_getDispatch(brlapi_param_t parameter)
{
if (parameter >= ARRAY_COUNT(paramDispatch)) return NULL;
return &paramDispatch[parameter];
}
static int checkParamLocalGlobal(Connection *c, brlapi_param_t param, brlapi_param_flags_t flags)
{
if (flags & BRLAPI_PARAMF_GLOBAL) {
if (!paramDispatch[param].global) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u does not make sense globally", param);
return 0;
}
} else {
if (!paramDispatch[param].local) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u does not make sense locally", param);
return 0;
}
}
return 1;
}
static int handleParamValue(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
brlapi_paramValuePacket_t *paramValue = &packet->paramValue;
brlapi_param_t param;
brlapi_param_subparam_t subparam;
brlapi_param_flags_t flags;
CHECKERR( (size >= sizeof(flags) + sizeof(param) + sizeof(subparam)), BRLAPI_ERROR_INVALID_PACKET, "wrong size for paramValue packet");
flags = ntohl(paramValue->flags);
param = ntohl(paramValue->param);
if (param >= sizeof(paramDispatch) / sizeof(*paramDispatch)) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "unknown parameter %u", param);
return 0;
}
ParamWriter *writeHandler = paramDispatch[param].write;
/* Check against read-only parameters */
if (!writeHandler) {
WERR(c->fd, BRLAPI_ERROR_READONLY_PARAMETER, "parameter %u not available for writing", param);
return 0;
}
if (!checkParamLocalGlobal(c, param, flags))
return 0;
subparam = ((brlapi_param_subparam_t)ntohl(paramValue->subparam_hi) << 32) || ntohl(paramValue->subparam_lo);
size -= sizeof(flags) + sizeof(param) + sizeof(subparam);
_brlapi_ntohParameter(param, paramValue, size);
{
const char *error;
lockMutex(&apiParamMutex);
paramUpdateConnection = c;
error = writeHandler(c, param, subparam, flags, paramValue->data, size);
paramUpdateConnection = NULL;
unlockMutex(&apiParamMutex);
if (error) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u write error: %s", param, error);
return 0;
}
}
if (!(flags & BRLAPI_PARAMF_GLOBAL)) {
handleParamUpdate(c, c, param, subparam, flags, paramValue->data, size);
}
writeAck(c->fd);
return 0;
}
/* sendConnectionParamUpdate: Send the parameter update to a connection */
static void sendConnectionParamUpdate(Connection *c, brlapi_param_t param, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, brlapi_paramValuePacket_t *paramValue, size_t size)
{
struct Subscription *s;
for (s=c->subscriptions.next; s!=&c->subscriptions; s=s->next) {
if (s->parameter == param
&& s->subparam == subparam
&& (s->flags & BRLAPI_PARAMF_GLOBAL) == (flags & BRLAPI_PARAMF_GLOBAL)
&& ((s->flags & BRLAPI_PARAMF_SELF) || (paramUpdateConnection != c)))
{
logMessage(LOG_CATEGORY(SERVER_EVENTS), "writing parameter %"PRIx32" update to fd %"PRIfd,param,c->fd);
brlapiserver_writePacket(c->fd,BRLAPI_PACKET_PARAM_UPDATE,paramValue,size);
break;
}
}
}
/* sendParamUpdate: Send the parameter update to connections bound to a given tree of ttys */
static void sendParamUpdate(Tty *tty, brlapi_param_t param, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, brlapi_paramValuePacket_t *paramValue, size_t size)
{
Connection *c;
Tty *t;
for (c = tty->connections->next; c!=tty->connections; c = c->next)
sendConnectionParamUpdate(c, param, subparam, flags, paramValue, size);
for (t = tty->subttys; t; t = t->next)
sendParamUpdate(t, param, subparam, flags, paramValue, size);
}
/* handleParamUpdate: Prepare and send the parameter update to all connections */
static void __handleParamUpdate(Connection *dest, brlapi_param_t param, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void *data, size_t size)
{
brlapi_packet_t response;
brlapi_paramValuePacket_t *paramValue = &response.paramValue;
unsigned char *p = paramValue->data;
paramValue->flags = htonl(flags);
paramValue->param = htonl(param);
paramValue->subparam_hi = htonl(subparam >> 32);
paramValue->subparam_lo = htonl(subparam & 0xfffffffful);
memcpy(p, data, size);
_brlapi_htonParameter(param, paramValue, size);
size += sizeof(flags) + sizeof(param) + sizeof(subparam);
if (!(flags & BRLAPI_PARAMF_GLOBAL)) {
sendConnectionParamUpdate(dest,param,subparam,flags,paramValue,size);
} else {
lockMutex(&apiConnectionsMutex);
sendParamUpdate(&ttys,param,subparam,flags,paramValue,size);
sendParamUpdate(&notty,param,subparam,flags,paramValue,size);
unlockMutex(&apiConnectionsMutex);
}
}
static void handleParamUpdate(Connection *source, Connection *dest, brlapi_param_t param, brlapi_param_subparam_t subparam, brlapi_param_flags_t flags, const void *data, size_t size)
{
lockMutex(&apiParamMutex);
paramUpdateConnection = source;
if (((flags & BRLAPI_PARAMF_GLOBAL) && paramState[param].global_subscriptions)
|| (!(flags & BRLAPI_PARAMF_GLOBAL) && paramState[param].local_subscriptions))
__handleParamUpdate(dest, param, subparam, flags, data, size);
paramUpdateConnection = NULL;
unlockMutex(&apiParamMutex);
}
void api_updateParameter(brlapi_param_t parameter, brlapi_param_subparam_t subparam)
{
const ParamDispatch *pd = param_getDispatch(parameter);
if (pd) {
if (pd->global) {
ParamReader *readHandler = pd->read;
if (readHandler) {
lockMutex(&apiParamMutex);
{
if (paramState[parameter].global_subscriptions) {
unsigned char data[BRLAPI_MAXPARAMSIZE];
size_t size = sizeof(data);
const char *error = readHandler(NULL, parameter, subparam, BRLAPI_PARAMF_GLOBAL, data, &size);
if (error) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "parameter %u read error: %s", parameter, error);
} else {
__handleParamUpdate(NULL, parameter, subparam, BRLAPI_PARAMF_GLOBAL, data, size);
}
}
}
unlockMutex(&apiParamMutex);
reportParameterUpdated(parameter, subparam);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "parameter %u is not readable", parameter);
}
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "parameter %u is not global", parameter);
}
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "parameter %u is out of range", parameter);
}
}
static int handleParamRequest(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
brlapi_paramRequestPacket_t *paramRequest = &packet->paramRequest;
brlapi_param_t param;
brlapi_param_subparam_t subparam;
brlapi_param_flags_t flags;
CHECKERR( (size == sizeof(brlapi_paramRequestPacket_t)), BRLAPI_ERROR_INVALID_PACKET, "wrong size for paramRequest packet: %lu vs %lu", (unsigned long) size, (unsigned long) sizeof(brlapi_paramRequestPacket_t));
flags = ntohl(paramRequest->flags);
param = ntohl(paramRequest->param);
if (param >= sizeof(paramDispatch) / sizeof(*paramDispatch)) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "unknown parameter %u", param);
return 0;
}
ParamReader *readHandler = paramDispatch[param].read;
/* Check against non-readable parameters */
if (!readHandler) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u not available for reading", param);
return 0;
}
if (!checkParamLocalGlobal(c, param, flags))
return 0;
subparam = (brlapi_param_subparam_t)ntohl(paramRequest->subparam_hi) << 32 | ntohl(paramRequest->subparam_lo);
if ((flags & BRLAPI_PARAMF_SUBSCRIBE) &&
(flags & BRLAPI_PARAMF_UNSUBSCRIBE)) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "subscribe and unsubscribe flags both set");
return 0;
}
lockMutex(&apiParamMutex);
if (flags & BRLAPI_PARAMF_SUBSCRIBE) {
/* subscribe to parameter updates */
{
brlapi_param_t root = paramDispatch[param].rootParameter;
if (root) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u not available for watching - %u should be watched instead", param, root);
unlockMutex(&apiParamMutex);
return 0;
}
}
struct Subscription *s;
lockMutex(&apiConnectionsMutex);
if (flags & BRLAPI_PARAMF_GLOBAL)
paramState[param].global_subscriptions++;
else
paramState[param].local_subscriptions++;
s = malloc(sizeof(*s));
s->parameter = param;
s->subparam = subparam;
s->flags = flags;
s->next = c->subscriptions.next;
s->prev = &c->subscriptions;
s->next->prev = s;
s->prev->next = s;
unlockMutex(&apiConnectionsMutex);
} else if (flags & BRLAPI_PARAMF_UNSUBSCRIBE) {
/* unsubscribe from parameter updates */
struct Subscription *s;
lockMutex(&apiConnectionsMutex);
for (s = c->subscriptions.next; s!=&c->subscriptions; s=s->next) {
if (s->parameter == param
&& (s->flags & BRLAPI_PARAMF_GLOBAL) == (flags & BRLAPI_PARAMF_GLOBAL))
break;
}
if (s != &c->subscriptions) {
if (flags & BRLAPI_PARAMF_GLOBAL)
paramState[param].global_subscriptions--;
else
paramState[param].local_subscriptions--;
s->next->prev = s->prev;
s->prev->next = s->next;
free(s);
} else {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "was not subscribed");
unlockMutex(&apiParamMutex);
unlockMutex(&apiConnectionsMutex);
return 0;
}
unlockMutex(&apiConnectionsMutex);
}
if (flags & BRLAPI_PARAMF_GET) { /* Ack by sending parameter value */
brlapi_packet_t response;
brlapi_paramValuePacket_t *paramValue = &response.paramValue;
paramValue->flags = htonl(flags & BRLAPI_PARAMF_GLOBAL);
paramValue->param = paramRequest->param;
paramValue->subparam_hi = paramRequest->subparam_hi;
paramValue->subparam_lo = paramRequest->subparam_lo;
size = sizeof(paramValue->data);
const char *error = readHandler(c, param, subparam, flags, paramValue->data, &size);
if (error) {
WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u read error: %s", param, error);
} else {
_brlapi_htonParameter(param, paramValue, size);
size += sizeof(flags) + sizeof(param) + sizeof(subparam);
brlapiserver_writePacket(c->fd,BRLAPI_PACKET_PARAM_VALUE,paramValue,size);
}
} else { /* Ack with ack */
writeAck(c->fd);
}
unlockMutex(&apiParamMutex);
return 0;
}
static int handleSync(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
writeAck(c->fd);
return 0;
}
static PacketHandlers packetHandlers = {
handleGetDriverName, handleGetModelIdentifier, handleGetDisplaySize,
handleEnterTtyMode, handleSetFocus, handleLeaveTtyMode,
handleKeyRanges, handleKeyRanges, handleWrite,
handleEnterRawMode, handleLeaveRawMode, handlePacket,
handleSuspendDriver, handleResumeDriver,
handleParamValue, handleParamRequest,
handleSync,
};
static void handleNewConnection(Connection *c)
{
brlapi_packet_t versionPacket;
versionPacket.version.protocolVersion = htonl(BRLAPI_PROTOCOL_VERSION);
brlapiserver_writePacket(c->fd,BRLAPI_PACKET_VERSION,&versionPacket.data,sizeof(versionPacket.version));
}
static int
hasKeyFile(const char *auth)
{
if (isAbsolutePath(auth))
return 1;
if (!strncmp(auth,"keyfile:", 8))
return 1;
if (strstr(auth,"+keyfile:"))
return 1;
return 0;
}
/* Function : handleUnauthorizedConnection */
/* Returns 1 if connection has to be removed */
static int handleUnauthorizedConnection(Connection *c, brlapi_packetType_t type, brlapi_packet_t *packet, size_t size)
{
if (c->auth == -1) {
if (type != BRLAPI_PACKET_VERSION) {
WERR(c->fd, BRLAPI_ERROR_PROTOCOL_VERSION, "wrong packet type (should be version)");
return 1;
}
{
brlapi_versionPacket_t *versionPacket = &packet->version;
brlapi_packet_t serverPacket;
brlapi_authServerPacket_t *authPacket = &serverPacket.authServer;
int nbmethods = 0;
if (size<sizeof(*versionPacket)) {
WERR(c->fd, BRLAPI_ERROR_PROTOCOL_VERSION, "wrong protocol version");
return 1;
}
c->clientVersion = ntohl(versionPacket->protocolVersion);
if (c->clientVersion < 8) {
/* We only provide compatibility with version 8 and later. */
WERR(c->fd, BRLAPI_ERROR_PROTOCOL_VERSION, "protocol version %"PRIu32" < 8 is not supported", c->clientVersion);
return 1;
}
/* TODO: move this inside auth.c */
if (authDescriptor && authPerform(authDescriptor, c->fd)) {
authPacket->type[nbmethods++] = htonl(BRLAPI_AUTH_NONE);
unauthConnections--;
c->auth = 1;
} else {
if (hasKeyFile(auth))
authPacket->type[nbmethods++] = htonl(BRLAPI_AUTH_KEY);
c->auth = 0;
}
brlapiserver_writePacket(c->fd,BRLAPI_PACKET_AUTH,&serverPacket,nbmethods*sizeof(authPacket->type));
return 0;
}
}
if (type!=BRLAPI_PACKET_AUTH) {
WERR(c->fd, BRLAPI_ERROR_PROTOCOL_VERSION, "wrong packet type (should be auth)");
return 1;
}
{
size_t authKeyLength = 0;
brlapi_packet_t authKey;
int authCorrect = 0;
brlapi_authClientPacket_t *authPacket = &packet->authClient;
int remaining = size;
uint32_t authType;
if (!strcmp(auth,"none"))
authCorrect = 1;
else {
authType = ntohl(authPacket->type);
remaining -= sizeof(authPacket->type);
/* TODO: move this inside auth.c */
switch(authType) {
case BRLAPI_AUTH_NONE:
if (authDescriptor) authCorrect = authPerform(authDescriptor, c->fd);
break;
case BRLAPI_AUTH_KEY:
if (hasKeyFile(auth)) {
char *path = brlapiserver_getKeyFile(auth);
int ret = brlapiserver_loadAuthKey(path,&authKeyLength,&authKey);
if (ret==-1) {
logMessage(LOG_WARNING,"Unable to load API authorization key from %s: %s in %s. You may use parameter auth=none if you don't want any authorization (dangerous)", path, strerror(brlapi_libcerrno), brlapi_errfun);
free(path);
break;
}
free(path);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "authorization key loaded");
authCorrect = (remaining==authKeyLength) && (!memcmp(&authPacket->key, &authKey, authKeyLength));
memset(&authKey, 0, authKeyLength);
memset(&authPacket->key, 0, remaining);
}
break;
default:
logMessage(LOG_CATEGORY(SERVER_EVENTS), "unsupported authorization method %"PRId32, authType);
break;
}
}
if (!authCorrect) {
writeError(c->fd, BRLAPI_ERROR_AUTHENTICATION);
logMessage(LOG_WARNING, "BrlAPI connection fd=%"PRIfd" failed authorization", c->fd);
return 0;
}
unauthConnections--;
writeAck(c->fd);
c->auth = 1;
return 0;
}
}
/* Function : processRequest */
/* Reads a packet fro c->fd and processes it */
/* Returns 1 if connection has to be removed */
/* If EOF is reached, closes fd and frees all associated resources */
static int processRequest(Connection *c, PacketHandlers *handlers)
{
PacketHandler p = NULL;
int res;
ssize_t size;
brlapi_packet_t *packet = (brlapi_packet_t *) c->packet.content;
brlapi_packetType_t type;
res = brlapi__readPacket(&c->packet, c->fd);
if (res==0) return 0; /* No packet ready */
if (res<0) {
if (res==-1) {
logMessage(LOG_WARNING,"read : %s (connection on fd %"PRIfd")",strerror(errno),c->fd);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "closing connection on fd %"PRIfd,c->fd);
}
if (c->raw) {
c->raw = 0;
lockMutex(&apiRawMutex);
rawConnection = NULL;
unlockMutex(&apiRawMutex);
logMessage(LOG_WARNING,"Client on fd %"PRIfd" did not give up raw mode properly",c->fd);
resetDevice();
} else if (c->suspend) {
c->suspend = 0;
lockMutex(&apiRawMutex);
suspendConnection = NULL;
unlockMutex(&apiRawMutex);
logMessage(LOG_WARNING,"Client on fd %"PRIfd" did not give up suspended mode properly",c->fd);
if (!resumeDriver())
logMessage(LOG_WARNING,"Couldn't resume braille driver");
}
if (c->tty) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "client on fd %"PRIfd" did not give up control of tty %#010x properly",c->fd,c->tty->number);
doLeaveTty(c);
}
return 1;
}
size = c->packet.header.size;
type = c->packet.header.type;
if (c->auth!=1) return handleUnauthorizedConnection(c, type, packet, size);
if (size>BRLAPI_MAXPACKETSIZE) {
logMessage(LOG_WARNING, "Discarding too large packet of type %s on fd %"PRIfd,brlapiserver_getPacketTypeName(type), c->fd);
return 0;
}
switch (type) {
case BRLAPI_PACKET_GETDRIVERNAME: p = handlers->getDriverName; break;
case BRLAPI_PACKET_GETMODELID: p = handlers->getModelIdentifier; break;
case BRLAPI_PACKET_GETDISPLAYSIZE: p = handlers->getDisplaySize; break;
case BRLAPI_PACKET_ENTERTTYMODE: p = handlers->enterTtyMode; break;
case BRLAPI_PACKET_SETFOCUS: p = handlers->setFocus; break;
case BRLAPI_PACKET_LEAVETTYMODE: p = handlers->leaveTtyMode; break;
case BRLAPI_PACKET_IGNOREKEYRANGES: p = handlers->ignoreKeyRanges; break;
case BRLAPI_PACKET_ACCEPTKEYRANGES: p = handlers->acceptKeyRanges; break;
case BRLAPI_PACKET_WRITE: p = handlers->write; break;
case BRLAPI_PACKET_ENTERRAWMODE: p = handlers->enterRawMode; break;
case BRLAPI_PACKET_LEAVERAWMODE: p = handlers->leaveRawMode; break;
case BRLAPI_PACKET_PACKET: p = handlers->packet; break;
case BRLAPI_PACKET_SUSPENDDRIVER: p = handlers->suspendDriver; break;
case BRLAPI_PACKET_RESUMEDRIVER: p = handlers->resumeDriver; break;
case BRLAPI_PACKET_PARAM_VALUE: p = handlers->parameterValue; break;
case BRLAPI_PACKET_PARAM_REQUEST: p = handlers->parameterRequest; break;
case BRLAPI_PACKET_SYNCHRONIZE: p = handlers->sync; break;
}
if (p!=NULL) {
logRequest(type, c->fd);
p(c, type, packet, size);
} else {
WEXC(c->fd,BRLAPI_ERROR_UNKNOWN_INSTRUCTION, type, packet, size, "unknown packet type %x", type);
}
return 0;
}
/****************************************************************************/
/** SOCKETS AND CONNECTIONS MANAGING **/
/****************************************************************************/
/*
* There is one server thread which first launches binding threads and then
* enters infinite loop trying to accept connections, read packets, etc.
*
* Binding threads loop trying to establish some socket, waiting for
* filesystems to be read/write or network to be configured.
*
* On windows, WSAEventSelect() is emulated by a standalone thread.
*/
/* Function: loopBind */
/* tries binding while temporary errors occur */
static int loopBind(SocketDescriptor fd, const struct sockaddr *address, socklen_t length)
{
char buffer[0X100] = {0};
const int maximum = 100;
int delay = 1;
int res;
while (1) {
{
lockUmask();
mode_t originalMask = umask(0);
res = bind(fd, address, length);
umask(originalMask);
unlockUmask();
}
if (res != -1) break;
if (!running) break;
if (
#ifdef EADDRNOTAVAIL
(errno != EADDRNOTAVAIL) &&
#endif /* EADDRNOTAVAIL */
#ifdef EADDRINUSE
(errno != EADDRINUSE) &&
#endif /* EADDRINUSE */
(errno != EROFS)) {
break;
}
if (!buffer[0]) {
formatAddress(buffer, sizeof(buffer), address, length);
}
logMessage(LOG_CATEGORY(SERVER_EVENTS), "bind waiting: %s: %s", buffer, strerror(errno));
approximateDelay(delay * MSECS_PER_SEC);
delay <<= 1;
delay = MIN(delay, maximum);
}
return res;
}
static SocketDescriptor newTcpSocket(int family, int type, int protocol, const struct sockaddr *addr, socklen_t len)
{
static const int yes = 1;
SocketDescriptor fd = socket(family, type, protocol);
if (fd != INVALID_SOCKET_DESCRIPTOR) {
//setCloseOnExec(fd, 1);
#ifdef SO_REUSEADDR
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&yes, sizeof(yes)) == -1) {
LogSocketError("setsockopt[SOCKET,REUSEADDR]");
}
#endif /* SO_REUSEADDR */
#ifdef SOL_TCP
#ifdef TCP_NODELAY
if (setsockopt(fd, SOL_TCP, TCP_NODELAY,
(const void *)&yes, sizeof(yes)) == -1) {
LogSocketError("setsockopt[TCP,NODELAY]");
}
#endif /* TCP_NODELAY */
#endif /* SOL_TCP */
if (loopBind(fd, addr, len) != -1) {
if (listen(fd, 1) != -1) {
return fd;
} else {
LogSocketError("listen");
}
} else {
LogSocketError("bind");
}
closeSocketDescriptor(fd);
} else {
setSocketErrno();
#ifdef EAFNOSUPPORT
if (errno != EAFNOSUPPORT)
#endif /* EAFNOSUPPORT */
logMessage(LOG_WARNING, "socket allocation error: %s", strerror(errno));
}
return INVALID_SOCKET_DESCRIPTOR;
}
/* Function : createTcpSocket */
/* Creates the listening socket for in-connections */
/* Returns the descriptor, or -1 if an error occurred */
static FileDescriptor createTcpSocket(struct socketInfo *info)
{
SocketDescriptor fd = INVALID_SOCKET_DESCRIPTOR;
#ifdef __MINGW32__
if (getaddrinfoProc) {
#endif /* __MINGW32__ */
#if defined(HAVE_GETADDRINFO) || defined(__MINGW32__)
static const struct addrinfo hints = {
.ai_flags = AI_PASSIVE,
.ai_family = PF_UNSPEC,
.ai_socktype = SOCK_STREAM
};
struct addrinfo *res, *cur;
int err = getaddrinfo(info->host, info->port, &hints, &res);
if (err) {
logMessage(LOG_WARNING,
"getaddrinfo(%s,%s): "
#ifdef HAVE_GAI_STRERROR
"%s"
#else /* HAVE_GAI_STRERROR */
"%d"
#endif /* HAVE_GAI_STRERROR */
, info->host
, info->port
#ifdef HAVE_GAI_STRERROR
,
#ifdef EAI_SYSTEM
(err == EAI_SYSTEM)? strerror(errno):
#endif /* EAI_SYSTEM */
gai_strerror(err)
#else /* HAVE_GAI_STRERROR */
, err
#endif /* HAVE_GAI_STRERROR */
);
return INVALID_FILE_DESCRIPTOR;
}
for (cur = res; cur; cur = cur->ai_next) {
fd = newTcpSocket(cur->ai_family, cur->ai_socktype, cur->ai_protocol,
cur->ai_addr, cur->ai_addrlen);
if (fd != INVALID_SOCKET_DESCRIPTOR) break;
}
if (!cur) fd = INVALID_SOCKET_DESCRIPTOR;
freeaddrinfo(res);
#endif /* HAVE_GETADDRINFO */
#ifdef __MINGW32__
} else {
#endif /* __MINGW32__ */
#if !defined(HAVE_GETADDRINFO) || defined(__MINGW32__)
struct hostent *he;
struct sockaddr_in addr = {
.sin_family = AF_INET
};
if (!info->port) {
addr.sin_port = htons(BRLAPI_SOCKETPORTNUM);
} else {
unsigned int port;
if (!isUnsignedInteger(&port, info->port)) {
struct servent *se;
if (!(se = getservbyname(info->port, "tcp"))) {
logMessage(LOG_ERR,
"TCP port %s: "
#ifdef __MINGW32__
"%d"
#else /* __MINGW32__ */
"%s"
#endif /* __MINGW32__ */
, info->port
#ifdef __MINGW32__
, WSAGetLastError()
#else /* __MINGW32__ */
, hstrerror(h_errno)
#endif /* __MINGW32__ */
);
return INVALID_FILE_DESCRIPTOR;
}
addr.sin_port = se->s_port;
} else if ((port > 0) && (port <= UINT16_MAX)) {
addr.sin_port = htons(port);
} else {
logMessage(LOG_ERR, "invalid TCP port: %s", info->port);
return INVALID_FILE_DESCRIPTOR;
}
}
if (!info->host) {
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
} else if ((addr.sin_addr.s_addr = inet_addr(info->host)) == htonl(INADDR_NONE)) {
if (!(he = gethostbyname(info->host))) {
logMessage(LOG_ERR,
"gethostbyname(%s): "
#ifdef __MINGW32__
"%d"
#else /* __MINGW32__ */
"%s"
#endif /* __MINGW32__ */
, info->host
#ifdef __MINGW32__
, WSAGetLastError()
#else /* __MINGW32__ */
, hstrerror(h_errno)
#endif /* __MINGW32__ */
);
return INVALID_FILE_DESCRIPTOR;
}
if (he->h_addrtype != AF_INET) {
#ifdef EAFNOSUPPORT
errno = EAFNOSUPPORT;
#else /* EAFNOSUPPORT */
errno = EINVAL;
#endif /* EAFNOSUPPORT */
logMessage(LOG_ERR, "unknown address type %d", he->h_addrtype);
return INVALID_FILE_DESCRIPTOR;
}
if (he->h_length > sizeof(addr.sin_addr)) {
errno = EINVAL;
logMessage(LOG_ERR, "address too big: %d", he->h_length);
return INVALID_FILE_DESCRIPTOR;
}
memcpy(&addr.sin_addr, he->h_addr, he->h_length);
}
fd = newTcpSocket(PF_INET, SOCK_STREAM, IPPROTO_TCP,
(struct sockaddr *)&addr, sizeof(addr));
#endif /* !HAVE_GETADDRINFO */
#ifdef __MINGW32__
}
#endif /* __MINGW32__ */
if (fd == INVALID_SOCKET_DESCRIPTOR) {
logMessage(LOG_WARNING,"unable to find a local TCP port %s:%s !",info->host,info->port);
}
if (info->host) {
free(info->host);
info->host = NULL;
}
if (info->port) {
free(info->port);
info->port = NULL;
}
if (fd == INVALID_SOCKET_DESCRIPTOR) {
return INVALID_FILE_DESCRIPTOR;
}
#ifdef __MINGW32__
if (!(info->overl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
logWindowsSystemError("CreateEvent");
closeSocketDescriptor(fd);
return INVALID_FILE_DESCRIPTOR;
}
logMessage(LOG_CATEGORY(SERVER_EVENTS), "event -> %p", info->overl.hEvent);
WSAEventSelect(fd, info->overl.hEvent, FD_ACCEPT);
#endif /* __MINGW32__ */
return (FileDescriptor)fd;
}
#if defined(PF_LOCAL)
#ifndef __MINGW32__
static int readPid(char *path)
/* read pid from specified file. Return 0 on any error */
{
char pids[16], *ptr;
pid_t pid;
int n;
FileDescriptor fd;
{
int openFlags = O_RDONLY;
#ifdef O_CLOEXEC
openFlags |= O_CLOEXEC;
#endif /* O_CLOEXEC */
fd = open(path, openFlags);
}
if (fd == -1) return 0;
n = read(fd, pids, sizeof(pids)-1);
closeFileDescriptor(fd);
if (n == -1) return 0;
pids[n] = 0;
pid = strtol(pids, &ptr, 10);
if (ptr != &pids[n]) return 0;
return pid;
}
static int
adjustPermissions (
const void *object, const char *container,
int (*getStatus) (const void *object, struct stat *status),
int (*setPermissions) (const void *object, mode_t permissions)
) {
uid_t user = geteuid();
int adjust = !user;
if (!adjust) {
struct stat status;
if (stat(container, &status) == -1) {
logSystemError("stat");
} else if (status.st_uid == user) {
adjust = 1;
}
}
if (adjust) {
struct stat status;
if (!getStatus(object, &status)) return 0;
{
mode_t oldPermissions = status.st_mode & ~S_IFMT;
mode_t newPermissions = oldPermissions;
#ifdef S_IRGRP
if (oldPermissions & S_IRUSR) newPermissions |= S_IRGRP | S_IROTH;
if (oldPermissions & S_IWUSR) newPermissions |= S_IWGRP | S_IWOTH;
if (oldPermissions & S_IXUSR) newPermissions |= S_IXGRP | S_IXOTH;
if (S_ISDIR(status.st_mode)) newPermissions |= S_ISVTX | S_IXOTH;
#endif
if (newPermissions != oldPermissions) {
if (!setPermissions(object, newPermissions)) {
return 0;
}
}
}
}
return 1;
}
static int
getPathStatus (const void *object, struct stat *status) {
const char *path = object;
if (stat(path, status) != -1) return 1;
logSystemError("stat");
return 0;
}
static int
setPathPermissions (const void *object, mode_t permissions) {
const char *path = object;
lockUmask();
int changed = chmod(path, permissions) != -1;
unlockUmask();
if (!changed) logSystemError("chmod");
return changed;
}
static int
adjustPathPermissions (const char *path) {
int ok = 0;
char *parent = getPathDirectory(path);
if (parent) {
if (adjustPermissions(path, parent, getPathStatus, setPathPermissions)) ok = 1;
free(parent);
}
return ok;
}
static int
getFileStatus (const void *object, struct stat *status) {
const int *fd = object;
if (fstat(*fd, status) != -1) return 1;
logSystemError("fstat");
return 0;
}
static int
setFilePermissions (const void *object, mode_t permissions) {
const int *fd = object;
lockUmask();
int changed = fchmod(*fd, permissions) != -1;
unlockUmask();
if (!changed) logSystemError("fchmod");
return changed;
}
static int
adjustFilePermissions (int fd, const char *directory) {
return adjustPermissions(
&fd, directory,
getFileStatus,
setFilePermissions
);
}
#endif /* __MINGW32__ */
/* Function : createLocalSocket */
/* Creates the listening socket for in-connections */
/* Returns 1, or 0 if an error occurred */
static FileDescriptor createLocalSocket(struct socketInfo *info)
{
int lpath=strlen(BRLAPI_SOCKETPATH),lport=strlen(info->port);
FileDescriptor fd;
#ifdef __MINGW32__
char path[lpath+lport+1];
memcpy(path,BRLAPI_SOCKETPATH,lpath);
memcpy(path+lpath,info->port,lport+1);
if ((fd = CreateNamedPipe(path,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
BRLAPI_MAXPACKETSIZE + sizeof(brlapi_header_t),
BRLAPI_MAXPACKETSIZE + sizeof(brlapi_header_t),
0, NULL)) == INVALID_HANDLE_VALUE) {
if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED)
logWindowsSystemError("CreateNamedPipe");
goto out;
}
logMessage(LOG_CATEGORY(SERVER_EVENTS), "CreateFile -> %"PRIfd,fd);
if (!info->overl.hEvent) {
if (!(info->overl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) {
logWindowsSystemError("CreateEvent");
goto outfd;
}
logMessage(LOG_CATEGORY(SERVER_EVENTS), "event -> %p",info->overl.hEvent);
}
if (!(ResetEvent(info->overl.hEvent))) {
logWindowsSystemError("ResetEvent");
goto outfd;
}
if (ConnectNamedPipe(fd, &info->overl)) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "already connected !");
return fd;
}
switch(GetLastError()) {
case ERROR_IO_PENDING: return fd;
case ERROR_PIPE_CONNECTED: SetEvent(info->overl.hEvent); return fd;
default: logWindowsSystemError("ConnectNamedPipe");
}
CloseHandle(info->overl.hEvent);
#else /* __MINGW32__ */
struct sockaddr_un sa;
char tmppath[lpath+lport+4];
char lockpath[lpath+lport+3];
struct stat st;
char pids[16];
pid_t pid;
int lock,n,done,res;
mode_t permissions = S_IRWXU | S_IRWXG | S_IRWXO;
if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0))==-1) {
logSystemError("socket");
goto out;
}
setCloseOnExec(fd, 1);
sa.sun_family = AF_LOCAL;
if (lpath+lport+1>sizeof(sa.sun_path)) {
logMessage(LOG_ERR, "Unix path too long");
goto outfd;
}
while (1) {
{
lockUmask();
int mkdirResult = mkdir(BRLAPI_SOCKETPATH, (permissions | S_ISVTX));
unlockUmask();
if (mkdirResult != -1) break;
}
if (!running) goto outfd;
if (errno == EEXIST) break;
if ((errno != EROFS) && (errno != ENOENT)) {
logSystemError("making socket directory");
goto outfd;
}
/* read-only, or not mounted yet, wait */
approximateDelay(1000);
}
if (!adjustPathPermissions(BRLAPI_SOCKETPATH)) {
goto outfd;
}
memcpy(sa.sun_path,BRLAPI_SOCKETPATH "/",lpath+1);
memcpy(sa.sun_path+lpath+1,info->port,lport+1);
memcpy(tmppath, BRLAPI_SOCKETPATH "/", lpath+1);
tmppath[lpath+1]='.';
memcpy(tmppath+lpath+2, info->port, lport);
memcpy(lockpath, tmppath, lpath+2+lport);
tmppath[lpath+2+lport]='_';
tmppath[lpath+2+lport+1]=0;
lockpath[lpath+2+lport]=0;
while (1) {
{
lockUmask();
{
int openFlags = O_WRONLY | O_CREAT | O_EXCL;
#ifdef O_CLOEXEC
openFlags |= O_CLOEXEC;
#endif /* O_CLOEXEC */
lock = open(
tmppath, openFlags,
(permissions & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))
);
}
unlockUmask();
}
if (lock != -1) break;
if (!running) goto outfd;
if (errno == EROFS) {
approximateDelay(1000);
continue;
}
if (errno != EEXIST) {
logSystemError("opening local socket lock");
goto outfd;
}
if ((pid = readPid(tmppath)) && pid != getpid()
&& (kill(pid, 0) != -1 || errno != ESRCH)) {
logMessage(LOG_ERR,"another BrlAPI server is already listening on %s (file %s exists)",info->port, tmppath);
goto outfd;
}
/* bogus file, myself or non-existent process, remove */
while (unlink(tmppath)) {
if (errno != EROFS) {
logSystemError("removing stale local socket lock");
goto outfd;
}
approximateDelay(1000);
}
}
n = snprintf(pids,sizeof(pids),"%d",getpid());
done = 0;
while ((res = write(lock,pids+done,n)) < n) {
if (!running) goto outlockfd;
if (res == -1) {
if (errno != ENOSPC) {
logSystemError("writing pid in local socket lock");
goto outlockfd;
}
approximateDelay(1000);
} else {
done += res;
n -= res;
}
}
while (1) {
if (!running) goto outtmp;
if (link(tmppath, lockpath) == -1) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "linking local socket lock: %s", strerror(errno));
/* but no action: link() might erroneously return errors, see manpage */
}
if (fstat(lock, &st) == -1) {
logSystemError("checking local socket lock");
goto outtmp;
}
if (st.st_nlink == 2) {
/* success */
break;
}
/* failed to link */
if ((pid = readPid(lockpath)) && pid != getpid()
&& (kill(pid, 0) != -1 || errno != ESRCH)) {
logMessage(LOG_ERR,"another BrlAPI server is already listening on %s (file %s exists)",info->port, lockpath);
goto outtmp;
}
/* bogus file, myself or non-existent process, remove */
if (unlink(lockpath)) {
logSystemError("removing stale local socket lock");
goto outtmp;
}
}
closeFileDescriptor(lock);
if (unlink(tmppath) == -1) {
logSystemError("removing temp local socket lock");
}
if (unlink(sa.sun_path) && errno != ENOENT) {
logSystemError("removing old socket");
goto outfd;
}
if (loopBind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
logMessage(LOG_WARNING, "bind: %s", strerror(errno));
goto outfd;
}
if (!adjustFilePermissions(fd, BRLAPI_SOCKETPATH)) {
goto outfd;
}
if (listen(fd,1)<0) {
logSystemError("listen");
goto outfd;
}
return fd;
outtmp:
unlink(tmppath);
outlockfd:
closeFileDescriptor(lock);
#endif /* __MINGW32__ */
outfd:
closeFileDescriptor(fd);
out:
return INVALID_FILE_DESCRIPTOR;
}
#endif /* PF_LOCAL */
static void createSocket(int num)
{
struct socketInfo *cinfo = &socketInfo[num];
logMessage(LOG_CATEGORY(SERVER_EVENTS), "creating socket: %d (%s:%s)",
num,
(cinfo->host? cinfo->host: "LOCAL"),
(cinfo->port? cinfo->port: "DEFAULT")
);
cinfo->fd =
#if defined(PF_LOCAL)
(cinfo->addrfamily == PF_LOCAL)? createLocalSocket(cinfo):
#endif /* PF_LOCAL */
createTcpSocket(cinfo);
if (cinfo->fd == INVALID_FILE_DESCRIPTOR) {
logMessage(LOG_WARNING, "error while creating socket %d", num);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "socket %d created (fd %"PRIfd")", num, cinfo->fd);
}
}
static void closeSockets(void *arg)
{
int i;
struct socketInfo *info;
for (i=0;i<serverSocketCount;i++) {
#ifdef __MINGW32__
pthread_cancel(socketThreads[i]);
#else /* __MINGW32__ */
pthread_kill(socketThreads[i], SIGUSR2);
#endif /* __MINGW32__ */
pthread_join(socketThreads[i], NULL);
info=&socketInfo[i];
if (info->fd>=0) {
if (closeFileDescriptor(info->fd)) {
logSystemError("closing socket");
}
info->fd=INVALID_FILE_DESCRIPTOR;
#ifdef __MINGW32__
if ((info->overl.hEvent)) {
CloseHandle(info->overl.hEvent);
info->overl.hEvent = NULL;
}
#else /* __MINGW32__ */
#if defined(PF_LOCAL)
if (info->addrfamily==PF_LOCAL) {
char *path;
int lpath=strlen(BRLAPI_SOCKETPATH),lport=strlen(info->port);
if ((path=malloc(lpath+lport+3))) {
memcpy(path,BRLAPI_SOCKETPATH "/",lpath+1);
memcpy(path+lpath+1,info->port,lport+1);
if (unlink(path) == -1) {
logSystemError("unlinking local socket");
}
path[lpath+1]='.';
memcpy(path+lpath+2,info->port,lport+1);
if (unlink(path) == -1) {
logSystemError("unlinking local socket lock");
}
free(path);
}
}
#endif /* PF_LOCAL */
#endif /* __MINGW32__ */
}
free(info->port);
info->port = NULL;
free(info->host);
info->host = NULL;
}
}
/* Function: addTtyFds */
/* recursively add fds of ttys */
#ifdef __MINGW32__
static void addTtyFds(HANDLE **lpHandles, int *nbAlloc, int *nbHandles, Tty *tty) {
#else /* __MINGW32__ */
static void addTtyFds(fd_set *fds, int *fdmax, Tty *tty) {
#endif /* __MINGW32__ */
{
Connection *c;
for (c = tty->connections->next; c != tty->connections; c = c -> next) {
#ifdef __MINGW32__
if (*nbHandles == *nbAlloc) {
*nbAlloc *= 2;
*lpHandles = realloc(*lpHandles,*nbAlloc*sizeof(**lpHandles));
}
(*lpHandles)[(*nbHandles)++] = c->packet.overl.hEvent;
#else /* __MINGW32__ */
if (c->fd>*fdmax) *fdmax = c->fd;
FD_SET(c->fd,fds);
#endif /* __MINGW32__ */
}
}
{
Tty *t;
for (t = tty->subttys; t; t = t->next)
#ifdef __MINGW32__
addTtyFds(lpHandles, nbAlloc, nbHandles, t);
#else /* __MINGW32__ */
addTtyFds(fds,fdmax,t);
#endif /* __MINGW32__ */
}
}
/* Function: handleTtyFds */
/* recursively handle ttys' fds */
static void handleTtyFds(fd_set *fds, time_t currentTime, Tty *tty) {
{
Connection *c,*next;
c = tty->connections->next;
while (c != tty->connections) {
int remove = 0;
next = c->next;
#ifdef __MINGW32__
if (WaitForSingleObject(c->packet.overl.hEvent, 0) == WAIT_OBJECT_0)
#else /* __MINGW32__ */
if (FD_ISSET(c->fd, fds))
#endif /* __MINGW32__ */
{
remove = processRequest(c, &packetHandlers);
} else {
remove = (c->auth != 1) && ((currentTime - c->upTime) > UNAUTH_TIMEOUT);
}
#ifndef __MINGW32__
FD_CLR(c->fd,fds);
#endif /* __MINGW32__ */
if (remove) removeFreeConnection(c);
c = next;
}
}
{
Tty *t,*next;
for (t = tty->subttys; t; t = next) {
next = t->next;
handleTtyFds(fds,currentTime,t);
}
}
if (tty!=&ttys && tty!=&notty
&& tty->connections->next == tty->connections && !tty->subttys) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "freeing tty %#010x",tty->number);
lockMutex(&apiConnectionsMutex);
removeTty(tty);
freeTty(tty);
unlockMutex(&apiConnectionsMutex);
}
}
#ifndef __MINGW32__
static sigset_t blockedSignalsMask;
static void initializeBlockedSignalsMask(void)
{
sigemptyset(&blockedSignalsMask);
sigaddset(&blockedSignalsMask, SIGTERM);
sigaddset(&blockedSignalsMask, SIGINT);
sigaddset(&blockedSignalsMask, SIGPIPE);
sigaddset(&blockedSignalsMask, SIGCHLD);
sigaddset(&blockedSignalsMask, SIGUSR1);
}
#endif /* __MINGW32__ */
static int prepareThread(void)
{
#ifndef __MINGW32__
if (pthread_sigmask(SIG_BLOCK, &blockedSignalsMask, NULL) != 0) {
logSystemError("pthread_sigmask[SIG_BLOCK]");
return 0;
}
#endif /* __MINGW32__ */
return 1;
}
THREAD_FUNCTION(createServerSocket) {
intptr_t num = (intptr_t) argument;
logMessage(LOG_CATEGORY(SERVER_EVENTS), "socket creation started: %"PRIdPTR, num);
if (prepareThread()) {
createSocket(num);
}
lockMutex(&apiSocketsMutex);
serverSocketsPending -= 1;
unlockMutex(&apiSocketsMutex);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "socket creation finished: %"PRIdPTR, num);
return NULL;
}
/* Function : runServer */
/* The server thread */
/* Returns NULL in any case */
THREAD_FUNCTION(runServer) {
char *hosts = (char *)argument;
int i;
int res;
struct sockaddr_storage addr;
socklen_t addrlen;
Connection *c;
time_t currentTime;
fd_set sockset;
FileDescriptor resfd;
#ifdef __MINGW32__
HANDLE *lpHandles;
int nbAlloc;
int nbHandles = 0;
#else /* __MINGW32__ */
int fdmax;
#endif /* __MINGW32__ */
logMessage(LOG_CATEGORY(SERVER_EVENTS), "server thread started");
if (!prepareThread()) goto finished;
if (auth && !isAbsolutePath(auth)) {
if (!(authDescriptor = authBeginServer(auth))) {
logMessage(LOG_WARNING, "Unable to start auth server");
goto finished;
}
}
socketHosts = splitString(hosts,'+',&serverSocketCount);
if (serverSocketCount > SERVER_SOCKET_LIMIT) {
logMessage(LOG_ERR, "too many hosts specified: %d > %d)",
serverSocketCount, SERVER_SOCKET_LIMIT);
goto finished;
}
if (serverSocketCount == 0) {
logMessage(LOG_INFO,"no hosts specified");
goto finished;
}
#ifdef __MINGW32__
nbAlloc = serverSocketCount;
#endif /* __MINGW32__ */
for (i=0;i<serverSocketCount;i++)
socketInfo[i].fd = INVALID_FILE_DESCRIPTOR;
#ifdef __MINGW32__
if ((getaddrinfoProc && WSAStartup(MAKEWORD(2,0), &wsadata))
|| (!getaddrinfoProc && WSAStartup(MAKEWORD(1,1), &wsadata))) {
logWindowsSocketError("Starting socket library");
goto finished;
}
#endif /* __MINGW32__ */
#ifdef __MINGW32__
pthread_cleanup_push(closeSockets,NULL);
#endif /* __MINGW32__ */
{
pthread_mutexattr_t attributes;
pthread_mutexattr_init(&attributes);
pthread_mutex_init(&apiSocketsMutex, &attributes);
serverSocketsPending = serverSocketCount;
}
for (i=0;i<serverSocketCount;i++) {
socketInfo[i].addrfamily=brlapiserver_expandHost(socketHosts[i],&socketInfo[i].host,&socketInfo[i].port);
#ifdef __MINGW32__
if (socketInfo[i].addrfamily != PF_LOCAL) {
#endif /* __MINGW32__ */
{
char name[0X100];
snprintf(name, sizeof(name), "server-socket-create-%d", i);
res = createThread(name, &socketThreads[i], NULL,
createServerSocket, (void *)(intptr_t)i);
}
if (res != 0) {
logMessage(LOG_WARNING,"pthread_create: %s",strerror(res));
for (i--;i>=0;i--) {
#ifdef __MINGW32__
pthread_cancel(socketThreads[i]);
#else /* __MINGW32__ */
pthread_kill(socketThreads[i], SIGUSR2);
#endif /* __MINGW32__ */
pthread_join(socketThreads[i], NULL);
}
goto finished;
}
#ifdef __MINGW32__
} else {
/* Windows doesn't have trouble with local sockets on read-only
* filesystems, but it has with inter-thread overlapped operations,
* so call from here
*/
createSocket(i);
}
#endif /* __MINGW32__ */
}
unauthConnections = 0;
unauthConnLog = 0;
while (running) {
#ifdef __MINGW32__
lpHandles = malloc(nbAlloc * sizeof(*lpHandles));
nbHandles = 0;
for (i=0;i<serverSocketCount;i++) {
if (socketInfo[i].fd != INVALID_HANDLE_VALUE) {
lpHandles[nbHandles++] = socketInfo[i].overl.hEvent;
}
}
lockMutex(&apiConnectionsMutex);
addTtyFds(&lpHandles, &nbAlloc, &nbHandles, &notty);
addTtyFds(&lpHandles, &nbAlloc, &nbHandles, &ttys);
unlockMutex(&apiConnectionsMutex);
if (!nbHandles) {
free(lpHandles);
approximateDelay(1000);
continue;
}
switch (WaitForMultipleObjects(nbHandles, lpHandles, FALSE, 1000)) {
case WAIT_TIMEOUT:
continue;
case WAIT_FAILED:
logWindowsSystemError("WaitForMultipleObjects");
break;
}
free(lpHandles);
#else /* __MINGW32__ */
/* Compute sockets set and fdmax */
FD_ZERO(&sockset);
fdmax=0;
lockMutex(&apiConnectionsMutex);
addTtyFds(&sockset, &fdmax, &notty);
addTtyFds(&sockset, &fdmax, &ttys);
unlockMutex(&apiConnectionsMutex);
{
struct timeval tv, *timeout;
lockMutex(&apiSocketsMutex);
for (i=0;i<serverSocketCount;i++) {
if (socketInfo[i].fd>=0) {
FD_SET(socketInfo[i].fd, &sockset);
if (socketInfo[i].fd>fdmax) {
fdmax = socketInfo[i].fd;
}
}
}
if (unauthConnections || serverSocketsPending) {
memset(&tv, 0, sizeof(tv));
tv.tv_sec = SERVER_SELECT_TIMEOUT;
timeout = &tv;
} else {
timeout = NULL;
}
unlockMutex(&apiSocketsMutex);
if (select(fdmax+1, &sockset, NULL, NULL, timeout) < 0) {
if (fdmax==0) continue; /* still no server socket */
logMessage(LOG_WARNING,"select: %s",strerror(errno));
break;
}
}
#endif /* __MINGW32__ */
time(&currentTime);
for (i=0;i<serverSocketCount;i++) {
char source[0X100];
#ifdef __MINGW32__
if (socketInfo[i].fd != INVALID_FILE_DESCRIPTOR &&
WaitForSingleObject(socketInfo[i].overl.hEvent, 0) == WAIT_OBJECT_0) {
if (socketInfo[i].addrfamily == PF_LOCAL) {
DWORD foo;
if (!(GetOverlappedResult(socketInfo[i].fd, &socketInfo[i].overl, &foo, FALSE))) {
logWindowsSystemError("GetOverlappedResult");
}
resfd = socketInfo[i].fd;
if ((socketInfo[i].fd = createLocalSocket(&socketInfo[i])) != INVALID_FILE_DESCRIPTOR) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "socket %d re-established (fd %"PRIfd", was %"PRIfd")",i,socketInfo[i].fd,resfd);
}
snprintf(source, sizeof(source), BRLAPI_SOCKETPATH "%s", socketInfo[i].port);
} else {
if (!ResetEvent(socketInfo[i].overl.hEvent)) {
logWindowsSystemError("ResetEvent in server loop");
}
#else /* __MINGW32__ */
if (socketInfo[i].fd>=0 && FD_ISSET(socketInfo[i].fd, &sockset)) {
#endif /* __MINGW32__ */
addrlen = sizeof(addr);
resfd = (FileDescriptor)accept((SocketDescriptor)socketInfo[i].fd, (struct sockaddr *) &addr, &addrlen);
if (resfd == INVALID_FILE_DESCRIPTOR) {
setSocketErrno();
logMessage(LOG_WARNING,"accept(%"PRIfd"): %s",socketInfo[i].fd,strerror(errno));
continue;
}
#ifndef __MINGW32__
if (resfd >= FD_SETSIZE) {
/* Will not be able to call select() on this */
setErrno(EMFILE);
logMessage(LOG_WARNING,"accept(%"PRIfd"): %s",socketInfo[i].fd,strerror(errno));
continue;
}
#endif /* !__MINGW32__ */
formatAddress(source, sizeof(source), &addr, addrlen);
#ifdef __MINGW32__
}
#endif /* __MINGW32__ */
logMessage(LOG_CATEGORY(SERVER_EVENTS),
"BrlAPI connection fd=%"PRIfd" accepted: %s", resfd, source
);
if (unauthConnections >= UNAUTH_LIMIT) {
writeError(resfd, BRLAPI_ERROR_CONNREFUSED);
closeFileDescriptor(resfd);
if (unauthConnLog==0) {
logMessage(LOG_WARNING, "Too many simultaneous unauthorized connections");
}
unauthConnLog++;
} else {
#ifndef __MINGW32__
if (!setBlockingIo(resfd, 0)) {
logMessage(LOG_WARNING, "Failed to switch to non-blocking mode: %s",strerror(errno));
break;
}
#endif /* __MINGW32__ */
c = createConnection(resfd, currentTime);
if (c==NULL) {
logMessage(LOG_WARNING,"Failed to create connection structure");
closeFileDescriptor(resfd);
} else {
unauthConnections++;
addConnection(c, notty.connections);
handleNewConnection(c);
}
}
}
}
handleTtyFds(&sockset,currentTime,&notty);
handleTtyFds(&sockset,currentTime,&ttys);
}
running = 0;
#ifdef __MINGW32__
pthread_cleanup_pop(1);
#else /* __MINGW32__ */
closeSockets(NULL);
#endif /* __MINGW32__ */
finished:
logMessage(LOG_CATEGORY(SERVER_EVENTS), "server thread finished");
return NULL;
}
/****************************************************************************/
/** MISCELLANEOUS FUNCTIONS **/
/****************************************************************************/
/* Function : initializeAcceptedKeys */
/* Specify which keys should be passed to the client by default, as soon */
/* as it controls the tty */
/* If client asked for commands, one lets it process routing cursor */
/* and screen-related commands */
/* If the client is interested in braille codes, one passes it nothing */
/* to let the user read the screen in case theree is an error */
static int initializeAcceptedKeys(Connection *c, int how)
{
if (how == BRL_KEYCODES) {
if (c && addKeyrange(0, BRLAPI_KEY_MAX, &c->acceptedKeys) == -1) return -1;
} else {
if (c) {
typedef struct {
int (*action) (brlapi_keyCode_t first, brlapi_keyCode_t last, KeyrangeList **list);
brlapi_rangeType_t type;
brlapi_keyCode_t code;
} KeyrangeEntry;
static const KeyrangeEntry keyrangeTable[] = {
{ .action = addKeyrange,
.type = brlapi_rangeType_all,
.code = 0
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_OFFLINE
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_NOOP
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_RESTARTBRL
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_BRL_START
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_BRL_STOP
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_RESTARTSPEECH
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SPK_START
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SPK_STOP
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SCR_START
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SCR_STOP
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SWITCHVT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SWITCHVT_PREV
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SWITCHVT_NEXT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SELECTVT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SELECTVT_PREV
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SELECTVT_NEXT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSXT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSAT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSPS2
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_CONTEXT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_ALERT
},
{ .action = removeKeyrange,
.type = brlapi_rangeType_command,
.code = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_PASSDOTS
},
{ .action = NULL }
};
const KeyrangeEntry *keyrange = keyrangeTable;
while (keyrange->action) {
brlapi_keyCode_t first;
brlapi_keyCode_t mask;
brlapi_keyCode_t last;
first = keyrange->code;
if (brlapiserver_getKeyrangeMask(keyrange->type, first, &mask) == -1) return -1;
last = first | mask;
if (keyrange->action(first, last, &c->acceptedKeys) == -1) return -1;
keyrange += 1;
}
}
}
return 0;
}
/* Function: ttyTerminationHandler */
/* Recursively removes connections */
static void ttyTerminationHandler(Tty *tty)
{
while (tty->connections->next != tty->connections) {
removeFreeConnection(tty->connections->next);
}
freeConnection(tty->connections);
{
Tty *t = tty->subttys;
while (t) {
ttyTerminationHandler(t);
t = t->next;
}
}
}
/* Function : terminationHandler */
/* Terminates driver */
static void terminationHandler(void)
{
int res;
running = 0;
#ifdef __MINGW32__
res = pthread_cancel(serverThread);
#else /* __MINGW32__ */
res = pthread_kill(serverThread, SIGUSR2);
#endif /* __MINGW32__ */
pthread_join(serverThread, NULL);
if (res != 0) {
logMessage(LOG_WARNING,"pthread_cancel: %s",strerror(res));
}
ttyTerminationHandler(&notty);
ttyTerminationHandler(&ttys);
if (authDescriptor) {
authEnd(authDescriptor);
authDescriptor = NULL;
}
#ifdef __MINGW32__
WSACleanup();
#endif /* __MINGW32__ */
if (socketHosts) {
deallocateStrings(socketHosts);
socketHosts = NULL;
}
}
/* Function: whoFillsTty */
/* Returns the connection which fills the tty */
static Connection *whoFillsTty(Tty *tty) {
Connection *c;
Tty *t;
for (c=tty->connections->next; c!=tty->connections; c = c->next)
if (c->brlbufstate!=EMPTY
&& c->client_priority != BRLAPI_PARAM_CLIENT_PRIORITY_DISABLE) {
goto found;
}
c = NULL;
found:
for (t = tty->subttys; t; t = t->next)
if (tty->focus==SCR_NO_VT || t->number == tty->focus) {
Connection *recur_c = whoFillsTty(t);
return recur_c ? recur_c : c;
}
return c;
}
static inline void setCurrentRootTty(void) {
ttys.focus = currentVirtualTerminal();
}
/* Function : api_writeWindow */
static int api_writeWindow(BrailleDisplay *brl, const wchar_t *text)
{
int ok = 1;
if (text)
memcpy(coreWindowText, text, displaySize * sizeof(*coreWindowText));
else
memset(coreWindowText, 0, displaySize * sizeof(*coreWindowText));
memcpy(coreWindowDots, brl->buffer, displaySize * sizeof(*coreWindowDots));
coreWindowCursor = brl->cursor;
setCurrentRootTty();
lockMutex(&apiConnectionsMutex);
lockMutex(&apiRawMutex);
if (!offline && !suspendConnection && !rawConnection && !whoFillsTty(&ttys)) {
lockMutex(&apiDriverMutex);
if (!trueBraille->writeWindow(brl, text)) ok = 0;
unlockMutex(&apiDriverMutex);
}
unlockMutex(&apiRawMutex);
unlockMutex(&apiConnectionsMutex);
return ok;
}
/* Function: whoGetsKey */
/* Returns the connection which gets that key */
static Connection *whoGetsKey(Tty *tty, brlapi_keyCode_t code, unsigned int how, unsigned int retainDots)
{
Connection *c;
Tty *t;
int passKey;
for (c=tty->connections->next; c!=tty->connections; c = c->next) {
lockMutex(&c->acceptedKeysMutex);
passKey = (c->how==how) && (inKeyrangeList(c->acceptedKeys,code) != NULL)
&& (how != BRL_COMMANDS || (!retainDots || c->retainDots));
unlockMutex(&c->acceptedKeysMutex);
if (passKey) goto found;
}
c = NULL;
found:
for (t = tty->subttys; t; t = t->next)
if (tty->focus==SCR_NO_VT || t->number == tty->focus) {
Connection *recur_c = whoGetsKey(t, code, how, retainDots);
return recur_c ? recur_c : c;
}
return c;
}
/* Temporary function, until we implement proper generic support for variables.
*/
static void broadcastKey(Tty *tty, brlapi_keyCode_t code, unsigned int how) {
Connection *c;
Tty *t;
for (c=tty->connections->next; c!=tty->connections; c = c->next) {
lockMutex(&c->acceptedKeysMutex);
if ((c->how==how) && (inKeyrangeList(c->acceptedKeys,code) != NULL))
writeKey(c->fd,code);
unlockMutex(&c->acceptedKeysMutex);
}
for (t = tty->subttys; t; t = t->next)
broadcastKey(t, code, how);
}
/* The core produced a key event, try to send it to a brlapi client. */
static int api__handleKeyEvent(brlapi_keyCode_t clientCode) {
Connection *c;
if (offline) {
broadcastKey(&ttys, BRLAPI_KEY_TYPE_CMD|BRLAPI_KEY_CMD_NOOP, BRL_COMMANDS);
offline = 0;
}
/* somebody gets the raw code */
if ((c = whoGetsKey(&ttys, clientCode, BRL_KEYCODES, 0))) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "transmitting accepted key %016"BRLAPI_PRIxKEYCODE" to fd %"PRIfd,clientCode,c->fd);
writeKey(c->fd,clientCode);
return 1;
}
return 0;
}
int api_handleKeyEvent(KeyGroup group, KeyNumber number, int press) {
brlapi_keyCode_t clientCode = makeDriverKeyCode(group, number, press);
logMessage(LOG_CATEGORY(SERVER_EVENTS), "API got key %02x %02x (press %d), thus client code %016"BRLAPI_PRIxKEYCODE, group, number, press, clientCode);
int ret;
lockMutex(&apiConnectionsMutex);
ret = api__handleKeyEvent(clientCode);
unlockMutex(&apiConnectionsMutex);
return ret;
}
/* The core produced a command, try to send it to a brlapi client.
* Return true if handled and false otherwise. */
static int api__handleCommand(int command) {
if (command == BRL_CMD_OFFLINE) {
if (!offline) {
broadcastKey(&ttys, BRLAPI_KEY_TYPE_CMD|BRLAPI_KEY_CMD_OFFLINE, BRL_COMMANDS);
offline = 1;
}
return 0;
}
if (offline) {
broadcastKey(&ttys, BRLAPI_KEY_TYPE_CMD|BRLAPI_KEY_CMD_NOOP, BRL_COMMANDS);
offline = 0;
}
if (command != EOF) {
Connection *c = NULL;
brlapi_keyCode_t code;
if (!cmdBrlttyToBrlapi(&code, command, 1)) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "command %08x could not be converted to BrlAPI with retainDots", command);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "command %08x -> client code %016"BRLAPI_PRIxKEYCODE, command, code);
c = whoGetsKey(&ttys, code, BRL_COMMANDS, 1);
}
if (!c) {
brlapi_keyCode_t alternate;
if (!cmdBrlttyToBrlapi(&alternate, command, 0)) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "command %08x could not be converted to BrlAPI without retainDots", command);
} else {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "command %08x -> client code %016"BRLAPI_PRIxKEYCODE, command, alternate);
c = whoGetsKey(&ttys, alternate, BRL_COMMANDS, 0);
if (c) code = alternate;
}
}
if (c) {
logMessage(LOG_CATEGORY(SERVER_EVENTS), "transmitting accepted command %lx as client code %016"BRLAPI_PRIxKEYCODE" to fd %"PRIfd,(unsigned long)command,code,c->fd);
writeKey(c->fd, code);
return 1;
}
}
return 0;
}
int api_handleCommand(int command) {
int handled;
lockMutex(&apiConnectionsMutex);
handled = api__handleCommand(command);
unlockMutex(&apiConnectionsMutex);
return handled;
}
/* Function : api_readCommand
* Call driver->readCommand unless the driver is suspended.
*/
static int api_readCommand(BrailleDisplay *brl, KeyTableCommandContext context) {
ssize_t size;
brlapi_packet_t packet;
int res;
int command = EOF;
lockMutex(&apiConnectionsMutex);
lockMutex(&apiRawMutex);
if (suspendConnection || !driverConstructed) {
unlockMutex(&apiRawMutex);
goto out;
}
if (rawConnection!=NULL) {
lockMutex(&apiDriverMutex);
size = trueBraille->readPacket(brl, &packet.data, BRLAPI_MAXPACKETSIZE);
unlockMutex(&apiDriverMutex);
if (size<0)
writeException(rawConnection->fd, BRLAPI_ERROR_DRIVERERROR, BRLAPI_PACKET_PACKET, NULL, 0);
else if (size)
brlapiserver_writePacket(rawConnection->fd,BRLAPI_PACKET_PACKET,&packet.data,size);
unlockMutex(&apiRawMutex);
goto out;
}
lockMutex(&apiDriverMutex);
res = trueBraille->readCommand(brl,context);
unlockMutex(&apiDriverMutex);
if (brl->resizeRequired)
brlResize(brl);
command = res;
/* some client may get raw mode only from now */
unlockMutex(&apiRawMutex);
out:
unlockMutex(&apiConnectionsMutex);
return command;
}
/* Function : api_flushOutput
* Flush writes to the braille device.
*/
int api_flushOutput(BrailleDisplay *brl) {
Connection *c;
static Connection *displayed_last;
int ok = 1;
int drain = 0;
int update = 0;
lockMutex(&apiParamMutex);
lockMutex(&apiConnectionsMutex);
lockMutex(&apiRawMutex);
if (suspendConnection) {
unlockMutex(&apiRawMutex);
goto out;
}
setCurrentRootTty();
c = whoFillsTty(&ttys);
if (!offline && c) {
lockMutex(&c->brailleWindowMutex);
lockMutex(&apiDriverMutex);
if (!driverConstructed && !driverConstructing) {
if (!resumeBrailleDriver(brl)) {
unlockMutex(&apiDriverMutex);
unlockMutex(&c->brailleWindowMutex);
unlockMutex(&apiRawMutex);
goto out;
}
}
if (c->brailleWindow.cursor) {
unsigned char newCursorOverlay = getCursorOverlay(brl);
if (newCursorOverlay != cursorOverlay) {
cursorOverlay = newCursorOverlay;
update = 1;
}
}
if (c != displayed_last || c->brlbufstate==TODISPLAY || update) {
unsigned char *oldbuf = disp->buffer, buf[displaySize];
disp->buffer = buf;
getDots(&c->brailleWindow, buf);
brl->cursor = c->brailleWindow.cursor-1;
if (!trueBraille->writeWindow(brl, c->brailleWindow.text)) ok = 0;
/* FIXME: the client should have gotten the notification when the write
* was received, rather than only when it eventually gets displayed
* (possibly only because of focus change) */
if (ok) handleParamUpdate(c, c, BRLAPI_PARAM_RENDERED_CELLS, 0, 0, disp->buffer, displaySize);
drain = 1;
disp->buffer = oldbuf;
displayed_last = c;
}
unlockMutex(&apiDriverMutex);
unlockMutex(&c->brailleWindowMutex);
} else {
/* no RAW, no connection filling tty, hence suspend if needed */
lockMutex(&apiDriverMutex);
if (!coreActive) {
if (driverConstructed) {
/* Put back core output before suspending */
unsigned char *oldbuf = disp->buffer;
disp->buffer = coreWindowDots;
brl->cursor = coreWindowCursor;
if (!trueBraille->writeWindow(brl, coreWindowText)) ok = 0;
disp->buffer = oldbuf;
suspendBrailleDriver();
}
unlockMutex(&apiDriverMutex);
unlockMutex(&apiRawMutex);
goto out;
}
unlockMutex(&apiDriverMutex);
}
if (!ok) {
unlockMutex(&apiRawMutex);
goto out;
}
if (drain)
drainBrailleOutput(brl, 0);
unlockMutex(&apiRawMutex);
out:
unlockMutex(&apiConnectionsMutex);
unlockMutex(&apiParamMutex);
return ok;
}
int api_resumeDriver(BrailleDisplay *brl) {
/* core is resuming or opening the device for the first time, let's try to go
* to normal state */
lockMutex(&apiRawMutex);
lockMutex(&apiDriverMutex);
if (!suspendConnection && !driverConstructed)
resumeBrailleDriver(brl);
unlockMutex(&apiDriverMutex);
unlockMutex(&apiRawMutex);
return (coreActive = driverConstructed);
}
/* try to get access to device. If suspended, returns 0 */
int api_claimDriver (BrailleDisplay *brl)
{
int ret;
lockMutex(&apiSuspendMutex);
ret = driverConstructed;
return ret;
}
void api_releaseDriver(BrailleDisplay *brl)
{
unlockMutex(&apiSuspendMutex);
}
void api_suspendDriver(BrailleDisplay *brl) {
/* core is suspending, going to core suspend state */
coreActive = 0;
flushBrailleOutput(brl);
}
static void brlResize(BrailleDisplay *brl)
{
displayDimensions[0] = htonl(brl->textColumns);
displayDimensions[1] = htonl(brl->textRows);
displaySize = brl->textColumns * brl->textRows;
coreWindowText = realloc(coreWindowText, displaySize * sizeof(*coreWindowText));
coreWindowDots = realloc(coreWindowDots, displaySize * sizeof(*coreWindowDots));
coreWindowCursor = 0;
handleParamUpdate(NULL, NULL, BRLAPI_PARAM_DISPLAY_SIZE, 0, BRLAPI_PARAMF_GLOBAL, displayDimensions, sizeof(displayDimensions));
}
REPORT_LISTENER(brlapi_handleReports)
{
if (parameters->reportIdentifier == REPORT_BRAILLE_DEVICE_ONLINE) {
BrailleDisplay *brl = parameters->listenerData;
flushBrailleOutput(brl);
}
}
static ReportListenerInstance *api_reportListener;
/* Function : api_linkServer */
/* Does all the link stuff to let api get events from the driver and */
/* writes from brltty */
void api_linkServer(BrailleDisplay *brl)
{
logMessage(LOG_CATEGORY(SERVER_EVENTS), "api link");
trueBraille=braille;
memcpy(&ApiBraille,braille,sizeof(BrailleDriver));
ApiBraille.writeWindow=api_writeWindow;
ApiBraille.readCommand=api_readCommand;
ApiBraille.readPacket = NULL;
ApiBraille.writePacket = NULL;
braille=&ApiBraille;
lockMutex(&apiDriverMutex);
disp = brl;
driverConstructed=1;
unlockMutex(&apiDriverMutex);
brlResize(brl);
lockMutex(&apiConnectionsMutex);
broadcastKey(&ttys, BRLAPI_KEY_TYPE_CMD|BRLAPI_KEY_CMD_NOOP, BRL_COMMANDS);
unlockMutex(&apiConnectionsMutex);
api_reportListener = registerReportListener(REPORT_BRAILLE_DEVICE_ONLINE, brlapi_handleReports, brl);
}
/* Function : api_unlinkServer */
/* Does all the unlink stuff to remove api from the picture */
void api_unlinkServer(BrailleDisplay *brl)
{
logMessage(LOG_CATEGORY(SERVER_EVENTS), "api unlink");
unregisterReportListener(api_reportListener);
lockMutex(&apiConnectionsMutex);
broadcastKey(&ttys, BRLAPI_KEY_TYPE_CMD|BRLAPI_KEY_CMD_OFFLINE, BRL_COMMANDS);
unlockMutex(&apiConnectionsMutex);
free(coreWindowText);
coreWindowText = NULL;
free(coreWindowDots);
coreWindowDots = NULL;
braille=trueBraille;
trueBraille=&noBraille;
lockMutex(&apiDriverMutex);
if (!coreActive && driverConstructed)
suspendBrailleDriver();
driverConstructed=0;
disp = NULL;
unlockMutex(&apiDriverMutex);
}
/* Function : api_logServerIdentity */
/* Identifies BrlApi */
void api_logServerIdentity(int full)
{
logMessage(LOG_NOTICE, RELEASE);
if (full) {
logMessage(LOG_INFO, COPYRIGHT);
}
}
#ifdef ENABLE_API_FUZZING
/* Function : api_connect */
/* Connect to our own API server */
static int api_connect(void){
int s;
struct sockaddr_storage ss;
size_t ss_size;
#ifdef PF_LOCAL
if (socketInfo[0].addrfamily == PF_LOCAL) {
struct sockaddr_un *sun = (struct sockaddr_un *) &ss;
s = socket(PF_LOCAL, SOCK_STREAM, 0);
ss_size = sizeof(*sun);
memset(sun, 0, ss_size);
sun->sun_family = AF_LOCAL;
sprintf(sun->sun_path, BRLAPI_SOCKETPATH "/%s", socketInfo[0].port);
} else
#endif /* PF_LOCAL */
{
struct sockaddr_in *sin = (struct sockaddr_in *) &ss;
s = socket(AF_INET, SOCK_STREAM, 0);
ss_size = sizeof(*sin);
memset(sin, 0, ss_size);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
sin->sin_port = htons(BRLAPI_SOCKETPORTNUM+atoi(socketInfo[0].port));
}
while (true) {
int c = connect(s, (struct sockaddr *) &ss, ss_size);
if (!c) break;
if (errno != ECONNREFUSED)
logMessage(LOG_WARNING, "Could not connect to API: %s", strerror(errno));
sleep(1);
}
return s;
}
/* Function : api_writeData */
/* Write to API socket some data */
static int api_writeData(int s, const void* data, size_t size) {
size_t remaining = size;
while (remaining) {
ssize_t done = write(s, data, remaining);
if (done < 0) {
if (errno != EINTR && errno != EAGAIN)
return 0;
done = 0;
}
remaining -= done;
data += done;
}
return 1;
}
/* Function : api_writeFile */
/* Write to API socket the content of a file */
static int api_writeFile(int s, const char* filename) {
struct stat st;
int fd;
size_t size;
fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
if (fstat(fd, &st) < 0) {
close(fd);
return 0;
}
size = st.st_size;
if (size == 0) return 0;
{
uint8_t buff[size];
ssize_t ret;
ret = read(fd, buff, size);
close(fd);
if (ret < size) return 0;
return api_writeData(s, buff, size);
}
}
/* Function : api_writeHead */
/* Write to API socket the version + auth head, so the fuzzer does not have to guess these */
static int api_writeHead(int s) {
const uint32_t versionPacket[] = {
htonl(4), htonl(BRLAPI_PACKET_VERSION),
htonl(BRLAPI_PROTOCOL_VERSION)
};
if (!api_writeData(s, versionPacket, sizeof(versionPacket))) return 0;
return 1;
}
/* Function : api_writeWrite */
/* Write to API socket the write header, so the fuzzer does not have to guess it */
static int api_writeWrite(int s, int utf8) {
{
const uint32_t enterTtyModePacket[] = { htonl(2*4), htonl(BRLAPI_PACKET_ENTERTTYMODE), htonl(1), htonl(1) };
if (!api_writeData(s, enterTtyModePacket, sizeof(enterTtyModePacket))) return 0;
}
{
const uint32_t writePacket[] = {
htonl(3 * 4 + displaySize + (utf8 ? 6 : 11)), htonl(BRLAPI_PACKET_WRITE),
htonl(BRLAPI_WF_REGION | BRLAPI_WF_TEXT | BRLAPI_WF_CHARSET),
htonl(1), htonl(displaySize),
};
if (!api_writeData(s, writePacket, sizeof(writePacket))) return 0;
}
return 1;
}
/* Function : api_writeLatin1Charset */
/* Write to API socket the write footer for latin1, so the fuzzer does not have to guess it */
static int api_writeLatin1Charset(int s) {
return api_writeData(s, "\x0aiso-8859-1", 11);
}
/* Function : api_writeUtf8Charset */
/* Write to API socket the write footer for UTF-8, so the fuzzer does not have to guess it */
static int api_writeUtf8Charset(int s) {
return api_writeData(s, "\x05UTF-8", 6);
}
/* Function : api_writeHeading */
/* Write to API socket the heading for a test */
static int api_writeHeading(int s) {
if (!fuzz_head && !api_writeHead(s)) return 0;
if (fuzz_write && !api_writeWrite(s, fuzz_writeutf8)) return 0;
return 1;
}
/* Function : api_writeTrailing */
/* Write to API socket the trailing for a test */
static int api_writeTrailing(int s) {
if (fuzz_write) {
if (fuzz_writeutf8) {
if (!api_writeUtf8Charset(s)) return 0;
} else {
if (!api_writeLatin1Charset(s)) return 0;
}
}
return 1;
}
/* Function : api_writeInput */
/* Write to API socket some data set */
static int api_writeInput(int s, const uint8_t *data, size_t size) {
if (fuzz_write) {
/* Reject sizes that don't match display size */
if (fuzz_writeutf8) {
char buf[size+1];
memcpy(buf, data, size);
buf[size] = 0;
if (countUtf8Characters((const char*) data) != displaySize) return -1;
} else {
if (size != displaySize) return -1;
}
}
if (!api_writeHeading(s)) return 0;
if (!api_writeData(s, data, size)) return 0;
if (!api_writeTrailing(s)) return 0;
return 1;
}
/* Function: api_readData */
/* Read all data coming from API socket */
static int api_readData(int s){
while (1) {
char buff[4096];
ssize_t r = read(s, buff, sizeof(buff));
if (r <= 0) {
close(s);
return r == 0;
}
}
}
/* Function: api_testOneInput */
/* Test feeding API with feeding one input set */
static int api_testOneInput(const uint8_t *data, size_t size)
{
int s = api_connect();
int r = api_writeInput(s, data, size);
if (r == -1) return -1; /* Reject this data */
if (r == 0) goto out;
shutdown(s, SHUT_WR); /* We are finished speaking */
api_readData(s);
out:
close(s);
return 0;
}
/* Prototype for LLVM fuzzer runner */
extern int LLVMFuzzerRunDriver(int *argc, char ***argv,
int (*UserCb)(const uint8_t *data, size_t size));
/* Function: fuzzerFunction */
/* Runs the LLVM fuzzer */
THREAD_FUNCTION(fuzzerFunction)
{
char runs[18];
char seed[18];
char *argv[] = { "brltty", "-max_len=1000", "-dict=Tools/brlapi.d/fuzz-dict", "-detect_leaks=0", "-rss_limit_mb=1000", runs, seed, NULL };
int a = sizeof(argv) / sizeof(argv[0]) - 1;
char **a_2 = argv;
snprintf(runs, sizeof(runs), "-runs=%d", fuzz_runs);
snprintf(seed, sizeof(seed), "-seed=%d", fuzz_seed);
sleep(1);
LLVMFuzzerRunDriver(&a, &a_2, &api_testOneInput);
_exit(EXIT_SUCCESS);
return 0;
}
/* Function: crasherFunction */
/* Runs one crasher reproducer */
THREAD_FUNCTION(crasherFunction)
{
int s, r = 0;
sleep(1);
s = api_connect();
if (!api_writeHeading(s)) goto out;
if (!api_writeFile(s, argument)) goto out;
if (!api_writeTrailing(s)) goto out;
shutdown(s, SHUT_WR);
api_readData(s);
r = 1;
out:
close(s);
if (r)
logMessage(LOG_ALERT, "crash test passed");
_exit(EXIT_SUCCESS);
return NULL;
}
#endif /* ENABLE_API_FUZZING */
/* Function : api_startServer */
/* Initializes BrlApi */
/* One first initialize the driver */
/* Then one creates the communication socket */
int api_startServer(BrailleDisplay *brl, char **parameters)
{
int res,i;
char *hosts =
#if defined(PF_LOCAL)
":0+127.0.0.1:0+::1:0"
#else /* PF_LOCAL */
"127.0.0.1:0+::1:0"
#endif /* PF_LOCAL */
;
{
char *operand = parameters[PARM_HOST];
if (*operand) hosts = operand;
}
auth = BRLAPI_DEFAUTH;
{
const char *operand = parameters[PARM_AUTH];
if (*operand) auth = operand;
}
pthread_mutexattr_t mattr;
coreActive=1;
if ((notty.connections = createConnection(INVALID_FILE_DESCRIPTOR,0)) == NULL) {
logMessage(LOG_WARNING, "Unable to create connections list");
goto noNottyConnections;
}
notty.connections->prev = notty.connections->next = notty.connections;
if ((ttys.connections = createConnection(INVALID_FILE_DESCRIPTOR, 0)) == NULL) {
logMessage(LOG_WARNING, "Unable to create ttys' connections list");
goto noTtysConnections;
}
ttys.connections->prev = ttys.connections->next = ttys.connections;
ttys.focus = SCR_NO_VT;
pthread_mutexattr_init(&mattr);
pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&apiConnectionsMutex,&mattr);
pthread_mutex_init(&apiDriverMutex,&mattr);
pthread_mutex_init(&apiRawMutex,&mattr);
pthread_mutex_init(&apiSuspendMutex,&mattr);
pthread_mutex_init(&apiParamMutex,&mattr);
#ifndef __MINGW32__
initializeBlockedSignalsMask();
asyncHandleSignal(SIGUSR2, asyncEmptySignalHandler, NULL);
#endif /* __MINGW32__ */
running = 1;
trueBraille=&noBraille;
if ((res = createThread("server-main", &serverThread, NULL,
runServer, hosts)) != 0) {
logMessage(LOG_WARNING,"pthread_create: %s",strerror(res));
running = 0;
for (i=0;i<serverSocketCount;i++) {
#ifdef __MINGW32__
pthread_cancel(socketThreads[i]);
#else /* __MINGW32__ */
pthread_kill(socketThreads[i], SIGUSR2);
#endif /* __MINGW32__ */
pthread_join(socketThreads[i], NULL);
}
goto noServerThread;
}
#ifdef ENABLE_API_FUZZING
validateOnOff(&fuzz_head, parameters[PARM_FUZZHEAD]);
validateOnOff(&fuzz_write, parameters[PARM_FUZZWRITE]);
validateOnOff(&fuzz_writeutf8, parameters[PARM_FUZZWRITEUTF8]);
const char *fuzz = parameters[PARM_FUZZ];
if (fuzz) {
validateInteger(&fuzz_runs, fuzz, NULL, NULL);
if (fuzz_runs) {
if (parameters[PARM_FUZZSEED])
validateInteger(&fuzz_seed, parameters[PARM_FUZZSEED], NULL, NULL);
createThread("fuzzer", &fuzzerThread, NULL, fuzzerFunction, NULL);
}
}
const char *crash = parameters[PARM_CRASH];
if (crash && crash[0])
createThread("crasher", &crasherThread, NULL, crasherFunction, (void*) crash);
#endif /* ENABLE_API_FUZZING */
return 1;
noServerThread:
freeConnection(ttys.connections);
noTtysConnections:
freeConnection(notty.connections);
noNottyConnections:
authEnd(authDescriptor);
authDescriptor = NULL;
return 0;
}
/* Function : api_stopServer */
/* End of BrlApi session. Closes the listening socket */
/* destroys opened connections and associated resources */
/* Closes the driver */
void api_stopServer(BrailleDisplay *brl)
{
terminationHandler();
}