process.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. #include "process.h"
  2. #include "../../app/native/async.h"
  3. #ifndef EMSCRIPTEN
  4. #include <thread>
  5. #include <atomic>
  6. #include <mutex>
  7. #include <condition_variable>
  8. struct semaphore{
  9. int count=0;
  10. std::mutex mutex;
  11. std::condition_variable cond_var;
  12. void wait(){
  13. std::unique_lock<std::mutex> lock( mutex );
  14. while( !count ) cond_var.wait( lock );
  15. --count;
  16. }
  17. void signal(){
  18. std::unique_lock<std::mutex> lock( mutex );
  19. ++count;
  20. cond_var.notify_one();
  21. }
  22. };
  23. #if _WIN32
  24. #include <windows.h>
  25. #include <tlhelp32.h>
  26. extern "C" WINBASEAPI WINBOOL WINAPI CancelIoEx( HANDLE hFile,LPOVERLAPPED lpOverlapped );
  27. #else
  28. #include <sys/ioctl.h>
  29. #include <unistd.h>
  30. #include <sys/wait.h>
  31. #include <signal.h>
  32. #endif
  33. namespace{
  34. #if _WIN32
  35. void terminateChildren( DWORD procid,HANDLE snapshot,int exitCode ){
  36. PROCESSENTRY32 procinfo;
  37. procinfo.dwSize=sizeof( procinfo );
  38. int gotinfo=Process32First( snapshot,&procinfo );
  39. while( gotinfo ){
  40. if( procinfo.th32ParentProcessID==procid ){
  41. // printf("process=%i parent=%i module=%x path=%s\n",procinfo.th32ProcessID,procinfo.th32ParentProcessID,procinfo.th32ModuleID,procinfo.szExeFile);
  42. terminateChildren( procinfo.th32ProcessID,snapshot,exitCode );
  43. HANDLE child=OpenProcess( PROCESS_ALL_ACCESS,0,procinfo.th32ProcessID );
  44. if( child ){
  45. int res=TerminateProcess( child,exitCode );
  46. CloseHandle( child );
  47. }
  48. }
  49. gotinfo=Process32Next( snapshot,&procinfo );
  50. }
  51. }
  52. int TerminateProcessGroup( HANDLE prochandle,int exitCode ){
  53. HANDLE snapshot;
  54. int procid=GetProcessId( prochandle );
  55. snapshot=CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS,0 );
  56. if( snapshot!=INVALID_HANDLE_VALUE ){
  57. terminateChildren( GetProcessId( prochandle ),snapshot,exitCode );
  58. CloseHandle( snapshot );
  59. }
  60. int res=TerminateProcess( prochandle,exitCode );
  61. return res;
  62. }
  63. #endif
  64. #ifndef _WIN32
  65. char **makeargv( const char *cmd ){
  66. int n,c;
  67. char *p;
  68. static char *args,**argv;
  69. if( args ) free( args );
  70. if( argv ) free( argv );
  71. args=(char*)malloc( strlen(cmd)+1 );
  72. strcpy( args,cmd );
  73. n=0;
  74. p=args;
  75. while( (c=*p++) ){
  76. if( c==' ' ){
  77. continue;
  78. }else if( c=='\"' ){
  79. while( *p && *p!='\"' ) ++p;
  80. }else{
  81. while( *p && *p!=' ' ) ++p;
  82. }
  83. if( *p ) ++p;
  84. ++n;
  85. }
  86. argv=(char**)malloc( (n+1)*sizeof(char*) );
  87. n=0;
  88. p=args;
  89. while( (c=*p++) ){
  90. if( c==' ' ){
  91. continue;
  92. }else if( c=='\"' ){
  93. argv[n]=p;
  94. while( *p && *p!='\"' ) ++p;
  95. }else{
  96. argv[n]=p-1;
  97. while( *p && *p!=' ' ) ++p;
  98. }
  99. if( *p ) *p++=0;
  100. ++n;
  101. }
  102. argv[n]=0;
  103. return argv;
  104. }
  105. #endif
  106. }
  107. struct bbProcess::Rep{
  108. std::atomic_int refs;
  109. semaphore stdoutSema;
  110. char stdoutBuf[4096];
  111. char *stdoutGet;
  112. int stdoutAvail=0;
  113. bool terminated=false;
  114. int exit;
  115. #if _WIN32
  116. HANDLE proc;
  117. HANDLE in;
  118. HANDLE out;
  119. HANDLE err;
  120. HANDLE breakEvent;
  121. Rep( HANDLE proc,HANDLE in,HANDLE out,HANDLE err,HANDLE breakEvent ):proc( proc ),in( in ),out( out ),err( err ),breakEvent( breakEvent ),exit( -1 ),refs( 1 ){
  122. }
  123. void close(){
  124. CloseHandle( in );
  125. CloseHandle( out );
  126. CloseHandle( err );
  127. }
  128. #else
  129. int proc;
  130. int in;
  131. int out;
  132. int err;
  133. Rep( int proc,int in,int out,int err ):proc( proc ),in( in ),out( out ),err( err ),exit( -1 ),refs( 1 ){
  134. }
  135. void close(){
  136. ::close( in );
  137. ::close( out );
  138. ::close( err );
  139. }
  140. #endif
  141. void retain(){
  142. ++refs;
  143. }
  144. void release(){
  145. if( --refs ) return;
  146. close();
  147. delete this;
  148. }
  149. };
  150. bbProcess::bbProcess():_rep( nullptr ){
  151. }
  152. bbProcess::~bbProcess(){
  153. if( _rep ) _rep->release();
  154. }
  155. bbBool bbProcess::start( bbString cmd ){
  156. if( _rep ) return false;
  157. #if _WIN32
  158. HANDLE in[2],out[2],err[2];
  159. SECURITY_ATTRIBUTES sa={sizeof(sa),0,1};
  160. CreatePipe( &in[0],&in[1],&sa,0 );
  161. CreatePipe( &out[0],&out[1],&sa,0 );
  162. CreatePipe( &err[0],&err[1],&sa,0 );
  163. HANDLE breakEvent=CreateEvent( &sa,0,0,"MX2_BREAK_EVENT" );
  164. STARTUPINFOA si={sizeof(si)};
  165. si.dwFlags=STARTF_USESTDHANDLES;
  166. si.hStdInput=in[0];
  167. si.hStdOutput=out[1];
  168. si.hStdError=err[1];
  169. PROCESS_INFORMATION pi={0};
  170. DWORD flags=CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW;
  171. int res=CreateProcessA( 0,(LPSTR)cmd.c_str(),0,0,TRUE,flags,0,0,&si,&pi );
  172. CloseHandle( in[0] );
  173. CloseHandle( out[1] );
  174. CloseHandle( err[1] );
  175. if( !res ){
  176. CloseHandle( in[1] );
  177. CloseHandle( out[0] );
  178. CloseHandle( err[0] );
  179. return false;
  180. }
  181. CloseHandle( pi.hThread );
  182. Rep *rep=new Rep( pi.hProcess,in[1],out[0],err[0],breakEvent );
  183. #else
  184. int in[2],out[2],err[2];
  185. pipe( in );
  186. pipe( out );
  187. pipe( err );
  188. char **argv=makeargv( bbCString( cmd ) );
  189. bool failed=false;
  190. int proc=vfork();
  191. if( !proc ){
  192. #if __linux
  193. setsid();
  194. #else
  195. setpgid(0,0);
  196. #endif
  197. dup2( in[0],0 );
  198. dup2( out[1],1 );
  199. dup2( err[1],2 );
  200. execvp( argv[0],argv );
  201. failed=true;
  202. _exit( 127 );
  203. }
  204. if( failed ) proc=-1;
  205. close( in[0] );
  206. close( out[1] );
  207. close( err[1] );
  208. if( proc==-1 ){
  209. close( in[1] );
  210. close( out[0] );
  211. close( err[0] );
  212. return false;
  213. }
  214. Rep *rep=new Rep( proc,in[1],out[0],err[0] );
  215. #endif
  216. //Create finished thread
  217. rep->retain();
  218. int callback=bbAddAsyncCallback( finished );
  219. std::thread( [=](){
  220. #if _WIN32
  221. WaitForSingleObject( rep->proc,INFINITE );
  222. GetExitCodeProcess( rep->proc,(DWORD*)&rep->exit );
  223. CloseHandle( rep->breakEvent );
  224. CloseHandle( rep->proc );
  225. #else
  226. int status;
  227. waitpid( rep->proc,&status,0 );
  228. if( WIFEXITED( status ) ){
  229. rep->exit=WEXITSTATUS( status );
  230. }else{
  231. rep->exit=-1;
  232. }
  233. #endif
  234. bbInvokeAsyncCallback( callback,true );
  235. rep->release();
  236. } ).detach();
  237. //Create stdoutReady thread
  238. rep->retain();
  239. int callback2=bbAddAsyncCallback( stdoutReady );
  240. std::thread( [=](){
  241. for(;;){
  242. #if _WIN32
  243. DWORD n=0;
  244. if( !ReadFile( rep->out,rep->stdoutBuf,4096,&n,0 ) ) break;
  245. if( n<=0 ) break;
  246. #else
  247. int n=read( rep->out,rep->stdoutBuf,4096 );
  248. if( n<=0 ) break;
  249. #endif
  250. rep->stdoutGet=rep->stdoutBuf;
  251. rep->stdoutAvail=n;
  252. bbInvokeAsyncCallback( callback2,false );
  253. rep->stdoutSema.wait();
  254. if( rep->stdoutAvail ) break;
  255. }
  256. rep->stdoutAvail=0;
  257. bbInvokeAsyncCallback( callback2,true );
  258. rep->release();
  259. } ).detach();
  260. _rep=rep;
  261. return true;
  262. }
  263. int bbProcess::exitCode(){
  264. if( !_rep ) return -1;
  265. return _rep->exit;
  266. }
  267. bbInt bbProcess::stdoutAvail(){
  268. if( !_rep ) return 0;
  269. return _rep->stdoutAvail;
  270. }
  271. bbString bbProcess::readStdout(){
  272. if( !_rep || !_rep->stdoutAvail ) return "";
  273. bbString str=bbString::fromCString( _rep->stdoutGet,_rep->stdoutAvail );
  274. _rep->stdoutAvail=0;
  275. _rep->stdoutSema.signal();
  276. return str;
  277. }
  278. bbInt bbProcess::readStdout( void *buf,int count ){
  279. if( !_rep || count<=0 || !_rep->stdoutAvail ) return 0;
  280. if( count>_rep->stdoutAvail ) count=_rep->stdoutAvail;
  281. memcpy( buf,_rep->stdoutGet,count );
  282. _rep->stdoutGet+=count;
  283. _rep->stdoutAvail-=count;
  284. if( !_rep->stdoutAvail ) _rep->stdoutSema.signal();
  285. return count;
  286. }
  287. void bbProcess::writeStdin( bbString str ){
  288. if( !_rep ) return;
  289. #if _WIN32
  290. WriteFile( _rep->in,str.c_str(),str.length(),0,0 );
  291. #else
  292. write( _rep->in,str.c_str(),str.length() );
  293. #endif
  294. }
  295. void bbProcess::sendBreak(){
  296. if( !_rep ) return;
  297. #if _WIN32
  298. SetEvent( _rep->breakEvent );
  299. #else
  300. killpg( _rep->proc,SIGTSTP );
  301. #endif
  302. }
  303. void bbProcess::terminate(){
  304. if( !_rep ) return;
  305. #if _WIN32
  306. TerminateProcessGroup( _rep->proc,-1 );
  307. CancelIoEx( _rep->out,0 );
  308. #else
  309. killpg( _rep->proc,SIGTERM );
  310. #endif
  311. }
  312. #else
  313. //***** Dummy emscripten version *****
  314. struct bbProcess::Rep{
  315. };
  316. void bbProcess::discard(){
  317. }
  318. bbBool bbProcess::start( bbString cmd ){
  319. return false;
  320. }
  321. bbInt bbProcess::exitCode(){
  322. return -1;
  323. }
  324. bbInt bbProcess::stdoutAvail(){
  325. return 0;
  326. }
  327. bbString bbProcess::readStdout(){
  328. return "";
  329. }
  330. bbInt bbProcess::readStdout( void *buf,bbInt count ){
  331. return 0;
  332. }
  333. void bbProcess::writeStdin( bbString str ){
  334. }
  335. void bbProcess::sendBreak(){
  336. }
  337. void bbProcess::terminate(){
  338. }
  339. #endif