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
00028 #include <config.h>
00029
00030 #include <sys/time.h>
00031 #include <sys/types.h>
00032 #include <sys/socket.h>
00033 #include <unistd.h>
00034
00035 #include <errno.h>
00036 #include <fcntl.h>
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <unistd.h>
00040 #include <assert.h>
00041
00042 #include <qsocketnotifier.h>
00043 #include "kprocess.h"
00044 #include "kprocctrl.h"
00045
00046 KProcessController *KProcessController::theKProcessController = 0;
00047
00048 struct sigaction KProcessController::oldChildHandlerData;
00049 bool KProcessController::handlerSet = false;
00050
00051 KProcessController::KProcessController()
00052 {
00053 assert( theKProcessController == 0 );
00054
00055 if (0 > pipe(fd))
00056 printf(strerror(errno));
00057
00058 fcntl(fd[0], F_SETFL, O_NONBLOCK);
00059 fcntl(fd[0], F_SETFD, FD_CLOEXEC);
00060 fcntl(fd[1], F_SETFD, FD_CLOEXEC);
00061
00062 notifier = new QSocketNotifier(fd[0], QSocketNotifier::Read);
00063 notifier->setEnabled(true);
00064 QObject::connect(notifier, SIGNAL(activated(int)),
00065 this, SLOT(slotDoHousekeeping(int)));
00066 connect( &delayedChildrenCleanupTimer, SIGNAL( timeout()),
00067 SLOT( delayedChildrenCleanup()));
00068
00069 theKProcessController = this;
00070
00071 setupHandlers();
00072 }
00073
00074
00075 void KProcessController::setupHandlers()
00076 {
00077 if( handlerSet )
00078 return;
00079 struct sigaction act;
00080 act.sa_handler=theSigCHLDHandler;
00081 sigemptyset(&(act.sa_mask));
00082 sigaddset(&(act.sa_mask), SIGCHLD);
00083
00084 sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0);
00085
00086 act.sa_flags = SA_NOCLDSTOP;
00087
00088
00089
00090
00091 #ifdef SA_RESTART
00092 act.sa_flags |= SA_RESTART;
00093 #endif
00094
00095 sigaction( SIGCHLD, &act, &oldChildHandlerData );
00096
00097 act.sa_handler=SIG_IGN;
00098 sigemptyset(&(act.sa_mask));
00099 sigaddset(&(act.sa_mask), SIGPIPE);
00100 act.sa_flags = 0;
00101 sigaction( SIGPIPE, &act, 0L);
00102 handlerSet = true;
00103 }
00104
00105 void KProcessController::resetHandlers()
00106 {
00107 if( !handlerSet )
00108 return;
00109 sigaction( SIGCHLD, &oldChildHandlerData, 0 );
00110
00111 handlerSet = false;
00112 }
00113
00114
00115 void KProcessController::addKProcess( KProcess* p )
00116 {
00117 sigset_t newset, oldset;
00118 sigemptyset( &newset );
00119 sigaddset( &newset, SIGCHLD );
00120 sigprocmask( SIG_BLOCK, &newset, &oldset );
00121 processList.append( p );
00122 sigprocmask( SIG_SETMASK, &oldset, 0 );
00123 }
00124
00125 void KProcessController::removeKProcess( KProcess* p )
00126 {
00127 sigset_t newset, oldset;
00128 sigemptyset( &newset );
00129 sigaddset( &newset, SIGCHLD );
00130 sigprocmask( SIG_BLOCK, &newset, &oldset );
00131 processList.remove( p );
00132 sigprocmask( SIG_SETMASK, &oldset, 0 );
00133 }
00134
00135
00136
00137
00138
00139
00140 struct waitdata
00141 {
00142 pid_t pid;
00143 int status;
00144 };
00145
00146 void KProcessController::theSigCHLDHandler(int arg)
00147 {
00148 struct waitdata wd;
00149
00150
00151 int saved_errno;
00152
00153 saved_errno = errno;
00154
00155
00156
00157 bool found = false;
00158 if( theKProcessController != 0 ) {
00159
00160 for( QValueList<KProcess*>::ConstIterator it = theKProcessController->processList.begin();
00161 it != theKProcessController->processList.end();
00162 ++it )
00163 {
00164 if( !(*it)->isRunning())
00165 continue;
00166 wd.pid = waitpid( (*it)->pid(), &wd.status, WNOHANG );
00167 if ( wd.pid > 0 ) {
00168 ::write(theKProcessController->fd[1], &wd, sizeof(wd));
00169 found = true;
00170 }
00171 }
00172 }
00173 if( !found && oldChildHandlerData.sa_handler != SIG_IGN
00174 && oldChildHandlerData.sa_handler != SIG_DFL )
00175 oldChildHandlerData.sa_handler( arg );
00176
00177 if( theKProcessController != 0 ) {
00178 static const struct waitdata dwd = { 0, 0 };
00179 ::write(theKProcessController->fd[1], &dwd, sizeof(dwd));
00180 } else {
00181 int dummy;
00182 while( waitpid( -1, &dummy, WNOHANG ) > 0 )
00183 ;
00184 }
00185
00186 errno = saved_errno;
00187 }
00188
00189
00190
00191 void KProcessController::slotDoHousekeeping(int )
00192 {
00193
00194
00195 int bytes_read = 0;
00196
00197 struct waitdata wd;
00198 do {
00199 bytes_read = ::read(fd[0], ((char *)&wd), sizeof(wd));
00200 if ((bytes_read == -1) && (errno == EAGAIN)) return;
00201 if ((bytes_read == -1) && (errno != EINTR))
00202 {
00203 fprintf(stderr,
00204 "Error: pipe read returned errno=%d "
00205 "in KProcessController::slotDoHousekeeping\n", errno);
00206 return;
00207 }
00208 } while (bytes_read <= 0);
00209
00210 if (bytes_read != sizeof(wd)) {
00211 fprintf(stderr,
00212 "Error: Could not read info from signal handler %d <> %d!\n",
00213 bytes_read, sizeof(wd));
00214 return;
00215 }
00216 if (wd.pid==0) {
00217 delayedChildrenCleanupTimer.start( 100, true );
00218 return;
00219 }
00220
00221 for( QValueList<KProcess*>::ConstIterator it = processList.begin();
00222 it != processList.end();
00223 ++it ) {
00224 KProcess* proc = *it;
00225 if (proc->pid() == wd.pid) {
00226
00227 if (proc->run_mode == KProcess::Block) {
00228
00229
00230
00231 proc->status = wd.status;
00232 proc->runs = false;
00233 } else {
00234 proc->processHasExited(wd.status);
00235 }
00236 return;
00237 }
00238 }
00239 }
00240
00241
00242
00243
00244 void KProcessController::delayedChildrenCleanup()
00245 {
00246 struct waitdata wd;
00247 while(( wd.pid = waitpid( -1, &wd.status, WNOHANG ) ) > 0 ) {
00248 for( QValueList<KProcess*>::ConstIterator it = processList.begin();
00249 it != processList.end();
00250 ++it )
00251 {
00252 if( !(*it)->isRunning() || (*it)->pid() != wd.pid )
00253 continue;
00254
00255 ::write(fd[1], &wd, sizeof(wd));
00256 break;
00257 }
00258 }
00259 }
00260
00261 KProcessController::~KProcessController()
00262 {
00263 assert( theKProcessController == this );
00264 resetHandlers();
00265
00266 notifier->setEnabled(false);
00267
00268 close(fd[0]);
00269 close(fd[1]);
00270
00271 delete notifier;
00272 theKProcessController = 0;
00273 }
00274
00275 bool
00276 KProcessController::waitForProcessExit(int timeout)
00277 {
00278
00279
00280
00281
00282 if (delayedChildrenCleanupTimer.isActive())
00283 {
00284 delayedChildrenCleanupTimer.stop();
00285 KProcessController::delayedChildrenCleanup();
00286 }
00287 do
00288 {
00289 struct timeval tv;
00290 tv.tv_sec = timeout;
00291 tv.tv_usec = 0;
00292 fd_set fds;
00293 FD_ZERO(&fds);
00294 FD_SET(fd[0], &fds);
00295 int result = select(fd[0]+1, &fds, 0, 0, &tv);
00296 if (result == 0)
00297 {
00298 return false;
00299 }
00300 else if (result < 0)
00301 {
00302 int error = errno;
00303 if ((error == ECHILD) || (error == EINTR))
00304 continue;
00305 return false;
00306 }
00307 else
00308 {
00309 slotDoHousekeeping(fd[0]);
00310 break;
00311 }
00312 } while (true);
00313 return true;
00314 }
00315
00316 #include "kprocctrl.moc"