| /* |
| * 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>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <io.h> |
| #include <fcntl.h> |
| |
| #include "log.h" |
| #include "system_windows.h" |
| #include "hostcmd_windows.h" |
| #include "hostcmd_internal.h" |
| |
| typedef struct { |
| DWORD identifier; |
| HANDLE *field; |
| } HandleEntry; |
| |
| static void |
| closeHandle (HANDLE *handle) { |
| if (*handle != INVALID_HANDLE_VALUE) { |
| if (*handle) { |
| CloseHandle(*handle); |
| } |
| |
| *handle = INVALID_HANDLE_VALUE; |
| } |
| } |
| |
| static HANDLE * |
| getParentHandle (HostCommandStream *hcs) { |
| return hcs->isInput? &hcs->package.outputHandle: &hcs->package.inputHandle; |
| } |
| |
| static HANDLE * |
| getChildHandle (HostCommandStream *hcs) { |
| return hcs->isInput? &hcs->package.inputHandle: &hcs->package.outputHandle; |
| } |
| |
| int |
| constructHostCommandPackageData (HostCommandPackageData *pkg) { |
| pkg->inputHandle = INVALID_HANDLE_VALUE; |
| pkg->outputHandle = INVALID_HANDLE_VALUE; |
| return 1; |
| } |
| |
| void |
| destructHostCommandPackageData (HostCommandPackageData *pkg) { |
| closeHandle(&pkg->inputHandle); |
| closeHandle(&pkg->outputHandle); |
| } |
| |
| int |
| prepareHostCommandStream (HostCommandStream *hcs, void *data) { |
| SECURITY_ATTRIBUTES attributes; |
| |
| ZeroMemory(&attributes, sizeof(attributes)); |
| attributes.nLength = sizeof(attributes); |
| attributes.bInheritHandle = TRUE; |
| attributes.lpSecurityDescriptor = NULL; |
| |
| if (CreatePipe(&hcs->package.inputHandle, &hcs->package.outputHandle, &attributes, 0)) { |
| if (SetHandleInformation(*getParentHandle(hcs), HANDLE_FLAG_INHERIT, 0)) { |
| return 1; |
| } else { |
| logWindowsSystemError("SetHandleInformation"); |
| } |
| } else { |
| logWindowsSystemError("CreatePipe"); |
| } |
| |
| return 0; |
| } |
| |
| typedef struct { |
| const HandleEntry *const handleTable; |
| } SetChildHandleData; |
| |
| static int |
| setChildHandle (HostCommandStream *hcs, void *data) { |
| SetChildHandleData *sch = data; |
| |
| *sch->handleTable[hcs->fileDescriptor].field = *getChildHandle(hcs); |
| return 1; |
| } |
| |
| static int |
| finishParentHostCommandStream (HostCommandStream *hcs, void *data) { |
| { |
| HANDLE *handle = getParentHandle(hcs); |
| int mode = hcs->isInput? O_WRONLY: O_RDONLY; |
| int fileDescriptor; |
| |
| if ((fileDescriptor = _open_osfhandle((intptr_t)*handle, mode)) == -1) { |
| logSystemError("_open_osfhandle"); |
| return 0; |
| } |
| *handle = INVALID_HANDLE_VALUE; |
| |
| if (!finishHostCommandStream(hcs, fileDescriptor)) { |
| _close(fileDescriptor); |
| return 0; |
| } |
| } |
| |
| closeHandle(getChildHandle(hcs)); |
| return 1; |
| } |
| |
| int |
| runCommand ( |
| int *result, |
| const char *const *command, |
| HostCommandStream *streams, |
| int asynchronous |
| ) { |
| int ok = 0; |
| char *line = makeWindowsCommandLine(command); |
| |
| if (line) { |
| STARTUPINFO startup; |
| |
| const HandleEntry handleTable[] = { |
| [0] = {.identifier=STD_INPUT_HANDLE , .field=&startup.hStdInput }, |
| [1] = {.identifier=STD_OUTPUT_HANDLE, .field=&startup.hStdOutput}, |
| [2] = {.identifier=STD_ERROR_HANDLE , .field=&startup.hStdError }, |
| }; |
| |
| const unsigned int handleCount = ARRAY_COUNT(handleTable); |
| const HandleEntry *const handleEnd = handleTable + handleCount; |
| |
| SetChildHandleData sch = { |
| .handleTable = handleTable |
| }; |
| |
| logMessage(LOG_DEBUG, "host command: %s", line); |
| |
| ZeroMemory(&startup, sizeof(startup)); |
| startup.cb = sizeof(startup); |
| startup.dwFlags = STARTF_USESTDHANDLES; |
| |
| { |
| const HandleEntry *hdl = handleTable; |
| |
| while (hdl < handleEnd) { |
| if ((*hdl->field = GetStdHandle(hdl->identifier)) == INVALID_HANDLE_VALUE) { |
| logWindowsSystemError("GetStdHandle"); |
| return 0; |
| } |
| |
| hdl += 1; |
| } |
| } |
| |
| if (processHostCommandStreams(streams, setChildHandle, &sch)) { |
| PROCESS_INFORMATION info; |
| |
| ZeroMemory(&info, sizeof(info)); |
| |
| if (CreateProcess(NULL, line, NULL, NULL, TRUE, |
| CREATE_NEW_PROCESS_GROUP, |
| NULL, NULL, &startup, &info)) { |
| if (processHostCommandStreams(streams, finishParentHostCommandStream, NULL)) { |
| ok = 1; |
| |
| if (asynchronous) { |
| *result = 0; |
| } else { |
| DWORD waitResult; |
| |
| *result = 0XFF; |
| |
| while ((waitResult = WaitForSingleObject(info.hProcess, INFINITE)) == WAIT_TIMEOUT); |
| |
| if (waitResult == WAIT_OBJECT_0) { |
| DWORD exitCode; |
| |
| if (GetExitCodeProcess(info.hProcess, &exitCode)) { |
| *result = exitCode; |
| } else { |
| logWindowsSystemError("GetExitCodeProcess"); |
| } |
| } else { |
| logWindowsSystemError("WaitForSingleObject"); |
| } |
| } |
| } |
| |
| CloseHandle(info.hProcess); |
| CloseHandle(info.hThread); |
| } else { |
| logWindowsSystemError("CreateProcess"); |
| } |
| } |
| |
| free(line); |
| } else { |
| logMallocError(); |
| } |
| |
| return ok; |
| } |