1 /* radare2 - LGPL - Copyright 2015-2020 pancake */
2
3 #include "r_lib.h"
4 #include "r_core.h"
5 #include "r_lang.h"
6 #if __WINDOWS__
7 #include <windows.h>
8 #endif
9 #ifdef _MSC_VER
10 #include <process.h>
11 #endif
12
13 #if __WINDOWS__
myCreateChildProcess(const char * szCmdline)14 static HANDLE myCreateChildProcess(const char * szCmdline) {
15 PROCESS_INFORMATION piProcInfo = {0};
16 STARTUPINFO siStartInfo = {0};
17 BOOL bSuccess = FALSE;
18 siStartInfo.cb = sizeof (STARTUPINFO);
19 siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
20 siStartInfo.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
21 siStartInfo.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
22 siStartInfo.hStdError = GetStdHandle (STD_ERROR_HANDLE);
23
24 LPTSTR cmdline_ = r_sys_conv_utf8_to_win (szCmdline);
25 bSuccess = CreateProcess (NULL, cmdline_, NULL, NULL,
26 TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
27 free (cmdline_);
28 return bSuccess ? piProcInfo.hProcess : NULL;
29 }
30
31 static HANDLE hPipeInOut = NULL;
32 static HANDLE hproc = NULL;
33 #define PIPE_BUF_SIZE 8192
34
lang_pipe_run_win(RLang * lang)35 static void lang_pipe_run_win(RLang *lang) {
36 CHAR buf[PIPE_BUF_SIZE];
37 BOOL bSuccess = TRUE;
38 int i, res = 0;
39 DWORD dwRead = 0, dwWritten = 0, dwEvent;
40 HANDLE hRead = CreateEvent (NULL, TRUE, FALSE, NULL);
41 if (!hRead) {
42 r_sys_perror ("lang_pipe_run_win/CreateEvent hRead");
43 return;
44 }
45 HANDLE hWritten = CreateEvent (NULL, TRUE, FALSE, NULL);
46 if (!hWritten) {
47 r_sys_perror ("lang_pipe_run_win/CreateEvent hWritten");
48 CloseHandle (hRead);
49 return;
50 }
51 r_cons_break_push (NULL, NULL);
52 do {
53 if (r_cons_is_breaked ()) {
54 TerminateProcess (hproc, 0);
55 break;
56 }
57 OVERLAPPED oRead = { 0 };
58 oRead.hEvent = hRead;
59 memset (buf, 0, PIPE_BUF_SIZE);
60 ReadFile (hPipeInOut, buf, PIPE_BUF_SIZE, NULL, &oRead);
61 HANDLE hReadEvents[] = { hRead, hproc };
62 dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hReadEvents), hReadEvents,
63 FALSE, INFINITE);
64 if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
65 break;
66 } else if (dwEvent == WAIT_FAILED) {
67 r_sys_perror ("lang_pipe_run_win/WaitForMultipleObjects read");
68 break;
69 }
70 bSuccess = GetOverlappedResult (hPipeInOut, &oRead, &dwRead, TRUE);
71 if (!bSuccess) {
72 break;
73 }
74 if (bSuccess && dwRead > 0) {
75 buf[sizeof (buf) - 1] = 0;
76 OVERLAPPED oWrite = { 0 };
77 oWrite.hEvent = hWritten;
78 char *res = lang->cmd_str ((RCore*)lang->user, buf);
79 if (res) {
80 int res_len = strlen (res) + 1;
81 for (i = 0; i < res_len; i++) {
82 memset (buf, 0, PIPE_BUF_SIZE);
83 dwWritten = 0;
84 int writelen = res_len - i;
85 WriteFile (hPipeInOut, res + i,
86 writelen > PIPE_BUF_SIZE ? PIPE_BUF_SIZE : writelen,
87 NULL, &oWrite);
88 HANDLE hWriteEvents[] = { hWritten, hproc };
89 dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hWriteEvents), hWriteEvents,
90 FALSE, INFINITE);
91 if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
92 break;
93 } else if (dwEvent == WAIT_FAILED) {
94 r_sys_perror ("lang_pipe_run_win/WaitForMultipleObjects write");
95 }
96 BOOL rc = GetOverlappedResult (hPipeInOut, &oWrite, &dwWritten, TRUE);
97 if (!rc) {
98 r_sys_perror ("lang_pipe_run_win/WriteFile res");
99 }
100 if (dwWritten > 0) {
101 i += dwWritten - 1;
102 } else {
103 // send null termination // chop
104 r_sys_perror ("lang_pipe_run_win/dwWritten");
105 //WriteFile (hPipeInOut, "", 1, &dwWritten, NULL);
106 //break;
107 }
108 }
109 free (res);
110 } else {
111 WriteFile (hPipeInOut, "", 1, NULL, &oWrite);
112 if (!GetOverlappedResult (hPipeInOut, &oWrite, &dwWritten, TRUE)) {
113 r_sys_perror ("lang_pipe_run_win/WriteFile nul");
114 }
115 }
116 }
117 } while (true);
118 r_cons_break_pop ();
119 CloseHandle (hWritten);
120 CloseHandle (hRead);
121 }
122 #else
env(const char * s,int f)123 static void env(const char *s, int f) {
124 char *a = r_str_newf ("%d", f);
125 r_sys_setenv (s, a);
126 // eprintf ("%s %s\n", s, a);
127 free (a);
128 }
129 #endif
130
lang_pipe_run(RLang * lang,const char * code,int len)131 static bool lang_pipe_run(RLang *lang, const char *code, int len) {
132 #if __UNIX__
133 int safe_in = dup (0);
134 int child, ret;
135 int input[2];
136 int output[2];
137
138 if (pipe (input) != 0) {
139 eprintf ("r_lang_pipe: pipe failed on input\n");
140 if (safe_in != -1) {
141 close (safe_in);
142 }
143 return false;
144 }
145 if (pipe (output) != 0) {
146 eprintf ("r_lang_pipe: pipe failed on output\n");
147 if (safe_in != -1) {
148 close (safe_in);
149 }
150 return false;
151 }
152
153 env ("R2PIPE_IN", input[0]);
154 env ("R2PIPE_OUT", output[1]);
155
156 child = r_sys_fork ();
157 if (child == -1) {
158 /* error */
159 perror ("pipe run");
160 } else if (!child) {
161 /* children */
162 r_sandbox_system (code, 1);
163 (void) write (input[1], "", 1);
164 close (input[0]);
165 close (input[1]);
166 close (output[0]);
167 close (output[1]);
168 fflush (stdout);
169 fflush (stderr);
170 r_sys_exit (0, true);
171 return false;
172 } else {
173 /* parent */
174 char *res, buf[8192]; // TODO: use the heap?
175 /* Close pipe ends not required in the parent */
176 close (output[1]);
177 close (input[0]);
178 r_cons_break_push (NULL, NULL);
179 for (;;) {
180 if (r_cons_is_breaked ()) {
181 break;
182 }
183 memset (buf, 0, sizeof (buf));
184 void *bed = r_cons_sleep_begin ();
185 ret = read (output[0], buf, sizeof (buf) - 1);
186 r_cons_sleep_end (bed);
187 if (ret < 1) {
188 break;
189 }
190 if (!buf[0]) {
191 continue;
192 }
193 buf[sizeof (buf) - 1] = 0;
194 res = lang->cmd_str ((RCore*)lang->user, buf);
195 if (res) {
196 // r_cons_print (res);
197 (void) write (input[1], res, strlen (res) + 1);
198 free (res);
199 } else {
200 eprintf ("r_lang_pipe: NULL reply for (%s)\n", buf);
201 (void) write (input[1], "", 1); // NULL byte
202 }
203 }
204 r_cons_break_pop ();
205 /* workaround to avoid stdin closed */
206 if (safe_in != -1) {
207 close (safe_in);
208 }
209 safe_in = -1;
210 char *term_in = ttyname (0);
211 if (term_in) {
212 safe_in = open (term_in, O_RDONLY);
213 if (safe_in != -1) {
214 dup2 (safe_in, 0);
215 } else {
216 eprintf ("Cannot open ttyname(0) %s\n", term_in);
217 }
218 }
219 }
220
221 close (input[0]);
222 close (input[1]);
223 close (output[0]);
224 close (output[1]);
225 if (safe_in != -1) {
226 close (safe_in);
227 }
228 waitpid (child, NULL, WNOHANG);
229 return true;
230 #else
231 #if __WINDOWS__
232 char *r2pipe_var = r_str_newf ("R2PIPE_IN%x", _getpid ());
233 char *r2pipe_paz = r_str_newf ("\\\\.\\pipe\\%s", r2pipe_var);
234 LPTSTR r2pipe_paz_ = r_sys_conv_utf8_to_win (r2pipe_paz);
235
236 SetEnvironmentVariable (TEXT ("R2PIPE_PATH"), r2pipe_paz_);
237 hPipeInOut = CreateNamedPipe (r2pipe_paz_,
238 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
239 PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,
240 PIPE_BUF_SIZE,
241 PIPE_BUF_SIZE,
242 0, NULL);
243 if (hPipeInOut == INVALID_HANDLE_VALUE) {
244 r_sys_perror ("lang_pipe_run/CreateNamedPipe");
245 goto beach;
246 }
247 HANDLE hConnected = CreateEvent (NULL, TRUE, FALSE, NULL);
248 if (!hConnected) {
249 r_sys_perror ("lang_pipe_run/CreateEvent hConnected");
250 goto pipe_cleanup;
251 }
252 OVERLAPPED oConnect = { 0 };
253 oConnect.hEvent = hConnected;
254 hproc = myCreateChildProcess (code);
255 BOOL connected = FALSE;
256 if (hproc) {
257 connected = ConnectNamedPipe (hPipeInOut, &oConnect);
258 DWORD err = GetLastError ();
259 if (!connected && err != ERROR_PIPE_CONNECTED) {
260 if (err == ERROR_IO_PENDING) {
261 HANDLE hEvents[] = { hConnected, hproc };
262 DWORD dwEvent = WaitForMultipleObjects (R_ARRAY_SIZE (hEvents), hEvents,
263 FALSE, INFINITE);
264 if (dwEvent == WAIT_OBJECT_0 + 1) { // hproc
265 goto cleanup;
266 } else if (dwEvent == WAIT_FAILED) {
267 r_sys_perror ("lang_pipe_run/WaitForMultipleObjects connect");
268 goto cleanup;
269 }
270 DWORD dummy;
271 connected = GetOverlappedResult (hPipeInOut, &oConnect, &dummy, TRUE);
272 err = GetLastError ();
273 }
274 if (!connected && err != ERROR_PIPE_CONNECTED) {
275 r_sys_perror ("lang_pipe_run/ConnectNamedPipe");
276 goto cleanup;
277 }
278 }
279 lang_pipe_run_win (lang);
280 }
281 cleanup:
282 CloseHandle (hConnected);
283 pipe_cleanup:
284 DeleteFile (r2pipe_paz_);
285 CloseHandle (hPipeInOut);
286 beach:
287 free (r2pipe_var);
288 free (r2pipe_paz);
289 free (r2pipe_paz_);
290 return hproc != NULL;
291 #endif
292 #endif
293 }
294
lang_pipe_file(RLang * lang,const char * file)295 static bool lang_pipe_file(RLang *lang, const char *file) {
296 return lang_pipe_run (lang, file, -1);
297 }
298
299 static RLangPlugin r_lang_plugin_pipe = {
300 .name = "pipe",
301 .ext = "pipe",
302 .license = "LGPL",
303 .desc = "Use #!pipe node script.js",
304 .run = lang_pipe_run,
305 .run_file = (void*)lang_pipe_file,
306 };
307