• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

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"

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal