obs-studio/libobs/util/pipe-windows.c
Richard Stanway fef92a8a43 libobs: Log if CreateProcessW fails on Windows
This is used by the test programs as well as ffmpeg-mux. If a process
fails to start it's quite bad so we would like to know why.
2023-08-26 16:44:53 -07:00

230 lines
4.6 KiB
C

/*
* Copyright (c) 2023 Lain Bailey <lain@obsproject.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "platform.h"
#include "bmem.h"
#include "pipe.h"
struct os_process_pipe {
bool read_pipe;
HANDLE handle;
HANDLE handle_err;
HANDLE process;
};
static bool create_pipe(HANDLE *input, HANDLE *output)
{
SECURITY_ATTRIBUTES sa = {0};
sa.nLength = sizeof(sa);
sa.bInheritHandle = true;
if (!CreatePipe(input, output, &sa, 0)) {
return false;
}
return true;
}
static inline bool create_process(const char *cmd_line, HANDLE stdin_handle,
HANDLE stdout_handle, HANDLE stderr_handle,
HANDLE *process)
{
PROCESS_INFORMATION pi = {0};
wchar_t *cmd_line_w = NULL;
STARTUPINFOW si = {0};
bool success = false;
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK;
si.hStdInput = stdin_handle;
si.hStdOutput = stdout_handle;
si.hStdError = stderr_handle;
DWORD flags = 0;
#ifndef SHOW_SUBPROCESSES
flags = CREATE_NO_WINDOW;
#endif
os_utf8_to_wcs_ptr(cmd_line, 0, &cmd_line_w);
if (cmd_line_w) {
success = !!CreateProcessW(NULL, cmd_line_w, NULL, NULL, true,
flags, NULL, NULL, &si, &pi);
if (success) {
*process = pi.hProcess;
CloseHandle(pi.hThread);
} else {
// Not logging the full command line is intentional
// as it may contain stream keys etc.
blog(LOG_ERROR, "CreateProcessW failed: %lu",
GetLastError());
}
bfree(cmd_line_w);
}
return success;
}
os_process_pipe_t *os_process_pipe_create(const char *cmd_line,
const char *type)
{
os_process_pipe_t *pp = NULL;
bool read_pipe;
HANDLE process;
HANDLE output;
HANDLE err_input, err_output;
HANDLE input;
bool success;
if (!cmd_line || !type) {
return NULL;
}
if (*type != 'r' && *type != 'w') {
return NULL;
}
if (!create_pipe(&input, &output)) {
return NULL;
}
if (!create_pipe(&err_input, &err_output)) {
return NULL;
}
read_pipe = *type == 'r';
success = !!SetHandleInformation(read_pipe ? input : output,
HANDLE_FLAG_INHERIT, false);
if (!success) {
goto error;
}
success = !!SetHandleInformation(err_input, HANDLE_FLAG_INHERIT, false);
if (!success) {
goto error;
}
success = create_process(cmd_line, read_pipe ? NULL : input,
read_pipe ? output : NULL, err_output,
&process);
if (!success) {
goto error;
}
pp = bmalloc(sizeof(*pp));
pp->handle = read_pipe ? input : output;
pp->read_pipe = read_pipe;
pp->process = process;
pp->handle_err = err_input;
CloseHandle(read_pipe ? output : input);
CloseHandle(err_output);
return pp;
error:
CloseHandle(output);
CloseHandle(input);
return NULL;
}
int os_process_pipe_destroy(os_process_pipe_t *pp)
{
int ret = 0;
if (pp) {
DWORD code;
CloseHandle(pp->handle);
CloseHandle(pp->handle_err);
WaitForSingleObject(pp->process, INFINITE);
if (GetExitCodeProcess(pp->process, &code))
ret = (int)code;
CloseHandle(pp->process);
bfree(pp);
}
return ret;
}
size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data, size_t len)
{
DWORD bytes_read;
bool success;
if (!pp) {
return 0;
}
if (!pp->read_pipe) {
return 0;
}
success = !!ReadFile(pp->handle, data, (DWORD)len, &bytes_read, NULL);
if (success && bytes_read) {
return bytes_read;
}
return 0;
}
size_t os_process_pipe_read_err(os_process_pipe_t *pp, uint8_t *data,
size_t len)
{
DWORD bytes_read;
bool success;
if (!pp || !pp->handle_err) {
return 0;
}
success =
!!ReadFile(pp->handle_err, data, (DWORD)len, &bytes_read, NULL);
if (success && bytes_read) {
return bytes_read;
} else
bytes_read = GetLastError();
return 0;
}
size_t os_process_pipe_write(os_process_pipe_t *pp, const uint8_t *data,
size_t len)
{
DWORD bytes_written;
bool success;
if (!pp) {
return 0;
}
if (pp->read_pipe) {
return 0;
}
success =
!!WriteFile(pp->handle, data, (DWORD)len, &bytes_written, NULL);
if (success && bytes_written) {
return bytes_written;
}
return 0;
}