]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - tools/src/tools/Utils/common/Subprocess.cpp
Initial revision
[karo-tx-redboot.git] / tools / src / tools / Utils / common / Subprocess.cpp
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 "eCosTrace.h"
46 #include "Subprocess.h"
47 #ifdef _WIN32
48   #include <tlhelp32.h>
49
50   HINSTANCE CSubprocess::hInstLib1 = VER_PLATFORM_WIN32_NT==CSubprocess::GetPlatform()?LoadLibrary(_T("PSAPI.DLL")):LoadLibrary(_T("Kernel32.DLL")) ;
51   HINSTANCE CSubprocess::hInstLib2 = VER_PLATFORM_WIN32_NT==CSubprocess::GetPlatform()?LoadLibrary(_T("NTDLL.DLL")):NULL;
52
53 #endif
54
55 //#define CloseHandle(x) TRACE(_T("CSubprocess::CloseHandle %x\n"),x);::CloseHandle(x)
56
57 const unsigned int CSubprocess::PROCESS_KILL_EXIT_CODE=0xCCFFCCFF;
58
59 CSubprocess::CSubprocess(bool bAutoDelete):
60   m_pfnContinue(DefaultContinuationFunc),
61   m_pContinuationFuncParam(0),
62   m_bAutoDelete(bAutoDelete),
63   m_bThreadTerminated(true),
64   m_bVerbose(false),
65   m_nExitCode(-1),
66   m_idProcess(0),
67   m_pLogparam(0),
68   m_pfnLogfunc(0),
69   m_bKillThread(false)
70 {
71 #ifdef _WIN32
72   m_hProcess=0;
73 #endif
74 }
75
76 CSubprocess::~CSubprocess()
77 {
78   Kill();
79   if(!CeCosThreadUtils::WaitFor(m_bThreadTerminated,1000)){
80     m_bKillThread=true;
81     CeCosThreadUtils::WaitFor(m_bThreadTerminated);  
82   }
83 #ifdef _WIN32
84   if(m_hProcess){
85     CloseHandle(m_hProcess);
86   }
87 #endif
88 }
89
90 bool CSubprocess::Run(LogFunc *pfnLog,void * pLogparam, LPCTSTR pszCmd,bool bBlock/*=true*/)
91 {
92   bool rc;
93   if(!m_bThreadTerminated){
94     rc=false;
95   } else {
96     m_pfnLogfunc=pfnLog;
97     m_pLogparam=pLogparam;
98 #ifdef _WIN32 
99     // UNIX does it from the thread func.  WIN32 could too, but it's nice to know at the time 
100     // of calling run whether the process is successfully created.
101     if(m_hProcess){
102       // Normally done in the dtor
103       CloseHandle(m_hProcess);
104     }
105     rc=CreateProcess(pszCmd);
106 #else 
107     m_strCmd=pszCmd;
108     rc=true;
109 #endif
110     if(rc){
111       m_bKillThread=false;
112       if(bBlock){
113         // When using RunThread, the manipulation of this Boolean is taken care of.
114         // Here we must do it ourselves.
115         m_bThreadTerminated=false;
116         ThreadFunc();
117         m_bThreadTerminated=true;
118       } else {
119         CeCosThreadUtils::RunThread(SThreadFunc,this,&m_bThreadTerminated,String::SFormat(_T("subprocess %d read"),m_idProcess));
120       }
121     }
122   }
123   return rc;
124 }
125
126 #ifdef _WIN32
127 bool CSubprocess::CreateProcess(LPCTSTR pszCmdline)
128 {
129   
130   STARTUPINFO   si;                    // For CreateProcess call
131   HANDLE        hrPipe,hwPipe,hwPipe2,m_hrPipeTemp,m_hwPipeTemp;
132   // Create the anonymous pipe
133   
134   SECURITY_ATTRIBUTES saPipe;          // Security for anonymous pipe
135   saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
136   saPipe.lpSecurityDescriptor = NULL;
137   saPipe.bInheritHandle = true;
138   
139   ::CreatePipe(&m_hrPipeTemp,&hwPipe,&saPipe,10240);
140   
141   // In most cases you can get away with using the same anonymous
142   // pipe write handle for both the child's standard output and
143   // standard error, but this may cause problems if the child app
144   // explicitly closes one of its standard output or error handles. If
145   // that happens, the anonymous pipe will close, since the child's
146   // standard output and error handles are really the same handle. The
147   // child won't be able to write to the other write handle since the
148   // pipe is now gone, and parent reads from the pipe will return
149   // ERROR_BROKEN_PIPE and child output will be lost. To solve this
150   // problem, simply duplicate the write end of the pipe to create
151   // another distinct, separate handle to the write end of the pipe.
152   // One pipe write handle will serve as standard out, the other as
153   // standard error. Now *both* write handles must be closed before the
154   // write end of the pipe actually closes.
155   
156   ::DuplicateHandle(::GetCurrentProcess(),                      // Source process
157     hwPipe,                     // Handle to duplicate
158     ::GetCurrentProcess(),   // Destination process
159     &hwPipe2,               // New handle, used as stderr by child 
160     0,                     // New access flags - ignored since DUPLICATE_SAME_ACCESS
161     true,                  // It's inheritable
162     DUPLICATE_SAME_ACCESS);
163   
164   ::CreatePipe(&hrPipe,&m_hwPipeTemp,&saPipe,10240);
165   
166
167   // Create new output read handle and the input write handles, setting
168   // the Properties to FALSE. Otherwise, the child inherits the
169   // properties and, as a result, non-closeable handles to the pipes
170   // are created.
171   DuplicateHandle(GetCurrentProcess(),m_hrPipeTemp,
172                        GetCurrentProcess(),
173                        &m_hrPipe, // Address of new handle.
174                        0,FALSE, // Make it uninheritable.
175                        DUPLICATE_SAME_ACCESS);
176
177   DuplicateHandle(GetCurrentProcess(),m_hwPipeTemp,
178                        GetCurrentProcess(),
179                        &m_hwPipe, // Address of new handle.
180                        0,FALSE, // Make it uninheritable.
181                        DUPLICATE_SAME_ACCESS);
182
183   // Close inheritable copies of the handles we do not want to be inherited:
184   CloseHandle(m_hrPipeTemp);
185   CloseHandle(m_hwPipeTemp);
186
187
188   memset(&si, 0, sizeof(si));
189   si.cb = sizeof(si);
190   
191   si.hStdOutput = hwPipe;
192   si.hStdError =  hwPipe2;
193   si.hStdInput =  hrPipe;
194   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
195   si.wShowWindow = SW_SHOW;
196   
197   LPCTSTR pszDir;
198   if(m_strDir.empty()){
199     pszDir=NULL; // current directory
200   } else {
201     pszDir=m_strDir;
202   }
203   
204   PROCESS_INFORMATION pi;
205   String strCmd(pszCmdline);
206
207   String strOrigpath;
208   if(!m_strPath.empty()){
209           int nSize=GetEnvironmentVariable(_T("PATH"), NULL, 0);
210           if(nSize>0){
211       GetEnvironmentVariable(_T("PATH"),strOrigpath.GetBuffer(nSize),nSize);
212       strOrigpath.ReleaseBuffer();
213       SetEnvironmentVariable(_T("PATH"),m_strPath);
214     }
215   }
216
217   bool rc=(TRUE==::CreateProcess(NULL,strCmd.GetBuffer(),NULL,NULL,true,DETACHED_PROCESS|CREATE_NEW_PROCESS_GROUP,NULL,pszDir,&si,&pi));
218
219   if(!m_strPath.empty()){
220     SetEnvironmentVariable(_T("PATH"),strOrigpath);
221   }
222
223   m_nErr=GetLastError();
224
225   strCmd.ReleaseBuffer();
226   
227   if(rc){
228     m_idProcess=pi.dwProcessId;
229     m_hProcess=pi.hProcess;
230     if(m_bVerbose){
231       Output(String::SFormat(_T("*** Process %d created \"%s\"\n"),m_idProcess,pszCmdline));
232     }
233     TRACE(String::SFormat(_T("Process %d created \"%s\"\n"),m_idProcess,pszCmdline));
234     m_nExitCode=STILL_ACTIVE;
235     CloseHandle(pi.hThread);
236   } else {
237     m_idProcess=0;
238     if(m_bVerbose){
239       Output(String::SFormat(_T("*** Failed to create process \"%s\" %s\n"),pszCmdline,(LPCTSTR)ErrorString()));
240     }
241     TRACE(String::SFormat(_T("Failed to create process \"%s\" %s\n"),pszCmdline,(LPCTSTR)ErrorString()));
242     m_nExitCode=GetLastError();
243     CloseHandle(m_hrPipe);m_hrPipe=INVALID_HANDLE_VALUE;
244     CloseHandle(m_hwPipe);m_hwPipe=INVALID_HANDLE_VALUE;
245   }
246   
247   CloseHandle(hrPipe);
248   CloseHandle(hwPipe);
249   CloseHandle(hwPipe2);
250   
251   return rc;
252   
253 }
254
255 void CSubprocess::ThreadFunc()
256 {
257
258   TRACE(_T("Reading from process %d\n"),m_idProcess);
259   
260   DWORD dwAvail;
261   
262   DWORD dwExitCode;
263
264   while (!m_bKillThread && m_pfnContinue(m_pContinuationFuncParam) && ::PeekNamedPipe(m_hrPipe, NULL, 0, 0, &dwAvail, NULL)){
265 //TRACE(_T("P%d\n"),dwAvail);
266     if(dwAvail){
267       dwAvail=MIN(dwAvail,80); // Read a maximum of 80 characters at a time
268       DWORD dwRead;
269       char *buf=new char[dwAvail+1];
270 //TRACE(_T("R%d\n"),dwAvail);
271       if(!::ReadFile(m_hrPipe, buf, dwAvail, &dwRead, NULL)){
272         TRACE(_T("ReadFile returns false\n"));
273         delete [] buf;
274         break;
275       }
276       buf[dwRead]='\0';
277       Output(String::CStrToUnicodeStr(buf));
278       delete [] buf;
279     }
280     else if (!ProcessAlive())
281     {
282         TRACE(_T("m_bThreadTerminated=%d\n"),m_bThreadTerminated);
283         break;
284     }
285     // Fix for hanging in an endless loop under Windows ME, by Bill Diehls <billabloke@yahoo.com>
286     else if (::GetExitCodeProcess(m_hProcess, &dwExitCode) && dwExitCode!=STILL_ACTIVE) 
287     {
288                 break;
289     }
290     else
291     {
292       CeCosThreadUtils::Sleep(250);
293     }
294   }
295
296   ::GetExitCodeProcess(m_hProcess, &dwExitCode);
297   m_nExitCode=dwExitCode;
298   
299 #ifdef _DEBUG
300   String str;
301   switch(dwExitCode){
302     case STILL_ACTIVE:
303       str=_T("still alive");
304       if(m_bKillThread){
305         str+=_T(" - requested to stop reading");
306       }
307       break;
308     case PROCESS_KILL_EXIT_CODE:
309       str=_T("killed");
310       break;
311     default:
312       str.Format(_T("terminated rc=%d"),dwExitCode);
313       break;
314   }
315   TRACE(_T("Finished reading from process %d (%s)\n"),m_idProcess,(LPCTSTR)str);
316 #endif
317
318   CloseHandle(m_hrPipe);m_hrPipe=INVALID_HANDLE_VALUE;
319   CloseHandle(m_hwPipe);m_hwPipe=INVALID_HANDLE_VALUE;
320   
321   if(m_bAutoDelete){
322     m_bThreadTerminated=true; // or else the dtor will block
323     delete this;
324   }
325 }
326
327 #else // UNIX
328
329 bool CSubprocess::CreateProcess(LPCTSTR pszCmdline)
330 {
331   m_idProcess=0;
332   int fdchild=-1; // the file descriptor for the child (slave) half of the pseudo-tty pair
333
334   // Get a free /dev/ptyp0 (master) and /dev/ttyp0 (slave) tty pair
335   String strMasterTty,strChildTty;
336   for(unsigned int c=0;c<64;c++){
337     strMasterTty.Format("/dev/pty%c%x",'p'+c/16,c%16);
338     
339     m_tty=open(strMasterTty, O_RDWR | O_NOCTTY);
340     if (-1!=m_tty) { 
341       strChildTty.Format("/dev/tty%c%x",'p'+c/16,c%16); 
342       
343       fdchild = open(strChildTty, O_RDWR);
344       if (-1==fdchild) {
345         close(m_tty);
346         m_tty=fdchild=-1;
347       } else {
348         VTRACE("opened %s - fd=%d\n",(LPCTSTR)strMasterTty,m_tty);
349         break;
350       }
351     }
352   }
353
354   if(-1==m_tty){
355     ERROR(_T("Failed to get a pty\n"));
356     return false;
357   }  
358   
359   TRACE(_T("Master pty %s (fd %d) slave pty %s (fd %d)\n"),(LPCTSTR)strMasterTty,m_tty,(LPCTSTR)strChildTty,fdchild);
360
361   m_idProcess=fork();
362   
363   switch (m_idProcess) {
364     // Fork failed
365     case -1:
366       TRACE(_T("Failed to create process - %s\n"),strerror(errno));
367       m_idProcess=0;
368       break;
369     case 0:
370       // Process is created (we're the child)
371       {
372         // Close all descriptors except the slave side of the pseudo-terminal
373         for (int fd = 0; fd < (int) sysconf(_SC_OPEN_MAX); fd++) {
374           if(fd!=fdchild){
375             close(fd);
376           }
377         }
378         setsid();
379         
380         dup2(fdchild, 0);
381         dup2(fdchild, 1);
382         dup2(fdchild, 2);
383         
384         close(fdchild);
385
386         if(!m_strDir.empty()){
387           if(0!=chdir(m_strDir)){
388             if(m_bVerbose){
389               fprintf(stderr,_T("*** Failed to change directory to %s\n"),(LPCTSTR)m_strDir);
390             }
391             exit (5);
392           }
393         }
394         if(m_bVerbose){
395           fprintf(stderr,_T("*** Process %d created \"%s\"\n"),m_idProcess,pszCmdline);
396         }
397
398         StringArray ar;
399         int argc=String(pszCmdline).Chop(ar,_TCHAR(' '),true);
400         TCHAR **argv=new TCHAR *[1+argc];
401         for(int i=0;i<argc;i++){
402           argv[i]=new TCHAR[1+strlen(ar[i])];  
403           strcpy(argv[i],ar[i]);
404         }
405         argv[argc]=0;
406         if(!m_strPath.empty()){
407           _tputenv(String::SFormat(_T("PATH=%s"),(LPCTSTR)m_strPath));
408         }
409         _texecvp(argv[0], argv);  
410       }
411
412       fprintf(stderr,"exec error - %s\n",strerror(errno));
413       exit(4);
414
415     default:
416       // Process is created (we're the parent)
417       TRACE(_T("Closing fd %d\n"),fdchild);
418       close(fdchild);
419       TRACE(_T("Forked to create process %s - process id <%d>\n"), pszCmdline, m_idProcess);
420       break;
421   }
422   return 0!=m_idProcess;
423 }
424
425 void CSubprocess::ThreadFunc()
426 {
427   if(!CreateProcess(m_strCmd)){
428     ERROR(_T("Failed to create process for %s\n"),(LPCTSTR)m_strCmd);
429   } else {  
430     fcntl(m_tty,F_SETFL,O_NONBLOCK);
431     int rc;
432     do {
433       TCHAR buf[4096];
434       rc=read(m_tty, buf, sizeof(buf)-1);
435       if(rc>=0){
436         buf[rc]='\0';
437       }
438       switch(rc){
439         case -1:
440           if(EAGAIN==errno){
441             CeCosThreadUtils::Sleep(250);
442           } else {
443               goto Done;
444           }
445           break;  
446         case 0:
447           goto Done;
448           continue;
449         default:
450           buf[rc]=_TCHAR('\0');
451           Output(String::CStrToUnicodeStr(buf));
452           continue;
453       }
454     } while(!m_bKillThread && m_pfnContinue(m_pContinuationFuncParam));
455 Done:
456     TRACE(_T("Closing fd %d\n"),m_tty);
457     close (m_tty);
458   
459     switch(waitpid(m_idProcess,&m_nExitCode,WNOHANG));
460   }
461   
462   if(m_bAutoDelete){
463     delete this;
464   }
465 }
466 #endif
467
468 void CSubprocess::Output (LPCTSTR psz)
469 {
470   m_pfnLogfunc(m_pLogparam,psz);
471 }
472
473 void CSubprocess::Send(LPCTSTR str)
474 {
475   char *psz=String(str).GetCString();
476   int nToWrite=strlen(psz);
477   const char *c=psz;
478   do {
479 #ifdef _WIN32
480     DWORD dwWritten;
481     if(!::WriteFile(m_hwPipe,psz,nToWrite,&dwWritten,0)){
482       break;
483     }
484 #else
485     int dwWritten = write(m_tty, c, nToWrite);
486     if(-1==dwWritten){
487       break;
488     } 
489 #endif
490     nToWrite-=(int)dwWritten;
491     c+=(int)dwWritten;
492   } while (nToWrite>0);
493   //::FlushFileBuffers(m_hwPipe);
494   delete [] psz;
495 }
496
497 bool CSubprocess::Kill(bool bRecurse)
498 {
499 TRACE(_T("CSubprocess::Kill pid %d recurse=%d\n"),m_idProcess,bRecurse);
500   PInfoArray arPinfo;
501   bool rc=false;
502   if(m_idProcess && -1!=m_idProcess){
503     // Start of with the easy one:
504     if(bRecurse) {
505       // Need to gather this information before we orphan our grandchildren:
506       PSExtract(arPinfo);
507     }
508     
509 #ifdef _WIN32
510
511     if(m_hProcess){
512       TRACE(_T("Terminate process %s\n"),(LPCTSTR)Name(m_idProcess));
513       rc=(TRUE==::TerminateProcess(m_hProcess,PROCESS_KILL_EXIT_CODE));
514       // dtor's (or subsequent Run's) responsibility to close the handle
515     }
516
517 #else
518     rc=(0==kill(m_idProcess,SIGTERM));
519     int status;
520     waitpid(m_idProcess,&status,WNOHANG);
521 #endif
522     
523     if(bRecurse) {
524       // kill process *and* its children
525       // FIXME: needs to be top-down
526       for(int i=0;i<(signed)arPinfo.size();i++){
527         if(arPinfo[i].IsChildOf(m_idProcess)){
528       
529 #ifdef _WIN32
530           // begin hack
531           const String strName(Name(arPinfo[i].PID));
532           if(_tcsstr(strName,_T("eCosTest")) || _tcsstr(strName,_T("cmd.EXE")) || _tcsstr(strName,_T("CMD.EXE")) || arPinfo[i].PID==(signed)GetCurrentProcessId()){
533             continue;
534           }
535           // end hack
536           HANDLE hProcess=::OpenProcess(PROCESS_TERMINATE,false,arPinfo[i].PID);
537           if(hProcess){
538             TRACE(_T("Terminate process %s\n"),(LPCTSTR)Name(arPinfo[i].PID));
539             rc&=(TRUE==::TerminateProcess(hProcess,PROCESS_KILL_EXIT_CODE));
540             CloseHandle(hProcess);
541           } else {
542             rc=false;
543           }
544 #else
545           rc&=(0==kill(arPinfo[i].PID,SIGTERM));
546           int status;
547           waitpid(arPinfo[i].PID,&status,WNOHANG);
548 #endif
549         }
550       }
551     }
552   }
553   return rc;
554 }
555
556 Time CSubprocess::CpuTime(bool bRecurse) const
557 {
558   Time t=0;
559   // kill process *and* its children
560   // FIXME: needs to be top-down
561   
562 #ifdef _WIN32
563   __int64 ftCreation,ftExit,ftKernel,ftUser;
564   if(m_hProcess && ::GetProcessTimes (m_hProcess,(FILETIME *)&ftCreation,(FILETIME *)&ftExit,(FILETIME *)&ftKernel,(FILETIME *)&ftUser)){
565     t+=Time((ftKernel+ftUser)/10000);
566   }
567
568   if(bRecurse){
569     PInfoArray arPinfo;
570     PSExtract(arPinfo);
571     if(m_idProcess && -1!=m_idProcess){
572       for(int i=0;i<(signed)arPinfo.size();i++){
573         if(arPinfo[i].IsChildOf(m_idProcess)){
574           t+=arPinfo[i].tCpu;
575         }
576       }
577     }
578   }
579 #else
580   PInfoArray arPinfo;
581   PSExtract(arPinfo);
582   for(int i=0;i<(signed)arPinfo.size();i++){
583     if(arPinfo[i].PID==m_idProcess || arPinfo[i].IsChildOf(m_idProcess)){
584       t+=arPinfo[i].tCpu;
585     }
586   }
587 #endif
588   return t;
589 }
590
591 #ifdef _WIN32
592 bool CSubprocess::PSExtract(CSubprocess::PInfoArray &arPinfo)
593 {
594   bool rc=false;
595   arPinfo.clear();
596   // If Windows NT:
597   switch(GetPlatform()) {
598   case VER_PLATFORM_WIN32_NT:
599     if(hInstLib1) {
600       
601       // Get procedure addresses.
602       static BOOL (WINAPI *lpfEnumProcesses)( DWORD *, DWORD cb, DWORD * ) = (BOOL(WINAPI *)(DWORD *,DWORD,DWORD*))GetProcAddress( hInstLib1, "EnumProcesses" ) ;
603       if( lpfEnumProcesses) {
604         
605         if(hInstLib2) {
606           
607           static DWORD (WINAPI *lpfNtQueryInformationProcess)( HANDLE, int, void *, DWORD, LPDWORD ) =
608             (DWORD(WINAPI *)(HANDLE, int, void *, DWORD, LPDWORD)) GetProcAddress( hInstLib2,"NtQueryInformationProcess" ) ;
609           
610           if(lpfNtQueryInformationProcess){
611             DWORD dwMaxPids=256;
612             DWORD dwPidSize;
613             DWORD *arPids = NULL ;
614             do {
615               delete [] arPids;
616               arPids=new DWORD[dwMaxPids];
617             } while(lpfEnumProcesses(arPids, dwMaxPids, &dwPidSize) && dwPidSize/sizeof(DWORD)==dwMaxPids) ;
618             
619             if(dwPidSize/sizeof(DWORD)<dwMaxPids){
620               rc=true;
621               for( DWORD dwIndex = 0 ; (signed)dwIndex < dwPidSize/sizeof(DWORD); dwIndex++ ) {
622                 // Regardless of OpenProcess success or failure, we
623                 // still call the enum func with the ProcID.
624                 DWORD pid=arPids[dwIndex];
625                 HANDLE hProcess=::OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid ); 
626                 if (hProcess ) {
627                   struct {
628                     DWORD ExitStatus; // receives process termination status
629                     DWORD PebBaseAddress; // receives process environment block address
630                     DWORD AffinityMask; // receives process affinity mask
631                     DWORD BasePriority; // receives process priority class
632                     ULONG UniqueProcessId; // receives process identifier
633                     ULONG InheritedFromUniqueProcessId; // receives parent process identifier
634                   } pbi;
635                   memset( &pbi, 0, sizeof(pbi)); 
636                   DWORD retLen; 
637                   __int64 ftCreation,ftExit,ftKernel,ftUser;
638                   if(lpfNtQueryInformationProcess(hProcess, 0 /*ProcessBasicInformation*/, &pbi, sizeof(pbi), &retLen)>=0 &&
639                     TRUE==::GetProcessTimes (hProcess,(FILETIME *)&ftCreation,(FILETIME *)&ftExit,(FILETIME *)&ftKernel,(FILETIME *)&ftUser)){
640                     // The second test is important.  It excludes orphaned processes who appear to have been adopted by virtue of a new
641                     // process having been created with the same ID as their original parent.
642                     PInfo p;
643                     p.PID=pid;
644                     p.PPID=pbi.InheritedFromUniqueProcessId;
645                     p.tCreation=ftCreation;
646                     p.tCpu=Time((ftKernel+ftUser)/10000);
647                     arPinfo.push_back(p);
648                   }
649                   
650                   CloseHandle(hProcess); 
651
652                 }
653               }
654             }
655             delete [] arPids;
656           }          
657         }
658       }      
659     }
660     break;
661   case VER_PLATFORM_WIN32_WINDOWS:
662     
663     if( hInstLib1) {
664       
665       static HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD)=
666         (HANDLE(WINAPI *)(DWORD,DWORD))GetProcAddress( hInstLib1,"CreateToolhelp32Snapshot" ) ;
667       static BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32)=
668         (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))GetProcAddress( hInstLib1, "Process32First" ) ;
669       static BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32)=
670         (BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))GetProcAddress( hInstLib1, "Process32Next" ) ;
671       if( lpfProcess32Next && lpfProcess32First && lpfCreateToolhelp32Snapshot) {
672         
673         // Get a handle to a Toolhelp snapshot of the systems
674         // processes.
675         HANDLE hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;
676         if(INVALID_HANDLE_VALUE != hSnapShot) {
677           // Get the first process' information.
678           PROCESSENTRY32 procentry;
679           procentry.dwSize = sizeof(PROCESSENTRY32) ;
680           if(lpfProcess32First( hSnapShot, &procentry )){
681             rc=true;
682             do {
683               PInfo p;
684               p.PID=procentry.th32ProcessID;
685               p.PPID=procentry.th32ParentProcessID;
686               arPinfo.push_back(p);
687             } while(lpfProcess32Next( hSnapShot, &procentry ));
688           }
689           CloseHandle(hSnapShot);
690         }
691       }
692     }
693     break;
694   default:
695     break;
696   }    
697
698   SetParents(arPinfo);
699
700   if(!rc){
701     ERROR(_T("Couldn't get process information!\n"));
702   }
703   return rc;
704 }
705
706 #else // UNIX
707
708 bool CSubprocess::PSExtract(CSubprocess::PInfoArray &arPinfo)
709 {
710   arPinfo.clear();
711   int i;
712   FILE *f=popen("ps -l",_T("r") MODE_TEXT);
713   if(f){
714     char buf[100];
715     while(fgets(buf,sizeof(buf)-1,f)){
716       TCHAR discard[100];
717       PInfo p;
718       // Output is in the form
719       //  F S   UID   PID  PPID  C PRI  NI ADDR    SZ WCHAN  TTY          TIME CMD
720       //100 S   490   877   876  0  70   0    -   368 wait4  pts/0    00:00:00 bash
721       int F,UID,C,PRI,NI,SZ,HH,MM,SS; 
722       bool rc=(15==_stscanf(buf,_T("%d %s %d %d %d %d %d %d %s %d %s %s %d:%d:%d"),&F,discard,&UID,&p.PID,&p.PPID,&C,&PRI,&NI,discard,&SZ,discard,discard,&HH,&MM,&SS));
723       if(rc){
724         p.tCpu=1000*(SS+60*(60*HH+MM));
725         arPinfo.push_back(p);
726       }
727     }
728     pclose(f);
729     for(i=0;i<(signed)arPinfo.size();i++){
730       int pid=arPinfo[i].PPID;
731       arPinfo[i].pParent=0;
732       for(int j=0;j<(signed)arPinfo.size();j++){
733         if(i!=j && arPinfo[j].PID==pid){
734           arPinfo[i].pParent=&arPinfo[j];
735           break;
736         }
737       }
738     }
739   } else {
740     ERROR(_T("Failed to run ps -l\n"));
741   }
742   return true; //FIXME
743 }
744
745 #endif
746
747 void CSubprocess::SetParents(CSubprocess::PInfoArray &arPinfo)
748 {
749   int i;
750   for(i=0;i<(signed)arPinfo.size();i++){
751     PInfo &p=arPinfo[i];
752     p.pParent=0;
753     for(int j=0;j<(signed)arPinfo.size();j++){
754       if(arPinfo[j].PID==p.PPID 
755 #ifdef _WIN32
756         && arPinfo[j].tCreation<p.tCreation
757 #endif
758         )
759       {
760         arPinfo[i].pParent=&arPinfo[j];
761         break;
762       }
763     }
764   }
765
766   // Check for circularity
767   bool bCircularity=false;
768   for(i=0;i<(signed)arPinfo.size();i++){
769     PInfo *p=&arPinfo[i];
770     for(int j=0;j<(signed)arPinfo.size() && p;j++){
771       p=p->pParent;
772     }
773     // If all is well, p should be NULL here.  Otherwise we have a loop.
774     if(p){
775       // Make sure it can't foul things up:
776       arPinfo[i].pParent=0;
777       bCircularity=true;
778     }
779   }
780   
781   if(bCircularity){
782     ERROR(_T("!!! Circularly linked process list at index %d\n"),i);
783     for(int k=0;k<(signed)arPinfo.size();k++){
784       const PInfo &p=arPinfo[k];
785       ERROR(_T("%d: %s ppid=%4d\n"),k,(LPCTSTR)Name(p.PID),p.PPID);
786     }
787   }
788 }
789
790 bool CSubprocess::PInfo::IsChildOf(int pid) const
791 {
792   for(PInfo *p=pParent;p && p!=this;p=p->pParent) { // guard against circular linkage
793     if(p->PID==pid){
794       return true;
795     }
796   }
797   return false;
798 }
799
800 const String CSubprocess::Name(int pid)
801 {
802   String str(String::SFormat(_T("id=%d"),pid));
803 #ifdef _DEBUG
804 #ifdef _WIN32
805   if(VER_PLATFORM_WIN32_NT==GetPlatform() && hInstLib1){
806     static BOOL (WINAPI *lpfEnumProcessModules)( HANDLE, HMODULE *, DWORD, LPDWORD ) =
807       (BOOL(WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( hInstLib1,"EnumProcessModules" ) ;
808     static DWORD (WINAPI *lpfGetModuleFileNameEx)( HANDLE, HMODULE, LPTSTR, DWORD )=
809       (DWORD (WINAPI *)(HANDLE, HMODULE,LPTSTR, DWORD )) GetProcAddress( hInstLib1,"GetModuleFileNameExA" ) ;
810     if( lpfEnumProcessModules &&  lpfGetModuleFileNameEx ) {
811       HANDLE hProcess=::OpenProcess(PROCESS_ALL_ACCESS,false,pid);
812       if(hProcess) {
813         HMODULE hMod;
814         DWORD dwSize;
815         if(lpfEnumProcessModules( hProcess, &hMod, sizeof(HMODULE), &dwSize ) ){
816           // Get Full pathname:
817           TCHAR buf[1+MAX_PATH];
818           lpfGetModuleFileNameEx( hProcess, hMod, buf, MAX_PATH);
819           str+=_TCHAR(' ');
820           str+=buf;
821         }
822         CloseHandle(hProcess);
823       }
824     }
825   }
826 #endif
827 #endif
828   return str;
829 }
830
831 #ifdef _WIN32
832 DWORD CSubprocess::GetPlatform()
833 {
834   OSVERSIONINFO  osver;
835   osver.dwOSVersionInfoSize = sizeof( osver ) ;
836   return GetVersionEx( &osver ) ? osver.dwPlatformId : (DWORD)-1;
837 }
838 #endif
839
840 bool CSubprocess::ProcessAlive()
841 {
842   return !m_bThreadTerminated;
843 }
844
845 void CSubprocess::CloseInput()
846 {
847 #ifdef _WIN32
848   CloseHandle(m_hwPipe);m_hwPipe=INVALID_HANDLE_VALUE;
849 #else
850   close(m_tty);
851 #endif
852 }
853
854 bool CSubprocess::Wait(Duration dTimeout)
855 {
856   return CeCosThreadUtils::WaitFor(m_bThreadTerminated,dTimeout);
857 }
858
859 const String CSubprocess::ErrorString() const
860 {
861 #ifdef _WIN32
862   TCHAR *pszMsg;
863   FormatMessage(  
864     FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
865     NULL,
866     m_nErr,
867     MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
868     (LPTSTR)&pszMsg,
869     0,
870     NULL 
871     );
872   return pszMsg;
873 #else 
874   return strerror(errno);
875 #endif
876 }
877