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 <kuser.h> 00026 00027 #include <QtCore/QMap> 00028 #include <QtCore/QFile> 00029 #include <QtCore/QSocketNotifier> 00030 00031 #include <unistd.h> 00032 #include <fcntl.h> 00033 #include <signal.h> 00034 #include <errno.h> 00035 #include <sys/wait.h> 00036 00037 KfsProcessController *KfsProcessController::s_instance = 0; 00038 int KfsProcessController::s_refCount = 0; 00039 00040 void KfsProcessController::ref() 00041 { 00042 if ( !s_refCount ) { 00043 s_instance = new KfsProcessController; 00044 setupHandlers(); 00045 } 00046 s_refCount++; 00047 } 00048 00049 void KfsProcessController::deref() 00050 { 00051 s_refCount--; 00052 if( !s_refCount ) { 00053 resetHandlers(); 00054 delete s_instance; 00055 s_instance = 0; 00056 } 00057 } 00058 00059 KfsProcessController* KfsProcessController::instance() 00060 { 00061 return s_instance; 00062 } 00063 00064 KfsProcessController::KfsProcessController() 00065 { 00066 if( pipe( m_fd ) ) 00067 { 00068 perror( "pipe" ); 00069 abort(); 00070 } 00071 00072 fcntl( m_fd[0], F_SETFL, O_NONBLOCK ); // in case slotDoHousekeeping is called without polling first 00073 fcntl( m_fd[1], F_SETFL, O_NONBLOCK ); // in case it fills up 00074 fcntl( m_fd[0], F_SETFD, FD_CLOEXEC ); 00075 fcntl( m_fd[1], F_SETFD, FD_CLOEXEC ); 00076 00077 m_notifier = new QSocketNotifier( m_fd[0], QSocketNotifier::Read ); 00078 m_notifier->setEnabled( true ); 00079 QObject::connect( m_notifier, SIGNAL(activated(int)), 00080 SLOT(slotDoHousekeeping())); 00081 } 00082 00083 KfsProcessController::~KfsProcessController() 00084 { 00085 #ifndef Q_OS_MAC 00086 /* not sure why, but this is causing lockups */ 00087 close( m_fd[0] ); 00088 close( m_fd[1] ); 00089 #else 00090 #warning FIXME: why does close() freeze up destruction? 00091 #endif 00092 } 00093 00094 00095 extern "C" { 00096 static void theReaper( int num ) 00097 { 00098 KfsProcessController::theSigCHLDHandler( num ); 00099 } 00100 } 00101 00102 struct sigaction KfsProcessController::s_oldChildHandlerData; 00103 bool KfsProcessController::s_handlerSet = false; 00104 00105 void KfsProcessController::setupHandlers() 00106 { 00107 if( s_handlerSet ) 00108 return; 00109 s_handlerSet = true; 00110 00111 struct sigaction act; 00112 sigemptyset( &act.sa_mask ); 00113 00114 act.sa_handler = SIG_IGN; 00115 act.sa_flags = 0; 00116 sigaction( SIGPIPE, &act, 0L ); 00117 00118 act.sa_handler = theReaper; 00119 act.sa_flags = SA_NOCLDSTOP; 00120 // CC: take care of SunOS which automatically restarts interrupted system 00121 // calls (and thus does not have SA_RESTART) 00122 #ifdef SA_RESTART 00123 act.sa_flags |= SA_RESTART; 00124 #endif 00125 sigaction( SIGCHLD, &act, &s_oldChildHandlerData ); 00126 00127 sigaddset( &act.sa_mask, SIGCHLD ); 00128 // Make sure we don't block this signal. gdb tends to do that :-( 00129 sigprocmask( SIG_UNBLOCK, &act.sa_mask, 0 ); 00130 } 00131 00132 void KfsProcessController::resetHandlers() 00133 { 00134 if( !s_handlerSet ) 00135 return; 00136 s_handlerSet = false; 00137 00138 sigset_t mask, omask; 00139 sigemptyset( &mask ); 00140 sigaddset( &mask, SIGCHLD ); 00141 sigprocmask( SIG_BLOCK, &mask, &omask ); 00142 00143 struct sigaction act; 00144 sigaction( SIGCHLD, &s_oldChildHandlerData, &act ); 00145 if (act.sa_handler != theReaper) { 00146 sigaction( SIGCHLD, &act, 0 ); 00147 s_handlerSet = true; 00148 } 00149 00150 sigprocmask( SIG_SETMASK, &omask, 0 ); 00151 // there should be no problem with SIGPIPE staying SIG_IGN 00152 } 00153 00154 // the pipe is needed to sync the child reaping with our event processing, 00155 // as otherwise there are race conditions, locking requirements, and things 00156 // generally get harder 00157 void KfsProcessController::theSigCHLDHandler( int arg ) 00158 { 00159 int saved_errno = errno; 00160 00161 char dummy = 0; 00162 ::write( instance()->m_fd[1], &dummy, 1 ); 00163 00164 if ( s_oldChildHandlerData.sa_handler != SIG_IGN && 00165 s_oldChildHandlerData.sa_handler != SIG_DFL ) { 00166 s_oldChildHandlerData.sa_handler( arg ); // call the old handler 00167 } 00168 00169 errno = saved_errno; 00170 } 00171 00172 void KfsProcessController::slotDoHousekeeping() 00173 { 00174 char dummy[16]; // somewhat bigger - just in case several have queued up 00175 ::read( m_fd[0], dummy, sizeof(dummy) ); 00176 00177 again: 00178 QList<KfsProcess*>::iterator it( m_kProcessList.begin() ); 00179 QList<KfsProcess*>::iterator eit( m_kProcessList.end() ); 00180 while( it != eit ) 00181 { 00182 KfsProcess *prc = *it; 00183 if( prc->runs && waitpid( prc->pid_, 0, WNOHANG ) > 0 ) 00184 { 00185 prc->processHasExited(); 00186 // the callback can nuke the whole process list and even 'this' 00187 if (!instance()) 00188 return; 00189 goto again; 00190 } 00191 ++it; 00192 } 00193 QList<int>::iterator uit( m_unixProcessList.begin() ); 00194 QList<int>::iterator ueit( m_unixProcessList.end() ); 00195 while( uit != ueit ) 00196 { 00197 if( waitpid( *uit, 0, WNOHANG ) > 0 ) 00198 { 00199 uit = m_unixProcessList.erase( uit ); 00200 deref(); // counterpart to addProcess, can invalidate 'this' 00201 } else 00202 ++uit; 00203 } 00204 } 00205 00206 void KfsProcessController::addKProcess( KfsProcess* p ) 00207 { 00208 m_kProcessList.append( p ); 00209 } 00210 00211 void KfsProcessController::removeKProcess( KfsProcess* p ) 00212 { 00213 m_kProcessList.removeAll( p ); 00214 } 00215 00216 void KfsProcessController::addProcess( int pid ) 00217 { 00218 m_unixProcessList.append( pid ); 00219 ref(); // make sure we stay around when the KfsProcess goes away 00220 } 00221 00223 // public member functions // 00225 00226 KfsProcess::KfsProcess( QObject* parent ) 00227 : QObject( parent ), 00228 runs(false), 00229 pid_(0) 00230 { 00231 KfsProcessController::ref(); 00232 KfsProcessController::instance()->addKProcess(this); 00233 } 00234 00235 KfsProcess::~KfsProcess() 00236 { 00237 KfsProcessController::instance()->removeKProcess(this); 00238 KfsProcessController::deref(); 00239 } 00240 00241 void KfsProcess::detach() 00242 { 00243 if (runs) { 00244 KfsProcessController::instance()->addProcess(pid_); 00245 runs = false; 00246 pid_ = 0; 00247 } 00248 } 00249 00250 KfsProcess &KfsProcess::operator<<(const char* arg) 00251 { 00252 arguments.append(QByteArray(arg)); 00253 return *this; 00254 } 00255 00256 KfsProcess &KfsProcess::operator<<(const QString& arg) 00257 { 00258 arguments.append(QFile::encodeName(arg)); 00259 return *this; 00260 } 00261 00262 bool KfsProcess::start() 00263 { 00264 if (runs) { 00265 kDebug(175) << "Attempted to start an already running process"; 00266 return false; 00267 } 00268 00269 uint n = arguments.count(); 00270 if (n == 0) { 00271 kDebug(175) << "Attempted to start a process without arguments"; 00272 return false; 00273 } 00274 char **arglist = static_cast<char **>(malloc( (n + 1) * sizeof(char *))); 00275 for (uint i = 0; i < n; i++) 00276 arglist[i] = arguments[i].data(); 00277 arglist[n] = 0; 00278 00279 int fd[2]; 00280 if (pipe(fd)) 00281 fd[0] = fd[1] = -1; // Pipe failed.. continue 00282 00283 pid_ = fork(); 00284 if (pid_ == 0) { 00285 // The child process 00286 00287 close(fd[0]); 00288 // Closing of fd[1] indicates that the execvp() succeeded! 00289 fcntl(fd[1], F_SETFD, FD_CLOEXEC); 00290 00291 // reset all signal handlers 00292 struct sigaction act; 00293 sigemptyset(&act.sa_mask); 00294 act.sa_handler = SIG_DFL; 00295 act.sa_flags = 0; 00296 for (int sig = 1; sig < NSIG; sig++) 00297 sigaction(sig, &act, 0L); 00298 00299 setsid(); 00300 execvp(arglist[0], arglist); 00301 00302 char resultByte = 1; 00303 write(fd[1], &resultByte, 1); 00304 _exit(-1); 00305 } else if (pid_ == -1) { 00306 // forking failed 00307 00308 pid_ = 0; 00309 free(arglist); 00310 return false; 00311 } 00312 // the parent continues here 00313 free(arglist); 00314 00315 // Check whether client could be started. 00316 close(fd[1]); 00317 for(;;) 00318 { 00319 char resultByte; 00320 int n = ::read(fd[0], &resultByte, 1); 00321 if (n == 1) 00322 { 00323 // exec() failed 00324 close(fd[0]); 00325 waitpid(pid_, 0, 0); 00326 pid_ = 0; 00327 return false; 00328 } 00329 if (n == -1) 00330 { 00331 if (errno == EINTR) 00332 continue; // Ignore 00333 } 00334 break; // success 00335 } 00336 close(fd[0]); 00337 00338 runs = true; 00339 return true; 00340 } 00341 00342 void KfsProcess::processHasExited() 00343 { 00344 // only successfully run processes ever get here 00345 runs = false; 00346 emit processExited(); 00347 } 00348 00349 #include "kfsprocess.moc"
KDE 4.7 API Reference