blob: e0b00415bd2a3c12c11e2101d1528923d2bbd2da [file] [log] [blame] [edit]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#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;
}