KIO
kfsprocess.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 1997 Christian Czezakte (e9025461@student.tuwien.ac.at) 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kfsprocess.h" 00021 00022 #include <config.h> 00023 00024 #include <kdebug.h> 00025 #include <kstandarddirs.h> 00026 //#include <kuser.h> 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 ); // in case slotDoHousekeeping is called without polling first 00074 fcntl( m_fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up 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 /* not sure why, but this is causing lockups */ 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 // CC: take care of SunOS which automatically restarts interrupted system 00122 // calls (and thus does not have SA_RESTART) 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 // Make sure we don't block this signal. gdb tends to do that :-( 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 // there should be no problem with SIGPIPE staying SIG_IGN 00153 } 00154 00155 // the pipe is needed to sync the child reaping with our event processing, 00156 // as otherwise there are race conditions, locking requirements, and things 00157 // generally get harder 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 ); // call the old handler 00168 } 00169 00170 errno = saved_errno; 00171 } 00172 00173 void KfsProcessController::slotDoHousekeeping() 00174 { 00175 char dummy[16]; // somewhat bigger - just in case several have queued up 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 // the callback can nuke the whole process list and even 'this' 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(); // counterpart to addProcess, can invalidate 'this' 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(); // make sure we stay around when the KfsProcess goes away 00221 } 00222 00224 // public member functions // 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; // Pipe failed.. continue 00283 00284 pid_ = fork(); 00285 if (pid_ == 0) { 00286 // The child process 00287 00288 close(fd[0]); 00289 // Closing of fd[1] indicates that the execvp() succeeded! 00290 fcntl(fd[1], F_SETFD, FD_CLOEXEC); 00291 00292 // reset all signal handlers 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 // forking failed 00308 00309 pid_ = 0; 00310 free(arglist); 00311 return false; 00312 } 00313 // the parent continues here 00314 free(arglist); 00315 00316 // Check whether client could be started. 00317 close(fd[1]); 00318 for(;;) 00319 { 00320 char resultByte; 00321 int n = ::read(fd[0], &resultByte, 1); 00322 if (n == 1) 00323 { 00324 // exec() failed 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; // Ignore 00334 } 00335 break; // success 00336 } 00337 close(fd[0]); 00338 00339 runs = true; 00340 return true; 00341 } 00342 00343 void KfsProcess::processHasExited() 00344 { 00345 // only successfully run processes ever get here 00346 runs = false; 00347 emit processExited(); 00348 } 00349 00350 #include "kfsprocess.moc"
KDE 4.6 API Reference