/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include "log.h" #include "pipe.h" #include "file.h" #include "async_io.h" struct NamedPipeObjectStruct { NamedPipeInputCallback *callback; void *data; int (*createPipe) (NamedPipeObject *obj); int (*monitorPipe) (NamedPipeObject *obj); void (*resetPipe) (NamedPipeObject *obj); void (*releaseResources) (NamedPipeObject *obj); struct { char *path; } host; struct { FileDescriptor descriptor; AsyncHandle monitor; } input; #if defined(__MINGW32__) struct { struct { AsyncHandle monitor; HANDLE event; OVERLAPPED overlapped; } connect; } windows; #endif /* */ }; static void initializeHostPath (NamedPipeObject *obj) { obj->host.path = NULL; } static void deallocateHostPath (NamedPipeObject *obj) { free(obj->host.path); initializeHostPath(obj); } static void removePipe (NamedPipeObject *obj) { unlink(obj->host.path); } static void initializeInputDescriptor (NamedPipeObject *obj) { obj->input.descriptor = INVALID_FILE_DESCRIPTOR; } static void closeInputDescriptor (NamedPipeObject *obj) { closeFileDescriptor(obj->input.descriptor); initializeInputDescriptor(obj); } static void initializeInputMonitor (NamedPipeObject *obj) { obj->input.monitor = NULL; } static void stopInputMonitor (NamedPipeObject *obj) { asyncCancelRequest(obj->input.monitor); initializeInputMonitor(obj); } ASYNC_INPUT_CALLBACK(handleNamedPipeInput) { NamedPipeObject *obj = parameters->data; if (parameters->error) { logMessage(LOG_WARNING, "named pipe input error: %s: %s", obj->host.path, strerror(parameters->error)); } else if (parameters->end) { logMessage(LOG_WARNING, "named pipe end-of-file: %s", obj->host.path); } else { const NamedPipeInputCallbackParameters input = { .buffer = parameters->buffer, .length = parameters->length, .data = obj->data }; return obj->callback(&input); } asyncDiscardHandle(obj->input.monitor); initializeInputMonitor(obj); if (obj->resetPipe) obj->resetPipe(obj); obj->monitorPipe(obj); return 0; } static int monitorInput (NamedPipeObject *obj) { if (!obj->input.monitor) { if (!asyncReadFile(&obj->input.monitor, obj->input.descriptor, 0X1000, handleNamedPipeInput, obj)) { return 0; } } return 1; } #if defined(__MINGW32__) static int createWindowsPipe (NamedPipeObject *obj) { obj->windows.connect.monitor = NULL; if ((obj->windows.connect.event = CreateEvent(NULL, TRUE, FALSE, NULL))) { if ((obj->input.descriptor = CreateNamedPipe(obj->host.path, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 0, 0, 0, NULL)) != INVALID_HANDLE_VALUE) { logMessage(LOG_DEBUG, "named pipe created: %s: handle=%u", obj->host.path, (unsigned int)obj->input.descriptor); return 1; } else { logWindowsSystemError("CreateNamedPipe"); } CloseHandle(obj->windows.connect.event); } else { logWindowsSystemError("CreateEvent"); } return 0; } static int doWindowsPipeConnected (NamedPipeObject *obj) { return monitorInput(obj); } ASYNC_MONITOR_CALLBACK(handleWindowsPipeConnected) { NamedPipeObject *obj = parameters->data; asyncDiscardHandle(obj->windows.connect.monitor); obj->windows.connect.monitor = NULL; doWindowsPipeConnected(obj); return 0; } static int monitorWindowsPipeConnect (NamedPipeObject *obj) { if (ResetEvent(obj->windows.connect.event)) { ZeroMemory(&obj->windows.connect.overlapped, sizeof(obj->windows.connect.overlapped)); obj->windows.connect.overlapped.hEvent = obj->windows.connect.event; if (ConnectNamedPipe(obj->input.descriptor, &obj->windows.connect.overlapped)) { if (doWindowsPipeConnected(obj)) { return 1; } } else { DWORD error = GetLastError(); if (error == ERROR_PIPE_CONNECTED) { if (doWindowsPipeConnected(obj)) { return 1; } } else if (error == ERROR_IO_PENDING) { if (asyncMonitorFileInput(&obj->windows.connect.monitor, obj->windows.connect.event, handleWindowsPipeConnected, obj)) { return 1; } } else { logWindowsError(error, "ConnectNamedPipe"); } } } else { logWindowsSystemError("ResetEvent"); } return 0; } static void disconnectWindowsPipe (NamedPipeObject *obj) { if (!DisconnectNamedPipe(obj->input.descriptor)) { logWindowsSystemError("DisconnectNamedPipe"); } } static void releaseWindowsResources (NamedPipeObject *obj) { if (obj->windows.connect.monitor) { asyncCancelRequest(obj->windows.connect.monitor); obj->windows.connect.monitor = NULL; } if (obj->windows.connect.event) { CloseHandle(obj->windows.connect.event); obj->windows.connect.event = NULL; } } static void setNamedPipeMethods (NamedPipeObject *obj) { obj->createPipe = createWindowsPipe; obj->monitorPipe = monitorWindowsPipeConnect; obj->resetPipe = disconnectWindowsPipe; obj->releaseResources = releaseWindowsResources; } #elif defined(S_ISFIFO) static int createFifo (NamedPipeObject *obj) { int result = mkfifo(obj->host.path, 0); if ((result == -1) && (errno == EEXIST)) { struct stat fifo; if (lstat(obj->host.path, &fifo) == -1) { logMessage(LOG_ERR, "cannot stat FIFO: %s: %s", obj->host.path, strerror(errno)); } else if (S_ISFIFO(fifo.st_mode)) { result = 0; } } if (result != -1) { if (chmod(obj->host.path, S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH) != -1) { // open read-write even though we only read to prevent an end-of-file condition if ((obj->input.descriptor = open(obj->host.path, O_RDWR|O_NONBLOCK)) != -1) { logMessage(LOG_DEBUG, "FIFO created: %s: fd=%d", obj->host.path, obj->input.descriptor); return 1; } else { logMessage(LOG_ERR, "cannot open FIFO: %s: %s", obj->host.path, strerror(errno)); } } else { logMessage(LOG_ERR, "cannot set FIFO permissions: %s: %s", obj->host.path, strerror(errno)); } removePipe(obj); } else { logMessage(LOG_ERR, "cannot create FIFO: %s: %s", obj->host.path, strerror(errno)); } return 0; } static void setNamedPipeMethods (NamedPipeObject *obj) { obj->createPipe = createFifo; } #else /* named pipe functions */ #warning named pipes not supported on this platform static void setNamedPipeMethods (NamedPipeObject *obj) { } #endif /* named pipes functions */ NamedPipeObject * newNamedPipeObject (const char *name, NamedPipeInputCallback *callback, void *data) { NamedPipeObject *obj; if ((obj = malloc(sizeof(*obj)))) { memset(obj, 0, sizeof(*obj)); obj->callback = callback; obj->data = data; obj->createPipe = NULL; obj->monitorPipe = monitorInput; obj->resetPipe = NULL; obj->releaseResources = NULL; setNamedPipeMethods(obj); initializeHostPath(obj); initializeInputDescriptor(obj); initializeInputMonitor(obj); { const char *directory = getNamedPipeDirectory(); obj->host.path = directory? makePath(directory, name): NULL; } if (obj->host.path) { if (!obj->createPipe) { logUnsupportedOperation("create named pipe"); } else if (obj->createPipe(obj)) { if (!obj->monitorPipe) { logUnsupportedOperation("monitor named pipe"); } else if (obj->monitorPipe(obj)) { return obj; } closeInputDescriptor(obj); removePipe(obj); } deallocateHostPath(obj); } free(obj); } else { logMallocError(); } return NULL; } void destroyNamedPipeObject (NamedPipeObject *obj) { logMessage(LOG_DEBUG, "destroying named pipe: %s", obj->host.path); if (obj->releaseResources) obj->releaseResources(obj); stopInputMonitor(obj); closeInputDescriptor(obj); removePipe(obj); deallocateHostPath(obj); free(obj); }