Process.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************/
  5. void ProcPriority(Int priority)
  6. {
  7. #if WINDOWS_OLD
  8. if(priority<=-2)priority= IDLE_PRIORITY_CLASS;else
  9. if(priority==-1)priority=BELOW_NORMAL_PRIORITY_CLASS;else
  10. if(priority== 0)priority= NORMAL_PRIORITY_CLASS;else
  11. if(priority== 1)priority=ABOVE_NORMAL_PRIORITY_CLASS;else
  12. priority= HIGH_PRIORITY_CLASS;
  13. SetPriorityClass(GetCurrentProcess(), priority);
  14. #elif APPLE || LINUX
  15. if(priority<=-2)priority= 20;else
  16. if(priority==-1)priority= 10;else
  17. if(priority== 0)priority= 0;else
  18. if(priority== 1)priority=-10;else
  19. priority=-20;
  20. setpriority(PRIO_PROCESS, App.processID(), priority);
  21. #endif
  22. }
  23. /******************************************************************************/
  24. #if WINDOWS_OLD
  25. static BOOL CALLBACK EnumWindowClose(HWND hwnd, LPARAM process_id)
  26. {
  27. if(WindowProc((Ptr)hwnd)==process_id)WindowClose((Ptr)hwnd);
  28. return true;
  29. }
  30. #endif
  31. void ProcClose(UInt id)
  32. {
  33. if(id)
  34. {
  35. #if WINDOWS_OLD
  36. EnumWindows(EnumWindowClose, id);
  37. #else
  38. // TODO: Unix 'ProcClose'
  39. #endif
  40. }
  41. }
  42. Bool ProcKill(UInt id)
  43. {
  44. Bool ok=false;
  45. if(id)
  46. {
  47. #if WINDOWS
  48. if(HANDLE hproc=OpenProcess(PROCESS_TERMINATE, false, id))
  49. {
  50. if(TerminateProcess(hproc, 0))ok=true;
  51. CloseHandle(hproc);
  52. }
  53. #else
  54. kill(id, SIGTERM);
  55. int status; pid_t ret=waitpid(id, &status, WNOHANG);
  56. ok=true;
  57. #endif
  58. }
  59. return ok;
  60. }
  61. Bool ProcWait(UInt id, Int milliseconds)
  62. {
  63. Bool ok=true;
  64. if(id)
  65. {
  66. #if WINDOWS
  67. if(HANDLE hproc=OpenProcess(SYNCHRONIZE, false, id))
  68. {
  69. if(WaitForSingleObject(hproc, (milliseconds<0) ? INFINITE : milliseconds)==WAIT_TIMEOUT)ok=false;
  70. CloseHandle(hproc);
  71. }
  72. #else
  73. if(milliseconds<0)waitpid(id, null, 0);else
  74. {
  75. UInt start=Time.curTimeMs();
  76. wait:;
  77. int status; pid_t pid=waitpid(id, &status, WNOHANG);
  78. if(pid==0)
  79. {
  80. UInt duration=Time.curTimeMs()-start; // this code was tested OK for UInt overflow
  81. if( duration<milliseconds){Time.wait(1); goto wait;} // wait again
  82. ok=false; // timeout
  83. }
  84. }
  85. #endif
  86. }
  87. return ok;
  88. }
  89. void ProcClose(C Str &name) { ProcClose(ProcFind(name));}
  90. Bool ProcKill (C Str &name) {return ProcKill (ProcFind(name));}
  91. /******************************************************************************/
  92. #if WINDOWS_OLD
  93. struct ProcWindowFind
  94. {
  95. UInt process_id;
  96. HWND hwnd;
  97. };
  98. static BOOL CALLBACK EnumWindowFind(HWND hwnd, LPARAM proc_window_find)
  99. {
  100. ProcWindowFind &pwf=*(ProcWindowFind*)proc_window_find;
  101. DWORD id; if(GetWindowThreadProcessId(hwnd, &id) && id==pwf.process_id)
  102. if(IsWindowVisible(hwnd) || IsIconic(hwnd))
  103. {
  104. pwf.hwnd=hwnd;
  105. return false;
  106. }else
  107. if(!pwf.hwnd) // if found a window but it is hidden, then set it only if previous was not set
  108. {
  109. pwf.hwnd=hwnd;
  110. }
  111. return true;
  112. }
  113. #endif
  114. Ptr ProcWindow(UInt id)
  115. {
  116. if(id)
  117. {
  118. #if WINDOWS_OLD
  119. ProcWindowFind pwf;
  120. pwf.process_id=id;
  121. pwf.hwnd =null;
  122. EnumWindows(EnumWindowFind, LPARAM(&pwf));
  123. return (Ptr)pwf.hwnd;
  124. #else
  125. if(id==App.processID())return App.hwnd();
  126. #endif
  127. }
  128. return null;
  129. }
  130. /******************************************************************************/
  131. #if WINDOWS_OLD && SUPPORT_WINDOWS_XP
  132. static BOOL (WINAPI *QueryFullProcessImageNameW)(HANDLE hProcess, DWORD dwFlags, LPWSTR lpExeName, PDWORD lpdwSize);
  133. static Bool QueryFullProcessImageNameTried;
  134. #endif
  135. Str ProcName(UInt id)
  136. {
  137. if(id)
  138. {
  139. #if WINDOWS_OLD
  140. HANDLE hproc=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION , false, id); // first try accessing using PROCESS_QUERY_LIMITED_INFORMATION, it's not supported on WindowsXP, but if we're using OS in which it's supported, then it can open more processes than method below
  141. #if SUPPORT_WINDOWS_XP
  142. if(!hproc)hproc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, id); // if failed, then try using WindowsXP way, 'PROCESS_VM_READ' is needed here
  143. #endif
  144. if( hproc)
  145. {
  146. wchar_t name[MAX_LONG_PATH]; name[0]=0;
  147. #if SUPPORT_WINDOWS_XP
  148. DWORD dword; HMODULE hmod; if(EnumProcessModules(hproc, &hmod, SIZE(hmod), &dword))GetModuleFileNameEx(hproc, hmod, name, Elms(name)); // this requires PROCESS_QUERY_INFORMATION|PROCESS_VM_READ
  149. if(!name[0]) // requesting name failed, it's possible that encountered process is 64-bit and we're 32-bit, or PROCESS_QUERY_LIMITED_INFORMATION was used, and 'QueryFullProcessImageNameW' needs to be used
  150. {
  151. if(!QueryFullProcessImageNameTried)
  152. {
  153. if(HMODULE kernel=GetModuleHandle(L"Kernel32.dll"))QueryFullProcessImageNameW=(decltype(QueryFullProcessImageNameW))GetProcAddress(kernel, "QueryFullProcessImageNameW"); // available on Vista+
  154. QueryFullProcessImageNameTried=true;
  155. }
  156. if(QueryFullProcessImageNameW)
  157. {
  158. #endif
  159. DWORD size=Elms(name); QueryFullProcessImageNameW(hproc, 0, name, &size);
  160. #if SUPPORT_WINDOWS_XP
  161. }
  162. }
  163. #endif
  164. CloseHandle(hproc);
  165. return name;
  166. }
  167. #elif APPLE
  168. #if 1 // returns process full path file name
  169. int mib[3], max_data=0;
  170. size_t size;
  171. mib[0]=CTL_KERN;
  172. mib[1]=KERN_ARGMAX;
  173. size=SIZE(max_data);
  174. if(!sysctl(mib, 2, &max_data, &size, null, 0))
  175. {
  176. Memt<Char8> temp; temp.setNum(max_data);
  177. mib[0]=CTL_KERN;
  178. mib[1]=KERN_PROCARGS2;
  179. mib[2]=id;
  180. size=(size_t)max_data;
  181. if(!sysctl(mib, 3, temp.data(), &size, null, 0))return FromUTF8(temp.data()+4);
  182. }
  183. #else // returns process window name
  184. ProcessSerialNumber psn;
  185. if(GetProcessForPID(id, &psn)==noErr)
  186. {
  187. CFStringRef proc_name=null; CopyProcessName(&psn, &proc_name);
  188. if(proc_name)
  189. {
  190. Char8 name[MAX_UTF_PATH]; CFStringGetCString(proc_name, name, Elms(name), kCFStringEncodingUTF8);
  191. CFRelease(proc_name);
  192. return FromUTF8(name);
  193. }
  194. }
  195. #endif
  196. #else
  197. // TODO: ANDROID, LINUX 'ProcName'
  198. #endif
  199. }
  200. return S;
  201. }
  202. void ProcModules(UInt id, MemPtr<Str> modules)
  203. {
  204. #if WINDOWS_OLD
  205. if(id)
  206. if(HANDLE hproc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, id)) // these params needed for 'EnumProcessModules' and 'GetModuleFileNameEx'
  207. {
  208. Memt<HMODULE> temp; temp.setNum(temp.maxElms());
  209. again:;
  210. DWORD num=0, size=temp.elms()*temp.elmSize();
  211. if(EnumProcessModules(hproc, temp.data(), size, &num))
  212. {
  213. Int elms=num/temp.elmSize();
  214. if(num>size){temp.setNum(elms); goto again;}
  215. modules.setNum(elms);
  216. REPA(modules)
  217. {
  218. wchar_t name[MAX_LONG_PATH]; name[0]=0; GetModuleFileNameEx(hproc, temp[i], name, Elms(name));
  219. modules[i]=name;
  220. }
  221. return;
  222. }
  223. }
  224. #endif
  225. modules.clear();
  226. }
  227. /******************************************************************************/
  228. UInt ProcFind(C Str &name)
  229. {
  230. UInt ret=0;
  231. if(name.is())
  232. {
  233. Bool drive=HasDrive(name);
  234. #if 0 && WINDOWS_OLD // old method, avoid since 'ProcName' should now support all cases
  235. HANDLE snap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  236. if( snap!=INVALID_HANDLE_VALUE)
  237. {
  238. PROCESSENTRY32 proc; proc.dwSize=SIZE(PROCESSENTRY32);
  239. if(Process32First(snap, &proc))do
  240. {
  241. if(drive){if(!EqualPath(name, ProcName(proc.th32ProcessID)))continue;}
  242. else {if(!Equal (name, proc.szExeFile ))continue;}
  243. ret=proc.th32ProcessID; break;
  244. }while(Process32Next(snap, &proc));
  245. CloseHandle(snap);
  246. }
  247. #else
  248. Memt<UInt> id; ProcList(id);
  249. REPA(id)
  250. {
  251. Str pn=ProcName(id[i]);
  252. if(drive){if(!EqualPath(name, pn ))continue;}
  253. else {if(!Equal (name, _GetBase(pn)))continue;}
  254. ret=id[i]; break;
  255. }
  256. #endif
  257. }
  258. return ret;
  259. }
  260. /******************************************************************************/
  261. void ProcList(MemPtr<UInt> id)
  262. {
  263. #if WINDOWS_OLD
  264. Memt<DWORD> temp; temp.setNum(temp.maxElms());
  265. again:;
  266. DWORD num=0, size=temp.elms()*temp.elmSize();
  267. if(EnumProcesses(temp.data(), size, &num))
  268. {
  269. if(num>=size){temp.addNum(Max(1, temp.elms()/4)); goto again;} // according to docs and tests, 'num' will be equal to 'size' if there are more processes that can fit into the buffer
  270. ASSERT(SIZE(DWORD)==SIZE(UInt)); id.setNum(num/temp.elmSize()).copyFrom((UInt*)temp.data());
  271. return;
  272. }
  273. #elif APPLE
  274. size_t size=0;
  275. int names[]={CTL_KERN, KERN_PROC, KERN_PROC_ALL},
  276. code=sysctl(names, Elms(names), null, &size, null, 0); // first call must be with null because it will fail with no info
  277. if(!code && size>0)
  278. {
  279. Memt<kinfo_proc> temp; temp.setNum(Max(temp.maxElms(), Int(size/temp.elmSize()*5/4))); // allocate a bit more
  280. again:;
  281. size_t size2=temp.elms()*temp.elmSize();
  282. code =sysctl(names, Elms(names), temp.data(), &size2, null, 0);
  283. if(!code && !size2 && size){size=0; temp.addNum(Max(1, temp.elms()/2)); goto again;} // if not enough memory then try again
  284. if(!code && size2>0)
  285. {
  286. id.setNum(size2/temp.elmSize()); REPAO(id)=temp[i].kp_proc.p_pid;
  287. return;
  288. }
  289. }
  290. #else
  291. // TODO: ANDROID, LINUX 'ProcList'
  292. #endif
  293. id.clear();
  294. }
  295. /******************************************************************************/
  296. // CONSOLE PROCESS
  297. /******************************************************************************/
  298. #if WINDOWS_OLD
  299. Bool ConsoleProcess::created( )C {return _proc!=null;}
  300. Bool ConsoleProcess::active ( )C {return _proc!=null && _thread.active();}
  301. Bool ConsoleProcess::wait (Int milliseconds) {return _proc==null || _thread.wait (milliseconds);}
  302. void ConsoleProcess::stop ( ) {ProcClose(_proc_id);}
  303. void ConsoleProcess::kill ( )
  304. {
  305. if(_proc)
  306. {
  307. TerminateProcess(_proc, 0);
  308. del();
  309. }
  310. }
  311. /******************************************************************************/
  312. static Bool ConsoleProcessFunc(Thread &thread)
  313. {
  314. ConsoleProcess &cp=*(ConsoleProcess*)thread.user;
  315. SyncLockerEx locker(cp._lock);
  316. if(cp._proc && cp._out_read)
  317. {
  318. HANDLE temp =null;
  319. UInt proc_id =cp._proc_id;
  320. DWORD available=0;
  321. Bool active =(WaitForSingleObject(cp._proc, 0)==WAIT_TIMEOUT);
  322. if( !active)
  323. {
  324. DWORD exit_code=0; GetExitCodeProcess(cp._proc, &exit_code); // warning: process may have used 'STILL_ACTIVE' for the 'ExitProcess'
  325. cp._exit_code=((exit_code==STILL_ACTIVE) ? -1 : exit_code);
  326. }
  327. if(PeekNamedPipe(cp._out_read, null, 0, null, &available, null)) // check if there's any data available, this is to prevent freezes in 'ReadFile' when there's nothing available
  328. if(available)
  329. if(DuplicateHandle(GetCurrentProcess(), cp._out_read, GetCurrentProcess(), &temp, 0, true, DUPLICATE_SAME_ACCESS))
  330. {
  331. Char8 buf[65536+1]; MIN(available, SIZE(buf)-1);
  332. read_again:;
  333. locker.off();
  334. DWORD read=0; ReadFile(temp, buf, available, &read, null); // read without 'SyncLock' on handle duplicate
  335. if( read)
  336. {
  337. buf[read]='\0'; if(!cp._binary)ReplaceSelf(buf, '\r', '\0');
  338. locker.on();
  339. if(proc_id==cp._proc_id) // if it's still the same process then append the data
  340. {
  341. if(!cp._binary)cp._data+=buf;else // append string in text mode
  342. {
  343. Int length=cp._data.length()+read, total=length+1; // get length, and total size (+ null character)
  344. if( total >cp._data._d.elms())cp._data._d.setNum(total);
  345. CopyFast(cp._data._d.data()+cp._data.length(), buf, read+1); // append read data (including null character)
  346. cp._data._length=length; // adjust length
  347. }
  348. if(!active && read==available){available=SIZE(buf)-1; goto read_again;} // if the process ended and full buffer was read then read again (there will be no delay this time) because we must access all output before closing the thread
  349. }else active=true; // if process changed then set active to true because we want the thread to keep on running and check for new process data in next step
  350. locker.off();
  351. }
  352. CloseHandle(temp);
  353. }
  354. if(active)
  355. {
  356. #if HAS_THREADS
  357. locker.off();
  358. Time.wait(1);
  359. #endif
  360. return true;
  361. }
  362. }
  363. return false;
  364. }
  365. void ConsoleProcess::del()
  366. {
  367. stop();
  368. SyncLocker locker(_lock);
  369. _data.clear();
  370. _exit_code=-1; _binary=false;
  371. _proc_id=0;
  372. if(_proc){CloseHandle(_proc); _proc=null;}
  373. if(_out_read){CloseHandle(_out_read); _out_read=null;}
  374. if(_in_write){CloseHandle(_in_write); _in_write=null;}
  375. }
  376. Bool ConsoleProcess::create(C Str &name, C Str &params, Bool hidden, Bool binary)
  377. {
  378. del();
  379. if(name.is())
  380. {
  381. HANDLE out_write=null,
  382. in_read =null;
  383. // Set up the security attributes struct
  384. SECURITY_ATTRIBUTES sa; Zero(sa);
  385. sa.nLength=SIZE(sa);
  386. sa.bInheritHandle=TRUE;
  387. SyncLockerEx locker(_lock);
  388. if(CreatePipe(&_out_read, &out_write, &sa, 0)) // Create the child output pipe
  389. {
  390. if(SetHandleInformation(_out_read, HANDLE_FLAG_INHERIT, 0)) // Ensure the read handle to the pipe for STDOUT is not inherited
  391. if(CreatePipe(&in_read, &_in_write, &sa, 0)) // Create the child input pipe
  392. {
  393. if(SetHandleInformation(_in_write, HANDLE_FLAG_INHERIT, 0)) // Ensure the write handle to the pipe for STDIN is not inherited
  394. {
  395. locker.off();
  396. PROCESS_INFORMATION pi;
  397. STARTUPINFOW si; Zero(si); si.cb=SIZE(si);
  398. si.dwFlags =STARTF_USESTDHANDLES;
  399. si.hStdInput =in_read;
  400. si.hStdOutput=out_write;
  401. si.hStdError =out_write;
  402. if(hidden)
  403. {
  404. si.dwFlags |=STARTF_USESHOWWINDOW;
  405. si.wShowWindow=SW_HIDE;
  406. }
  407. Str app=name, base=GetBase(app), cmd, cur_dir;
  408. if(GetExt(app)=="bat" || GetExt(app)=="cmd")
  409. {
  410. cur_dir=GetPath(app);
  411. cmd =(Contains(base, ' ') ? S+"/c \""+base+'"' : S+"/c "+base); // must use quotes only when needed, otherwise this will fail
  412. wchar_t comspec[MAX_LONG_PATH]; comspec[0]='\0'; if(GetEnvironmentVariable(L"COMSPEC", comspec, Elms(comspec)))app=comspec;
  413. //cmd =S+"/c "+name+"";
  414. //cmd =S+"\"/c "+name+"\"";//+' '+params+"\"";
  415. //cmd =S+"/c \""+name+' '+params+"\"";
  416. //cmd =S+"/c \""+name+"\"";
  417. //cmd =S+"/c \""+name+"\" "+params+"";
  418. //cmd =S+"\"cmd.exe\" /c \""+name+"\"";
  419. //name="cmd.exe";
  420. //name="cmd";
  421. //name=S;
  422. //cmd =S+'"'+name+'"';
  423. }else cmd=base;
  424. if(params.is())cmd.space()+=params;
  425. Memt<Char> cmd_temp; cmd_temp.setNum(cmd.length()+1); Set(cmd_temp.data(), cmd, cmd_temp.elms()); // can be very long, copy to 'cmd_temp' because 'CreateProcessW' can modify it
  426. if(CreateProcessW(app, WChar(cmd_temp.data()), null, null, TRUE, CREATE_NEW_CONSOLE, null, cur_dir, &si, &pi))
  427. {
  428. locker.on();
  429. _proc =pi.hProcess;
  430. _proc_id=pi.dwProcessId;
  431. _binary =binary;
  432. locker.off();
  433. if(!_thread.active() || _thread.wantStop())_thread.create(ConsoleProcessFunc, this);
  434. }
  435. }
  436. CloseHandle(in_read); in_read=null;
  437. }
  438. CloseHandle(out_write); out_write=null;
  439. }
  440. }
  441. return _proc!=null;
  442. }
  443. Bool ConsoleProcess::createMem(C Str &script, C Str &cur_dir, Bool hidden, Bool binary)
  444. {
  445. del();
  446. if(script.is())
  447. {
  448. wchar_t comspec[MAX_LONG_PATH]; comspec[0]='\0'; if(GetEnvironmentVariable(L"COMSPEC", comspec, Elms(comspec)))
  449. {
  450. HANDLE out_write=null,
  451. in_read =null;
  452. // Set up the security attributes struct
  453. SECURITY_ATTRIBUTES sa; Zero(sa);
  454. sa.nLength=SIZE(sa);
  455. sa.bInheritHandle=TRUE;
  456. SyncLockerEx locker(_lock);
  457. if(CreatePipe(&_out_read, &out_write, &sa, 0)) // Create the child output pipe
  458. {
  459. if(SetHandleInformation(_out_read, HANDLE_FLAG_INHERIT, 0)) // Ensure the read handle to the pipe for STDOUT is not inherited
  460. if(CreatePipe(&in_read, &_in_write, &sa, 0)) // Create the child input pipe
  461. {
  462. if(SetHandleInformation(_in_write, HANDLE_FLAG_INHERIT, 0)) // Ensure the write handle to the pipe for STDIN is not inherited
  463. {
  464. locker.off();
  465. PROCESS_INFORMATION pi;
  466. STARTUPINFOW si; Zero(si); si.cb=SIZE(si);
  467. si.dwFlags =STARTF_USESTDHANDLES;
  468. si.hStdInput =in_read;
  469. si.hStdOutput=out_write;
  470. si.hStdError =out_write;
  471. if(hidden)
  472. {
  473. si.dwFlags |=STARTF_USESHOWWINDOW;
  474. si.wShowWindow=SW_HIDE;
  475. }
  476. Str cmd=S+"/c "+script; cmd.replace('\r', '\0').replace('\n', '&'); // commands need to be separated with '&' character
  477. Memt<Char> cmd_temp; cmd_temp.setNum(cmd.length()+1); Set(cmd_temp.data(), cmd, cmd_temp.elms()); // can be very long, copy to 'cmd_temp' because 'CreateProcessW' can modify it
  478. if(CreateProcessW(comspec, WChar(cmd_temp.data()), null, null, TRUE, CREATE_NEW_CONSOLE, null, cur_dir, &si, &pi))
  479. {
  480. locker.on();
  481. _proc =pi.hProcess;
  482. _proc_id=pi.dwProcessId;
  483. _binary =binary;
  484. locker.off();
  485. if(!_thread.active() || _thread.wantStop())_thread.create(ConsoleProcessFunc, this);
  486. }
  487. }
  488. CloseHandle(in_read); in_read=null;
  489. }
  490. CloseHandle(out_write); out_write=null;
  491. }
  492. }
  493. }
  494. return _proc!=null;
  495. }
  496. /******************************************************************************/
  497. #elif WINDOWS_NEW
  498. Bool ConsoleProcess::created ( )C {return false;}
  499. Bool ConsoleProcess::active ( )C {return false;}
  500. Bool ConsoleProcess::wait (Int milliseconds) {return false;}
  501. void ConsoleProcess::stop ( ) {}
  502. void ConsoleProcess::kill ( ) {}
  503. void ConsoleProcess::del ( ) {}
  504. Bool ConsoleProcess::create (C Str &name , C Str &params , Bool hidden, Bool binary) {return false;}
  505. Bool ConsoleProcess::createMem(C Str &script, C Str &cur_dir, Bool hidden, Bool binary) {return false;}
  506. #else
  507. #define FD_READ 0
  508. #define FD_WRITE 1
  509. static pid_t popen2(CChar8 *cur_dir, CChar8 *command, int *in_fd, int *out_fd)
  510. {
  511. if( in_fd)* in_fd=0;
  512. if(out_fd)*out_fd=0;
  513. int p_stdin[2], p_stdout[2];
  514. if(pipe(p_stdin ))return -1;
  515. if(pipe(p_stdout)){close(p_stdin[0]); close(p_stdin[1]); return -1;}
  516. pid_t pid=fork();
  517. if(pid==0) // this code gets executed on the child process
  518. {
  519. close(p_stdin [FD_WRITE]); dup2(p_stdin [FD_READ ], STDIN_FILENO);
  520. close(p_stdout[FD_READ ]); dup2(p_stdout[FD_WRITE], STDOUT_FILENO);
  521. dup2(p_stdout[FD_WRITE], STDERR_FILENO);
  522. if(Is(cur_dir))chdir(cur_dir);
  523. execl("/bin/sh", "sh", "-c", command, __null);
  524. perror(null);
  525. _exit(0);
  526. }else
  527. if(pid<0) // error
  528. {
  529. close(p_stdin[0]); close(p_stdout[0]);
  530. close(p_stdin[1]); close(p_stdout[1]);
  531. pid=0; // return 0
  532. }else // success
  533. {
  534. if( in_fd)* in_fd=p_stdin [FD_WRITE];else close(p_stdin [FD_WRITE]);
  535. if(out_fd)*out_fd=p_stdout[FD_READ ];else close(p_stdout[FD_READ ]);
  536. }
  537. return pid;
  538. }
  539. Bool ConsoleProcess::created( )C {return _proc_id!=0;}
  540. Bool ConsoleProcess::active ( )C {return _proc_id!=0 && _thread.active();}
  541. Bool ConsoleProcess::wait (Int milliseconds) {return _proc_id==0 || _thread.wait (milliseconds);}
  542. void ConsoleProcess::stop () {} // do nothing
  543. void ConsoleProcess::kill () {if(_proc_id){ProcKill(_proc_id); del();}}
  544. void ConsoleProcess::del ()
  545. {
  546. stop();
  547. SyncLocker locker(_lock);
  548. _data.clear();
  549. _exit_code=-1; _binary=false;
  550. _proc_id=0;
  551. if(_out_read){close(_out_read); _out_read=0;}
  552. if(_in_write){close(_in_write); _in_write=0;}
  553. }
  554. static Bool ConsoleProcessFunc(Thread &thread)
  555. {
  556. ConsoleProcess &cp=*(ConsoleProcess*)thread.user;
  557. SyncLockerEx locker(cp._lock);
  558. if(cp._proc_id && cp._out_read)
  559. {
  560. UInt proc_id=cp._proc_id;
  561. int status; pid_t pid=waitpid(proc_id, &status, WNOHANG);
  562. Bool active=(pid==0);
  563. if( !active)cp._exit_code=(WIFEXITED(status) ? WEXITSTATUS(status) : -1);
  564. // read
  565. {
  566. int temp =dup(cp._out_read);
  567. if( temp>=0)
  568. {
  569. read_again:;
  570. locker.off();
  571. fd_set fd; int time=0;
  572. #if WINDOWS
  573. fd.fd_count=1; fd.fd_array[0]=temp;
  574. #else
  575. FD_ZERO(&fd); FD_SET(temp, &fd);
  576. #endif
  577. timeval tv; tv.tv_sec=time/1000; tv.tv_usec=(time%1000)*1000;
  578. if(select(temp+1, &fd, null, null, &tv)>0) // if there's data available
  579. {
  580. Char8 buf[65536+1]; Int to_read=SIZE(buf)-1; // make room for '\0'
  581. #if APPLE // do this only on Apple, as on Linux 'fstat' will give zero even if there's data available
  582. struct stat s; fstat(temp, &s); MIN(to_read, s.st_size); // limit reading to remaining file size, to avoid freezes when trying to read from empty file
  583. #endif
  584. Int r=read(temp, buf, to_read);
  585. if( r>0)
  586. {
  587. buf[r]='\0'; if(!cp._binary)ReplaceSelf(buf, '\r', '\0');
  588. locker.on();
  589. if(proc_id==cp._proc_id) // if it's still the same process then append the data
  590. {
  591. if(!cp._binary)cp._data+=buf;else // append string in text mode
  592. {
  593. Int length=cp._data.length()+r, total=length+1; // get length, and total size (+ null character)
  594. if( total >cp._data._d.elms())cp._data._d.setNum(total);
  595. CopyFast(cp._data._d.data()+cp._data.length(), buf, r+1); // append read data (including null character)
  596. cp._data._length=length; // adjust length
  597. }
  598. if(!active && r==to_read)goto read_again;// if the process ended and full buffer was read then read again (there will be no delay this time) because we must access all output before closing the thread
  599. }else active=true; // if process changed then set active to true because we want the thread to keep on running and check for new process data in next step
  600. locker.off();
  601. }
  602. }
  603. close(temp);
  604. }
  605. }
  606. if(active)
  607. {
  608. #if HAS_THREADS
  609. locker.off();
  610. Time.wait(1);
  611. #endif
  612. return true;
  613. }
  614. }
  615. return false;
  616. }
  617. Bool ConsoleProcess::create(C Str &name, C Str &params, Bool hidden, Bool binary)
  618. {
  619. del();
  620. if(name.is())
  621. {
  622. Str8 cur_dir=UnixPathUTF8(S), command=S+'"'+UnixPathUTF8(name)+'"';
  623. if(params.is())command.space()+=UnixPathUTF8(params);
  624. SyncLockerEx locker(_lock);
  625. if(_proc_id=popen2(cur_dir, command, null, &_out_read))
  626. {
  627. _binary=binary;
  628. locker.off();
  629. if(!_thread.active() || _thread.wantStop())_thread.create(ConsoleProcessFunc, this);
  630. }
  631. }
  632. return _proc_id!=0;
  633. }
  634. Bool ConsoleProcess::createMem(C Str &script, C Str &cur_dir, Bool hidden, Bool binary) {del(); return false;}
  635. #endif
  636. /******************************************************************************/
  637. Str ConsoleProcess::get()
  638. {
  639. if(_data.is())
  640. {
  641. SyncLocker locker(_lock);
  642. if(_binary)
  643. {
  644. Str temp;
  645. temp._length =_data.length();
  646. temp._d.setNum(_data.length()+1); temp._d.last()='\0'; REPA(_data)temp._d[i]=_data._d[i]; // do raw copy of 'Char8' to 'Char' to avoid code-page changes, can't do 'CopyFastN' because we're copying from 'Str8' to 'Str'
  647. return temp;
  648. }else
  649. #if WINDOWS
  650. if(1) // Windows uses Code Pages
  651. {
  652. Str s; Char8 last=_data.last();
  653. if((last&0x80) && active())_data.removeLast();else last='\0'; // possibly beginning of a multi-byte character, and the process is still active (which may generate more characters) then don't process this char now (remove it from '_data' and keep it in 'last' to restore later), otherwise clear it
  654. if(Int length=_data.length())
  655. {
  656. Int size=MultiByteToWideChar(CP_ACP, 0, _data(), length, null, 0); if(size>0)
  657. {
  658. s.reserve(size);
  659. if(size==MultiByteToWideChar(CP_ACP, 0, _data(), length, WChar(s._d.data()), s._d.elms()))s._d[s._length=size]='\0';else s.clear();
  660. }
  661. }
  662. _data.clear()+=last; // restore what was removed
  663. return s;
  664. }else
  665. #endif
  666. { // UTF8
  667. /* We need to skip UTF8 chars that aren't complete:
  668. UTF8 is encoded in following way:
  669. Bytes Byte0 Byte1 Byte2
  670. 1 0xxxxxxx
  671. 2 110xxxxx 10xxxxxx - this needs fixing if we're missing last 1 byte
  672. 3 1110xxxx 10xxxxxx 10xxxxxx - this needs fixing if we're missing last 2 bytes
  673. */
  674. Int skip=0;
  675. Char8 last=_data.last(), temp[2];
  676. if( last&0x80)
  677. {
  678. if((last&0xE0)==0xC0)skip=1;else // 110xxxxx, missing
  679. if((last&0xF0)==0xE0)skip=1;else // 1110xxxx, missing, missing
  680. if((_data[_data.length()-2]&0xF0)==0xE0)skip=2; // 1110xxxx, available, missing
  681. REP(skip){temp[i]=_data.last(); _data.removeLast();}
  682. }
  683. Str str=FromUTF8(_data); _data.clear(); FREP(skip)_data+=temp[i]; return str;
  684. }
  685. }
  686. return S;
  687. }
  688. /******************************************************************************/
  689. #if WINDOWS_OLD
  690. #if 0
  691. }
  692. #include "../../../ThirdPartyLibs/begin.h"
  693. #include <SubAuth.h>
  694. #include "../../../ThirdPartyLibs/end.h"
  695. namespace EE{
  696. #endif
  697. #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
  698. #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
  699. #define DUPLICATE_SAME_ATTRIBUTES 0x00000004
  700. #define SystemHandleInformation 16
  701. typedef struct _UNICODE_STRING {
  702. USHORT Length;
  703. USHORT MaximumLength;
  704. PWSTR Buffer;
  705. } UNICODE_STRING, *PUNICODE_STRING;
  706. enum OBJECT_INFORMATION_CLASS
  707. {
  708. ObjectBasicInformation,
  709. ObjectNameInformation,
  710. ObjectTypeInformation,
  711. ObjectTypesInformation,
  712. ObjectHandleFlagInformation,
  713. ObjectSessionInformation,
  714. MaxObjectInfoClass
  715. };
  716. typedef struct _SYSTEM_HANDLE
  717. {
  718. ULONG ProcessId;
  719. BYTE ObjectTypeNumber;
  720. BYTE Flags;
  721. USHORT Handle;
  722. PVOID Object;
  723. ACCESS_MASK GrantedAccess;
  724. } SYSTEM_HANDLE, *PSYSTEM_HANDLE;
  725. typedef struct _SYSTEM_HANDLE_INFORMATION
  726. {
  727. ULONG HandleCount;
  728. SYSTEM_HANDLE Handles[1];
  729. } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
  730. typedef enum _POOL_TYPE
  731. {
  732. NonPagedPool,
  733. PagedPool,
  734. NonPagedPoolMustSucceed,
  735. DontUseThisType,
  736. NonPagedPoolCacheAligned,
  737. PagedPoolCacheAligned,
  738. NonPagedPoolCacheAlignedMustS
  739. } POOL_TYPE, *PPOOL_TYPE;
  740. typedef struct _OBJECT_BASIC_INFORMATION
  741. {
  742. ULONG Attributes;
  743. ACCESS_MASK GrantedAccess;
  744. ULONG HandleCount;
  745. ULONG PointerCount;
  746. ULONG PagedPoolCharge;
  747. ULONG NonPagedPoolCharge;
  748. ULONG Reserved[3];
  749. ULONG NameInfoSize;
  750. ULONG TypeInfoSize;
  751. ULONG SecurityDescriptorSize;
  752. LARGE_INTEGER CreationTime;
  753. } OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
  754. typedef struct _OBJECT_TYPE_INFORMATION
  755. {
  756. UNICODE_STRING Name;
  757. ULONG TotalNumberOfObjects;
  758. ULONG TotalNumberOfHandles;
  759. ULONG TotalPagedPoolUsage;
  760. ULONG TotalNonPagedPoolUsage;
  761. ULONG TotalNamePoolUsage;
  762. ULONG TotalHandleTableUsage;
  763. ULONG HighWaterNumberOfObjects;
  764. ULONG HighWaterNumberOfHandles;
  765. ULONG HighWaterPagedPoolUsage;
  766. ULONG HighWaterNonPagedPoolUsage;
  767. ULONG HighWaterNamePoolUsage;
  768. ULONG HighWaterHandleTableUsage;
  769. ULONG InvalidAttributes;
  770. GENERIC_MAPPING GenericMapping;
  771. ULONG ValidAccess;
  772. BOOLEAN SecurityRequired;
  773. BOOLEAN MaintainHandleCount;
  774. USHORT MaintainTypeList;
  775. POOL_TYPE PoolType;
  776. ULONG PagedPoolUsage;
  777. ULONG NonPagedPoolUsage;
  778. } OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
  779. static Byte HANDLE_TYPE_PROCESS;
  780. static NTSTATUS (NTAPI *NtQuerySystemInformation)(ULONG SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
  781. static NTSTATUS (NTAPI *NtDuplicateObject )(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG Attributes, ULONG Options);
  782. static NTSTATUS (NTAPI *NtQueryObject )(HANDLE ObjectHandle, ULONG ObjectInformationClass, PVOID ObjectInformation, ULONG ObjectInformationLength, PULONG ReturnLength);
  783. static Int Compare(C SYSTEM_HANDLE &a, C SYSTEM_HANDLE &b) {return Compare((UInt)a.ProcessId, (UInt)b.ProcessId);}
  784. #endif
  785. Bool GetProcessesAccessingThisProcess(MemPtr<ProcessAccess> proc, Bool write_only, Mems<Byte> *temp)
  786. {
  787. proc.clear();
  788. #if WINDOWS_OLD
  789. if(!HANDLE_TYPE_PROCESS)
  790. {
  791. if(HMODULE ntdll=GetModuleHandle(L"ntdll.dll"))
  792. if(NtQueryObject =(decltype(NtQueryObject ))GetProcAddress(ntdll, "NtQueryObject"))
  793. if(NtDuplicateObject =(decltype(NtDuplicateObject ))GetProcAddress(ntdll, "NtDuplicateObject"))
  794. if(NtQuerySystemInformation=(decltype(NtQuerySystemInformation))GetProcAddress(ntdll, "NtQuerySystemInformation")) // set this as last so by checking 'NtQuerySystemInformation' below, we know that all are loaded
  795. {}
  796. // set type after handles were obtained
  797. VecI4 ver=OSVerNumber(); // WinXP is 5.1, Vista is 6.0, Win7 is 6.1, Win8 is 6.2, Win8.1 is 6.3, Win10 is 10
  798. if(Compare(ver, VecI4(6, 1, 0, 0))>=0)HANDLE_TYPE_PROCESS=7;else // Win7/8/10 - https://cybercoding.wordpress.com/2011/08/20/delphi-process-detection-handle-table-enumeration/ "In Windows 7 Sp1 ObjectTypeNumber = 7 is Process!"
  799. if(Compare(ver, VecI4(6, 0, 0, 0))>=0)HANDLE_TYPE_PROCESS=6;else // Vista
  800. HANDLE_TYPE_PROCESS=5; // WinXP
  801. // HANDLE_TYPE_FILE - Windows XP 28, Windows Vista 28, Windows 7 28, Windows 8 31, Windows 8.1 30 - https://www.codeproject.com/Tips/992827/Section-Handles-Enumeration-Extending-File-Unlocki
  802. }
  803. if(NtQuerySystemInformation)
  804. {
  805. Mems<Byte> temp_local; if(!temp)temp=&temp_local;
  806. again:
  807. ULONG size=0; NTSTATUS status=NtQuerySystemInformation(SystemHandleInformation, temp->data(), temp->elms(), &size);
  808. if(status==STATUS_INFO_LENGTH_MISMATCH)
  809. {
  810. MAX(size, temp->elms()); // maximize with 'temp' in case 'size' is left at zero
  811. temp->setNum(Max(65536, UInt(size+size/4))); // max with 65536 in case 'size' is 0, add extra size/4 in case there will be needed more memory in the next call than what was now
  812. goto again;
  813. }
  814. if(NT_SUCCESS(status))
  815. {
  816. UInt pid=0;
  817. HANDLE proc_handle=null, cur_proc=GetCurrentProcess(); // 'cur_proc' doesn't need to be closed
  818. SYSTEM_HANDLE_INFORMATION &shi=*(SYSTEM_HANDLE_INFORMATION*)temp->data();
  819. SYSTEM_HANDLE *handles=shi.Handles; Int handle_count=Min((UInt)shi.HandleCount, INT_MAX);
  820. //Sort(handles, handle_count, Compare); //compare by PID so we can obtain process handle one by one, no need to do this because handle array is groupped by PID's (handles with for the same process are located together)
  821. const UInt read_access=PROCESS_VM_READ , // |GENERIC_READ |READ_CONTROL,
  822. write_access=PROCESS_VM_WRITE |GENERIC_WRITE|WRITE_DAC|WRITE_OWNER,
  823. access=(write_only ? write_access : read_access|write_access);
  824. FREP(handle_count)
  825. {
  826. C SYSTEM_HANDLE &handle=handles[i];
  827. if(handle.ObjectTypeNumber==HANDLE_TYPE_PROCESS) // check only "Process" handles
  828. if(handle.GrantedAccess&access) // only that have desired access
  829. if(handle.ProcessId!=App.processID()) // skip self
  830. {
  831. if(pid!=handle.ProcessId)
  832. {
  833. pid=handle.ProcessId;
  834. CloseHandle(proc_handle);
  835. proc_handle=OpenProcess(PROCESS_DUP_HANDLE, false, pid);
  836. }
  837. if(proc_handle)
  838. {
  839. HANDLE dup_handle=null;
  840. NtDuplicateObject(proc_handle, HANDLE(handle.Handle), cur_proc, &dup_handle, PROCESS_QUERY_LIMITED_INFORMATION, 0, 0); // don't use DUPLICATE_SAME_ATTRIBUTES because exception can occur when trying to release handles that were "protected from close via NtSetInformationObject", don't use DUPLICATE_SAME_ACCESS because if handle would have write permission then probably we would have to, but we don't want to get write permission, but only check the PID, first try accessing using PROCESS_QUERY_LIMITED_INFORMATION, it's not supported on WindowsXP, but if we're using OS in which it's supported, then it can open more processes than method below
  841. #if SUPPORT_WINDOWS_XP
  842. if(!dup_handle)NtDuplicateObject(proc_handle, HANDLE(handle.Handle), cur_proc, &dup_handle, PROCESS_QUERY_INFORMATION , 0, 0); // don't use DUPLICATE_SAME_ATTRIBUTES because exception can occur when trying to release handles that were "protected from close via NtSetInformationObject", don't use DUPLICATE_SAME_ACCESS because if handle would have write permission then probably we would have to, but we don't want to get write permission, but only check the PID, if failed, then try using WindowsXP way, 'PROCESS_VM_READ' is not needed here
  843. #endif
  844. if( dup_handle)
  845. {
  846. if(GetProcessId(dup_handle)==App.processID()) // if accessing our process
  847. {
  848. ProcessAccess &p=proc.New();
  849. p.proc_id=pid;
  850. p.write=FlagTest(handle.GrantedAccess, write_access);
  851. }
  852. #if 0
  853. // !! don't check type/name for HANDLE_TYPE_FILE with handle.GrantedAccess==0X0012019F or it may freeze - https://forum.sysinternals.com/topic18892.html !!
  854. {
  855. union
  856. {
  857. OBJECT_TYPE_INFORMATION type;
  858. OBJECT_BASIC_INFORMATION basic;
  859. UNICODE_STRING name;
  860. Byte temp[SIZE(OBJECT_TYPE_INFORMATION)+MAX_LONG_PATH*SIZE(Char)];
  861. }u;
  862. if(NT_SUCCESS(NtQueryObject(dup_handle, ObjectTypeInformation, &u.type, SIZE(u), null)))
  863. {
  864. // some known types: "Key", "Event", "Semaphore", "Timer", "WaitCompletionPacket", "IoCompletion", "ALPC Port", "IRTimer", "TpWorkerFactory", "DxgkSharedSyncObject", "WindowStation", "Process"
  865. if(Equal(u.type.Name.Buffer, "Thread", true))
  866. {
  867. #undef GetThreadId
  868. Handles.add(S+ProcName(pid)+", Thread:"+(UInt)GetThreadId(dup_handle));
  869. #define GetThreadId _GetThreadId
  870. }
  871. }
  872. if(NT_SUCCESS(NtQueryObject(dup_handle, ObjectNameInformation, &u.name, SIZE(u), null)))
  873. {
  874. int z=0;
  875. }
  876. if(NT_SUCCESS(NtQueryObject(dup_handle, ObjectBasicInformation, &u.basic, SIZE(u.basic), null)))
  877. {
  878. int z=0;
  879. }
  880. }
  881. #endif
  882. CloseHandle(dup_handle);
  883. }
  884. }
  885. }
  886. }
  887. CloseHandle(proc_handle);
  888. return true;
  889. }
  890. }
  891. #endif
  892. return false;
  893. }
  894. /******************************************************************************/
  895. }
  896. /******************************************************************************/