2022-04-05 10:11:19 +00:00
/***
Copyright ( C ) 2021 J Reece Wilson ( a / k / a " Reece " ) . All rights reserved .
2022-12-16 00:41:01 +00:00
File : AuProcess . Unix . cpp
2022-04-05 10:11:19 +00:00
File : Process . Linux . cpp
Date : 2021 - 6 - 12
Author : Reece
* * */
# include <RuntimeInternal.hpp>
2022-12-16 00:41:01 +00:00
# include "AuProcesses.hpp"
# include "AuProcess.Unix.hpp"
2022-04-05 10:11:19 +00:00
# include <unistd.h>
# include <fcntl.h>
# include <sys/syscall.h>
# include <sys/types.h>
# include <sys/wait.h>
2022-04-05 10:18:35 +00:00
2022-12-16 00:41:01 +00:00
# include <Source/Threading/Primitives/AuSemaphore.Unix.hpp>
2022-08-10 15:04:41 +00:00
2023-08-30 00:28:05 +00:00
# if defined(AURORA_COMPILER_CLANG)
// warning: enumeration values 'kEnumCount' and 'kEnumInvalid' not handled in switch [-Wswitch
# pragma clang diagnostic ignored "-Wswitch"
// Yea, I don't give a shit.
# endif
2022-08-10 15:36:03 +00:00
# if defined(AURORA_IS_LINUX_DERIVED)
# include <sys/prctl.h>
# include <setjmp.h>
# if !defined(CLONE_CLEAR_SIGHAND)
# define CLONE_CLEAR_SIGHAND 0x100000000ULL
# endif
2022-08-10 15:04:41 +00:00
# endif
2022-04-05 10:11:19 +00:00
namespace Aurora : : Processes
{
2022-04-05 10:24:48 +00:00
static AuThreadPrimitives : : RWLockUnique_t gRWLock ;
2022-04-05 10:11:19 +00:00
static AuHashMap < pid_t , ProcessImpl * > gPidLookupMap ;
2022-06-11 23:52:46 +00:00
struct ProcessAliveLoopSource : AuLoop : : LSEvent
2022-05-04 19:34:46 +00:00
{
ProcessAliveLoopSource ( ) ;
virtual AuLoop : : ELoopSource GetType ( ) override ;
} ;
ProcessAliveLoopSource : : ProcessAliveLoopSource ( ) : LSEvent ( false , false , true )
{ }
AuLoop : : ELoopSource ProcessAliveLoopSource : : GetType ( )
{
return AuLoop : : ELoopSource : : eProcessDead ;
}
2023-08-21 16:48:42 +00:00
ProcessImpl : : ProcessImpl ( StartupParameters & & params ) : startup_ ( AuMove ( params ) )
2022-04-05 10:11:19 +00:00
{
AuIOFS : : NormalizePath ( this - > startup_ . process , this - > startup_ . process ) ;
2022-08-10 15:04:41 +00:00
if ( this - > startup_ . workingDirectory )
{
AuString a ;
AuIOFS : : NormalizePath ( a , this - > startup_ . workingDirectory . value ( ) ) ;
this - > startup_ . workingDirectory = a ;
}
2022-04-05 10:11:19 +00:00
this - > startup_ . args . insert ( startup_ . args . begin ( ) , startup_ . process ) ;
for ( const auto & arg : this - > startup_ . args )
{
this - > cargs_ . push_back ( arg . c_str ( ) ) ;
this - > debug_ + = arg + " " ;
}
this - > cargs_ . push_back ( nullptr ) ;
if ( this - > debug_ . size ( ) )
{
this - > debug_ . resize ( this - > debug_ . size ( ) - 1 ) ;
}
2022-04-13 14:43:38 +00:00
this - > type_ = this - > startup_ . type ;
2022-04-05 10:11:19 +00:00
}
ProcessImpl : : ~ ProcessImpl ( )
{
if ( this - > type_ = = ESpawnType : : eSpawnChildProcessWorker )
{
TryKill ( ) ;
Terminate ( ) ;
}
{
2022-04-05 10:24:48 +00:00
AU_LOCK_GUARD ( gRWLock - > AsWritable ( ) ) ;
2022-04-05 10:11:19 +00:00
2022-04-05 10:18:35 +00:00
if ( this - > alive_ )
2022-04-05 10:11:19 +00:00
{
2022-04-05 10:18:35 +00:00
if ( this - > type_ = = ESpawnType : : eSpawnThreadLeader )
{
: : kill ( this - > pidt_ , SIGCONT ) ;
}
2022-04-05 10:11:19 +00:00
}
2022-04-05 10:18:35 +00:00
AuTryRemove ( gPidLookupMap , this - > pidt_ ) ;
2022-04-05 10:11:19 +00:00
}
ShutdownPipes ( ) ;
}
AuUInt ProcessImpl : : GetProcessId ( )
{
return this - > pidt_ ;
}
void ProcessImpl : : ByeLol ( AuSInt code )
{
this - > exitCode_ = code ;
if ( this - > finished_ )
{
this - > finished_ - > Set ( ) ;
}
2022-05-04 19:34:46 +00:00
if ( this - > loopSource_ )
{
this - > loopSource_ - > Set ( ) ;
}
2022-04-05 10:11:19 +00:00
}
void ProcessImpl : : ShutdownPipes ( )
{
2022-06-23 12:25:20 +00:00
if ( ! this - > bDontRelOut_ )
{
if ( auto fd = AuExchange ( pipeStdOut_ [ 1 ] , { } ) )
{
: : close ( fd ) ;
}
}
if ( ! this - > bDontRelErr_ )
{
if ( auto fd = AuExchange ( pipeStdErr_ [ 1 ] , { } ) )
{
: : close ( fd ) ;
}
}
if ( ! this - > bDontRelIn_ )
{
if ( auto fd = AuExchange ( pipeStdIn_ [ 0 ] , { } ) )
{
: : close ( fd ) ;
}
}
2022-04-05 10:11:19 +00:00
}
bool ProcessImpl : : TryKill ( )
{
2022-04-05 10:24:48 +00:00
AU_LOCK_GUARD ( gRWLock - > AsReadable ( ) ) ;
2022-04-05 10:18:35 +00:00
2022-04-05 10:11:19 +00:00
if ( this - > alive_ )
{
2022-05-04 15:34:02 +00:00
if ( : : kill ( this - > pidt_ , SIGTERM ) = = 0 )
{
2023-08-11 15:51:42 +00:00
return this - > finished_ - > LockMS ( 500 ) ;
2022-05-04 15:34:02 +00:00
}
2022-04-05 10:11:19 +00:00
}
2022-04-05 10:18:35 +00:00
2022-04-05 10:11:19 +00:00
return true ;
}
bool ProcessImpl : : Terminate ( )
{
2022-04-05 10:24:48 +00:00
AU_LOCK_GUARD ( gRWLock - > AsReadable ( ) ) ;
2022-04-05 10:18:35 +00:00
2022-04-05 10:11:19 +00:00
if ( this - > alive_ )
{
return : : kill ( this - > pidt_ , SIGKILL ) = = 0 ;
}
2022-04-05 10:18:35 +00:00
2022-04-05 10:11:19 +00:00
return true ;
}
AuSPtr < Aurora : : Threading : : IWaitable > ProcessImpl : : AsWaitable ( )
{
return this - > finished_ ;
}
2022-06-12 13:53:35 +00:00
AuSPtr < AuLoop : : ILoopSource > ProcessImpl : : AsLoopSource ( )
2022-05-04 19:34:46 +00:00
{
return this - > loopSource_ ;
}
2022-04-05 10:11:19 +00:00
AuSInt ProcessImpl : : GetExitCode ( )
{
return this - > exitCode_ ;
}
bool ProcessImpl : : Read ( EStandardHandle stream , const AuMemoryViewStreamWrite & destination , bool nonblock )
{
auto handle = stream = = EStandardHandle : : eStdError ? this - > pipeStdErr_ [ 0 ] : this - > pipeStdOut_ [ 0 ] ;
if ( handle < 0 )
{
2022-04-13 14:43:38 +00:00
SysPushErrorUninitialized ( ) ;
2022-04-05 10:11:19 +00:00
return false ;
}
2022-05-04 15:34:02 +00:00
auto control = : : fcntl ( handle , F_GETFL ) ;
2022-04-05 10:11:19 +00:00
auto ref = control ;
if ( nonblock )
{
control | = O_NONBLOCK ;
}
else
{
control & = ~ O_NONBLOCK ;
}
if ( ref ! = control )
{
2022-05-04 15:34:02 +00:00
: : fcntl ( handle , F_SETFL , control ) ;
2022-04-05 10:11:19 +00:00
}
2022-04-13 14:43:38 +00:00
int tmp ;
do
{
2022-05-04 15:34:02 +00:00
tmp = : : read ( handle , destination . ptr , destination . length ) ;
2022-04-13 14:43:38 +00:00
} while ( ( tmp = = - 1 & & errno = = EINTR ) ) ;
if ( tmp < = 0 )
2022-04-05 10:11:19 +00:00
{
2022-04-15 14:46:07 +00:00
if ( tmp = = 0 )
2022-04-13 14:43:38 +00:00
{
2022-04-15 14:46:07 +00:00
return nonblock ;
2022-04-13 14:43:38 +00:00
}
2022-04-15 14:46:07 +00:00
SysPushErrorMem ( ) ;
2022-04-05 10:11:19 +00:00
return false ;
}
destination . outVariable = tmp ;
return true ;
}
bool ProcessImpl : : Write ( const AuMemoryViewStreamRead & source )
{
2022-04-05 10:18:35 +00:00
auto handle = this - > pipeStdIn_ [ 1 ] ;
2022-04-05 10:11:19 +00:00
if ( ! handle )
{
return false ;
}
2022-05-04 19:34:46 +00:00
auto control = : : fcntl ( handle , F_GETFL ) ;
auto ref = control ;
if ( /*nonblock*/ true )
{
control | = O_NONBLOCK ;
}
else
{
control & = ~ O_NONBLOCK ;
}
if ( ref ! = control )
{
: : fcntl ( handle , F_SETFL , control ) ;
}
return : : write ( handle , source . ptr , source . length ) = = source . length ;
2022-04-05 10:11:19 +00:00
}
2022-06-23 12:25:20 +00:00
static bool InitProcessStdHandles ( EStreamForward fwd , int * fds )
2022-04-05 10:11:19 +00:00
{
2022-06-23 12:25:20 +00:00
switch ( fwd )
2022-04-05 10:11:19 +00:00
{
2022-06-23 12:25:20 +00:00
case EStreamForward : : eCurrentProcess :
// Intentionally NO-OP
break ;
case EStreamForward : : eAsyncPipe :
if ( : : pipe ( fds ) )
2022-04-05 10:11:19 +00:00
{
2022-04-13 14:43:38 +00:00
SysPushErrorMem ( ) ;
2022-04-05 10:11:19 +00:00
return false ;
}
2022-06-23 12:25:20 +00:00
break ;
case EStreamForward : : eNull :
fds [ 0 ] = : : open ( " /dev/null " , O_RDWR ) ;
fds [ 1 ] = : : open ( " /dev/null " , O_RDWR ) ;
break ;
2023-08-11 15:51:42 +00:00
case EStreamForward : : eNewConsoleWindow :
SysPushErrorGeneric ( " AuProcesses is not the right place for PTY support. At least not in this form (this level of abstraction only cares for pipes). " ) ;
break ;
2022-04-05 10:11:19 +00:00
}
2022-06-23 12:25:20 +00:00
return true ;
}
2022-04-05 10:11:19 +00:00
2022-06-23 12:25:20 +00:00
bool ProcessImpl : : Init ( )
{
InitProcessStdHandles ( this - > startup_ . fwdOut , this - > pipeStdOut_ ) ;
InitProcessStdHandles ( this - > startup_ . fwdErr , this - > pipeStdErr_ ) ;
InitProcessStdHandles ( this - > startup_ . fwdIn , this - > pipeStdIn_ ) ;
2022-04-05 10:11:19 +00:00
2022-05-04 19:34:46 +00:00
this - > loopSource_ = AuMakeShared < ProcessAliveLoopSource > ( ) ;
if ( ! this - > loopSource_ )
{
return false ;
}
2022-04-05 10:11:19 +00:00
this - > finished_ = AuThreadPrimitives : : EventShared ( false , false , true ) ;
2022-05-04 19:34:46 +00:00
if ( ! this - > finished_ )
{
return false ;
}
2022-06-23 12:25:20 +00:00
if ( ( this - > startup_ . fwdIn = = EStreamForward : : eAsyncPipe ) | |
( this - > startup_ . fwdOut = = EStreamForward : : eAsyncPipe ) )
2022-05-04 19:34:46 +00:00
{
2023-08-11 15:51:42 +00:00
this - > fsHandle_ = AuIO : : IOHandleShared ( ) ;
2022-05-04 19:34:46 +00:00
if ( ! this - > fsHandle_ )
{
return false ;
}
this - > fsStream_ = AuMakeShared < ProcessPipeFileStream > ( ) ;
if ( ! this - > fsStream_ )
{
return false ;
}
2023-08-11 15:51:42 +00:00
this - > fsHandle_ - > InitFromPairMove ( this - > pipeStdOut_ [ 0 ] , this - > pipeStdIn_ [ 1 ] ) ;
2022-05-04 19:34:46 +00:00
this - > fsStream_ - > Init ( this - > fsHandle_ ) ;
}
2022-06-23 12:25:20 +00:00
if ( this - > startup_ . fwdErr = = EStreamForward : : eAsyncPipe )
2022-05-04 19:34:46 +00:00
{
2023-08-11 15:51:42 +00:00
this - > fsErrorHandle_ = AuIO : : IOHandleShared ( ) ;
2022-05-04 19:34:46 +00:00
if ( ! this - > fsErrorHandle_ )
{
return false ;
}
this - > fsErrorStream_ = AuMakeShared < ProcessPipeFileStream > ( ) ;
if ( ! this - > fsErrorStream_ )
{
return false ;
}
2023-08-11 15:51:42 +00:00
this - > fsErrorHandle_ - > InitFromPairMove ( this - > pipeStdErr_ [ 0 ] , - 1 ) ;
2022-05-04 19:34:46 +00:00
this - > fsErrorStream_ - > Init ( this - > fsErrorHandle_ ) ;
}
return true ;
2022-04-05 10:11:19 +00:00
}
2022-05-12 08:04:32 +00:00
AuSPtr < AuIO : : IAsyncTransaction > ProcessImpl : : NewAsyncTransaction ( )
2022-05-04 15:34:02 +00:00
{
2022-05-13 00:43:54 +00:00
return this - > fsStream_ ? this - > fsStream_ - > NewTransaction ( ) : AuSPtr < AuIO : : IAsyncTransaction > { } ;
2022-05-04 15:34:02 +00:00
}
2022-05-12 08:04:32 +00:00
AuSPtr < AuIO : : IAsyncTransaction > ProcessImpl : : NewErrorStreamAsyncTransaction ( )
2022-05-04 15:34:02 +00:00
{
2022-05-13 00:43:54 +00:00
return this - > fsErrorStream_ ? this - > fsErrorStream_ - > NewTransaction ( ) : AuSPtr < AuIO : : IAsyncTransaction > { } ;
2022-05-04 15:34:02 +00:00
}
2022-08-10 15:04:41 +00:00
void ProcessImpl : : ForkMain ( )
{
if ( this - > startup_ . fwdIn ! = EStreamForward : : eCurrentProcess )
{
: : dup2 ( pipeStdIn_ [ 0 ] , STDIN_FILENO ) ;
: : close ( pipeStdIn_ [ 0 ] ) ;
: : close ( pipeStdIn_ [ 1 ] ) ;
}
if ( this - > startup_ . fwdErr ! = EStreamForward : : eCurrentProcess )
{
: : dup2 ( pipeStdErr_ [ 1 ] , STDERR_FILENO ) ;
: : close ( pipeStdIn_ [ 0 ] ) ;
: : close ( pipeStdIn_ [ 1 ] ) ;
}
if ( this - > startup_ . fwdOut ! = EStreamForward : : eCurrentProcess )
{
: : dup2 ( pipeStdOut_ [ 1 ] , STDOUT_FILENO ) ;
: : close ( pipeStdIn_ [ 0 ] ) ;
: : close ( pipeStdIn_ [ 1 ] ) ;
}
2022-08-10 15:36:03 +00:00
# if defined(AURORA_IS_LINUX_DERIVED)
if ( this - > type_ = = ESpawnType : : eSpawnChildProcessWorker )
2022-08-10 15:04:41 +00:00
{
2022-08-10 15:36:03 +00:00
: : prctl ( PR_SET_PDEATHSIG , SIGTERM ) ;
2022-08-10 15:04:41 +00:00
}
2022-08-10 15:36:03 +00:00
# endif
: : setsid ( ) ;
2022-08-10 15:04:41 +00:00
# if defined(AURORA_IS_XNU_DERIVED)
if ( this - > startup_ . workingDirectory )
{
: : pthread_chdir_np ( this - > startup_ . workingDirectory . value ( ) . c_str ( ) ) ;
}
# else
if ( this - > startup_ . workingDirectory )
{
: : chroot ( this - > startup_ . workingDirectory . value ( ) . c_str ( ) ) ;
}
# endif
: : execv ( this - > startup_ . process . c_str ( ) , ( char * const * ) this - > cargs_ . data ( ) ) ; // https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
SysPushErrorGen ( " execv didn't overwrite the process map. Launch: {} ({}) " , this - > startup_ . process , this - > debug_ ) ;
}
2022-04-05 10:11:19 +00:00
bool ProcessImpl : : Start ( )
{
this - > exitCode_ = 0x10110100 ;
if ( this - > type_ = = ESpawnType : : eSpawnOvermap )
{
2022-05-04 19:34:46 +00:00
: : execv ( this - > startup_ . process . c_str ( ) , ( char * const * ) this - > cargs_ . data ( ) ) ; // https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
2022-04-05 10:11:19 +00:00
SysPushErrorGen ( " execv didn't overwrite the process map, given {} ({}) " , this - > startup_ . process , this - > debug_ ) ;
return false ;
}
pid_t pid ;
{
2022-05-04 15:34:02 +00:00
pid = : : fork ( ) ;
2022-04-05 10:11:19 +00:00
if ( pid = = 0 )
{
2022-08-10 15:04:41 +00:00
this - > ForkMain ( ) ;
2022-04-13 14:43:38 +00:00
return false ;
}
else if ( pid > 0 )
{
if ( pipeStdOut_ [ 1 ] )
2022-04-05 10:11:19 +00:00
{
2022-04-13 14:43:38 +00:00
: : close ( AuExchange ( pipeStdOut_ [ 1 ] , 0 ) ) ;
}
if ( pipeStdErr_ [ 1 ] )
{
: : close ( AuExchange ( pipeStdErr_ [ 1 ] , 0 ) ) ;
2022-04-05 10:11:19 +00:00
}
2022-04-13 14:43:38 +00:00
if ( pipeStdIn_ [ 0 ] )
{
: : close ( AuExchange ( pipeStdIn_ [ 0 ] , 0 ) ) ;
}
this - > pidt_ = pid ;
2022-04-05 10:11:19 +00:00
this - > alive_ = true ;
2022-04-13 14:43:38 +00:00
{
2022-06-23 12:25:20 +00:00
AU_LOCK_GUARD ( gRWLock - > AsWritable ( ) ) ;
2022-04-13 14:43:38 +00:00
SysAssert ( AuTryInsert ( gPidLookupMap , pid , this ) ) ;
}
2022-04-05 10:11:19 +00:00
2022-04-13 14:43:38 +00:00
return true ;
2022-04-05 10:11:19 +00:00
}
else
{
2022-04-13 14:43:38 +00:00
return false ;
2022-04-05 10:11:19 +00:00
}
}
return true ;
}
2023-08-21 16:48:42 +00:00
AUKN_SYM IProcess * SpawnNew ( StartupParameters & & params )
2022-04-05 10:11:19 +00:00
{
2023-08-21 16:48:42 +00:00
auto ret = _new ProcessImpl ( AuMove ( params ) ) ;
2022-04-05 10:11:19 +00:00
if ( ! ret )
{
2022-04-13 14:43:38 +00:00
SysPushErrorMem ( ) ;
2022-04-05 10:11:19 +00:00
return { } ;
}
if ( ! ret - > Init ( ) )
{
2022-04-13 14:43:38 +00:00
SysPushErrorNested ( ) ;
2022-04-05 10:11:19 +00:00
delete ret ;
return { } ;
}
return ret ;
}
AUKN_SYM void SpawnRelease ( IProcess * process )
{
AuSafeDelete < ProcessImpl * > ( process ) ;
}
static void HandleChildTermiantion ( pid_t pid , int code )
{
2022-04-05 10:24:48 +00:00
AU_LOCK_GUARD ( gRWLock - > AsReadable ( ) ) ;
2022-04-05 10:11:19 +00:00
auto handler = gPidLookupMap . find ( pid ) ;
2022-04-13 14:43:38 +00:00
if ( handler = = gPidLookupMap . end ( ) )
{
return ;
}
2022-04-05 10:11:19 +00:00
{
auto process = * handler ;
process . second - > ByeLol ( code ) ;
}
}
static void SigChldHandler ( int )
{
int code ;
pid_t pid ;
while ( true )
{
pid = wait3 ( & code , WNOHANG , nullptr ) ;
if ( ( pid = = 0 ) | |
( pid = = - 1 ) )
{
break ;
}
HandleChildTermiantion ( pid , code ) ;
}
}
void InitUnix ( )
{
2022-04-05 10:24:48 +00:00
gRWLock = AuThreadPrimitives : : RWLockUnique ( ) ;
2022-04-06 01:24:38 +00:00
struct sigaction action =
{
. sa_handler = SigChldHandler ,
. sa_flags = SA_ONSTACK
} ;
2022-05-04 15:34:02 +00:00
: : sigemptyset ( & action . sa_mask ) ;
: : sigaction ( SIGCHLD , & action , nullptr ) ;
2022-04-05 10:11:19 +00:00
}
void DeinitUnix ( )
{
2022-04-06 01:24:38 +00:00
struct sigaction action =
{
. sa_handler = SIG_DFL ,
. sa_flags = SA_ONSTACK | SA_NOCLDWAIT
} ;
2022-05-04 15:34:02 +00:00
: : sigemptyset ( & action . sa_mask ) ;
: : sigaction ( SIGCHLD , & action , nullptr ) ;
2022-04-06 01:24:38 +00:00
2022-04-05 10:24:48 +00:00
gRWLock . reset ( ) ;
2022-04-05 10:11:19 +00:00
}
2022-04-05 10:24:48 +00:00
}