/* * 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-2021 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 . */ /* 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 OUR_STACK_MIN 0X10000 #ifndef PTHREAD_STACK_MIN #define PTHREAD_STACK_MIN OUR_STACK_MIN #endif /* PTHREAD_STACK_MIN */ #define RELEASE "BrlAPI Server: release " BRLAPI_RELEASE #define COPYRIGHT " Copyright (C) 2002-2021 by Sébastien Hinderer , \ Samuel Thibault " #include "prologue.h" #include #include #include #include #include #include #include #include #ifdef HAVE_ICONV_H #include #endif /* HAVE_ICONV_H */ #ifdef HAVE_ALLOCA_H #include #endif /* HAVE_ALLOCA_H */ #ifdef __MINGW32__ #include "system_windows.h" #include "win_pthread.h" #else /* __MINGW32__ */ #include #include #include #include #include #include #ifdef __ANDROID__ #ifndef PAGE_SIZE #include #endif /* PAGE_SIZE */ #endif /* __ANDROID__ */ #include #ifdef HAVE_SYS_SELECT_H #include #else /* HAVE_SYS_SELECT_H */ #include #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, PARM_STACKSIZE } Parameters; const char *const api_serverParameters[] = { "auth", "host", "stacksize", NULL }; static size_t stackSize; #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 */ 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; } 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 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; itext[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->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"); freeBrailleWindow(&c->brailleWindow); 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; 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; ifd,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, unsigned int textLen) { for (int i=0; ibrailleWindow.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; 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"); 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"); 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) { 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 */ rsiz = ntohl( *((uint32_t *) p) ); p += sizeof(uint32_t); remaining -= sizeof(uint32_t); /* region size */ CHECKEXC( (rbeg >= 1) && (rbeg <= displaySize), BRLAPI_ERROR_INVALID_PARAMETER, "invalid region start" ); CHECKEXC( (rsiz > 0) && (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; } 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 */ 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 */ } } if (isUTF8) { logConversionDecision(c, "UTF-8", "internal conversion"); size_t outLeft = rsiz; 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" ); CHECKEXC(!inLeft, BRLAPI_ERROR_INVALID_PACKET, "text too big"); CHECKEXC(!outLeft, BRLAPI_ERROR_INVALID_PACKET, "text too small"); lockMutex(&c->brailleWindowMutex); wmemcpy(c->brailleWindow.text+rbeg-1, outBuff, rsiz); } else if (isLatin1) { logConversionDecision(c, "ISO_8859-1", "internal conversion"); lockMutex(&c->brailleWindowMutex); convertFromLatin1(c, rbeg, rsiz, text, textLen); } #ifdef HAVE_ICONV_H else if (charset) { logConversionDecision(c, charset, "iconv conversion"); wchar_t textBuf[rsiz]; char *in = (char *) text, *out = (char *) textBuf; size_t sin = textLen, sout = sizeof(textBuf); 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"); CHECKEXC(!sin, BRLAPI_ERROR_INVALID_PACKET, "text too big"); CHECKEXC(!sout, BRLAPI_ERROR_INVALID_PACKET, "text too small"); logConversionResult(c, rsiz, textLen); lockMutex(&c->brailleWindowMutex); wmemcpy(c->brailleWindow.text+rbeg-1, textBuf, rsiz); } #endif /* HAVE_ICONV_H */ else { logConversionDecision(c, "ISO_8859-1", "assumed"); lockMutex(&c->brailleWindowMutex); convertFromLatin1(c, rbeg, rsiz, text, textLen); } // 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); if (!orAttr) memset(c->brailleWindow.orAttr+rbeg-1,0x00,rsiz); } else { lockMutex(&c->brailleWindowMutex); } if (andAttr) memcpy(c->brailleWindow.andAttr+rbeg-1,andAttr,rsiz); if (orAttr) memcpy(c->brailleWindow.orAttr+rbeg-1,orAttr,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 = isSixDotBraille()? 6: 8; return NULL; } PARAM_WRITER(computerBrailleCellSize) { const brlapi_param_computerBrailleCellSize_t *computerBrailleCellSize = data; PARAM_ASSERT_SIZE(computerBrailleCellSize); setSixDotBraille(*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 = getBlinkPercentage(&screenCursorBlinkDescriptor); return NULL; } PARAM_WRITER(cursorBlinkPercentage) { const brlapi_param_cursorBlinkPercentage_t *cursorBlinkPercentage = data; PARAM_ASSERT_SIZE(cursorBlinkPercentage); if (!setBlinkPercentage(&screenCursorBlinkDescriptor, *cursorBlinkPercentage)) return "unsupported cursor blink percentage"; return NULL; } /* BRLAPI_PARAM_RENDERED_CELLS */ PARAM_READER(renderedCells) { lockMutex(&apiDriverMutex); if (disp) { 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; iretainDots)) { 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); } #ifdef ENABLE_CONTRACTED_BRAILLE /* 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); } #endif /* ENABLE_CONTRACTED_BRAILLE */ /* 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, }, #ifdef ENABLE_CONTRACTED_BRAILLE [BRLAPI_PARAM_LITERARY_BRAILLE_TABLE] = { .global = 1, .read = param_literaryBrailleTable_read, .write = param_literaryBrailleTable_write, }, #endif /* ENABLE_CONTRACTED_BRAILLE */ [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 ¶mDispatch[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(¬ty,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 */ if (param == BRLAPI_PARAM_SERVER_VERSION) { WERR(c->fd, BRLAPI_ERROR_INVALID_PARAMETER, "parameter %u not available for watching - it won't change", param); unlockMutex(&apiParamMutex); return 0; } { 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) { 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 PacketHandlers packetHandlers = { handleGetDriverName, handleGetModelIdentifier, handleGetDisplaySize, handleEnterTtyMode, handleSetFocus, handleLeaveTtyMode, handleKeyRanges, handleKeyRanges, handleWrite, handleEnterRawMode, handleLeaveRawMode, handlePacket, handleSuspendDriver, handleResumeDriver, handleParamValue, handleParamRequest, }; 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 (sizefd, 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; } 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 ((res = bind(fd, address, length)) == -1) { 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) { #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; fd = open(path, O_RDONLY); 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 char *path) { int adjust = !geteuid(); if (!adjust) { char *directory = getPathDirectory(path); if (directory) { struct stat status; if (stat(directory, &status) == -1) { logSystemError("stat"); } else if (status.st_uid == geteuid()) { adjust = 1; } free(directory); } } if (adjust) { struct stat status; if (stat(path, &status) == -1) { logSystemError("stat"); 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; #endif if (newPermissions != oldPermissions) { if (chmod(path, newPermissions) == -1) { logSystemError("chmod"); return 0; } } } } return 1; } #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; } sa.sun_family = AF_LOCAL; if (lpath+lport+1>sizeof(sa.sun_path)) { logMessage(LOG_ERR, "Unix path too long"); goto outfd; } while (mkdir(BRLAPI_SOCKETPATH, (permissions | S_ISVTX)) == -1) { 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 (!adjustPermissions(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 ((lock = open(tmppath, O_WRONLY|O_CREAT|O_EXCL, (permissions & (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)))) == -1) { 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 (res == -1) { if (errno != ENOSPC) { logSystemError("writing pid in local socket lock"); goto outlockfd; } approximateDelay(1000); } else { done += res; n -= res; } } while (1) { 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 outtmp; } if (loopBind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { logMessage(LOG_WARNING, "bind: %s", strerror(errno)); goto outfd; } if (!adjustPermissions(sa.sun_path)) { 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;ifd>=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!=¬ty && 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; pthread_attr_t attr; 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__ */ pthread_attr_init(&attr); /* don't care if it fails */ pthread_attr_setstacksize(&attr,stackSize); for (i=0;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=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(¤tTime); for (i=0;i=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,¬ty); 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(¬ty); 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) 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); } } /* 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; } stackSize = MAX(PTHREAD_STACK_MIN, OUR_STACK_MIN); { const char *operand = parameters[PARM_STACKSIZE]; if (*operand) { int size; static const int minSize = PTHREAD_STACK_MIN; if (validateInteger(&size, operand, &minSize, NULL)) { stackSize = size; } else { logMessage(LOG_WARNING, "%s: %s", gettext("invalid thread stack size"), operand); } } } auth = BRLAPI_DEFAUTH; { const char *operand = parameters[PARM_AUTH]; if (*operand) auth = operand; } pthread_attr_t attr; 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); pthread_attr_init(&attr); pthread_attr_setstacksize(&attr,stackSize); #ifndef __MINGW32__ initializeBlockedSignalsMask(); asyncHandleSignal(SIGUSR2, asyncEmptySignalHandler, NULL); #endif /* __MINGW32__ */ running = 1; trueBraille=&noBraille; if ((res = createThread("server-main", &serverThread, &attr, runServer, hosts)) != 0) { logMessage(LOG_WARNING,"pthread_create: %s",strerror(res)); running = 0; for (i=0;i