process_win.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. /*
  2. * Copyright (c) 2009-2011 Nokia Corporation and/or its subsidiary(-ies).
  3. * All rights reserved.
  4. * This component and the accompanying materials are made available
  5. * under the terms of the License "Eclipse Public License v1.0"
  6. * which accompanies this distribution, and is available
  7. * at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. *
  9. * Initial Contributors:
  10. * Nokia Corporation - initial contribution.
  11. *
  12. * Contributors:
  13. *
  14. * Description:
  15. *
  16. */
  17. #include <unistd.h>
  18. #include "talon_process.h"
  19. #include "buffer.h"
  20. #include <errno.h>
  21. #include <windows.h>
  22. #include <tchar.h>
  23. #include <stdio.h>
  24. //#include <strsafe.h>
  25. #include "log.h"
  26. #define RETURN(x) { retval=x; goto cleanup; }
  27. #define CLEANUP() cleanup:
  28. #define READSIZE 4096
  29. typedef struct ReadOpStruct {
  30. HANDLE semaphore;
  31. HANDLE thread;
  32. DWORD timeout;
  33. DWORD error;
  34. HANDLE file;
  35. BOOL success;
  36. char *space;
  37. DWORD nbytes;
  38. int id;
  39. struct ReadOpStruct *next;
  40. } ReadOp;
  41. typedef struct {
  42. ReadOp *first;
  43. ReadOp *last;
  44. HANDLE semaphore;
  45. } ReadOpQ;
  46. proc *process_new(void)
  47. {
  48. proc *p = malloc(sizeof(proc));
  49. p->output = buffer_new();
  50. if (!p->output)
  51. {
  52. free(p);
  53. return NULL;
  54. }
  55. p->starttime = 0;
  56. p->endtime = 0;
  57. p->returncode = 1;
  58. p->pid = 0;
  59. p->causeofdeath = PROC_NORMALDEATH;
  60. return p;
  61. }
  62. #define TALONMAXERRSTR 1024
  63. void printlasterror(void)
  64. {
  65. LPTSTR msg;
  66. DWORD err = GetLastError();
  67. char buf[1024];
  68. msg=buf;
  69. DEBUG(("error %d\n",err));
  70. FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
  71. FORMAT_MESSAGE_FROM_SYSTEM |
  72. FORMAT_MESSAGE_IGNORE_INSERTS,
  73. NULL, // lpSource
  74. err, // dwMessageId,
  75. 0,
  76. //MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), // dwLanguageId,
  77. msg,
  78. 0,
  79. NULL
  80. );
  81. DEBUG(("%s\n",msg));
  82. //LocalFree(msg);
  83. }
  84. typedef struct
  85. {
  86. HANDLE read, write;
  87. } tl_stream;
  88. /* Because windows is d**b, there is no way to avoid blocking on an anonymous
  89. * pipe. We can't use CancelIO to stop reads since that's only in newer
  90. * versions of Win***ws. So what we are left with is putting the read operation
  91. * into a thread, timing out in the main body and ignoring this thread if we
  92. * feel we have to.
  93. * */
  94. DWORD readpipe_thread(void *param)
  95. {
  96. ReadOpQ *io_ops = (ReadOpQ *)param;
  97. ReadOp *iopipe_op;
  98. /* have our own buffer since we don't want to risk that the
  99. * caller's buffer might have disappeared by the time
  100. * our readfile unblocks.
  101. */
  102. while (1)
  103. {
  104. WaitForSingleObject(io_ops->semaphore, INFINITE);
  105. iopipe_op = io_ops->last;
  106. DEBUG(("readpipe_thread: pre-ReadFile%d: %d \n", iopipe_op->id, iopipe_op->nbytes));
  107. iopipe_op->success = ReadFile(iopipe_op->file, iopipe_op->space, iopipe_op->nbytes, &iopipe_op->nbytes, NULL);
  108. iopipe_op->error = GetLastError();
  109. DEBUG(("readpipe_thread: post-ReadFile%d: %d read, err %d\n", iopipe_op->id, iopipe_op->nbytes,iopipe_op->error));
  110. ReleaseSemaphore(iopipe_op->semaphore, 1, NULL);
  111. }
  112. }
  113. proc *process_run(char executable[], char *args[], int timeout)
  114. {
  115. proc *retval = NULL;
  116. tl_stream stdout_p;
  117. tl_stream stdin_p;
  118. SECURITY_ATTRIBUTES saAttr;
  119. PROCESS_INFORMATION pi;
  120. STARTUPINFO si;
  121. BOOL createproc_success = FALSE;
  122. BOOL timedout = FALSE;
  123. TCHAR *commandline = NULL;
  124. proc *p = process_new();
  125. if (p == NULL)
  126. return NULL;
  127. /* Make sure pipe handles are inherited */
  128. saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
  129. saAttr.bInheritHandle = TRUE;
  130. saAttr.lpSecurityDescriptor = NULL;
  131. p->causeofdeath = PROC_PIPECREATE;
  132. DEBUG(("making pipes \n"));
  133. /* Child's Stdout */
  134. if ( ! CreatePipe(&stdout_p.read, &stdout_p.write, &saAttr, 1) )
  135. {
  136. printlasterror();
  137. RETURN(p);
  138. }
  139. DEBUG(("stdout done \n"));
  140. /* read handle to the pipe for STDOUT is not inherited */
  141. if ( ! SetHandleInformation(stdout_p.read, HANDLE_FLAG_INHERIT, 0) )
  142. {
  143. printlasterror();
  144. RETURN(p);
  145. }
  146. DEBUG(("stdout noinherit \n"));
  147. /* a pipe for the child process's STDIN */
  148. if ( ! CreatePipe(&stdin_p.read, &stdin_p.write, &saAttr, 0) )
  149. {
  150. printlasterror();
  151. RETURN(p);
  152. }
  153. DEBUG(("stdin done \n"));
  154. /* write handle to the pipe for STDIN not inherited */
  155. if ( ! SetHandleInformation(stdin_p.read, HANDLE_FLAG_INHERIT, 0) )
  156. {
  157. printlasterror();
  158. RETURN(p);
  159. }
  160. DEBUG(("pipes done \n"));
  161. p->causeofdeath = PROC_START;
  162. ZeroMemory( &si, sizeof(STARTUPINFO) );
  163. ZeroMemory( &pi, sizeof(PROCESS_INFORMATION) );
  164. si.cb = sizeof(STARTUPINFO);
  165. si.hStdError = stdout_p.write;
  166. si.hStdOutput = stdout_p.write;
  167. /*
  168. Rather than use the stdin pipe, which would be
  169. si.hStdInput = stdin_p.read;
  170. Pass on talon's own standard input to the child process
  171. This helps with programs like xcopy which demand that
  172. they are attached to a console and not just any type of
  173. input file.
  174. */
  175. si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
  176. si.dwFlags |= STARTF_USESTDHANDLES;
  177. DEBUG(("pre commandline \n"));
  178. /* create the commandline string */
  179. int len = strlen(executable) + 1;
  180. int i = 1;
  181. while (args[i] != NULL)
  182. {
  183. len += strlen(args[i++]) + 1;
  184. }
  185. len+=2;
  186. commandline = malloc(len*2);
  187. if (! commandline)
  188. RETURN(p);
  189. commandline[0] = '\0';
  190. strcat(commandline, executable);
  191. strcat(commandline, " ");
  192. i = 1;
  193. while (args[i] != NULL)
  194. {
  195. strcat(commandline, args[i]);
  196. strcat(commandline, " ");
  197. i++;
  198. }
  199. /* Get the read thread ready to go before creating
  200. * the process.
  201. */
  202. ReadOpQ *ropq = malloc(sizeof(ReadOpQ));
  203. ropq->first=NULL;
  204. ropq->last=NULL;
  205. ropq->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
  206. DEBUG(("Creating read thread. \n"));
  207. DWORD readpipe_threadid;
  208. HANDLE h_readpipe_thread = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE) readpipe_thread, (void*)ropq, 0, &readpipe_threadid);
  209. /* ready to run the process */
  210. DEBUG(("process executable:\n%s \n", executable));
  211. DEBUG(("process commandline:\n%s \n", commandline));
  212. DEBUG(("\n"));
  213. createproc_success = CreateProcess(NULL,
  214. commandline, // command line, starts with executable, possibly fully-pathed
  215. NULL, // process security attributes
  216. NULL, // primary thread security attributes
  217. TRUE, // handles are inherited
  218. 0, // creation flags
  219. NULL, // use parent's environment
  220. NULL, // use parent's current directory
  221. &si, // STARTUPINFO pointer
  222. &pi); // receives PROCESS_INFORMATION
  223. if (! createproc_success)
  224. {
  225. DWORD last_error = GetLastError();
  226. LPVOID msg_buff;
  227. FormatMessage(
  228. FORMAT_MESSAGE_ALLOCATE_BUFFER |
  229. FORMAT_MESSAGE_FROM_SYSTEM |
  230. FORMAT_MESSAGE_IGNORE_INSERTS,
  231. NULL,
  232. last_error,
  233. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  234. (LPTSTR) &msg_buff,
  235. 0, NULL );
  236. DEBUG(("CreateProcess failed (error code %d):\n%s\n",
  237. last_error, msg_buff));
  238. LocalFree(msg_buff);
  239. p->causeofdeath = PROC_SOMEODDDEATH;
  240. RETURN(p);
  241. }
  242. DEBUG(("Closing Handles. \n"));
  243. if (!CloseHandle(stdout_p.write))
  244. RETURN(p);
  245. if (!CloseHandle(stdin_p.read))
  246. RETURN(p);
  247. DEBUG(("Closed Handles. \n"));
  248. static int id=0;
  249. do
  250. {
  251. char *space = buffer_makespace(p->output, READSIZE);
  252. DWORD waitres;
  253. ReadOp *iopipe_op = malloc(sizeof(ReadOp));
  254. iopipe_op->semaphore = CreateSemaphore(NULL, 0, 1, NULL);
  255. iopipe_op->thread = h_readpipe_thread;
  256. iopipe_op->timeout = timeout;
  257. iopipe_op->file = stdout_p.read;
  258. iopipe_op->space = malloc(READSIZE);
  259. iopipe_op->id = id++;
  260. iopipe_op->nbytes = READSIZE;
  261. iopipe_op->next = NULL;
  262. if (!ropq->first)
  263. {
  264. ropq->first = iopipe_op;
  265. ropq->last = iopipe_op;
  266. } else {
  267. ropq->last->next = iopipe_op;
  268. ropq->last = iopipe_op;
  269. }
  270. ReleaseSemaphore(ropq->semaphore, 1, NULL);
  271. DEBUG(("waiting for read %d\n", timeout));
  272. waitres = WaitForSingleObject(iopipe_op->semaphore, timeout);
  273. DEBUG(("read wait finished result= %d\n", waitres));
  274. if (waitres != WAIT_OBJECT_0)
  275. {
  276. DEBUG(("timeout \n"));
  277. timedout = TRUE;
  278. break;
  279. }
  280. else
  281. {
  282. DEBUG(("read signalled: nbytes: %d \n", iopipe_op->nbytes));
  283. if (iopipe_op->nbytes <= 0)
  284. {
  285. break;
  286. }
  287. memcpy(space, iopipe_op->space, iopipe_op->nbytes);
  288. buffer_usespace(p->output, iopipe_op->nbytes);
  289. DEBUG(("buffer took on nbytes: %d \n", iopipe_op->nbytes));
  290. }
  291. }
  292. while (1);
  293. if (timedout == FALSE)
  294. {
  295. DEBUG(("Wait for process exit\n"));
  296. // Wait until child process exits.
  297. WaitForSingleObject(pi.hProcess, INFINITE);
  298. DEBUG(("Process exited\n"));
  299. DWORD exitcode;
  300. if (GetExitCodeProcess(pi.hProcess, &exitcode))
  301. {
  302. p->causeofdeath = PROC_NORMALDEATH;
  303. p->returncode = exitcode;
  304. DEBUG(("process exited normally = %d:\n", p->returncode));
  305. RETURN(p);
  306. } else {
  307. p->causeofdeath = PROC_SOMEODDDEATH;
  308. p->returncode = 128;
  309. DEBUG(("process terminated \n"));
  310. RETURN(p);
  311. }
  312. } else {
  313. TerminateProcess(pi.hProcess,1);
  314. p->causeofdeath = PROC_TIMEOUTDEATH;
  315. p->returncode = 128;
  316. DEBUG(("process timedout \n"));
  317. RETURN(p);
  318. }
  319. /* Clean up the read operation queue
  320. ReadOp *r = ropq.first;
  321. do
  322. {
  323. CloseHandle(r->semaphore);
  324. free(r->space);
  325. free(r);
  326. r = r->next;
  327. } while (r != NULL); */
  328. CLEANUP();
  329. if (retval == NULL)
  330. {
  331. if (p)
  332. process_free(&p);
  333. }
  334. if (commandline)
  335. free(commandline);
  336. return retval;
  337. }
  338. void process_free(proc **pp)
  339. {
  340. if (!pp)
  341. return;
  342. if (! *pp)
  343. return;
  344. if ((*pp)->output)
  345. buffer_free(&((*pp)->output));
  346. free(*pp);
  347. *pp = NULL;
  348. }