CVS housekeeping.
[reactos.git] / rosapps / devutils / vmingw / process.cpp
1 /********************************************************************
2 * Module: process.cpp. This is part of Visual-MinGW.
3 *
4 * Purpose: Procedures to invoke MinGW compiler.
5 *
6 * Authors: Manu B.
7 *
8 * License: Visual-MinGW is covered by GNU General Public License,
9 * Copyright (C) 2001 Manu B.
10 * See license.htm for more details.
11 *
12 * Note: The following article from MSDN explanes how to handle Callback
13 * procedures :
14 * Calling All Members: Member Functions as Callbacks.
15 * by Dale Rogerson.
16 * Microsoft Developer Network Technology Group.
17 * April 30, 1992.
18 * http://msdn.microsoft.com/archive/default.asp
19 *
20 * Revisions:
21 *
22 ********************************************************************/
23 #include <windows.h>
24 #include <stdio.h>
25 #include <process.h>
26 #include <time.h>
27 #include <process.h>
28 #include "process.h"
29 #include "project.h"
30 #include "main.h"
31 #include "rsrc.h"
32
33 extern CCriticalSection CriticalSection;
34 extern CMessageBox MsgBox;
35 char errmsg[128];
36
37 // For winApp.isWinNT and winApp.Report.Append
38 extern CWinApp winApp;
39
40 /********************************************************************
41 * Class: CCommandDlg.
42 *
43 * Purpose:
44 *
45 * Revisions:
46 *
47 ********************************************************************/
48 CCommandDlg::CCommandDlg(){
49 *cmdLine = '\0';
50 }
51
52 CCommandDlg::~CCommandDlg(){
53 }
54
55 HWND CCommandDlg::Create(void){
56 return CreateParam(&winApp, IDD_COMMAND, 0);
57 }
58
59 LRESULT CALLBACK CCommandDlg::CDlgProc(UINT Message, WPARAM wParam, LPARAM lParam){
60 switch(Message){
61 case WM_INITDIALOG:
62 return OnInitDialog((HWND) wParam, lParam);
63
64 case WM_COMMAND:
65 OnCommand(HIWORD(wParam), LOWORD(wParam), (HWND) lParam);
66 break;
67
68 case WM_CLOSE:
69 EndDlg(0);
70 break;
71 }
72 return FALSE;
73 }
74
75 BOOL CCommandDlg::OnInitDialog(HWND, LPARAM){
76 hCmdLine = GetItem(IDC_CMDLINE);
77
78 SetItemText(hCmdLine, cmdLine);
79 // Show the dialog.
80 Show();
81 return TRUE;
82 }
83
84 BOOL CCommandDlg::OnCommand(WORD, WORD wID, HWND){
85 switch (wID){
86 case IDOK:
87 GetItemText(hCmdLine, cmdLine, sizeof(cmdLine));
88 //MsgBox.DisplayString(cmdLine);
89 winApp.Process.CommandLine(cmdLine);
90 return TRUE;
91
92 case IDCANCEL:
93 EndDlg(IDCANCEL);
94 return FALSE;
95 }
96 return FALSE;
97 }
98
99
100 /********************************************************************
101 * Class: CTask.
102 *
103 * Purpose:
104 *
105 * Revisions:
106 *
107 ********************************************************************/
108 CTask::CTask(){
109 *cmdLine = '\0';
110 *szFileName = '\0';
111 creationFlag = 0;
112 outputFlag = 0;
113 }
114
115 CTask::~CTask(){
116 }
117
118
119 /********************************************************************
120 * Class: CStack.
121 *
122 * Purpose:
123 *
124 * Revisions:
125 *
126 ********************************************************************/
127 CStack::CStack(){
128 retBuf = NULL;
129 }
130
131 CStack::~CStack(){
132 DestroyList();
133 if (retBuf)
134 delete retBuf;
135 }
136
137 void CStack::DetachCurrent(void){
138 // Empty list ?
139 if (current != NULL){
140 CNode * node = current;
141
142 // Detach node from the list.
143 if (node->next != NULL)
144 node->next->prev = node->prev;
145 if (node->prev != NULL)
146 node->prev->next = node->next;
147
148 // Set current node.
149 if(node->next != NULL)
150 current = node->next;
151 else
152 current = node->prev;
153
154 if (current == NULL){
155 // Now, the list is empty.
156 first = last = NULL;
157
158 }else if (first == node){
159 // Detached node was first.
160 first = current;
161
162 }else if (last == node){
163 // Detached node was last.
164 last = current;
165 }
166 count--;
167 }
168 }
169
170 /********************************************************************
171 * Push/Pop/Flush.
172 ********************************************************************/
173 int CStack::Push(CTask * newTask){
174 InsertLast(newTask);
175 return Length();
176 }
177
178 CTask * CStack::Pop(void){
179 // Delete return buffer.
180 if (retBuf){
181 delete retBuf;
182 retBuf = NULL;
183 }
184
185 // Get first node. (FIFO stack)
186 retBuf = (CTask*) First();
187
188 // The Stack is empty ?
189 if (!retBuf)
190 return NULL;
191
192 // Detach current node from the list. Return a pointer to it.
193 DetachCurrent();
194 return retBuf;
195 }
196
197 void CStack::Flush(void){
198 DestroyList();
199 if (retBuf)
200 delete retBuf;
201 retBuf = NULL;
202 }
203
204
205 /********************************************************************
206 * Class: CPipes.
207 *
208 * Purpose: Creates needed pipes, depending on creationFlag.
209 * Like GNU Make does, we use an Handle array for our pipes.
210 * Parent Process Side is stdXXX[0] and Child Process Side is stdXXX[1].
211 *
212 * Ex: PARENT ->[0]IN_PIPE[1]-> CHILD_IO ->[1]OUT_PIPE[0]-> PARENT
213 * ->[1]ERR_PIPE[0]-> PARENT
214 * Revisions:
215 *
216 ********************************************************************/
217 CPipes::CPipes(){
218 hIn[0] = NULL;
219 hIn[1] = NULL;
220 hOut[0] = NULL;
221 hOut[1] = NULL;
222 hErr[0] = NULL;
223 hErr[1] = NULL;
224 }
225
226 CPipes::~CPipes(){
227 }
228
229 bool CPipes::Create(WORD creationFlag, bool winNT){
230 /* Create needed pipes according to creationFlag */
231 /* Parent side of pipes is [0], child side is [1] */
232 HANDLE hDup;
233 SECURITY_ATTRIBUTES sa;
234 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
235 sa.bInheritHandle = TRUE;
236 sa.lpSecurityDescriptor = NULL;
237
238 if (winNT){
239 /* Create a security descriptor for Windows NT */
240 SECURITY_DESCRIPTOR sd;
241 if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)){
242 sprintf(errmsg, "vm error: Process.cpp InitializeSecurityDescriptor(winNT) failed (e=%d)", (int)GetLastError());
243 winApp.Report.Append(errmsg, LVOUT_ERROR);
244 return false;
245 }
246 if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE)){
247 sprintf(errmsg, "vm error: Process.cpp SetSecurityDescriptorDacl(winNT) failed (e=%d)", (int)GetLastError());
248 winApp.Report.Append(errmsg, LVOUT_ERROR);
249 return false;
250 }
251 sa.lpSecurityDescriptor = &sd;
252 }
253
254 /* Input pipe */
255 if (!CreatePipe(&hIn[1], &hIn[0], &sa, 0)){
256 sprintf(errmsg, "vm error: Process.cpp CreatePipe(In) failed (e=%d)", (int)GetLastError());
257 winApp.Report.Append(errmsg, LVOUT_ERROR);
258 return false;
259 }
260
261 if (!DuplicateHandle(GetCurrentProcess(),
262 hIn[0],
263 GetCurrentProcess(),
264 &hDup,
265 0,
266 FALSE,
267 DUPLICATE_SAME_ACCESS)){
268 sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(In) failed (e=%d)", (int)GetLastError());
269 winApp.Report.Append(errmsg, LVOUT_ERROR);
270 return false;
271 }
272 CloseHandle(hIn[0]);
273 hIn[0] = hDup;
274
275 /* Output pipe */
276 if (!CreatePipe(&hOut[0], &hOut[1], &sa, 0)){
277 sprintf(errmsg, "vm error: Process.cpp CreatePipe(Out) failed (e=%d)", (int)GetLastError());
278 winApp.Report.Append(errmsg, LVOUT_ERROR);
279 return false;
280 }
281
282 if (!DuplicateHandle(GetCurrentProcess(),
283 hOut[0],
284 GetCurrentProcess(),
285 &hDup,
286 0,
287 FALSE,
288 DUPLICATE_SAME_ACCESS)){
289 sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(Out) failed (e=%d)", (int)GetLastError());
290 winApp.Report.Append(errmsg, LVOUT_ERROR);
291 return false;
292 }
293 CloseHandle(hOut[0]);
294 hOut[0] = hDup;
295
296 /* Error pipe */
297 if (!(creationFlag & OUTERR_PIPE) && (creationFlag & ERR_PIPE)){
298 if (!CreatePipe(&hErr[0], &hErr[1], &sa, 0)){
299 sprintf(errmsg, "vm error: Process.cpp CreatePipe(Err) failed (e=%d)", (int)GetLastError());
300 winApp.Report.Append(errmsg, LVOUT_ERROR);
301 return false;
302 }
303
304 if (!DuplicateHandle(GetCurrentProcess(),
305 hErr[0],
306 GetCurrentProcess(),
307 &hDup,
308 0,
309 FALSE,
310 DUPLICATE_SAME_ACCESS)){
311 sprintf(errmsg, "vm error: Process.cpp DuplicateHandle(Err) failed (e=%d)", (int)GetLastError());
312 winApp.Report.Append(errmsg, LVOUT_ERROR);
313 return false;
314 }
315 CloseHandle(hErr[0]);
316 hErr[0] = hDup;
317 }
318 return true;
319 }
320
321 bool CPipes::CloseChildSide(void){
322 return Close(1);
323 }
324
325 bool CPipes::CloseParentSide(void){
326 return Close(0);
327 }
328
329 bool CPipes::Close(int side){
330
331 if (side < 0 || side > 1)
332 return false;
333
334 if (hIn[side]){
335 CloseHandle(hIn[side]);
336 hIn[side] = NULL;
337 }
338
339 if (hOut[side]){
340 CloseHandle(hOut[side]);
341 hOut[side] = NULL;
342 }
343
344 if (hErr[side]){
345 CloseHandle(hErr[side]);
346 hErr[side] = NULL;
347 }
348 return true;
349 }
350
351
352 /********************************************************************
353 * Class: CProcess.
354 *
355 * Purpose:
356 *
357 * Revisions:
358 *
359 ********************************************************************/
360 CProcess::CProcess(){
361 Running = false;
362 exitCode = 0;
363
364 pi.hProcess = 0;
365 pi.hThread = 0;
366 pi.dwProcessId = 0;
367 pi.dwThreadId = 0;
368 }
369
370 CProcess::~CProcess(){
371 }
372
373 /********************************************************************
374 * Manage Tasks.
375 ********************************************************************/
376 bool CProcess::isRunning(void){
377 if (Running){
378 MsgBox.DisplayWarning("A process is already running !");
379 return true;
380 }
381 return false;
382 }
383
384 CTask * CProcess::AddTask(char * cmdLine, WORD creationFlag, WORD outputFlag){
385 CTask * newTask = new CTask;
386
387 strcpy(newTask->cmdLine, cmdLine);
388 newTask->creationFlag = creationFlag;
389 newTask->outputFlag = outputFlag;
390 Push(newTask);
391 return newTask;
392 }
393
394 bool CProcess::CmdCat(char * cmdLine){
395 CTask * task = (CTask*) GetCurrent();
396 if (!task)
397 return false;
398
399 strcat(task->cmdLine, cmdLine);
400 return true;
401 }
402
403 /********************************************************************
404 * RunNext/Run/RunProcess.
405 ********************************************************************/
406 void __cdecl call_thread(void * ptr){
407 /* C++ adapter */
408 ((CProcess *) ptr)->Run_Thread_Internal();
409 }
410
411 void CProcess::Run(void){
412 // Check if something is already running before creating a thread.
413 if (!Running){
414 // Call Run_Thread_Internal()
415 _beginthread(call_thread, 1024 * 1024, (void *) this);
416 }
417 }
418
419 void CProcess::Run_Thread_Internal(void){
420 exitCode = 0;
421 /* Execute each task */
422 for ( ; ; ){
423 /* If previous task returns an error code, abort */
424 if (exitCode != 0)
425 break;
426
427 // Get one task to execute.
428 currTask = Pop();
429
430 // Nothing to run.
431 if (!currTask)
432 break;
433
434 /* Show command lines ?*/
435 winApp.Report.Append(currTask->cmdLine, LVOUT_NORMAL);
436
437 if (RunProcess(currTask)){
438 winApp.Report.Append("Abort !", LVOUT_NORMAL);
439 exitCode = 1;
440 break;
441 }
442 }
443
444 // Successful ?
445 if (exitCode == 0)
446 winApp.Report.Append("Performed successfully.", LVOUT_NORMAL);
447
448 Flush();
449 Running = false;
450 return;
451 }
452
453 bool CProcess::RunProcess(CTask * task){
454 if (!task)
455 return false;
456
457 bool usePipes = task->creationFlag;
458 STARTUPINFO si = {sizeof(STARTUPINFO), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
459 0, 0, 0, 0, 0};
460
461 /* PROCESS_INFORMATION */
462 pi.hProcess = 0;
463 pi.hThread = 0;
464 pi.dwProcessId = 0;
465 pi.dwThreadId = 0;
466
467 /* Process creation with pipes */
468 if (usePipes){
469 /* Create needed pipes according to creationFlag */
470 if(!Pipes.Create(task->creationFlag, winApp.isWinNT)){
471 Pipes.CloseChildSide();
472 Pipes.CloseParentSide();
473 return false;
474 }
475
476 si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
477 si.wShowWindow = SW_HIDE;
478 //si.wShowWindow = SW_SHOWNORMAL;
479
480 /* Set pipe handles */
481 if (Pipes.hIn[1] != NULL && Pipes.hOut[1] != NULL){
482 si.hStdInput = Pipes.hIn[1];
483 si.hStdOutput = Pipes.hOut[1];
484 if (Pipes.hErr[1] == NULL)
485 si.hStdError = Pipes.hOut[1];
486 else
487 si.hStdError = Pipes.hErr[1];
488 }else{
489 sprintf(errmsg, "vm error: Process.cpp Invalid pipe handle");
490 winApp.Report.Append(errmsg, LVOUT_ERROR);
491 Pipes.CloseChildSide();
492 Pipes.CloseParentSide();
493 return false;
494 }
495 }
496
497 /* Create the child process */
498 Running = CreateProcess(NULL,
499 task->cmdLine,
500 NULL,
501 NULL,
502 usePipes,
503 0,
504 NULL,
505 /*startDir[0] ? startDir :*/ NULL,
506 &si,
507 &pi);
508
509 if (!Running){
510 /* CreateProcess failed. Close handles and return */
511 Pipes.CloseChildSide();
512 Pipes.CloseParentSide();
513 sprintf(errmsg, "vm error: Process.cpp CreateProcess failed (e=%d)", (int)GetLastError());
514 winApp.Report.Append(errmsg, LVOUT_ERROR);
515 return false;
516 }else{
517 /* Close child process handles */
518 Pipes.CloseChildSide();
519
520 if (!(usePipes & IN_PIPE)){
521 /* Don't use the Input pipe */
522 ::CloseHandle(Pipes.hIn[0]);
523 Pipes.hIn[0] = NULL;
524 }
525 }
526
527 //sprintf(errmsg, "vm debug: enter io loop");
528 //winApp.Report.Append(errmsg, LVOUT_ERROR);
529 if (usePipes){
530 /* Initialize buffers */
531 *outBuf = 0;
532 chr = outBuf;
533 bool bResult;
534 for ( ; ; ){
535 Sleep(100L);
536
537 bResult = ReadStdOut(task, Pipes.hOut[0]);
538 if (bResult != NO_ERROR)
539 break;
540
541 ::GetExitCodeProcess(pi.hProcess, &exitCode);
542 if (exitCode != STILL_ACTIVE){
543 break;
544 }
545 }
546 }
547 //sprintf(errmsg, "vm debug: exit io loop");
548 //winApp.Report.Append(errmsg, LVOUT_ERROR);
549
550 /* The child process is running. Perform I/O until terminated */
551 ::WaitForSingleObject(pi.hProcess, INFINITE);
552 /* Process terminated. Get exit code. */
553 ::GetExitCodeProcess(pi.hProcess, &exitCode);
554 if (exitCode == NO_ERROR){
555 return NO_ERROR;
556 }
557 /* Close handles */
558 Pipes.CloseParentSide();
559 ::CloseHandle(pi.hProcess);
560 if (pi.hThread){
561 ::CloseHandle(pi.hThread);
562 pi.hThread = NULL;
563 }
564 return exitCode;
565 }
566
567 /********************************************************************
568 * Pipes input/output.
569 ********************************************************************/
570 void CProcess::WriteStdIn(HANDLE hPipe, WORD){
571 if (!hPipe)
572 return;
573
574 return;
575 }
576
577 void CProcess::ReadStdErr(HANDLE hPipe, WORD){
578 if (!hPipe)
579 return;
580
581 return;
582 }
583
584 long CProcess::ReadStdOut(CTask * task, HANDLE hPipe){
585 if (!task || !hPipe)
586 return ERROR_INVALID_FUNCTION;
587
588 /* Copy each char and output lines while there is something to read */
589 for ( ; ; ){
590 // Copy one char, return if nothing available.
591 if (!ReadOneChar(hPipe, chr))
592 break;
593
594 // Ignore CR.
595 if (*chr == '\r')
596 continue;
597
598 if (*chr != '\n'){
599 chr++;
600 /* @@TODO Overflow
601 if ((chr - outBuf) >= max_len)
602 realloc(buffer);*/
603 // End of line
604 }else if (*chr =='\n'){
605 *chr = '\0';
606 // Output error lines to List View.
607 if (task->outputFlag == STDOUT_FILE_APPEND){
608 WriteFileAppend(task->szFileName, outBuf, (chr - outBuf));
609 }else{
610 OutputLine(task->outputFlag, outBuf, (chr - outBuf));
611 }
612 *outBuf = '\0';
613 chr = outBuf;
614 }
615 }
616 return NO_ERROR;
617 }
618
619 int CProcess::ReadOneChar(HANDLE hPipe, char * chrin){
620 DWORD bytesRead = 0;
621 DWORD bytesAvail = 0;
622
623 if (!PeekNamedPipe(hPipe, chrin, (DWORD)1, &bytesRead, &bytesAvail, NULL))
624 return 0;
625
626 if (bytesAvail == 0)
627 return 0;
628
629 if (!ReadFile(hPipe, chrin, (DWORD)1, &bytesRead, NULL))
630 return 0;
631
632 return bytesRead;
633 }
634
635 bool CProcess::CommandLine(char * cmdLine){
636 if (!Pipes.hIn[0])
637 return false;
638 if (!Running || !currTask || !currTask->creationFlag)
639 return false;
640 int len = strlen(cmdLine);
641 if (len){
642 strcpy(inBuf, cmdLine);
643 char * s = inBuf;
644 s+=len;
645 *s = '\r';
646 s++;
647 *s = '\n';
648 s++;
649 *s = '\0';
650 }
651 DWORD written;
652
653 if (!WriteFile(Pipes.hIn[0], inBuf, strlen(inBuf), &written, 0))
654 return false;
655
656 return true;
657 }
658
659 bool CProcess::WriteFileAppend(char * fileName, char * line, int /*len*/){
660 if (!*fileName)
661 return false;
662
663 /* Append one line of text to a file */
664 FILE * file = fopen(fileName, "a");
665 if (file){
666 fprintf(file, line);
667 fprintf(file, "\n");
668 fclose(file);
669 return true;
670 }
671 return false;
672 }
673
674 bool CProcess::OutputLine(WORD outputFlag, char * line, int /*len*/){
675 /* Output error lines to List View */
676
677 CriticalSection.Enter();
678 winApp.Report.Append(line, outputFlag);
679 CriticalSection.Leave();
680 return true;
681 }
682