comparison host/tools/Utils/win32/SubProcess.cpp @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents
children
comparison
equal deleted inserted replaced
75:41bf073c0c32 76:435cced73e2f
1 //####COPYRIGHTBEGIN####
2 //
3 // ----------------------------------------------------------------------------
4 // Copyright (C) 1998, 1999, 2000 Red Hat, Inc.
5 //
6 // This program is part of the eCos host tools.
7 //
8 // This program is free software; you can redistribute it and/or modify it
9 // under the terms of the GNU General Public License as published by the Free
10 // Software Foundation; either version 2 of the License, or (at your option)
11 // any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 // more details.
17 //
18 // You should have received a copy of the GNU General Public License along with
19 // this program; if not, write to the Free Software Foundation, Inc.,
20 // 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 //
22 // ----------------------------------------------------------------------------
23 //
24 //####COPYRIGHTEND####
25 //===========================================================================
26 //===========================================================================
27 //#####DESCRIPTIONBEGIN####
28 //
29 // Author(s): sdf
30 // Contact(s): sdf
31 // Date: 1998/08/11
32 // Version: 0.01
33 // Purpose:
34 // Description: This is the implementation of the class which allows for spawning subprocesses
35 //
36 // Requires:
37 // Provides:
38 // See also:
39 // Known bugs:
40 // Usage:
41 //
42 //####DESCRIPTIONEND####
43 //
44 //===========================================================================
45 #include "subprocess.h"
46
47 const UINT CSubprocess::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF;
48
49 CSubprocess::CSubprocess(bool bVerbose):
50 m_dwParentThreadId(0),
51 m_pstrOutput(0),
52 m_idProcess(0),
53 m_bThreadRunning(false),
54 m_bVerbose(bVerbose),
55 m_dwExitCode(0xffffffff),
56 m_bAutoDelete(false),
57 m_hThread(0),
58 m_pfnLogfunc(0),
59 m_pLogparam(0),
60 m_hwndParent(0)
61 {
62 }
63
64 // Run (non-blocking and discarding output)
65 /* static */ unsigned int CSubprocess::Run(LPCTSTR pszCmd,LPCTSTR pszDir)
66 {
67 CSubprocess *psp=new CSubprocess(false);
68 psp->m_bAutoDelete=true;
69 String strOutput;
70 return psp->Run(strOutput,pszCmd,pszDir);
71 }
72
73 unsigned int CSubprocess::Run(LogFunc *pfnLog,void * pLogparam, LPCTSTR pszCmd,LPCTSTR pszDir)
74 {
75 unsigned int rc;
76 if(m_bThreadRunning){
77 rc=0;
78 } else {
79 m_pfnLogfunc=pfnLog;
80 m_pLogparam=pLogparam;
81 m_strCmd=pszCmd;
82 m_strDir=pszDir;
83 rc=CreateProcess();
84 if(rc){
85 ThreadFunc();
86 }
87 }
88 return rc;
89 }
90
91 #ifdef _WIN32
92 // Non-blocking: WM_SUBPROCESS messages are posted to HWND supplied as argument
93 unsigned int CSubprocess::Run(HWND hwndParent,LPCTSTR pszCmd,LPCTSTR pszDir)
94 {
95 unsigned int rc;
96 if(m_bThreadRunning){
97 rc=0;
98 } else {
99 m_hwndParent=hwndParent;
100 m_strCmd=pszCmd;
101 m_strDir=pszDir;
102 rc=CreateProcess();
103 if(rc){
104 DWORD dwID;
105 m_bThreadRunning=true;
106 m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID);
107 }
108 }
109 return rc;
110 }
111
112 // Non-blocking: WM_SUBPROCESS messages are posted to thread supplied as argument
113 unsigned int CSubprocess::Run(DWORD dwParentThreadId,LPCTSTR pszCmd,LPCTSTR pszDir)
114 {
115 unsigned int rc;
116 if(m_bThreadRunning){
117 rc=0;
118 } else {
119 m_dwParentThreadId=dwParentThreadId;
120 m_strCmd=pszCmd;
121 m_strDir=pszDir;
122 rc=CreateProcess();
123 if(rc){
124 DWORD dwID;
125 m_bThreadRunning=true;
126 m_hThread=::CreateThread(NULL,0,ThreadFunc,this,0,&dwID);
127 }
128 }
129 return rc;
130 }
131 #endif
132
133 // Blocking: output is placed in string supplied as argument
134 unsigned int CSubprocess::Run(String &strOutput,LPCTSTR pszCmd,LPCTSTR pszDir)
135 {
136 strOutput=_T("");
137 m_strCmd=pszCmd;
138 m_strDir=pszDir;
139 m_pstrOutput=&strOutput;
140 unsigned int rc=CreateProcess();
141 if(rc){
142 ThreadFunc();
143 }
144 return rc;
145 }
146
147 CSubprocess::~CSubprocess()
148 {
149 Kill();
150
151 if(m_hThread){ // running non-blocking and thread started (but possibly already finished)
152 if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
153 // In general this is bad news, but we can't allow the thread to live on if
154 // the class object is about to be deleted.
155 if(m_bVerbose){
156 Output(_T("*** CSubprocess: Forcibly terminating thread\n"));
157 }
158 ::TerminateThread(m_hThread,0);
159 }
160 ::CloseHandle(m_hThread);
161 }
162 }
163
164 #ifdef _WIN32
165 unsigned int CSubprocess::CreateProcess()
166 {
167
168 STARTUPINFO si; // For CreateProcess call
169 HANDLE hrPipe,hwPipe,hwPipe2;
170 // Create the anonymous pipe
171
172 SECURITY_ATTRIBUTES saPipe; // Security for anonymous pipe
173 saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
174 saPipe.lpSecurityDescriptor = NULL;
175 saPipe.bInheritHandle = true;
176
177 ::CreatePipe(&m_hrPipe,&hwPipe,&saPipe,80);
178
179 // In most cases you can get away with using the same anonymous
180 // pipe write handle for both the child's standard output and
181 // standard error, but this may cause problems if the child app
182 // explicitly closes one of its standard output or error handles. If
183 // that happens, the anonymous pipe will close, since the child's
184 // standard output and error handles are really the same handle. The
185 // child won't be able to write to the other write handle since the
186 // pipe is now gone, and parent reads from the pipe will return
187 // ERROR_BROKEN_PIPE and child output will be lost. To solve this
188 // problem, simply duplicate the write end of the pipe to create
189 // another distinct, separate handle to the write end of the pipe.
190 // One pipe write handle will serve as standard out, the other as
191 // standard error. Now *both* write handles must be closed before the
192 // write end of the pipe actually closes.
193
194 ::DuplicateHandle(::GetCurrentProcess(), // Source process
195 hwPipe, // Handle to duplicate
196 ::GetCurrentProcess(), // Destination process
197 &hwPipe2, // New handle, used as stderr by child
198 0, // New access flags - ignored since DUPLICATE_SAME_ACCESS
199 true, // It's inheritable
200 DUPLICATE_SAME_ACCESS);
201
202 ::CreatePipe(&hrPipe,&m_hwPipe,&saPipe,80);
203
204 memset(&si, 0, sizeof(si));
205 si.cb = sizeof(si);
206
207 si.hStdOutput = hwPipe;
208 si.hStdError = hwPipe2;
209 si.hStdInput = hrPipe;
210 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
211 si.wShowWindow = SW_SHOW;
212
213 LPCTSTR pszDir=0;
214 if(m_strDir.GetLength()){
215 pszDir=m_strDir; // NULL = start in current
216 }
217
218 PROCESS_INFORMATION pi;
219 unsigned int rc=(::CreateProcess(NULL, // Application name
220 m_strCmd.GetBuffer(m_strCmd.GetLength()), // Full command line for child
221 NULL, // Process security descriptor
222 NULL, // Thread security descriptor
223 true, // Inherit handles? Also use if STARTF_USESTDHANDLES
224 // Creation flags of
225 // CREATE_NEW_PROCESS_GROUP so we can use
226 // GenerateConsoleCtrlEvent to
227 // terminate the child
228 DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,
229 NULL, // Inherited environment address
230 pszDir,&si,&pi) ? pi.dwProcessId : 0);
231
232 m_strCmd.ReleaseBuffer();
233 String strMsg;
234
235 if(rc){
236 m_idProcess=pi.dwProcessId;
237 m_hProcess=pi.hProcess;
238 strMsg.Format(_T("*** Process %d created \"%s\"\n"),m_idProcess,(LPCTSTR)m_strCmd);
239 m_dwExitCode=STILL_ACTIVE;
240 } else {
241 strMsg.Format(_T("*** Failed to create process \"%s\"\n"),(LPCTSTR)m_strCmd);
242 m_dwExitCode=GetLastError();
243 }
244
245 if(m_bVerbose){
246 Output(strMsg);
247 }
248
249 ::CloseHandle(hrPipe);
250 ::CloseHandle(hwPipe);
251 ::CloseHandle(hwPipe2);
252 ::CloseHandle(pi.hThread);
253
254 return rc;
255
256 }
257 #else // UNIX
258 unsigned int CSubprocess::CreateProcess()
259 {
260 int pipe_ends_w[2];
261 if (pipe(pipe_ends_w) < 0 ) {
262 Log(_T("Failed to create pipe_ends_w - %s\n"),strerror(errno));
263 } else {
264 int pipe_ends_r[2];
265 if (pipe(pipe_ends_r) < 0 ) {
266 Log(_T("Failed to create pipe_ends_r - %s\n"),strerror(errno));
267 } else {
268 int pid=fork();
269
270 switch (pid) {
271 // Fork failed
272 case -1:
273 Log(_T("Failed to create gdb process - %s\n"),strerror(errno));
274 pid=0;
275 break;
276 case 0:
277 // Process is created (we're the child)
278 // No point in outputting except via output streams
279
280 // Input to child process
281 if (dup2(pipe_ends_w[0], 0) < 0) {
282 _ftprintf(stderr,(_T("dup2 error\n"));
283 exit(1);
284 }
285
286 // Output from process
287 if (dup2(pipe_ends_r[1], 2) < 0) {
288 _ftprintf(stderr,(_T("dup2 error\n"));
289 exit(2);
290 }
291 if (dup2(pipe_ends_r[1], 1) < 0) {
292 _ftprintf(stderr,(_T("dup2 error\n"));
293 exit(3);
294 }
295 setvbuf(stdout,0,_IONBF,0);
296 setvbuf(stderr,0,_IONBF,0);
297 if(m_bVerbose){
298 strMsg.Format(_T("*** Process %d created \"%s\"\n"),pid,(LPCTSTR)m_strCmd);
299 }
300
301 StringArray ar;
302 int argc=m_strCmd.Chop(ar,_TCHAR(' '),true);
303 TCHAR **argv=new TCHAR *[1+argc];
304 int i;
305 for(i=0;i<argc;i++){
306 argv[i]=ar[i];
307 }
308 argv[i]=0;
309 _texecvp(argv[0], argv);
310 delete [] argv;
311 _ftprintf(stderr,(_T("exec error - %s\n"),strerror(errno));
312
313 exit(4);
314
315 default:
316 // Process is created (we're the parent)
317
318 TRACE(_T("Forked to create gdb process %s - pid is <%d>\n"), pszCmdline, pid);
319 if (fcntl(pipe_ends_r[0], F_SETFL, O_NONBLOCK) <0) {
320 Log(_T("Couldn't set pipe non-blocking - %s\n"),strerror(errno));
321 } else {
322 m_rPipeHandle=(void *)pipe_ends_r[0];
323 m_wPipeHandle=(void *)pipe_ends_w[1];
324 }
325 break;
326 }
327 close (pipe_ends_r[0]);
328 close (pipe_ends_r[1]);
329 }
330 close (pipe_ends_w[0]);
331 close (pipe_ends_w[1]);
332 }
333 return pid;
334 }
335 #endif
336
337 DWORD CSubprocess::ThreadFunc()
338
339 {
340
341 DWORD dwAvail;
342
343 while (::PeekNamedPipe(m_hrPipe, NULL, 0, 0, &dwAvail, NULL)){
344 if(dwAvail){
345 DWORD dwRead;
346 char *buf=new char[dwAvail+1];
347 if(!::ReadFile(m_hrPipe, buf, dwAvail, &dwRead, NULL)){
348 delete [] buf;
349 break;
350 }
351 buf[dwRead]='\0';
352 Output(String::CStrToUnicodeStr(buf));
353 delete [] buf;
354 } else {
355 DWORD dw;
356 ::GetExitCodeProcess(m_hProcess, &dw);
357
358 if(STILL_ACTIVE!=dw){
359 break;
360 }
361
362 Sleep(250);
363 }
364 }
365
366 ::GetExitCodeProcess(m_hProcess, &m_dwExitCode);
367 String strMsg;
368 if(m_bVerbose){
369 if(PROCESS_KILL_EXIT_CODE==m_dwExitCode){
370 strMsg.Format(_T("\n*** Process %d killed\n"),m_idProcess);
371 } else {
372 strMsg.Format(_T("\n*** Process %d terminated (rc=%d)\n"),m_idProcess,m_dwExitCode);
373 }
374 Output(strMsg);
375 }
376
377 if(m_dwParentThreadId || m_hwndParent){
378 Post(0);
379 }
380 m_idProcess=0;
381
382 ::CloseHandle(m_hrPipe);
383 ::CloseHandle(m_hwPipe);
384 ::CloseHandle(m_hProcess);
385 m_bThreadRunning=false;
386 DWORD rc=m_dwExitCode; // protect against auto-delete
387
388 if(m_bAutoDelete){
389 delete this;
390 }
391
392 return rc;
393
394 }
395
396 bool CSubprocess::Kill()
397 {
398 if(0!=m_idProcess){
399 //TRACE(_T("*** Killing process %d(ctrl/c)\n"),m_idProcess);
400 //::GenerateConsoleCtrlEvent( CTRL_C_EVENT, m_idProcess);
401 //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
402 //TRACE(_T("*** Killing process %d(ctrl/break)\n"),m_idProcess);
403 //::GenerateConsoleCtrlEvent( CTRL_BREAK_EVENT, m_idProcess);
404 //if(WAIT_TIMEOUT==WaitForSingleObject(m_hThread,1000)){
405 if(m_bVerbose){
406 String strMsg;
407 strMsg.Format(_T("*** Killing process %d (TerminateProcess)\n"),m_idProcess);
408 Output(strMsg);
409 }
410 ::TerminateProcess(m_hProcess,PROCESS_KILL_EXIT_CODE);
411 //}
412 //}
413 }
414 return 0==m_idProcess;
415 }
416
417 void CSubprocess::Output (LPCTSTR psz)
418 {
419 if(m_dwParentThreadId || m_hwndParent){
420 TCHAR *pszCopy=new TCHAR[1+_tcslen(psz)];
421 _tcscpy(pszCopy,psz);
422 Post((LPARAM)pszCopy);
423 } else if (m_pfnLogfunc) {
424 m_pfnLogfunc(m_pLogparam,psz);
425 } else {
426 assert(NULL!=m_pstrOutput);
427 (*m_pstrOutput)+=psz;
428 }
429 }
430
431
432 void CSubprocess::Post(LPARAM lParam)
433 {
434 if(m_hwndParent){
435 ::PostMessage(m_hwndParent,WM_SUBPROCESS,(WPARAM)this,lParam);
436 } else {
437 assert(m_dwParentThreadId);
438 ::PostThreadMessage(m_dwParentThreadId,WM_SUBPROCESS,(WPARAM)m_idProcess,lParam);
439 }
440 }
441
442 void CSubprocess::CygKill()
443 {
444
445 // Map win32 pids to cygwin format
446 PtrArray map;
447
448 int nKillCount;
449 int nGid=-1;
450 do {
451 nKillCount=0;
452 // Execute a "ps -l" to find out what's out there in Cygwin land
453 CSubprocess sp;
454 String strOutput;
455 sp.Run(strOutput,_T("ps -l"));
456 if(0==strOutput.GetLength()){
457 if(m_bVerbose){
458 Output(_T("*** Warning: could not run ps to effect a Cygkill()\n"));
459 }
460 } else {
461 // Walk through the lines by line and extract what we need
462 for(LPCTSTR psz=-1+(LPCTSTR)strOutput;psz;psz=_tcschr(psz,_TCHAR('\n'))){
463 psz++; // move over the '\n'
464 CygProcessInfo cpi;
465 if(0==_tcsstr(psz,_T("ps.exe")) && 4==_stscanf(2+psz,_T("%d %d %d %d"),&cpi.nPid,&cpi.nPpid,&cpi.nPgid,&cpi.nWinpid) && cpi.nWinpid){
466 for(unsigned int i=0;i<map.size();i++){
467 if(((CygProcessInfo *)map[i])->nWinpid==cpi.nWinpid){
468 break;
469 }
470 }
471
472 if(i==map.size()){
473 // No existing match - add:
474 map.push_back(new CygProcessInfo(cpi));
475 if((int)m_idProcess==cpi.nWinpid){
476 nGid=cpi.nPgid;
477 }
478 }
479 }
480 }
481
482 // Kill everything in the same group as our process
483 if(-1!=nGid){
484 for (unsigned int i=0;i<map.size();i++){
485 CygProcessInfo *pcpi=(CygProcessInfo *)map[i];
486 if(pcpi->nPgid==nGid){
487 // Check that the process exists
488 HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid);
489 if(hProcess){
490 CloseHandle(hProcess);
491 String strCmd,strOut;
492 strCmd.Format(_T("kill %d"),pcpi->nPid);
493 pcpi->nPgid=-pcpi->nPgid; // prevent this being done more than once
494 CSubprocess::Run(strCmd);
495 nKillCount++;
496 }
497 }
498 }
499 }
500
501 }
502 } while (nKillCount>0);
503
504 // Now we've sent a kill to every process. Go back and check they are really dead (use windows API this time)
505 for(unsigned int i=0;i<map.size();i++){
506 CygProcessInfo *pcpi=(CygProcessInfo *)map[i];
507 if(abs(pcpi->nPgid)==nGid){
508 HANDLE hProcess=OpenProcess(PROCESS_TERMINATE,false,pcpi->nWinpid);
509 if(hProcess){
510 ::TerminateProcess(hProcess,0x7fffffff);
511 CloseHandle(hProcess);
512 }
513 }
514 }
515
516 for(i=0;i<map.size();i++){
517 delete (CygProcessInfo *)map[i];
518 }
519 }
520
521 void CSubprocess::Send(LPCTSTR str)
522 {
523 int nLength=_tcslen(str);
524 char *psz=new char[1+nLength];
525 #ifdef _UNICODE
526 WideCharToMultiByte(CP_ACP, 0, str, -1, psz, 1+nLength, NULL, NULL);
527 #else
528 strcpy(psz,str);
529 #endif
530
531 DWORD dwWritten;
532 ::WriteFile(m_hwPipe,psz,nLength,&dwWritten,0);
533 delete [] psz;
534 }
535
536
537
538 void CSubprocess::CloseInput()
539 {
540 ::CloseHandle(m_hwPipe);
541 }
542