123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- #include "process.h"
- #include "../../app/native/async.h"
- #ifndef EMSCRIPTEN
- #include <thread>
- #include <atomic>
- #include <mutex>
- #include <condition_variable>
- struct semaphore{
- int count=0;
- std::mutex mutex;
- std::condition_variable cond_var;
-
- void wait(){
- std::unique_lock<std::mutex> lock( mutex );
- while( !count ) cond_var.wait( lock );
- --count;
- }
-
- void signal(){
- std::unique_lock<std::mutex> lock( mutex );
- ++count;
- cond_var.notify_one();
- }
- };
- #if _WIN32
- #include <windows.h>
- #include <tlhelp32.h>
- extern "C" WINBASEAPI WINBOOL WINAPI CancelIoEx( HANDLE hFile,LPOVERLAPPED lpOverlapped );
- #else
- #include <sys/ioctl.h>
- #include <unistd.h>
- #include <sys/wait.h>
- #include <signal.h>
- #endif
- namespace{
- #if _WIN32
- void terminateChildren( DWORD procid,HANDLE snapshot,int exitCode ){
-
- PROCESSENTRY32 procinfo;
-
- procinfo.dwSize=sizeof( procinfo );
-
- int gotinfo=Process32First( snapshot,&procinfo );
-
- while( gotinfo ){
-
- if( procinfo.th32ParentProcessID==procid ){
-
- // printf("process=%i parent=%i module=%x path=%s\n",procinfo.th32ProcessID,procinfo.th32ParentProcessID,procinfo.th32ModuleID,procinfo.szExeFile);
- terminateChildren( procinfo.th32ProcessID,snapshot,exitCode );
-
- HANDLE child=OpenProcess( PROCESS_ALL_ACCESS,0,procinfo.th32ProcessID );
-
- if( child ){
- int res=TerminateProcess( child,exitCode );
- CloseHandle( child );
- }
- }
-
- gotinfo=Process32Next( snapshot,&procinfo );
- }
- }
-
- int TerminateProcessGroup( HANDLE prochandle,int exitCode ){
- HANDLE snapshot;
-
- int procid=GetProcessId( prochandle );
-
- snapshot=CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS,0 );
-
- if( snapshot!=INVALID_HANDLE_VALUE ){
-
- terminateChildren( GetProcessId( prochandle ),snapshot,exitCode );
- CloseHandle( snapshot );
- }
-
- int res=TerminateProcess( prochandle,exitCode );
- return res;
- }
-
- #endif
- #ifndef _WIN32
- char **makeargv( const char *cmd ){
- int n,c;
- char *p;
- static char *args,**argv;
-
- if( args ) free( args );
- if( argv ) free( argv );
- args=(char*)malloc( strlen(cmd)+1 );
- strcpy( args,cmd );
-
- n=0;
- p=args;
- while( (c=*p++) ){
- if( c==' ' ){
- continue;
- }else if( c=='\"' ){
- while( *p && *p!='\"' ) ++p;
- }else{
- while( *p && *p!=' ' ) ++p;
- }
- if( *p ) ++p;
- ++n;
- }
- argv=(char**)malloc( (n+1)*sizeof(char*) );
- n=0;
- p=args;
- while( (c=*p++) ){
- if( c==' ' ){
- continue;
- }else if( c=='\"' ){
- argv[n]=p;
- while( *p && *p!='\"' ) ++p;
- }else{
- argv[n]=p-1;
- while( *p && *p!=' ' ) ++p;
- }
- if( *p ) *p++=0;
- ++n;
- }
- argv[n]=0;
- return argv;
- }
-
- #endif
- }
- struct bbProcess::Rep{
- std::atomic_int refs;
-
- semaphore stdoutSema;
- char stdoutBuf[4096];
- char *stdoutGet;
- int stdoutAvail=0;
- bool terminated=false;
- int exit;
- #if _WIN32
- HANDLE proc;
- HANDLE in;
- HANDLE out;
- HANDLE err;
- HANDLE breakEvent;
-
- 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 ){
- }
-
- void close(){
- CloseHandle( in );
- CloseHandle( out );
- CloseHandle( err );
- }
- #else
- int proc;
- int in;
- int out;
- int err;
- Rep( int proc,int in,int out,int err ):proc( proc ),in( in ),out( out ),err( err ),exit( -1 ),refs( 1 ){
- }
-
- void close(){
- ::close( in );
- ::close( out );
- ::close( err );
- }
- #endif
-
- void retain(){
- ++refs;
- }
-
- void release(){
- if( --refs ) return;
-
- close();
-
- delete this;
- }
- };
- bbProcess::bbProcess():_rep( nullptr ){
- }
- bbProcess::~bbProcess(){
- if( _rep ) _rep->release();
- }
- bbBool bbProcess::start( bbString cmd ){
- if( _rep ) return false;
-
- #if _WIN32
- HANDLE in[2],out[2],err[2];
- SECURITY_ATTRIBUTES sa={sizeof(sa),0,1};
- CreatePipe( &in[0],&in[1],&sa,0 );
- CreatePipe( &out[0],&out[1],&sa,0 );
- CreatePipe( &err[0],&err[1],&sa,0 );
- HANDLE breakEvent=CreateEvent( &sa,0,0,"MX2_BREAK_EVENT" );
- STARTUPINFOA si={sizeof(si)};
- si.dwFlags=STARTF_USESTDHANDLES;
- si.hStdInput=in[0];
- si.hStdOutput=out[1];
- si.hStdError=err[1];
- PROCESS_INFORMATION pi={0};
-
- DWORD flags=CREATE_NEW_PROCESS_GROUP|CREATE_NO_WINDOW;
-
- int res=CreateProcessA( 0,(LPSTR)cmd.c_str(),0,0,TRUE,flags,0,0,&si,&pi );
- CloseHandle( in[0] );
- CloseHandle( out[1] );
- CloseHandle( err[1] );
- if( !res ){
- CloseHandle( in[1] );
- CloseHandle( out[0] );
- CloseHandle( err[0] );
- return false;
- }
- CloseHandle( pi.hThread );
-
- Rep *rep=new Rep( pi.hProcess,in[1],out[0],err[0],breakEvent );
-
- #else
-
- int in[2],out[2],err[2];
- pipe( in );
- pipe( out );
- pipe( err );
- char **argv=makeargv( bbCString( cmd ) );
-
- bool failed=false;
- int proc=vfork();
- if( !proc ){
- #if __linux
- setsid();
- #else
- setpgid(0,0);
- #endif
- dup2( in[0],0 );
- dup2( out[1],1 );
- dup2( err[1],2 );
- execvp( argv[0],argv );
-
- failed=true;
- _exit( 127 );
- }
-
- if( failed ) proc=-1;
- close( in[0] );
- close( out[1] );
- close( err[1] );
- if( proc==-1 ){
- close( in[1] );
- close( out[0] );
- close( err[0] );
- return false;
- }
-
- Rep *rep=new Rep( proc,in[1],out[0],err[0] );
-
- #endif
- //Create finished thread
- rep->retain();
- int callback=bbAddAsyncCallback( finished );
-
- std::thread( [=](){
-
- #if _WIN32
-
- WaitForSingleObject( rep->proc,INFINITE );
-
- GetExitCodeProcess( rep->proc,(DWORD*)&rep->exit );
-
- CloseHandle( rep->breakEvent );
-
- CloseHandle( rep->proc );
-
- #else
-
- int status;
- waitpid( rep->proc,&status,0 );
-
- if( WIFEXITED( status ) ){
- rep->exit=WEXITSTATUS( status );
- }else{
- rep->exit=-1;
- }
-
- #endif
-
- bbInvokeAsyncCallback( callback,true );
-
- rep->release();
- } ).detach();
-
-
- //Create stdoutReady thread
- rep->retain();
-
- int callback2=bbAddAsyncCallback( stdoutReady );
-
- std::thread( [=](){
-
- for(;;){
-
- #if _WIN32
- DWORD n=0;
- if( !ReadFile( rep->out,rep->stdoutBuf,4096,&n,0 ) ) break;
- if( n<=0 ) break;
- #else
- int n=read( rep->out,rep->stdoutBuf,4096 );
- if( n<=0 ) break;
- #endif
- rep->stdoutGet=rep->stdoutBuf;
-
- rep->stdoutAvail=n;
- bbInvokeAsyncCallback( callback2,false );
-
- rep->stdoutSema.wait();
-
- if( rep->stdoutAvail ) break;
- }
-
- rep->stdoutAvail=0;
-
- bbInvokeAsyncCallback( callback2,true );
-
- rep->release();
- } ).detach();
-
- _rep=rep;
-
- return true;
- }
- int bbProcess::exitCode(){
- if( !_rep ) return -1;
- return _rep->exit;
- }
- bbInt bbProcess::stdoutAvail(){
- if( !_rep ) return 0;
- return _rep->stdoutAvail;
- }
- bbString bbProcess::readStdout(){
- if( !_rep || !_rep->stdoutAvail ) return "";
- bbString str=bbString::fromCString( _rep->stdoutGet,_rep->stdoutAvail );
-
- _rep->stdoutAvail=0;
-
- _rep->stdoutSema.signal();
-
- return str;
- }
- bbInt bbProcess::readStdout( void *buf,int count ){
- if( !_rep || count<=0 || !_rep->stdoutAvail ) return 0;
- if( count>_rep->stdoutAvail ) count=_rep->stdoutAvail;
-
- memcpy( buf,_rep->stdoutGet,count );
-
- _rep->stdoutGet+=count;
- _rep->stdoutAvail-=count;
-
- if( !_rep->stdoutAvail ) _rep->stdoutSema.signal();
-
- return count;
- }
- void bbProcess::writeStdin( bbString str ){
- if( !_rep ) return;
- #if _WIN32
- WriteFile( _rep->in,str.c_str(),str.length(),0,0 );
- #else
- write( _rep->in,str.c_str(),str.length() );
- #endif
- }
- void bbProcess::sendBreak(){
- if( !_rep ) return;
-
- #if _WIN32
- SetEvent( _rep->breakEvent );
- #else
- killpg( _rep->proc,SIGTSTP );
- #endif
- }
- void bbProcess::terminate(){
- if( !_rep ) return;
- #if _WIN32
- TerminateProcessGroup( _rep->proc,-1 );
-
- CancelIoEx( _rep->out,0 );
- #else
- killpg( _rep->proc,SIGTERM );
- #endif
- }
- #else
- //***** Dummy emscripten version *****
- struct bbProcess::Rep{
- };
- void bbProcess::discard(){
- }
- bbBool bbProcess::start( bbString cmd ){
- return false;
- }
-
- bbInt bbProcess::exitCode(){
- return -1;
- }
-
- bbInt bbProcess::stdoutAvail(){
- return 0;
- }
-
- bbString bbProcess::readStdout(){
- return "";
- }
- bbInt bbProcess::readStdout( void *buf,bbInt count ){
- return 0;
- }
- void bbProcess::writeStdin( bbString str ){
- }
- void bbProcess::sendBreak(){
- }
- void bbProcess::terminate(){
- }
- #endif
|