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