Rizin
unix-like reverse engineering framework and cli tools
pipe_helper.c
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2015-2020 pancake <pancake@nopcode.org>
2 // SPDX-License-Identifier: LGPL-3.0-only
3 
4 #include <rz_lib.h>
5 #include <rz_core.h>
6 #include <rz_lang.h>
7 #if __WINDOWS__
8 #include <windows.h>
9 #endif
10 #ifdef _MSC_VER
11 #include <process.h>
12 #endif
13 
14 #if __WINDOWS__
15 static HANDLE myCreateChildProcess(const char *szCmdline) {
16  PROCESS_INFORMATION piProcInfo = { 0 };
17  STARTUPINFO siStartInfo = { 0 };
18  BOOL bSuccess = FALSE;
19  siStartInfo.cb = sizeof(STARTUPINFO);
20  siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
21  siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
22  siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
23  siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
24 
25  LPTSTR cmdline_ = rz_sys_conv_utf8_to_win(szCmdline);
26  bSuccess = CreateProcess(NULL, cmdline_, NULL, NULL,
27  TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
28  free(cmdline_);
29  return bSuccess ? piProcInfo.hProcess : NULL;
30 }
31 
32 static HANDLE hPipeInOut = NULL;
33 static HANDLE hproc = NULL;
34 #define PIPE_BUF_SIZE 8192
35 
36 static void lang_pipe_run_win(RzLang *lang) {
37  CHAR buf[PIPE_BUF_SIZE];
38  BOOL bSuccess = TRUE;
39  int i;
40  DWORD dwRead = 0, dwWritten = 0, dwEvent;
41  HANDLE hRead = CreateEvent(NULL, TRUE, FALSE, NULL);
42  if (!hRead) {
43  rz_sys_perror("lang_pipe_run_win/CreateEvent hRead");
44  return;
45  }
46  HANDLE hWritten = CreateEvent(NULL, TRUE, FALSE, NULL);
47  if (!hWritten) {
48  rz_sys_perror("lang_pipe_run_win/CreateEvent hWritten");
49  CloseHandle(hRead);
50  return;
51  }
53  do {
54  if (rz_cons_is_breaked()) {
55  TerminateProcess(hproc, 0);
56  break;
57  }
58  OVERLAPPED oRead = { 0 };
59  oRead.hEvent = hRead;
60  memset(buf, 0, PIPE_BUF_SIZE);
61  ReadFile(hPipeInOut, buf, PIPE_BUF_SIZE, NULL, &oRead);
62  HANDLE hReadEvents[] = { hRead, hproc };
63  dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hReadEvents), hReadEvents,
64  FALSE, INFINITE);
65  if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
66  break;
67  } else if (dwEvent == WAIT_FAILED) {
68  rz_sys_perror("lang_pipe_run_win/WaitForMultipleObjects read");
69  break;
70  }
71  bSuccess = GetOverlappedResult(hPipeInOut, &oRead, &dwRead, TRUE);
72  if (!bSuccess) {
73  break;
74  }
75  if (bSuccess && dwRead > 0) {
76  buf[sizeof(buf) - 1] = 0;
77  OVERLAPPED oWrite = { 0 };
78  oWrite.hEvent = hWritten;
79  char *res = lang->cmd_str((RzCore *)lang->user, buf);
80  if (res) {
81  int res_len = strlen(res) + 1;
82  for (i = 0; i < res_len; i++) {
83  memset(buf, 0, PIPE_BUF_SIZE);
84  dwWritten = 0;
85  int writelen = res_len - i;
86  WriteFile(hPipeInOut, res + i,
87  writelen > PIPE_BUF_SIZE ? PIPE_BUF_SIZE : writelen,
88  NULL, &oWrite);
89  HANDLE hWriteEvents[] = { hWritten, hproc };
90  dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hWriteEvents), hWriteEvents,
91  FALSE, INFINITE);
92  if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
93  break;
94  } else if (dwEvent == WAIT_FAILED) {
95  rz_sys_perror("lang_pipe_run_win/WaitForMultipleObjects write");
96  }
97  BOOL rc = GetOverlappedResult(hPipeInOut, &oWrite, &dwWritten, TRUE);
98  if (!rc) {
99  rz_sys_perror("lang_pipe_run_win/WriteFile res");
100  }
101  if (dwWritten > 0) {
102  i += dwWritten - 1;
103  } else {
104  // send null termination // chop
105  rz_sys_perror("lang_pipe_run_win/dwWritten");
106  // WriteFile (hPipeInOut, "", 1, &dwWritten, NULL);
107  // break;
108  }
109  }
110  free(res);
111  } else {
112  WriteFile(hPipeInOut, "", 1, NULL, &oWrite);
113  if (!GetOverlappedResult(hPipeInOut, &oWrite, &dwWritten, TRUE)) {
114  rz_sys_perror("lang_pipe_run_win/WriteFile nul");
115  }
116  }
117  }
118  } while (true);
120  CloseHandle(hWritten);
121  CloseHandle(hRead);
122 }
123 #else
124 static void env(const char *s, int f) {
125  char *a = rz_str_newf("%d", f);
126  rz_sys_setenv(s, a);
127  // eprintf ("%s %s\n", s, a);
128  free(a);
129 }
130 #endif
131 
132 RZ_IPI int lang_pipe_run(RzLang *lang, const char *code, int len) {
133 #if __UNIX__
134  int safe_in = dup(0);
135  int child, ret;
136  int input[2];
137  int output[2];
138 
139  if (rz_sys_pipe(input, false) != 0) {
140  eprintf("rz_lang_pipe: pipe failed on input\n");
141  if (safe_in != -1) {
142  close(safe_in);
143  }
144  return false;
145  }
146  if (rz_sys_pipe(output, false) != 0) {
147  eprintf("rz_lang_pipe: pipe failed on output\n");
148  if (safe_in != -1) {
149  close(safe_in);
150  }
151  return false;
152  }
153 
154  env("RZ_PIPE_IN", input[0]);
155  env("RZ_PIPE_OUT", output[1]);
156 
157  child = rz_sys_fork();
158  if (child == -1) {
159  /* error */
160  perror("pipe run");
161  } else if (!child) {
162  /* children */
164  rz_xwrite(input[1], "", 1);
169  fflush(stdout);
170  fflush(stderr);
171  rz_sys_exit(0, true);
172  return false;
173  } else {
174  /* parent */
175  char *res, buf[8192]; // TODO: use the heap?
176  /* Close pipe ends not required in the parent */
180  for (;;) {
181  if (rz_cons_is_breaked()) {
182  break;
183  }
184  memset(buf, 0, sizeof(buf));
185  void *bed = rz_cons_sleep_begin();
186  ret = read(output[0], buf, sizeof(buf) - 1);
187  rz_cons_sleep_end(bed);
188  if (ret < 1) {
189  break;
190  }
191  if (!buf[0]) {
192  continue;
193  }
194  buf[sizeof(buf) - 1] = 0;
195  res = lang->cmd_str((RzCore *)lang->user, buf);
196  // eprintf ("%d %s\n", ret, buf);
197  if (res) {
198  rz_xwrite(input[1], res, strlen(res) + 1);
199  free(res);
200  } else {
201  eprintf("rz_lang_pipe: NULL reply for (%s)\n", buf);
202  rz_xwrite(input[1], "", 1); // NULL byte
203  }
204  }
206  /* workaround to avoid stdin closed */
207  if (safe_in != -1) {
208  close(safe_in);
209  }
210  safe_in = -1;
211  char *term_in = ttyname(0);
212  if (term_in) {
213  safe_in = open(term_in, O_RDONLY);
214  if (safe_in != -1) {
215  dup2(safe_in, 0);
216  } else {
217  eprintf("Cannot open ttyname(0) %s\n", term_in);
218  }
219  }
220  }
221 
226  if (safe_in != -1) {
227  close(safe_in);
228  }
229  waitpid(child, NULL, WNOHANG);
230  return true;
231 #else
232 #if __WINDOWS__
233  char *rzpipe_var = rz_str_newf("RZ_PIPE_IN%x", _getpid());
234  char *rzpipe_paz = rz_str_newf("\\\\.\\pipe\\%s", rzpipe_var);
235  LPTSTR rzpipe_paz_ = rz_sys_conv_utf8_to_win(rzpipe_paz);
236 
237  SetEnvironmentVariable(TEXT("RZ_PIPE_PATH"), rzpipe_paz_);
238  hPipeInOut = CreateNamedPipe(rzpipe_paz_,
239  PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
240  PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
241  PIPE_BUF_SIZE,
242  PIPE_BUF_SIZE,
243  0, NULL);
244  if (hPipeInOut == INVALID_HANDLE_VALUE) {
245  rz_sys_perror("lang_pipe_run/CreateNamedPipe");
246  goto beach;
247  }
248  HANDLE hConnected = CreateEvent(NULL, TRUE, FALSE, NULL);
249  if (!hConnected) {
250  rz_sys_perror("lang_pipe_run/CreateEvent hConnected");
251  goto pipe_cleanup;
252  }
253  OVERLAPPED oConnect = { 0 };
254  oConnect.hEvent = hConnected;
255  hproc = myCreateChildProcess(code);
256  BOOL connected = FALSE;
257  if (hproc) {
258  connected = ConnectNamedPipe(hPipeInOut, &oConnect);
259  DWORD err = GetLastError();
260  if (!connected && err != ERROR_PIPE_CONNECTED) {
261  if (err == ERROR_IO_PENDING) {
262  HANDLE hEvents[] = { hConnected, hproc };
263  DWORD dwEvent = WaitForMultipleObjects(RZ_ARRAY_SIZE(hEvents), hEvents,
264  FALSE, INFINITE);
265  if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
266  goto cleanup;
267  } else if (dwEvent == WAIT_FAILED) {
268  rz_sys_perror("lang_pipe_run/WaitForMultipleObjects connect");
269  goto cleanup;
270  }
271  DWORD dummy;
272  connected = GetOverlappedResult(hPipeInOut, &oConnect, &dummy, TRUE);
273  err = GetLastError();
274  }
275  if (!connected && err != ERROR_PIPE_CONNECTED) {
276  rz_sys_perror("lang_pipe_run/ConnectNamedPipe");
277  goto cleanup;
278  }
279  }
280  lang_pipe_run_win(lang);
281  }
282 cleanup:
283  CloseHandle(hConnected);
284 pipe_cleanup:
285  DeleteFile(rzpipe_paz_);
286  CloseHandle(hPipeInOut);
287 beach:
288  free(rzpipe_var);
289  free(rzpipe_paz);
290  free(rzpipe_paz_);
291  return hproc != NULL;
292 #endif
293 #endif
294 }
size_t len
Definition: 6502dis.c:15
#define RZ_IPI
Definition: analysis_wasm.c:11
lzma_index ** i
Definition: index.h:629
static bool err
Definition: armass.c:435
RZ_API void * rz_cons_sleep_begin(void)
Definition: cons.c:443
RZ_API void rz_cons_break_pop(void)
Definition: cons.c:361
RZ_API void rz_cons_break_push(RzConsBreak cb, void *user)
Definition: cons.c:357
RZ_API bool rz_cons_is_breaked(void)
Definition: cons.c:373
RZ_API void rz_cons_sleep_end(void *user)
Definition: cons.c:450
#define NULL
Definition: cris-opc.c:27
static static fork const void static count close
Definition: sflib.h:33
void cleanup(void)
Definition: enough.c:244
RZ_API void Ht_() free(HtName_(Ht) *ht)
Definition: ht_inc.c:130
voidpf void * buf
Definition: ioapi.h:138
#define INVALID_HANDLE_VALUE
Definition: iowin32.c:21
return memset(p, 0, total)
static static fork const void static count static fd const char const char static newpath char char char static envp time_t static t const char static mode static whence const char static dir time_t static t unsigned static seconds const char struct utimbuf static buf static inc static sig const char static mode dup
Definition: sflib.h:68
static static fork const void static count static fd const char static mode const char static pathname const char static path const char static dev const char static group static getpid static getuid void void static data static pause const char static mode static sync const char const char static newpath const char static pathname unsigned long static filedes void static end_data_segment static handler static getegid char static len static pgid const char static path dup2
Definition: sflib.h:94
#define TRUE
Definition: mybfd.h:103
#define FALSE
Definition: mybfd.h:102
RZ_IPI int lang_pipe_run(RzLang *lang, const char *code, int len)
Definition: pipe_helper.c:132
static void env(const char *s, int f)
Definition: pipe_helper.c:124
#define eprintf(x, y...)
Definition: rlcc.c:7
static RzSocket * s
Definition: rtr.c:28
RZ_API char * rz_str_newf(const char *fmt,...) RZ_PRINTF_CHECK(1
RZ_API int rz_sys_setenv(const char *key, const char *value)
Set an environment variable in the calling process.
Definition: sys.c:405
RZ_API int rz_sys_pipe(int pipefd[2], bool close_on_exec)
Definition: sys.c:1458
RZ_API void rz_sys_exit(int status, bool nocleanup)
Definition: sys.c:183
#define rz_sys_xsystem(cmd)
Definition: rz_sys.h:83
RZ_API int rz_sys_fork(void)
Definition: sys.c:1679
RZ_API int rz_sys_pipe_close(int fd)
Definition: sys.c:1462
#define rz_sys_perror(x)
Definition: rz_types.h:336
#define rz_xwrite(fd, buf, count)
Definition: rz_types.h:642
#define RZ_ARRAY_SIZE(x)
Definition: rz_types.h:300
#define O_RDONLY
Definition: sftypes.h:486
#define f(i)
Definition: sha256.c:46
#define a(i)
Definition: sha256.c:41
Definition: inftree9.h:24
RzCoreCmdStrCallback cmd_str
Definition: rz_lang.h:23
void * user
Definition: rz_lang.h:19
DWORD * HANDLE
DWORD
static bool input(void *ud, zip_uint8_t *data, zip_uint64_t length)
diff_output_t output
Definition: zipcmp.c:237
int read(izstream &zs, T *x, Items items)
Definition: zstream.h:115