00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "CeylanProcess.h"
00028
00029 #include "CeylanLogPlug.h"
00030 #include "CeylanSystem.h"
00031 #include "CeylanStandardFile.h"
00032 #include "CeylanDirectory.h"
00033 #include "CeylanInputStream.h"
00034 #include "CeylanOutputStream.h"
00035 #include "CeylanOperators.h"
00036 #include "CeylanTypes.h"
00037
00038
00039 #ifdef CEYLAN_USES_CONFIG_H
00040 #include "CeylanConfig.h"
00041 #endif // CEYLAN_USES_CONFIG_H
00042
00043
00044
00045 extern "C"
00046 {
00047
00048 #ifdef CEYLAN_USES_UNISTD_H
00049 #include <unistd.h>
00050 #endif // CEYLAN_USES_UNISTD_H
00051
00052 #ifdef CEYLAN_USES_SYS_WAIT_H
00053 #include <sys/wait.h>
00054 #endif // CEYLAN_USES_SYS_WAIT_H
00055
00056 #ifdef CEYLAN_USES_PWD_H
00057 #include <pwd.h>
00058 #endif // CEYLAN_USES_PWD_H
00059
00060 #ifdef CEYLAN_USES_SYS_TIMES_H
00061 #include <sys/times.h>
00062 #endif // CEYLAN_USES_SYS_TIMES_H
00063
00064 #ifdef CEYLAN_USES_SYS_TYPES_H
00065 #include <sys/types.h>
00066 #endif // CEYLAN_USES_SYS_TYPES_H
00067
00068 #ifdef CEYLAN_USES_SIGNAL_H
00069 #include <signal.h>
00070 #endif // CEYLAN_USES_SIGNAL_H
00071
00072 #ifdef CEYLAN_USES_PROCESS_H
00073 #include <process.h>
00074 #endif // CEYLAN_USES_PROCESS_H
00075
00076 }
00077
00078
00079 #include <csignal>
00080 #include <cerrno>
00081
00082
00083
00084 using std::string ;
00085 using std::list ;
00086
00087 using namespace Ceylan ;
00088 using namespace Ceylan::System ;
00089 using namespace Ceylan::Log ;
00090
00091
00092 string Process::_Owner ;
00093 string Process::_Path ;
00094 string Process::_Executable ;
00095
00096
00097 list<string> Process::_ArgumentList ;
00098
00099 bool Process::_Saved = false ;
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113 ProcessException::ProcessException( const string message ) :
00114 RunnableException( message )
00115 {
00116
00117 }
00118
00119
00120
00121 ProcessException::~ProcessException() throw()
00122 {
00123
00124 }
00125
00126
00127
00128
00129 Process::Process() :
00130 Runnable(),
00131 _id ( 0 ),
00132 _error ( 0 )
00133 {
00134
00135 }
00136
00137
00138
00139 Process::Process( const string & name ) :
00140 Runnable( name ),
00141 _id ( 0 ),
00142 _error ( 0 )
00143 {
00144
00145 }
00146
00147
00148
00149 Process::~Process() throw()
00150 {
00151
00152 }
00153
00154
00155
00156 void Process::run()
00157 {
00158
00159 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00160
00161
00162 pid_t res = ::fork() ;
00163
00164 if ( res == -1 )
00165 {
00166 _error = errno ;
00167 processCreationFailed() ;
00168 return ;
00169 }
00170
00171 _id = static_cast<Pid>( res ) ;
00172
00173 _error = 0 ;
00174
00175 if ( _id == 0 )
00176 {
00177
00178
00179 _id = ::getpid() ;
00180
00181 start() ;
00182
00183 }
00184
00185
00186
00187 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00188
00189 throw ProcessException( "Process::run: "
00190 "advanced process management feature not available." ) ;
00191
00192 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00193
00194 }
00195
00196
00197
00198 void Process::kill()
00199 {
00200
00201 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00202
00203 if ( _id <= 0 )
00204 return ;
00205
00206 int ret = ::kill( _id, SIGTERM ) ;
00207
00208 if ( ret != 0 )
00209 {
00210 _error = errno ;
00211 throw ProcessException( "Process::kill: could not kill process "
00212 + Ceylan::toString( _id ) + ": "
00213 + System::explainError( _error ) ) ;
00214 }
00215 else
00216 {
00217 ErrorCode executionInfo ;
00218 WaitChildProcess( * this, & executionInfo ) ;
00219 }
00220
00221 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00222
00223 throw ProcessException( "Process::kill: "
00224 "advanced process management feature not available." ) ;
00225
00226 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00227
00228 }
00229
00230
00231
00232 bool Process::isRunning() const
00233 {
00234
00235 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00236
00237 pid_t p = 0 ;
00238
00239 if ( _id > 0 )
00240 {
00241 bool stayInLoop = true ;
00242 while( stayInLoop )
00243 {
00244 p = ::waitpid( _id, 0, WNOHANG ) ;
00245 stayInLoop = ( p < 0 && errno == EINTR ) ;
00246 }
00247 }
00248
00249 return p == 0 ;
00250
00251 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00252
00253 throw ProcessException( "Process::isRunning: "
00254 "advanced process management feature not available." ) ;
00255
00256 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00257
00258 }
00259
00260
00261
00262 const string Process::toString( Ceylan::VerbosityLevels level ) const
00263 {
00264
00265 return "Process whose PID is " + Ceylan::toString( _id ) ;
00266
00267 }
00268
00269
00270
00271
00272
00273
00274
00275 Pid Process::GetHostingPID()
00276 {
00277
00278 #if CEYLAN_ARCH_NINTENDO_DS
00279
00280 return 0 ;
00281
00282 #else // CEYLAN_ARCH_NINTENDO_DS
00283
00284
00285 #ifdef CEYLAN_USES_PROCESS_H
00286
00287 return ::_getpid() ;
00288
00289 #else // CEYLAN_USES_PROCESS_H
00290
00291 #ifdef CEYLAN_USES_GETPID
00292
00293 return ::getpid() ;
00294
00295 #else // CEYLAN_USES_GETPID
00296
00297 throw ProcessException( "Process::GetHostingPID: "
00298 "not supported on this platform" ) ;
00299
00300 #endif // CEYLAN_USES_GETPID
00301
00302 #endif // CEYLAN_USES_PROCESS_H
00303
00304 #endif // CEYLAN_ARCH_NINTENDO_DS
00305
00306 }
00307
00308
00309
00310 Pid Process::GetParentID()
00311 {
00312
00313 #ifdef CEYLAN_USES_GETPID
00314
00315 return ::getpid() ;
00316
00317 #else // CEYLAN_USES_GETPID
00318
00319 throw ProcessException( "Process::GetParentID: "
00320 "not supported on this platform" ) ;
00321
00322 #endif // CEYLAN_USES_GETPID
00323
00324 }
00325
00326
00327
00328 Process::ExitReason Process::WaitChildProcess( const Process & childProcess,
00329 ErrorCode * executionInfo )
00330 {
00331
00332 #ifdef CEYLAN_USES_WAITPID
00333
00334 int status = 0 ;
00335 ExitReason ret = BadChildPid ;
00336
00337 Pid p = ::waitpid( childProcess.getPID(), & status, 0 ) ;
00338
00339
00340
00341 if ( p == childProcess.getPID() )
00342 {
00343
00344
00345
00346
00347
00348
00349 if ( WIFEXITED( status ) )
00350 {
00351 if ( executionInfo != 0 )
00352 * executionInfo = WEXITSTATUS( status ) ;
00353 ret = ExitedNormally ;
00354 }
00355 else if ( WIFSIGNALED( status ) )
00356 {
00357 if ( executionInfo != 0 )
00358 * executionInfo = WTERMSIG( status ) ;
00359 ret = Signaled ;
00360 }
00361 else if ( WIFSTOPPED( status ) )
00362 {
00363 if ( executionInfo != 0 )
00364 * executionInfo = WSTOPSIG( status ) ;
00365 ret = Stopped ;
00366 }
00367
00368 }
00369
00370 return ret ;
00371
00372
00373 #else // CEYLAN_USES_WAITPID
00374
00375 throw ProcessException( "Process::WaitChildProcess: "
00376 "not supported on this platform" ) ;
00377
00378 #endif // CEYLAN_USES_WAITPID
00379
00380 }
00381
00382
00383
00384 string Process::GetOwner()
00385 {
00386
00387 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00388
00389 if ( _Owner.empty() )
00390 {
00391
00392 struct passwd * p ;
00393 p = ::getpwuid( ::geteuid() ) ;
00394
00395 if ( p != 0 )
00396 _Owner = p->pw_name ;
00397 else
00398 throw ProcessException( "Process::GetOwner: "
00399 "unable to determine process owner." ) ;
00400 }
00401
00402 return _Owner ;
00403
00404 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00405
00406 throw ProcessException( "Process::GetOwner: "
00407 "advanced process management feature not available" ) ;
00408
00409 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00410
00411 }
00412
00413
00414
00415 void Process::RunExecutable(
00416 const string & filename,
00417 const list<string> & argv,
00418 const string & stdoutFilename,
00419 const string & stderrFilename,
00420 const string & stdinFilename )
00421 {
00422
00423 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00424
00425
00426
00427 char ** cargv = new char * [ argv.size() + 2 ] ;
00428
00429 cargv[0] = new char[ filename.size() + 1 ] ;
00430 filename.copy( cargv[0], filename.size() ) ;
00431 cargv[0][ filename.size() ] = 0 ;
00432
00433 int currentArg = 1 ;
00434
00435 for( list<string>::const_iterator it = argv.begin();
00436 it != argv.end(); it++ )
00437 {
00438 cargv[currentArg] = new char[ (*it).size() + 1 ] ;
00439 (*it).copy( cargv[currentArg], (*it).size() ) ;
00440 cargv[currentArg][ (*it).size() ] = 0 ;
00441 currentArg++ ;
00442 }
00443
00444 cargv[currentArg] = 0 ;
00445
00446
00447
00448 if ( ! stdoutFilename.empty()
00449 && ! RedirectStdout( stdoutFilename ) )
00450 throw ProcessException(
00451 "Process::runExecutable: could not redirect stdout into "
00452 + stdoutFilename ) ;
00453
00454 if ( ! stderrFilename.empty()
00455 && ! RedirectStderr( stderrFilename ) )
00456 throw ProcessException(
00457 "Process::runExecutable: could not redirect stderr into "
00458 + stderrFilename ) ;
00459
00460 if ( ! stdinFilename.empty()
00461 && ! RedirectStdin( stdinFilename ) )
00462 throw ProcessException(
00463 "Process::runExecutable: could not redirect stdin into "
00464 + stdinFilename ) ;
00465
00466
00467
00468 ::execvp( cargv[0], cargv ) ;
00469
00470 throw ProcessException(
00471 "Process::runExecutable: could not execute code from "
00472 + filename + ": "
00473 + System::explainError( errno ) + " " + cargv[0] ) ;
00474
00475 for ( Ceylan::Uint16 i = 0; cargv[i] != 0 ; i++ )
00476 delete [] cargv[i] ;
00477
00478 delete [] cargv ;
00479
00480 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00481
00482 throw ProcessException( "Process::RunExecutable: "
00483 "advanced process management feature not available" ) ;
00484
00485 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00486
00487 }
00488
00489
00490
00491 bool Process::RedirectStdout( const string & filename )
00492 {
00493
00494 #if CEYLAN_ARCH_NINTENDO_DS
00495
00496 throw ProcessException( "Process::RedirectStdout failed: "
00497 "not available on the Nintendo DS." ) ;
00498
00499 #else // CEYLAN_ARCH_NINTENDO_DS
00500
00501 #if CEYLAN_USES_FILE_DESCRIPTORS
00502
00503 bool ret = false ;
00504
00505 try
00506 {
00507
00508 StandardFile & f = StandardFile::Create( filename ) ;
00509
00510 ret = DuplicateStream( f.getFileDescriptor(), STDOUT_FILENO ) ;
00511 f.close() ;
00512
00513 delete & f ;
00514
00515 }
00516 catch( const SystemException & e )
00517 {
00518 LogPlug::error(
00519 "Ceylan::System::Process: could not redirect stdout to "
00520 + filename + ": " + e.toString() ) ;
00521 }
00522
00523 return ret ;
00524
00525 #else // CEYLAN_USES_FILE_DESCRIPTORS
00526
00527 throw ProcessException( "Process::RedirectStdout: "
00528 "file descriptor feature not available" ) ;
00529
00530 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00531
00532 #endif // CEYLAN_ARCH_NINTENDO_DS
00533
00534 }
00535
00536
00537
00538 bool Process::RedirectStdout( OutputStream & os )
00539 {
00540
00541 #if CEYLAN_ARCH_NINTENDO_DS
00542
00543 throw ProcessException( "Process::RedirectStdout failed: "
00544 "not available on the Nintendo DS." ) ;
00545
00546 #else // CEYLAN_ARCH_NINTENDO_DS
00547
00548 #if CEYLAN_USES_FILE_DESCRIPTORS
00549
00550 return DuplicateStream( os.getOutputStreamID(), STDOUT_FILENO ) ;
00551
00552 #else // CEYLAN_USES_FILE_DESCRIPTORS
00553
00554 throw ProcessException( "Process::RedirectStdout: "
00555 "file descriptor feature not available" ) ;
00556
00557 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00558
00559 #endif // CEYLAN_ARCH_NINTENDO_DS
00560
00561 }
00562
00563
00564
00565 bool Process::RedirectStderr( const string & filename )
00566 {
00567
00568 #if CEYLAN_ARCH_NINTENDO_DS
00569
00570 throw ProcessException( "Process::RedirectStderr failed: "
00571 "not available on the Nintendo DS." ) ;
00572
00573 #else // CEYLAN_ARCH_NINTENDO_DS
00574
00575 #if CEYLAN_USES_FILE_DESCRIPTORS
00576
00577 bool ret = false ;
00578
00579 try
00580 {
00581
00582 StandardFile & f = StandardFile::Create( filename ) ;
00583
00584 ret = DuplicateStream( f.getFileDescriptor(), STDERR_FILENO ) ;
00585 f.close() ;
00586
00587 delete & f ;
00588
00589 }
00590 catch( const SystemException & e )
00591 {
00592 LogPlug::error(
00593 "Ceylan::System::Process: could not redirect stderr to "
00594 + filename + ": " + e.toString() ) ;
00595 }
00596
00597 return ret ;
00598
00599 #else // CEYLAN_USES_FILE_DESCRIPTORS
00600
00601 throw ProcessException( "Process::RedirectStderr: "
00602 "file descriptor feature not available" ) ;
00603
00604 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00605
00606 #endif // CEYLAN_ARCH_NINTENDO_DS
00607
00608 }
00609
00610
00611
00612 bool Process::RedirectStderr( OutputStream & os )
00613 {
00614
00615 #if CEYLAN_ARCH_NINTENDO_DS
00616
00617 throw ProcessException( "Process::RedirectStderr failed: "
00618 "not available on the Nintendo DS." ) ;
00619
00620 #else // CEYLAN_ARCH_NINTENDO_DS
00621
00622 #if CEYLAN_USES_FILE_DESCRIPTORS
00623
00624 return DuplicateStream( os.getOutputStreamID(), STDERR_FILENO ) ;
00625
00626 #else // CEYLAN_USES_FILE_DESCRIPTORS
00627
00628 throw ProcessException( "Process::RedirectStderr: "
00629 "file descriptor feature not available" ) ;
00630
00631 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00632
00633 #endif // CEYLAN_ARCH_NINTENDO_DS
00634
00635 }
00636
00637
00638
00639 bool Process::RedirectStdin( const string & filename )
00640 {
00641
00642 #if CEYLAN_ARCH_NINTENDO_DS
00643
00644 throw ProcessException( "Process::RedirectStdin failed: "
00645 "not available on the Nintendo DS." ) ;
00646
00647 #else // CEYLAN_ARCH_NINTENDO_DS
00648
00649 #if CEYLAN_USES_FILE_DESCRIPTORS
00650
00651
00652 bool ret = false ;
00653
00654 try
00655 {
00656
00657 StandardFile & f = StandardFile::Create( filename ) ;
00658
00659 ret = DuplicateStream( f.getFileDescriptor(), STDIN_FILENO ) ;
00660 f.close() ;
00661
00662 delete & f ;
00663
00664 }
00665 catch( const SystemException & e )
00666 {
00667 LogPlug::error(
00668 "Ceylan::System::Process: could not redirect stdin to "
00669 + filename + ": " + e.toString() ) ;
00670 }
00671
00672 return ret ;
00673
00674 #else // CEYLAN_USES_FILE_DESCRIPTORS
00675
00676 throw ProcessException( "Process::RedirectStdin: "
00677 "file descriptor feature not available" ) ;
00678
00679 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00680
00681 #endif // CEYLAN_ARCH_NINTENDO_DS
00682
00683 }
00684
00685
00686
00687 bool Process::RedirectStdin( InputStream & is )
00688 {
00689
00690 #if CEYLAN_ARCH_NINTENDO_DS
00691
00692 throw ProcessException( "Process::RedirectStdin failed: "
00693 "not available on the Nintendo DS." ) ;
00694
00695 #else // CEYLAN_ARCH_NINTENDO_DS
00696
00697 #if CEYLAN_USES_FILE_DESCRIPTORS
00698
00699 return DuplicateStream( is.getInputStreamID(), STDIN_FILENO ) ;
00700
00701 #else // CEYLAN_USES_FILE_DESCRIPTORS
00702
00703 throw ProcessException( "Process::RedirectStdin: "
00704 "file descriptor feature not available" ) ;
00705
00706 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00707
00708 #endif // CEYLAN_ARCH_NINTENDO_DS
00709
00710 }
00711
00712
00713
00714 void Process::processCreationFailed()
00715 {
00716
00717 throw ProcessException( "Ceylan::Process:: process creation failed: "
00718 + System::explainError( _error ) ) ;
00719
00720 }
00721
00722
00723
00724 Ceylan::Uint32 Process::GetTime()
00725 {
00726
00727 #if CEYLAN_ARCH_NINTENDO_DS
00728
00729 throw ProcessException( "Process::RedirectStdout failed: "
00730 "not available on the Nintendo DS." ) ;
00731
00732 #else // CEYLAN_ARCH_NINTENDO_DS
00733
00734 #if CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00735
00736 static long clockticks = 0 ;
00737
00738 if ( clockticks == 0 )
00739 clockticks = ::sysconf( _SC_CLK_TCK ) ;
00740
00741 if ( clockticks < 0 )
00742 throw ProcessException(
00743 "Process::GetTime: unable to determine system clock ticks: "
00744 + System::explainError( errno ) ) ;
00745
00746 if ( clockticks == 0 )
00747 throw ProcessException( "Process::GetTime: "
00748 "clock ticks equal to zero according to the system" ) ;
00749
00750 struct tms t ;
00751
00752 if ( ::times( & t ) == static_cast<clock_t>( -1 ) )
00753 throw ProcessException( "Process::GetTime: unable to determine "
00754 "time spent in the process: "
00755 + System::explainError( errno ) ) ;
00756
00757 return static_cast<Ceylan::Uint32>(
00758 static_cast<SignedLongInteger>( t.tms_utime ) / clockticks ) ;
00759
00760 #else // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00761
00762 throw ProcessException( "Process::GetTime: "
00763 "advanced process management feature not available" ) ;
00764
00765 #endif // CEYLAN_USES_ADVANCED_PROCESS_MANAGEMENT
00766
00767 #endif // CEYLAN_ARCH_NINTENDO_DS
00768
00769 }
00770
00771
00772
00773 void Process::SaveState( int argc, char ** argv )
00774 {
00775
00776 _Path = Directory::GetCurrentWorkingDirectoryPath() ;
00777 _Executable = argv[ 0 ] ;
00778
00779 for ( Ceylan::Uint16 i = 1; i < argc; i++ )
00780 _ArgumentList.push_back( argv[ i ] ) ;
00781
00782 _Saved = true ;
00783
00784 }
00785
00786
00787
00788 void Process::Restart()
00789 {
00790
00791 if ( ! _Saved )
00792 throw ProcessException( "Ceylan::System::Process::restart: "
00793 "process command line not available." ) ;
00794
00795 try
00796 {
00797 Directory::ChangeWorkingDirectory( _Path ) ;
00798 }
00799 catch ( const DirectoryException & e )
00800 {
00801 throw ProcessException( "Ceylan::System::Process::restart: "
00802 "could not change directory to initial path " + _Path ) ;
00803 }
00804
00805 RunExecutable( _Executable, _ArgumentList ) ;
00806
00807 throw ProcessException( "Ceylan::System::Process::restart: "
00808 "could not run executable " + _Executable ) ;
00809
00810 }
00811
00812
00813
00814 bool Process::DuplicateStream( FileDescriptor FDOld, FileDescriptor FDNew )
00815 {
00816
00817 #if CEYLAN_ARCH_NINTENDO_DS
00818
00819 throw Features::FeatureNotAvailableException(
00820 "Process::DuplicateStream failed: not available on the Nintendo DS." ) ;
00821
00822 #else // CEYLAN_ARCH_NINTENDO_DS
00823
00824 #if CEYLAN_USES_FILE_DESCRIPTORS
00825 return ::dup2( FDOld, FDNew ) == FDNew ;
00826 #endif // CEYLAN_USES_FILE_DESCRIPTORS
00827
00828 throw Features::FeatureNotAvailableException( "Process::DuplicateStream: "
00829 "file descriptor feature not available" ) ;
00830
00831 #endif // CEYLAN_ARCH_NINTENDO_DS
00832
00833 }
00834