00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kfsprocess.h"
00021
00022 #include <config.h>
00023
00024 #include <kdebug.h>
00025 #include <kstandarddirs.h>
00026
00027
00028 #include <QtCore/QMap>
00029 #include <QtCore/QFile>
00030 #include <QtCore/QSocketNotifier>
00031
00032 #include <unistd.h>
00033 #include <fcntl.h>
00034 #include <signal.h>
00035 #include <errno.h>
00036 #include <sys/wait.h>
00037
00038 KfsProcessController *KfsProcessController::s_instance = 0;
00039 int KfsProcessController::s_refCount = 0;
00040
00041 void KfsProcessController::ref()
00042 {
00043 if ( !s_refCount ) {
00044 s_instance = new KfsProcessController;
00045 setupHandlers();
00046 }
00047 s_refCount++;
00048 }
00049
00050 void KfsProcessController::deref()
00051 {
00052 s_refCount--;
00053 if( !s_refCount ) {
00054 resetHandlers();
00055 delete s_instance;
00056 s_instance = 0;
00057 }
00058 }
00059
00060 KfsProcessController* KfsProcessController::instance()
00061 {
00062 return s_instance;
00063 }
00064
00065 KfsProcessController::KfsProcessController()
00066 {
00067 if( pipe( m_fd ) )
00068 {
00069 perror( "pipe" );
00070 abort();
00071 }
00072
00073 fcntl( m_fd[0], F_SETFL, O_NONBLOCK );
00074 fcntl( m_fd[1], F_SETFL, O_NONBLOCK );
00075 fcntl( m_fd[0], F_SETFD, FD_CLOEXEC );
00076 fcntl( m_fd[1], F_SETFD, FD_CLOEXEC );
00077
00078 m_notifier = new QSocketNotifier( m_fd[0], QSocketNotifier::Read );
00079 m_notifier->setEnabled( true );
00080 QObject::connect( m_notifier, SIGNAL(activated(int)),
00081 SLOT(slotDoHousekeeping()));
00082 }
00083
00084 KfsProcessController::~KfsProcessController()
00085 {
00086 #ifndef Q_OS_MAC
00087
00088 close( m_fd[0] );
00089 close( m_fd[1] );
00090 #else
00091 #warning FIXME: why does close() freeze up destruction?
00092 #endif
00093 }
00094
00095
00096 extern "C" {
00097 static void theReaper( int num )
00098 {
00099 KfsProcessController::theSigCHLDHandler( num );
00100 }
00101 }
00102
00103 struct sigaction KfsProcessController::s_oldChildHandlerData;
00104 bool KfsProcessController::s_handlerSet = false;
00105
00106 void KfsProcessController::setupHandlers()
00107 {
00108 if( s_handlerSet )
00109 return;
00110 s_handlerSet = true;
00111
00112 struct sigaction act;
00113 sigemptyset( &act.sa_mask );
00114
00115 act.sa_handler = SIG_IGN;
00116 act.sa_flags = 0;
00117 sigaction( SIGPIPE, &act, 0L );
00118
00119 act.sa_handler = theReaper;
00120 act.sa_flags = SA_NOCLDSTOP;
00121
00122
00123 #ifdef SA_RESTART
00124 act.sa_flags |= SA_RESTART;
00125 #endif
00126 sigaction( SIGCHLD, &act, &s_oldChildHandlerData );
00127
00128 sigaddset( &act.sa_mask, SIGCHLD );
00129
00130 sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 );
00131 }
00132
00133 void KfsProcessController::resetHandlers()
00134 {
00135 if( !s_handlerSet )
00136 return;
00137 s_handlerSet = false;
00138
00139 sigset_t mask, omask;
00140 sigemptyset( &mask );
00141 sigaddset( &mask, SIGCHLD );
00142 sigprocmask( SIG_BLOCK, &mask, &omask );
00143
00144 struct sigaction act;
00145 sigaction( SIGCHLD, &s_oldChildHandlerData, &act );
00146 if (act.sa_handler != theReaper) {
00147 sigaction( SIGCHLD, &act, 0 );
00148 s_handlerSet = true;
00149 }
00150
00151 sigprocmask( SIG_SETMASK, &omask, 0 );
00152
00153 }
00154
00155
00156
00157
00158 void KfsProcessController::theSigCHLDHandler( int arg )
00159 {
00160 int saved_errno = errno;
00161
00162 char dummy = 0;
00163 ::write( instance()->m_fd[1], &dummy, 1 );
00164
00165 if ( s_oldChildHandlerData.sa_handler != SIG_IGN &&
00166 s_oldChildHandlerData.sa_handler != SIG_DFL ) {
00167 s_oldChildHandlerData.sa_handler( arg );
00168 }
00169
00170 errno = saved_errno;
00171 }
00172
00173 void KfsProcessController::slotDoHousekeeping()
00174 {
00175 char dummy[16];
00176 ::read( m_fd[0], dummy, sizeof(dummy) );
00177
00178 again:
00179 QList<KfsProcess*>::iterator it( m_kProcessList.begin() );
00180 QList<KfsProcess*>::iterator eit( m_kProcessList.end() );
00181 while( it != eit )
00182 {
00183 KfsProcess *prc = *it;
00184 if( prc->runs && waitpid( prc->pid_, 0, WNOHANG ) > 0 )
00185 {
00186 prc->processHasExited();
00187
00188 if (!instance())
00189 return;
00190 goto again;
00191 }
00192 ++it;
00193 }
00194 QList<int>::iterator uit( m_unixProcessList.begin() );
00195 QList<int>::iterator ueit( m_unixProcessList.end() );
00196 while( uit != ueit )
00197 {
00198 if( waitpid( *uit, 0, WNOHANG ) > 0 )
00199 {
00200 uit = m_unixProcessList.erase( uit );
00201 deref();
00202 } else
00203 ++uit;
00204 }
00205 }
00206
00207 void KfsProcessController::addKProcess( KfsProcess* p )
00208 {
00209 m_kProcessList.append( p );
00210 }
00211
00212 void KfsProcessController::removeKProcess( KfsProcess* p )
00213 {
00214 m_kProcessList.removeAll( p );
00215 }
00216
00217 void KfsProcessController::addProcess( int pid )
00218 {
00219 m_unixProcessList.append( pid );
00220 ref();
00221 }
00222
00224
00226
00227 KfsProcess::KfsProcess( QObject* parent )
00228 : QObject( parent ),
00229 runs(false),
00230 pid_(0)
00231 {
00232 KfsProcessController::ref();
00233 KfsProcessController::instance()->addKProcess(this);
00234 }
00235
00236 KfsProcess::~KfsProcess()
00237 {
00238 KfsProcessController::instance()->removeKProcess(this);
00239 KfsProcessController::deref();
00240 }
00241
00242 void KfsProcess::detach()
00243 {
00244 if (runs) {
00245 KfsProcessController::instance()->addProcess(pid_);
00246 runs = false;
00247 pid_ = 0;
00248 }
00249 }
00250
00251 KfsProcess &KfsProcess::operator<<(const char* arg)
00252 {
00253 arguments.append(QByteArray(arg));
00254 return *this;
00255 }
00256
00257 KfsProcess &KfsProcess::operator<<(const QString& arg)
00258 {
00259 arguments.append(QFile::encodeName(arg));
00260 return *this;
00261 }
00262
00263 bool KfsProcess::start()
00264 {
00265 if (runs) {
00266 kDebug(175) << "Attempted to start an already running process";
00267 return false;
00268 }
00269
00270 uint n = arguments.count();
00271 if (n == 0) {
00272 kDebug(175) << "Attempted to start a process without arguments";
00273 return false;
00274 }
00275 char **arglist = static_cast<char **>(malloc( (n + 1) * sizeof(char *)));
00276 for (uint i = 0; i < n; i++)
00277 arglist[i] = arguments[i].data();
00278 arglist[n] = 0;
00279
00280 int fd[2];
00281 if (pipe(fd))
00282 fd[0] = fd[1] = -1;
00283
00284 pid_ = fork();
00285 if (pid_ == 0) {
00286
00287
00288 close(fd[0]);
00289
00290 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
00291
00292
00293 struct sigaction act;
00294 sigemptyset(&act.sa_mask);
00295 act.sa_handler = SIG_DFL;
00296 act.sa_flags = 0;
00297 for (int sig = 1; sig < NSIG; sig++)
00298 sigaction(sig, &act, 0L);
00299
00300 setsid();
00301 execvp(arglist[0], arglist);
00302
00303 char resultByte = 1;
00304 write(fd[1], &resultByte, 1);
00305 _exit(-1);
00306 } else if (pid_ == -1) {
00307
00308
00309 pid_ = 0;
00310 free(arglist);
00311 return false;
00312 }
00313
00314 free(arglist);
00315
00316
00317 close(fd[1]);
00318 for(;;)
00319 {
00320 char resultByte;
00321 int n = ::read(fd[0], &resultByte, 1);
00322 if (n == 1)
00323 {
00324
00325 close(fd[0]);
00326 waitpid(pid_, 0, 0);
00327 pid_ = 0;
00328 return false;
00329 }
00330 if (n == -1)
00331 {
00332 if (errno == EINTR)
00333 continue;
00334 }
00335 break;
00336 }
00337 close(fd[0]);
00338
00339 runs = true;
00340 return true;
00341 }
00342
00343 void KfsProcess::processHasExited()
00344 {
00345
00346 runs = false;
00347 emit processExited();
00348 }
00349
00350 #include "kfsprocess.moc"