rizin/librz/lang/p/pipe_helper.c
2022-06-28 21:55:26 +08:00

294 lines
7.8 KiB
C

// SPDX-FileCopyrightText: 2015-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_lib.h>
#include <rz_core.h>
#include <rz_lang.h>
#if __WINDOWS__
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <process.h>
#endif
#if __WINDOWS__
static HANDLE myCreateChildProcess(const char *szCmdline) {
PROCESS_INFORMATION piProcInfo = { 0 };
STARTUPINFO siStartInfo = { 0 };
BOOL bSuccess = FALSE;
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
LPTSTR cmdline_ = rz_sys_conv_utf8_to_win(szCmdline);
bSuccess = CreateProcess(NULL, cmdline_, NULL, NULL,
TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
free(cmdline_);
return bSuccess ? piProcInfo.hProcess : NULL;
}
static HANDLE hPipeInOut = NULL;
static HANDLE hproc = NULL;
#define PIPE_BUF_SIZE 8192
static void lang_pipe_run_win(RzLang *lang) {
CHAR buf[PIPE_BUF_SIZE];
BOOL bSuccess = TRUE;
int i;
DWORD dwRead = 0, dwWritten = 0, dwEvent;
HANDLE hRead = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hRead) {
rz_sys_perror("lang_pipe_run_win/CreateEvent hRead");
return;
}
HANDLE hWritten = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hWritten) {
rz_sys_perror("lang_pipe_run_win/CreateEvent hWritten");
CloseHandle(hRead);
return;
}
rz_cons_break_push(NULL, NULL);
do {
if (rz_cons_is_breaked()) {
TerminateProcess(hproc, 0);
break;
}
OVERLAPPED oRead = { 0 };
oRead.hEvent = hRead;
memset(buf, 0, PIPE_BUF_SIZE);
ReadFile(hPipeInOut, buf, PIPE_BUF_SIZE, NULL, &oRead);
HANDLE hReadEvents[] = { hRead, hproc };
dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hReadEvents), hReadEvents,
FALSE, INFINITE);
if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
break;
} else if (dwEvent == WAIT_FAILED) {
rz_sys_perror("lang_pipe_run_win/WaitForMultipleObjects read");
break;
}
bSuccess = GetOverlappedResult(hPipeInOut, &oRead, &dwRead, TRUE);
if (!bSuccess) {
break;
}
if (bSuccess && dwRead > 0) {
buf[sizeof(buf) - 1] = 0;
OVERLAPPED oWrite = { 0 };
oWrite.hEvent = hWritten;
char *res = lang->cmd_str((RzCore *)lang->user, buf);
if (res) {
int res_len = strlen(res) + 1;
for (i = 0; i < res_len; i++) {
memset(buf, 0, PIPE_BUF_SIZE);
dwWritten = 0;
int writelen = res_len - i;
WriteFile(hPipeInOut, res + i,
writelen > PIPE_BUF_SIZE ? PIPE_BUF_SIZE : writelen,
NULL, &oWrite);
HANDLE hWriteEvents[] = { hWritten, hproc };
dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hWriteEvents), hWriteEvents,
FALSE, INFINITE);
if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
break;
} else if (dwEvent == WAIT_FAILED) {
rz_sys_perror("lang_pipe_run_win/WaitForMultipleObjects write");
}
BOOL rc = GetOverlappedResult(hPipeInOut, &oWrite, &dwWritten, TRUE);
if (!rc) {
rz_sys_perror("lang_pipe_run_win/WriteFile res");
}
if (dwWritten > 0) {
i += dwWritten - 1;
} else {
// send null termination // chop
rz_sys_perror("lang_pipe_run_win/dwWritten");
// WriteFile (hPipeInOut, "", 1, &dwWritten, NULL);
// break;
}
}
free(res);
} else {
WriteFile(hPipeInOut, "", 1, NULL, &oWrite);
if (!GetOverlappedResult(hPipeInOut, &oWrite, &dwWritten, TRUE)) {
rz_sys_perror("lang_pipe_run_win/WriteFile nul");
}
}
}
} while (true);
rz_cons_break_pop();
CloseHandle(hWritten);
CloseHandle(hRead);
}
#else
static void env(const char *s, int f) {
char *a = rz_str_newf("%d", f);
rz_sys_setenv(s, a);
// eprintf ("%s %s\n", s, a);
free(a);
}
#endif
RZ_IPI int lang_pipe_run(RzLang *lang, const char *code, int len) {
#if __UNIX__
int safe_in = dup(0);
int child, ret;
int input[2];
int output[2];
if (rz_sys_pipe(input, false) != 0) {
eprintf("rz_lang_pipe: pipe failed on input\n");
if (safe_in != -1) {
close(safe_in);
}
return false;
}
if (rz_sys_pipe(output, false) != 0) {
eprintf("rz_lang_pipe: pipe failed on output\n");
if (safe_in != -1) {
close(safe_in);
}
return false;
}
env("RZ_PIPE_IN", input[0]);
env("RZ_PIPE_OUT", output[1]);
child = rz_sys_fork();
if (child == -1) {
/* error */
perror("pipe run");
} else if (!child) {
/* children */
rz_sys_xsystem(code);
rz_xwrite(input[1], "", 1);
rz_sys_pipe_close(input[0]);
rz_sys_pipe_close(input[1]);
rz_sys_pipe_close(output[0]);
rz_sys_pipe_close(output[1]);
fflush(stdout);
fflush(stderr);
rz_sys_exit(0, true);
return false;
} else {
/* parent */
char *res, buf[8192]; // TODO: use the heap?
/* Close pipe ends not required in the parent */
rz_sys_pipe_close(output[1]);
rz_sys_pipe_close(input[0]);
rz_cons_break_push(NULL, NULL);
for (;;) {
if (rz_cons_is_breaked()) {
break;
}
memset(buf, 0, sizeof(buf));
void *bed = rz_cons_sleep_begin();
ret = read(output[0], buf, sizeof(buf) - 1);
rz_cons_sleep_end(bed);
if (ret < 1) {
break;
}
if (!buf[0]) {
continue;
}
buf[sizeof(buf) - 1] = 0;
res = lang->cmd_str((RzCore *)lang->user, buf);
// eprintf ("%d %s\n", ret, buf);
if (res) {
rz_xwrite(input[1], res, strlen(res) + 1);
free(res);
} else {
eprintf("rz_lang_pipe: NULL reply for (%s)\n", buf);
rz_xwrite(input[1], "", 1); // NULL byte
}
}
rz_cons_break_pop();
/* workaround to avoid stdin closed */
if (safe_in != -1) {
close(safe_in);
}
safe_in = -1;
char *term_in = ttyname(0);
if (term_in) {
safe_in = open(term_in, O_RDONLY);
if (safe_in != -1) {
dup2(safe_in, 0);
} else {
eprintf("Cannot open ttyname(0) %s\n", term_in);
}
}
}
rz_sys_pipe_close(input[0]);
rz_sys_pipe_close(input[1]);
rz_sys_pipe_close(output[0]);
rz_sys_pipe_close(output[1]);
if (safe_in != -1) {
close(safe_in);
}
waitpid(child, NULL, WNOHANG);
return true;
#else
#if __WINDOWS__
char *rzpipe_var = rz_str_newf("RZ_PIPE_IN%x", _getpid());
char *rzpipe_paz = rz_str_newf("\\\\.\\pipe\\%s", rzpipe_var);
LPTSTR rzpipe_paz_ = rz_sys_conv_utf8_to_win(rzpipe_paz);
SetEnvironmentVariable(TEXT("RZ_PIPE_PATH"), rzpipe_paz_);
hPipeInOut = CreateNamedPipe(rzpipe_paz_,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
PIPE_BUF_SIZE,
PIPE_BUF_SIZE,
0, NULL);
if (hPipeInOut == INVALID_HANDLE_VALUE) {
rz_sys_perror("lang_pipe_run/CreateNamedPipe");
goto beach;
}
HANDLE hConnected = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!hConnected) {
rz_sys_perror("lang_pipe_run/CreateEvent hConnected");
goto pipe_cleanup;
}
OVERLAPPED oConnect = { 0 };
oConnect.hEvent = hConnected;
hproc = myCreateChildProcess(code);
BOOL connected = FALSE;
if (hproc) {
connected = ConnectNamedPipe(hPipeInOut, &oConnect);
DWORD err = GetLastError();
if (!connected && err != ERROR_PIPE_CONNECTED) {
if (err == ERROR_IO_PENDING) {
HANDLE hEvents[] = { hConnected, hproc };
DWORD dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hEvents), hEvents,
FALSE, INFINITE);
if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
goto cleanup;
} else if (dwEvent == WAIT_FAILED) {
rz_sys_perror("lang_pipe_run/WaitForMultipleObjects connect");
goto cleanup;
}
DWORD dummy;
connected = GetOverlappedResult(hPipeInOut, &oConnect, &dummy, TRUE);
err = GetLastError();
}
if (!connected && err != ERROR_PIPE_CONNECTED) {
rz_sys_perror("lang_pipe_run/ConnectNamedPipe");
goto cleanup;
}
}
lang_pipe_run_win(lang);
}
cleanup:
CloseHandle(hConnected);
pipe_cleanup:
DeleteFile(rzpipe_paz_);
CloseHandle(hPipeInOut);
beach:
free(rzpipe_var);
free(rzpipe_paz);
free(rzpipe_paz_);
return hproc != NULL;
#endif
#endif
}