File indexing completed on 2024-05-12 04:51:20
0001 /* 0002 SPDX-FileCopyrightText: 2009 Nokia Corporation and /or its subsidiary(-ies). 0003 Contact: Qt Software Information (qt-info@nokia.com) 0004 0005 This file is part of the QtCore module of the Qt Toolkit. 0006 0007 $QT_BEGIN_LICENSE:LGPL$ 0008 Commercial Usage 0009 Licensees holding valid Qt Commercial licenses may use this file in 0010 accordance with the Qt Commercial License Agreement provided with the 0011 Software or, alternatively, in accordance with the terms contained in 0012 a written agreement between you and Nokia. 0013 0014 GNU Lesser General Public License Usage 0015 Alternatively, this file may be used under the terms of the GNU Lesser 0016 General Public License version 2.1 as published by the Free Software 0017 Foundation and appearing in the file LICENSE.LGPL included in the 0018 packaging of this file. Please review the following information to 0019 ensure the GNU Lesser General Public License version 2.1 requirements 0020 will be met: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 0021 0022 In addition, as a special exception, Nokia gives you certain 0023 additional rights. These rights are described in the Nokia Qt LGPL 0024 Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this 0025 package. 0026 0027 GNU General Public License Usage 0028 Alternatively, this file may be used under the terms of the GNU 0029 General Public License version 3.0 as published by the Free Software 0030 Foundation and appearing in the file LICENSE.GPL included in the 0031 packaging of this file. Please review the following information to 0032 ensure the GNU General Public License version 3.0 requirements will be 0033 met: https://www.gnu.org/licenses/gpl-3.0.html. 0034 0035 If you are unsure which license is appropriate for your use, please 0036 contact the sales department at qt-sales@nokia.com. 0037 $QT_END_LICENSE$ 0038 0039 */ 0040 0041 //#define QPROCESS_DEBUG 0042 #include "qdebug.h" 0043 #include <QElapsedTimer> 0044 #ifndef QT_NO_PROCESS 0045 0046 #if defined QPROCESS_DEBUG 0047 #include "QString" 0048 #include <ctype.h> 0049 0050 /* 0051 Returns a human readable representation of the first \a len 0052 characters in \a data. 0053 */ 0054 //QT_BEGIN_NAMESPACE 0055 static QByteArray qt_prettyDebug(const char *data, int len, int maxSize) 0056 { 0057 if (!data) return "(null)"; 0058 QByteArray out; 0059 for (int i = 0; i < len; ++i) { 0060 char c = data[i]; 0061 if (isprint(c)) { 0062 out += c; 0063 } else switch (c) { 0064 case '\n': out += "\\n"; break; 0065 case '\r': out += "\\r"; break; 0066 case '\t': out += "\\t"; break; 0067 default: 0068 QString tmp; 0069 tmp.sprintf("\\%o", c); 0070 out += tmp.toLatin1(); 0071 } 0072 } 0073 0074 if (len < maxSize) 0075 out += "..."; 0076 0077 return out; 0078 } 0079 //QT_END_NAMESPACE 0080 #endif 0081 0082 #include "qplatformdefs.h" 0083 0084 #include "k3bqprocess.h" 0085 #include "k3bqprocess_p.h" 0086 0087 #ifdef Q_OS_MAC 0088 #include <private/qcore_mac_p.h> 0089 #endif 0090 0091 //#include <private/qcoreapplication_p.h> 0092 //#include <private/qthread_p.h> 0093 #include <QDateTime> 0094 #include <QFile> 0095 #include <QFileInfo> 0096 #include <QList> 0097 #include <QMap> 0098 #include <QMutex> 0099 #include <QRegularExpression> 0100 #include <qsemaphore.h> 0101 #include <qsocketnotifier.h> 0102 #include <QThread> 0103 0104 #include <errno.h> 0105 #include <stdlib.h> 0106 #include <string.h> 0107 0108 //QT_BEGIN_NAMESPACE 0109 #ifdef Q_OS_INTEGRITY 0110 static inline char *strdup(const char *data) 0111 { 0112 return qstrdup(data); 0113 } 0114 #endif 0115 0116 static qint64 qt_native_read(int fd, char *data, qint64 maxlen) 0117 { 0118 qint64 ret = 0; 0119 do { 0120 ret = ::read(fd, data, maxlen); 0121 } while (ret == -1 && errno == EINTR); 0122 return ret; 0123 } 0124 0125 static qint64 qt_native_write(int fd, const char *data, qint64 len) 0126 { 0127 qint64 ret = 0; 0128 do { 0129 ret = ::write(fd, data, len); 0130 } while (ret == -1 && errno == EINTR); 0131 return ret; 0132 } 0133 0134 static void qt_native_close(int fd) 0135 { 0136 int ret; 0137 do { 0138 ret = ::close(fd); 0139 } while (ret == -1 && errno == EINTR); 0140 } 0141 0142 static void qt_native_sigaction(int signum, const struct sigaction *act, 0143 struct sigaction *oldact) 0144 { 0145 int ret; 0146 do { 0147 ret = ::sigaction(signum, act, oldact); 0148 } while (ret == -1 && errno == EINTR); 0149 } 0150 0151 static void qt_native_dup2(int oldfd, int newfd) 0152 { 0153 int ret; 0154 do { 0155 ret = ::dup2(oldfd, newfd); 0156 } while (ret == -1 && errno == EINTR); 0157 } 0158 0159 static void qt_native_chdir(const char *path) 0160 { 0161 int ret; 0162 do { 0163 ret = ::chdir(path); 0164 } while (ret == -1 && errno == EINTR); 0165 } 0166 0167 static void qt_native_execve(const char *filename, char *const argv[], 0168 char *const envp[]) 0169 { 0170 int ret; 0171 do { 0172 ret = ::execve(filename, argv, envp); 0173 } while (ret == -1 && errno == EINTR); 0174 } 0175 0176 static void qt_native_execv(const char *path, char *const argv[]) 0177 { 0178 int ret; 0179 do { 0180 ret = ::execv(path, argv); 0181 } while (ret == -1 && errno == EINTR); 0182 } 0183 0184 static void qt_native_execvp(const char *file, char *const argv[]) 0185 { 0186 int ret; 0187 do { 0188 ret = ::execvp(file, argv); 0189 } while (ret == -1 && errno == EINTR); 0190 } 0191 0192 static int qt_native_select(fd_set *fdread, fd_set *fdwrite, int timeout) 0193 { 0194 struct timeval tv; 0195 tv.tv_sec = timeout / 1000; 0196 tv.tv_usec = (timeout % 1000) * 1000; 0197 0198 int ret; 0199 do { 0200 ret = select(FD_SETSIZE, fdread, fdwrite, 0, timeout < 0 ? 0 : &tv); 0201 } while (ret < 0 && (errno == EINTR)); 0202 return ret; 0203 } 0204 0205 /* 0206 Returns the difference between msecs and elapsed. If msecs is -1, 0207 however, -1 is returned. 0208 */ 0209 static int qt_timeout_value(int msecs, int elapsed) 0210 { 0211 if (msecs == -1) 0212 return -1; 0213 0214 int timeout = msecs - elapsed; 0215 return timeout < 0 ? 0 : timeout; 0216 } 0217 0218 static int qt_qprocess_deadChild_pipe[2]; 0219 static void (*qt_sa_old_sigchld_handler)(int) = 0; 0220 static void qt_sa_sigchld_handler(int signum) 0221 { 0222 qt_native_write(qt_qprocess_deadChild_pipe[1], "", 1); 0223 #if defined (QPROCESS_DEBUG) 0224 fprintf(stderr, "*** SIGCHLD\n"); 0225 #endif 0226 0227 if (qt_sa_old_sigchld_handler && qt_sa_old_sigchld_handler != SIG_IGN) 0228 qt_sa_old_sigchld_handler(signum); 0229 } 0230 0231 0232 struct K3bQProcessInfo { 0233 K3bQProcess *process; 0234 int deathPipe; 0235 int exitResult; 0236 pid_t pid; 0237 int serialNumber; 0238 }; 0239 0240 class K3bQProcessManager : public QThread 0241 { 0242 Q_OBJECT 0243 public: 0244 K3bQProcessManager(); 0245 ~K3bQProcessManager() override; 0246 0247 void run() override; 0248 void catchDeadChildren(); 0249 void add(pid_t pid, K3bQProcess *process); 0250 void remove(K3bQProcess *process); 0251 void lock(); 0252 void unlock(); 0253 0254 private: 0255 QMutex mutex; 0256 QMap<int, K3bQProcessInfo *> children; 0257 }; 0258 0259 Q_GLOBAL_STATIC(K3bQProcessManager, processManager) 0260 0261 K3bQProcessManager::K3bQProcessManager() 0262 { 0263 #if defined (QPROCESS_DEBUG) 0264 qDebug() << "K3bQProcessManager::K3bQProcessManager()"; 0265 #endif 0266 // initialize the dead child pipe and make it non-blocking. in the 0267 // extremely unlikely event that the pipe fills up, we do not under any 0268 // circumstances want to block. 0269 ::pipe(qt_qprocess_deadChild_pipe); 0270 ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFD, FD_CLOEXEC); 0271 ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFD, FD_CLOEXEC); 0272 ::fcntl(qt_qprocess_deadChild_pipe[0], F_SETFL, 0273 ::fcntl(qt_qprocess_deadChild_pipe[0], F_GETFL) | O_NONBLOCK); 0274 ::fcntl(qt_qprocess_deadChild_pipe[1], F_SETFL, 0275 ::fcntl(qt_qprocess_deadChild_pipe[1], F_GETFL) | O_NONBLOCK); 0276 0277 // set up the SIGCHLD handler, which writes a single byte to the dead 0278 // child pipe every time a child dies. 0279 struct sigaction oldAction; 0280 struct sigaction action; 0281 memset(&action, 0, sizeof(action)); 0282 action.sa_handler = qt_sa_sigchld_handler; 0283 action.sa_flags = SA_NOCLDSTOP; 0284 qt_native_sigaction(SIGCHLD, &action, &oldAction); 0285 if (oldAction.sa_handler != qt_sa_sigchld_handler) 0286 qt_sa_old_sigchld_handler = oldAction.sa_handler; 0287 } 0288 0289 K3bQProcessManager::~K3bQProcessManager() 0290 { 0291 // notify the thread that we're shutting down. 0292 qt_native_write(qt_qprocess_deadChild_pipe[1], "@", 1); 0293 qt_native_close(qt_qprocess_deadChild_pipe[1]); 0294 wait(); 0295 0296 // on certain unixes, closing the reading end of the pipe will cause 0297 // select in run() to block forever, rather than return with EBADF. 0298 qt_native_close(qt_qprocess_deadChild_pipe[0]); 0299 0300 qt_qprocess_deadChild_pipe[0] = -1; 0301 qt_qprocess_deadChild_pipe[1] = -1; 0302 0303 qDeleteAll(children.values()); 0304 children.clear(); 0305 0306 struct sigaction oldAction; 0307 struct sigaction action; 0308 memset(&action, 0, sizeof(action)); 0309 action.sa_handler = qt_sa_old_sigchld_handler; 0310 action.sa_flags = SA_NOCLDSTOP; 0311 qt_native_sigaction(SIGCHLD, &action, &oldAction); 0312 if (oldAction.sa_handler != qt_sa_sigchld_handler) { 0313 qt_native_sigaction(SIGCHLD, &oldAction, 0); 0314 } 0315 } 0316 0317 void K3bQProcessManager::run() 0318 { 0319 forever { 0320 fd_set readset; 0321 FD_ZERO(&readset); 0322 FD_SET(qt_qprocess_deadChild_pipe[0], &readset); 0323 0324 #if defined (QPROCESS_DEBUG) 0325 qDebug() << "K3bQProcessManager::run() waiting for children to die"; 0326 #endif 0327 0328 // block forever, or until activity is detected on the dead child 0329 // pipe. the only other peers are the SIGCHLD signal handler, and the 0330 // K3bQProcessManager destructor. 0331 int nselect = select(qt_qprocess_deadChild_pipe[0] + 1, &readset, 0, 0, 0); 0332 if (nselect < 0) { 0333 if (errno == EINTR) 0334 continue; 0335 break; 0336 } 0337 0338 // empty only one byte from the pipe, even though several SIGCHLD 0339 // signals may have been delivered in the meantime, to avoid race 0340 // conditions. 0341 char c; 0342 if (qt_native_read(qt_qprocess_deadChild_pipe[0], &c, 1) < 0 || c == '@') 0343 break; 0344 0345 // catch any and all children that we can. 0346 catchDeadChildren(); 0347 } 0348 } 0349 0350 void K3bQProcessManager::catchDeadChildren() 0351 { 0352 QMutexLocker locker(&mutex); 0353 0354 // try to catch all children whose pid we have registered, and whose 0355 // deathPipe is still valid (i.e, we have not already notified it). 0356 QMap<int, K3bQProcessInfo *>::Iterator it = children.begin(); 0357 while (it != children.end()) { 0358 // notify all children that they may have died. they need to run 0359 // waitpid() in their own thread. 0360 K3bQProcessInfo *info = it.value(); 0361 qt_native_write(info->deathPipe, "", 1); 0362 0363 #if defined (QPROCESS_DEBUG) 0364 qDebug() << "K3bQProcessManager::run() sending death notice to" << info->process; 0365 #endif 0366 ++it; 0367 } 0368 } 0369 0370 static QBasicAtomicInt idCounter = Q_BASIC_ATOMIC_INITIALIZER(1); 0371 0372 void K3bQProcessManager::add(pid_t pid, K3bQProcess *process) 0373 { 0374 #if defined (QPROCESS_DEBUG) 0375 qDebug() << "K3bQProcessManager::add() adding pid" << pid << "process" << process; 0376 #endif 0377 0378 // insert a new info structure for this process 0379 K3bQProcessInfo *info = new K3bQProcessInfo; 0380 info->process = process; 0381 info->deathPipe = process->d_func()->deathPipe[1]; 0382 info->exitResult = 0; 0383 info->pid = pid; 0384 0385 int serial = idCounter.fetchAndAddRelaxed(1); 0386 process->d_func()->serial = serial; 0387 children.insert(serial, info); 0388 } 0389 0390 void K3bQProcessManager::remove(K3bQProcess *process) 0391 { 0392 QMutexLocker locker(&mutex); 0393 0394 int serial = process->d_func()->serial; 0395 K3bQProcessInfo *info = children.value(serial); 0396 if (!info) 0397 return; 0398 0399 #if defined (QPROCESS_DEBUG) 0400 qDebug() << "K3bQProcessManager::remove() removing pid" << info->pid << "process" << info->process; 0401 #endif 0402 0403 children.remove(serial); 0404 delete info; 0405 } 0406 0407 void K3bQProcessManager::lock() 0408 { 0409 mutex.lock(); 0410 } 0411 0412 void K3bQProcessManager::unlock() 0413 { 0414 mutex.unlock(); 0415 } 0416 0417 static void qt_create_pipe(int *pipe) 0418 { 0419 if (pipe[0] != -1) 0420 qt_native_close(pipe[0]); 0421 if (pipe[1] != -1) 0422 qt_native_close(pipe[1]); 0423 #ifdef Q_OS_IRIX 0424 if (::socketpair(AF_UNIX, SOCK_STREAM, 0, pipe) == -1) { 0425 qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", 0426 pipe, qPrintable(qt_error_string(errno))); 0427 } 0428 #else 0429 if (::pipe(pipe) != 0) { 0430 qWarning("QProcessPrivate::createPipe: Cannot create pipe %p: %s", 0431 pipe, qPrintable(qt_error_string(errno))); 0432 } 0433 #endif 0434 ::fcntl(pipe[0], F_SETFD, FD_CLOEXEC); 0435 ::fcntl(pipe[1], F_SETFD, FD_CLOEXEC); 0436 } 0437 0438 void K3bQProcessPrivate::destroyPipe(int *pipe) 0439 { 0440 if (pipe[1] != -1) { 0441 qt_native_close(pipe[1]); 0442 pipe[1] = -1; 0443 } 0444 if (pipe[0] != -1) { 0445 qt_native_close(pipe[0]); 0446 pipe[0] = -1; 0447 } 0448 } 0449 0450 /* 0451 Create the pipes to a K3bQProcessPrivate::Channel. 0452 0453 This function must be called in order: stdin, stdout, stderr 0454 */ 0455 bool K3bQProcessPrivate::createChannel(Channel &channel) 0456 { 0457 Q_Q(K3bQProcess); 0458 0459 if (&channel == &stderrChannel && processChannelMode == ::QProcess::MergedChannels) { 0460 channel.pipe[0] = -1; 0461 channel.pipe[1] = -1; 0462 return true; 0463 } 0464 0465 if (channel.type == Channel::Normal) { 0466 // we're piping this channel to our own process 0467 qt_create_pipe(channel.pipe); 0468 0469 // create the socket notifiers 0470 // if (threadData->eventDispatcher) { 0471 if (&channel == &stdinChannel) { 0472 channel.notifier = new QSocketNotifier(channel.pipe[1], 0473 QSocketNotifier::Write, q); 0474 QObject::connect(channel.notifier, SIGNAL(activated(int)), 0475 q, SLOT(_q_canWrite())); 0476 if (!(processFlags & K3bQProcess::RawStdin)) { 0477 // in raw mode we want to know when the process is ready to read from stdin 0478 channel.notifier->setEnabled(false); 0479 } 0480 } else if ( &channel == &stderrChannel || !(processFlags&K3bQProcess::RawStdout) ){ 0481 channel.notifier = new QSocketNotifier(channel.pipe[0], 0482 QSocketNotifier::Read, q); 0483 const char *receiver; 0484 if (&channel == &stdoutChannel) 0485 receiver = SLOT(_q_canReadStandardOutput()); 0486 else 0487 receiver = SLOT(_q_canReadStandardError()); 0488 QObject::connect(channel.notifier, SIGNAL(activated(int)), 0489 q, receiver); 0490 } 0491 // } 0492 0493 return true; 0494 } else if (channel.type == Channel::Redirect) { 0495 // we're redirecting the channel to/from a file 0496 QByteArray fname = QFile::encodeName(channel.file); 0497 0498 if (&channel == &stdinChannel) { 0499 // try to open in read-only mode 0500 channel.pipe[1] = -1; 0501 if ( (channel.pipe[0] = QT_OPEN(fname, O_RDONLY)) != -1) 0502 return true; // success 0503 0504 q->setErrorString(K3bQProcess::tr("Could not open input redirection for reading")); 0505 } else { 0506 int mode = O_WRONLY | O_CREAT; 0507 if (channel.append) 0508 mode |= O_APPEND; 0509 else 0510 mode |= O_TRUNC; 0511 0512 channel.pipe[0] = -1; 0513 if ( (channel.pipe[1] = QT_OPEN(fname, mode, 0666)) != -1) 0514 return true; // success 0515 0516 q->setErrorString(K3bQProcess::tr("Could not open output redirection for writing")); 0517 } 0518 0519 // could not open file 0520 processError = ::QProcess::FailedToStart; 0521 emit q->error(processError); 0522 cleanup(); 0523 return false; 0524 } else { 0525 Q_ASSERT_X(channel.process, "K3bQProcess::start", "Internal error"); 0526 0527 Channel *source; 0528 Channel *sink; 0529 0530 if (channel.type == Channel::PipeSource) { 0531 // we are the source 0532 source = &channel; 0533 sink = &channel.process->stdinChannel; 0534 0535 Q_ASSERT(source == &stdoutChannel); 0536 Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); 0537 } else { 0538 // we are the sink; 0539 source = &channel.process->stdoutChannel; 0540 sink = &channel; 0541 0542 Q_ASSERT(sink == &stdinChannel); 0543 Q_ASSERT(source->process == this && source->type == Channel::PipeSource); 0544 } 0545 0546 if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) { 0547 // already created, do nothing 0548 return true; 0549 } else { 0550 Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE); 0551 Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE); 0552 0553 Q_PIPE pipe[2] = { -1, -1 }; 0554 qt_create_pipe(pipe); 0555 sink->pipe[0] = pipe[0]; 0556 source->pipe[1] = pipe[1]; 0557 0558 return true; 0559 } 0560 } 0561 } 0562 0563 static char **_q_dupEnvironment(const QStringList &environment, int *envc) 0564 { 0565 // if LD_LIBRARY_PATH exists in the current environment, but 0566 // not in the environment list passed by the programmer, then 0567 // copy it over. 0568 #if defined(Q_OS_MAC) 0569 static const char libraryPath[] = "DYLD_LIBRARY_PATH"; 0570 #else 0571 static const char libraryPath[] = "LD_LIBRARY_PATH"; 0572 #endif 0573 const QString libraryPathString = QLatin1String(libraryPath); 0574 QStringList env = environment; 0575 static const QRegularExpression rx(QLatin1Char('^') + libraryPathString + QLatin1Char('=')); 0576 QStringList matches = env.filter(rx); 0577 const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath)); 0578 if (matches.isEmpty() && !envLibraryPath.isEmpty()) { 0579 QString entry = libraryPathString; 0580 entry += QLatin1Char('='); 0581 entry += envLibraryPath; 0582 env << libraryPathString + QLatin1Char('=') + envLibraryPath; 0583 } 0584 0585 char **envp = new char *[env.count() + 1]; 0586 envp[env.count()] = 0; 0587 0588 for (int j = 0; j < env.count(); ++j) { 0589 QString item = env.at(j); 0590 envp[j] = ::strdup(item.toLocal8Bit().constData()); 0591 } 0592 0593 *envc = env.count(); 0594 return envp; 0595 } 0596 0597 // under QNX RTOS we have to use vfork() when multithreading 0598 inline pid_t qt_fork() 0599 { 0600 #if defined(Q_OS_QNX) 0601 return vfork(); 0602 #else 0603 return fork(); 0604 #endif 0605 } 0606 0607 #ifdef Q_OS_MAC 0608 Q_GLOBAL_STATIC(QMutex, cfbundleMutex); 0609 #endif 0610 0611 void K3bQProcessPrivate::startProcess() 0612 { 0613 Q_Q(K3bQProcess); 0614 0615 #if defined (QPROCESS_DEBUG) 0616 qDebug("K3bQProcessPrivate::startProcess()"); 0617 #endif 0618 0619 processManager()->start(); 0620 0621 // Initialize pipes 0622 qt_create_pipe(childStartedPipe); 0623 // if (threadData->eventDispatcher) { 0624 startupSocketNotifier = new QSocketNotifier(childStartedPipe[0], 0625 QSocketNotifier::Read, q); 0626 QObject::connect(startupSocketNotifier, SIGNAL(activated(int)), 0627 q, SLOT(_q_startupNotification())); 0628 // } 0629 0630 qt_create_pipe(deathPipe); 0631 ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); 0632 ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); 0633 // if (threadData->eventDispatcher) { 0634 deathNotifier = new QSocketNotifier(deathPipe[0], 0635 QSocketNotifier::Read, q); 0636 QObject::connect(deathNotifier, SIGNAL(activated(int)), 0637 q, SLOT(_q_processDied())); 0638 // } 0639 0640 if (!createChannel(stdinChannel) || 0641 !createChannel(stdoutChannel) || 0642 !createChannel(stderrChannel)) 0643 return; 0644 0645 // Start the process (platform dependent) 0646 q->setProcessState(::QProcess::Starting); 0647 0648 // Create argument list with right number of elements, and set the final 0649 // one to 0. 0650 char **argv = new char *[arguments.count() + 2]; 0651 argv[arguments.count() + 1] = 0; 0652 0653 // Encode the program name. 0654 QByteArray encodedProgramName = QFile::encodeName(program); 0655 #ifdef Q_OS_MAC 0656 // allow invoking of .app bundles on the Mac. 0657 QFileInfo fileInfo(QString::fromUtf8(encodedProgramName.constData())); 0658 if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) { 0659 QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, 0660 QCFString(fileInfo.absoluteFilePath()), 0661 kCFURLPOSIXPathStyle, true); 0662 { 0663 // CFBundle is not reentrant, since CFBundleCreate might return a reference 0664 // to a cached bundle object. Protect the bundle calls with a mutex lock. 0665 QMutexLocker lock(cfbundleMutex()); 0666 QCFType<CFBundleRef> bundle = CFBundleCreate(0, url); 0667 url = CFBundleCopyExecutableURL(bundle); 0668 } 0669 if (url) { 0670 QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); 0671 encodedProgramName += "/Contents/MacOS/" + static_cast<QString>(str).toUtf8(); 0672 } 0673 } 0674 #endif 0675 0676 // Add the program name to the argument list. 0677 char *dupProgramName = ::strdup(encodedProgramName.constData()); 0678 argv[0] = dupProgramName; 0679 0680 // Add every argument to the list 0681 for (int i = 0; i < arguments.count(); ++i) { 0682 QString arg = arguments.at(i); 0683 #ifdef Q_OS_MAC 0684 // Mac OS X uses UTF8 for exec, regardless of the system locale. 0685 argv[i + 1] = ::strdup(arg.toUtf8().constData()); 0686 #else 0687 argv[i + 1] = ::strdup(arg.toLocal8Bit().constData()); 0688 #endif 0689 } 0690 0691 // Duplicate the environment. 0692 int envc = 0; 0693 char **envp = _q_dupEnvironment(environment, &envc); 0694 0695 // Encode the working directory if it's non-empty, otherwise just pass 0. 0696 const char *workingDirPtr = 0; 0697 QByteArray encodedWorkingDirectory; 0698 if (!workingDirectory.isEmpty()) { 0699 encodedWorkingDirectory = QFile::encodeName(workingDirectory); 0700 workingDirPtr = encodedWorkingDirectory.constData(); 0701 } 0702 0703 // If the program does not specify a path, generate a list of possible 0704 // locations for the binary using the PATH environment variable. 0705 char **path = 0; 0706 int pathc = 0; 0707 if (!program.contains(QLatin1Char('/'))) { 0708 const QString pathEnv = QString::fromLocal8Bit(::getenv("PATH")); 0709 if (!pathEnv.isEmpty()) { 0710 QStringList pathEntries = pathEnv.split(QLatin1Char(':'), Qt::SkipEmptyParts); 0711 if (!pathEntries.isEmpty()) { 0712 pathc = pathEntries.size(); 0713 path = new char *[pathc + 1]; 0714 path[pathc] = 0; 0715 0716 for (int k = 0; k < pathEntries.size(); ++k) { 0717 QByteArray tmp = QFile::encodeName(pathEntries.at(k)); 0718 if (!tmp.endsWith('/')) tmp += '/'; 0719 tmp += encodedProgramName; 0720 path[k] = ::strdup(tmp.constData()); 0721 } 0722 } 0723 } 0724 } 0725 0726 // Start the process manager, and fork off the child process. 0727 processManager()->lock(); 0728 pid_t childPid = qt_fork(); 0729 int lastForkErrno = errno; 0730 if (childPid != 0) { 0731 // Clean up duplicated memory. 0732 free(dupProgramName); 0733 for (int i = 1; i <= arguments.count(); ++i) 0734 free(argv[i]); 0735 for (int i = 0; i < envc; ++i) 0736 free(envp[i]); 0737 for (int i = 0; i < pathc; ++i) 0738 free(path[i]); 0739 delete [] argv; 0740 delete [] envp; 0741 delete [] path; 0742 } 0743 if (childPid < 0) { 0744 // Cleanup, report error and return 0745 #if defined (QPROCESS_DEBUG) 0746 qDebug("qt_fork failed: %s", qt_error_string(lastForkErrno)); 0747 #endif 0748 processManager()->unlock(); 0749 q->setProcessState(::QProcess::NotRunning); 0750 processError = ::QProcess::FailedToStart; 0751 q->setErrorString(K3bQProcess::tr("Resource error (fork failure): %1").arg(qt_error_string(lastForkErrno))); 0752 emit q->error(processError); 0753 cleanup(); 0754 return; 0755 } 0756 0757 // Start the child. 0758 if (childPid == 0) { 0759 execChild(workingDirPtr, path, argv, envp); 0760 ::_exit(-1); 0761 } 0762 0763 // Register the child. In the mean time, we can get a SIGCHLD, so we need 0764 // to keep the lock held to avoid a race to catch the child. 0765 processManager()->add(childPid, q); 0766 pid = Q_PID(childPid); 0767 processManager()->unlock(); 0768 0769 // parent 0770 // close the ends we don't use and make all pipes non-blocking 0771 ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK); 0772 qt_native_close(childStartedPipe[1]); 0773 childStartedPipe[1] = -1; 0774 0775 if (stdinChannel.pipe[0] != -1) { 0776 qt_native_close(stdinChannel.pipe[0]); 0777 stdinChannel.pipe[0] = -1; 0778 } 0779 0780 if (stdinChannel.pipe[1] != -1 && !(processFlags&K3bQProcess::RawStdin)) 0781 ::fcntl(stdinChannel.pipe[1], F_SETFL, ::fcntl(stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK); 0782 0783 if (stdoutChannel.pipe[1] != -1) { 0784 qt_native_close(stdoutChannel.pipe[1]); 0785 stdoutChannel.pipe[1] = -1; 0786 } 0787 0788 if (stdoutChannel.pipe[0] != -1 && !(processFlags&K3bQProcess::RawStdout)) 0789 ::fcntl(stdoutChannel.pipe[0], F_SETFL, ::fcntl(stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK); 0790 0791 if (stderrChannel.pipe[1] != -1) { 0792 qt_native_close(stderrChannel.pipe[1]); 0793 stderrChannel.pipe[1] = -1; 0794 } 0795 if (stderrChannel.pipe[0] != -1) 0796 ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK); 0797 } 0798 0799 void K3bQProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp) 0800 { 0801 ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored 0802 0803 Q_Q(K3bQProcess); 0804 0805 // copy the stdin socket 0806 qt_native_dup2(stdinChannel.pipe[0], fileno(stdin)); 0807 0808 // copy the stdout and stderr if asked to 0809 if (processChannelMode != ::QProcess::ForwardedChannels) { 0810 qt_native_dup2(stdoutChannel.pipe[1], fileno(stdout)); 0811 0812 // merge stdout and stderr if asked to 0813 if (processChannelMode == ::QProcess::MergedChannels) { 0814 qt_native_dup2(fileno(stdout), fileno(stderr)); 0815 } else { 0816 qt_native_dup2(stderrChannel.pipe[1], fileno(stderr)); 0817 } 0818 } 0819 0820 // make sure this fd is closed if execvp() succeeds 0821 qt_native_close(childStartedPipe[0]); 0822 ::fcntl(childStartedPipe[1], F_SETFD, FD_CLOEXEC); 0823 0824 // enter the working directory 0825 if (workingDir) 0826 qt_native_chdir(workingDir); 0827 0828 // this is a virtual call, and it base behavior is to do nothing. 0829 q->setupChildProcess(); 0830 0831 // execute the process 0832 if (environment.isEmpty()) { 0833 qt_native_execvp(argv[0], argv); 0834 } else { 0835 if (path) { 0836 char **arg = path; 0837 while (*arg) { 0838 argv[0] = *arg; 0839 #if defined (QPROCESS_DEBUG) 0840 fprintf(stderr, "K3bQProcessPrivate::execChild() searching / starting %s\n", argv[0]); 0841 #endif 0842 qt_native_execve(argv[0], argv, envp); 0843 ++arg; 0844 } 0845 } else { 0846 #if defined (QPROCESS_DEBUG) 0847 fprintf(stderr, "K3bQProcessPrivate::execChild() starting %s\n", argv[0]); 0848 #endif 0849 qt_native_execve(argv[0], argv, envp); 0850 } 0851 } 0852 0853 // notify failure 0854 #if defined (QPROCESS_DEBUG) 0855 fprintf(stderr, "K3bQProcessPrivate::execChild() failed, notifying parent process\n"); 0856 #endif 0857 qt_native_write(childStartedPipe[1], "", 1); 0858 qt_native_close(childStartedPipe[1]); 0859 childStartedPipe[1] = -1; 0860 } 0861 0862 bool K3bQProcessPrivate::processStarted() 0863 { 0864 char c; 0865 int i = qt_native_read(childStartedPipe[0], &c, 1); 0866 if (startupSocketNotifier) { 0867 startupSocketNotifier->setEnabled(false); 0868 startupSocketNotifier->deleteLater(); 0869 startupSocketNotifier = 0; 0870 } 0871 qt_native_close(childStartedPipe[0]); 0872 childStartedPipe[0] = -1; 0873 0874 #if defined (QPROCESS_DEBUG) 0875 qDebug("K3bQProcessPrivate::processStarted() == %s", i <= 0 ? "true" : "false"); 0876 #endif 0877 return i <= 0; 0878 } 0879 0880 qint64 K3bQProcessPrivate::bytesAvailableFromStdout() const 0881 { 0882 size_t nbytes = 0; 0883 qint64 available = 0; 0884 if (::ioctl(stdoutChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) 0885 available = (qint64) nbytes; 0886 #if defined (QPROCESS_DEBUG) 0887 qDebug("K3bQProcessPrivate::bytesAvailableFromStdout() == %lld", available); 0888 #endif 0889 return available; 0890 } 0891 0892 qint64 K3bQProcessPrivate::bytesAvailableFromStderr() const 0893 { 0894 size_t nbytes = 0; 0895 qint64 available = 0; 0896 if (::ioctl(stderrChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) 0897 available = (qint64) nbytes; 0898 #if defined (QPROCESS_DEBUG) 0899 qDebug("K3bQProcessPrivate::bytesAvailableFromStderr() == %lld", available); 0900 #endif 0901 return available; 0902 } 0903 0904 qint64 K3bQProcessPrivate::readFromStdout(char *data, qint64 maxlen) 0905 { 0906 qint64 bytesRead = qt_native_read(stdoutChannel.pipe[0], data, maxlen); 0907 #if defined QPROCESS_DEBUG 0908 qDebug("K3bQProcessPrivate::readFromStdout(%p \"%s\", %lld) == %lld", 0909 data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead); 0910 #endif 0911 return bytesRead; 0912 } 0913 0914 qint64 K3bQProcessPrivate::readFromStderr(char *data, qint64 maxlen) 0915 { 0916 qint64 bytesRead = qt_native_read(stderrChannel.pipe[0], data, maxlen); 0917 #if defined QPROCESS_DEBUG 0918 qDebug("K3bQProcessPrivate::readFromStderr(%p \"%s\", %lld) == %lld", 0919 data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead); 0920 #endif 0921 return bytesRead; 0922 } 0923 0924 static void qt_ignore_sigpipe() 0925 { 0926 // Set to ignore SIGPIPE once only. 0927 static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0); 0928 if (atom.testAndSetRelaxed(0, 1)) { 0929 struct sigaction noaction; 0930 memset(&noaction, 0, sizeof(noaction)); 0931 noaction.sa_handler = SIG_IGN; 0932 qt_native_sigaction(SIGPIPE, &noaction, 0); 0933 } 0934 } 0935 0936 qint64 K3bQProcessPrivate::writeToStdin(const char *data, qint64 maxlen) 0937 { 0938 qt_ignore_sigpipe(); 0939 0940 qint64 written = qt_native_write(stdinChannel.pipe[1], data, maxlen); 0941 #if defined QPROCESS_DEBUG 0942 qDebug("K3bQProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld", 0943 data, qt_prettyDebug(data, maxlen, 16).constData(), maxlen, written); 0944 #endif 0945 return written; 0946 } 0947 0948 void K3bQProcessPrivate::terminateProcess() 0949 { 0950 #if defined (QPROCESS_DEBUG) 0951 qDebug("K3bQProcessPrivate::killProcess()"); 0952 #endif 0953 if (pid) 0954 ::kill(pid_t(pid), SIGTERM); 0955 } 0956 0957 void K3bQProcessPrivate::killProcess() 0958 { 0959 #if defined (QPROCESS_DEBUG) 0960 qDebug("K3bQProcessPrivate::killProcess()"); 0961 #endif 0962 if (pid) 0963 ::kill(pid_t(pid), SIGKILL); 0964 } 0965 0966 bool K3bQProcessPrivate::waitForStarted(int msecs) 0967 { 0968 Q_Q(K3bQProcess); 0969 0970 #if defined (QPROCESS_DEBUG) 0971 qDebug("K3bQProcessPrivate::waitForStarted(%d) waiting for child to start (fd = %d)", msecs, 0972 childStartedPipe[0]); 0973 #endif 0974 0975 fd_set fds; 0976 FD_ZERO(&fds); 0977 FD_SET(childStartedPipe[0], &fds); 0978 int ret; 0979 do { 0980 ret = qt_native_select(&fds, 0, msecs); 0981 } while (ret < 0 && errno == EINTR); 0982 if (ret == 0) { 0983 processError = ::QProcess::Timedout; 0984 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 0985 #if defined (QPROCESS_DEBUG) 0986 qDebug("K3bQProcessPrivate::waitForStarted(%d) == false (timed out)", msecs); 0987 #endif 0988 return false; 0989 } 0990 0991 bool startedEmitted = _q_startupNotification(); 0992 #if defined (QPROCESS_DEBUG) 0993 qDebug("K3bQProcessPrivate::waitForStarted() == %s", startedEmitted ? "true" : "false"); 0994 #endif 0995 return startedEmitted; 0996 } 0997 0998 bool K3bQProcessPrivate::waitForReadyRead(int msecs) 0999 { 1000 Q_Q(K3bQProcess); 1001 #if defined (QPROCESS_DEBUG) 1002 qDebug("K3bQProcessPrivate::waitForReadyRead(%d)", msecs); 1003 #endif 1004 1005 QElapsedTimer stopWatch; 1006 stopWatch.start(); 1007 1008 forever { 1009 fd_set fdread; 1010 fd_set fdwrite; 1011 1012 FD_ZERO(&fdread); 1013 FD_ZERO(&fdwrite); 1014 1015 if (processState == ::QProcess::Starting) 1016 FD_SET(childStartedPipe[0], &fdread); 1017 1018 if (stdoutChannel.pipe[0] != -1) 1019 FD_SET(stdoutChannel.pipe[0], &fdread); 1020 if (stderrChannel.pipe[0] != -1) 1021 FD_SET(stderrChannel.pipe[0], &fdread); 1022 1023 FD_SET(deathPipe[0], &fdread); 1024 1025 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) 1026 FD_SET(stdinChannel.pipe[1], &fdwrite); 1027 1028 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 1029 int ret = qt_native_select(&fdread, &fdwrite, timeout); 1030 if (ret < 0) { 1031 if (errno == EINTR) 1032 continue; 1033 break; 1034 } 1035 if (ret == 0) { 1036 processError = ::QProcess::Timedout; 1037 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 1038 return false; 1039 } 1040 1041 if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { 1042 if (!_q_startupNotification()) 1043 return false; 1044 } 1045 1046 bool readyReadEmitted = false; 1047 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) { 1048 bool canRead = _q_canReadStandardOutput(); 1049 if (processChannel == ::QProcess::StandardOutput && canRead) 1050 readyReadEmitted = true; 1051 } 1052 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) { 1053 bool canRead = _q_canReadStandardError(); 1054 if (processChannel == ::QProcess::StandardError && canRead) 1055 readyReadEmitted = true; 1056 } 1057 if (readyReadEmitted) 1058 return true; 1059 1060 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) 1061 _q_canWrite(); 1062 1063 if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { 1064 if (_q_processDied()) 1065 return false; 1066 } 1067 } 1068 return false; 1069 } 1070 1071 bool K3bQProcessPrivate::waitForBytesWritten(int msecs) 1072 { 1073 Q_Q(K3bQProcess); 1074 #if defined (QPROCESS_DEBUG) 1075 qDebug("K3bQProcessPrivate::waitForBytesWritten(%d)", msecs); 1076 #endif 1077 1078 if (processFlags&K3bQProcess::RawStdin) 1079 return true; 1080 1081 QElapsedTimer stopWatch; 1082 stopWatch.start(); 1083 1084 while (!writeBuffer.isEmpty()) { 1085 fd_set fdread; 1086 fd_set fdwrite; 1087 1088 FD_ZERO(&fdread); 1089 FD_ZERO(&fdwrite); 1090 1091 if (processState == ::QProcess::Starting) 1092 FD_SET(childStartedPipe[0], &fdread); 1093 1094 if (stdoutChannel.pipe[0] != -1) 1095 FD_SET(stdoutChannel.pipe[0], &fdread); 1096 if (stderrChannel.pipe[0] != -1) 1097 FD_SET(stderrChannel.pipe[0], &fdread); 1098 1099 FD_SET(deathPipe[0], &fdread); 1100 1101 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) 1102 FD_SET(stdinChannel.pipe[1], &fdwrite); 1103 1104 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 1105 int ret = qt_native_select(&fdread, &fdwrite, timeout); 1106 if (ret < 0) { 1107 if (errno == EINTR) 1108 continue; 1109 break; 1110 } 1111 1112 if (ret == 0) { 1113 processError = ::QProcess::Timedout; 1114 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 1115 return false; 1116 } 1117 1118 if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { 1119 if (!_q_startupNotification()) 1120 return false; 1121 } 1122 1123 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) 1124 return _q_canWrite(); 1125 1126 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) 1127 _q_canReadStandardOutput(); 1128 1129 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) 1130 _q_canReadStandardError(); 1131 1132 if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { 1133 if (_q_processDied()) 1134 return false; 1135 } 1136 } 1137 1138 return false; 1139 } 1140 1141 bool K3bQProcessPrivate::waitForFinished(int msecs) 1142 { 1143 Q_Q(K3bQProcess); 1144 #if defined (QPROCESS_DEBUG) 1145 qDebug("K3bQProcessPrivate::waitForFinished(%d)", msecs); 1146 #endif 1147 1148 QElapsedTimer stopWatch; 1149 stopWatch.start(); 1150 1151 forever { 1152 fd_set fdread; 1153 fd_set fdwrite; 1154 1155 FD_ZERO(&fdread); 1156 FD_ZERO(&fdwrite); 1157 1158 if (processState == ::QProcess::Starting) 1159 FD_SET(childStartedPipe[0], &fdread); 1160 1161 if (stdoutChannel.pipe[0] != -1) 1162 FD_SET(stdoutChannel.pipe[0], &fdread); 1163 if (stderrChannel.pipe[0] != -1) 1164 FD_SET(stderrChannel.pipe[0], &fdread); 1165 1166 if (processState == ::QProcess::Running) 1167 FD_SET(deathPipe[0], &fdread); 1168 1169 if (!writeBuffer.isEmpty() && stdinChannel.pipe[1] != -1) 1170 FD_SET(stdinChannel.pipe[1], &fdwrite); 1171 1172 int timeout = qt_timeout_value(msecs, stopWatch.elapsed()); 1173 int ret = qt_native_select(&fdread, &fdwrite, timeout); 1174 if (ret < 0) { 1175 if (errno == EINTR) 1176 continue; 1177 break; 1178 } 1179 if (ret == 0) { 1180 processError = ::QProcess::Timedout; 1181 q->setErrorString(K3bQProcess::tr("Process operation timed out")); 1182 return false; 1183 } 1184 1185 if (childStartedPipe[0] != -1 && FD_ISSET(childStartedPipe[0], &fdread)) { 1186 if (!_q_startupNotification()) 1187 return false; 1188 } 1189 if (stdinChannel.pipe[1] != -1 && FD_ISSET(stdinChannel.pipe[1], &fdwrite)) 1190 _q_canWrite(); 1191 1192 if (stdoutChannel.pipe[0] != -1 && FD_ISSET(stdoutChannel.pipe[0], &fdread)) 1193 _q_canReadStandardOutput(); 1194 1195 if (stderrChannel.pipe[0] != -1 && FD_ISSET(stderrChannel.pipe[0], &fdread)) 1196 _q_canReadStandardError(); 1197 1198 if (deathPipe[0] == -1 || FD_ISSET(deathPipe[0], &fdread)) { 1199 if (_q_processDied()) 1200 return true; 1201 } 1202 } 1203 return false; 1204 } 1205 1206 bool K3bQProcessPrivate::waitForWrite(int msecs) 1207 { 1208 fd_set fdwrite; 1209 FD_ZERO(&fdwrite); 1210 FD_SET(stdinChannel.pipe[1], &fdwrite); 1211 1212 int ret; 1213 do { 1214 ret = qt_native_select(0, &fdwrite, msecs < 0 ? 0 : msecs) == 1; 1215 } while (ret < 0 && errno == EINTR); 1216 return ret == 1; 1217 } 1218 1219 void K3bQProcessPrivate::findExitCode() 1220 { 1221 Q_Q(K3bQProcess); 1222 processManager()->remove(q); 1223 } 1224 1225 bool K3bQProcessPrivate::waitForDeadChild() 1226 { 1227 Q_Q(K3bQProcess); 1228 1229 // read a byte from the death pipe 1230 char c; 1231 qt_native_read(deathPipe[0], &c, 1); 1232 1233 // check if our process is dead 1234 int exitStatus; 1235 pid_t waitResult = 0; 1236 do { 1237 waitResult = waitpid(pid_t(pid), &exitStatus, WNOHANG); 1238 } while ((waitResult == -1 && errno == EINTR)); 1239 if (waitResult > 0) { 1240 processManager()->remove(q); 1241 crashed = !WIFEXITED(exitStatus); 1242 exitCode = WEXITSTATUS(exitStatus); 1243 #if defined QPROCESS_DEBUG 1244 qDebug() << "K3bQProcessPrivate::waitForDeadChild() dead with exitCode" 1245 << exitCode << ", crashed?" << crashed; 1246 #endif 1247 return true; 1248 } 1249 #if defined QPROCESS_DEBUG 1250 qDebug() << "K3bQProcessPrivate::waitForDeadChild() not dead!"; 1251 #endif 1252 return false; 1253 } 1254 1255 void K3bQProcessPrivate::_q_notified() 1256 { 1257 } 1258 1259 /*! \internal 1260 */ 1261 bool K3bQProcessPrivate::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory, qint64 *pid) 1262 { 1263 processManager()->start(); 1264 1265 QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory); 1266 1267 // To catch the startup of the child 1268 int startedPipe[2]; 1269 ::pipe(startedPipe); 1270 // To communicate the pid of the child 1271 int pidPipe[2]; 1272 ::pipe(pidPipe); 1273 1274 pid_t childPid = qt_fork(); 1275 if (childPid == 0) { 1276 struct sigaction noaction; 1277 memset(&noaction, 0, sizeof(noaction)); 1278 noaction.sa_handler = SIG_IGN; 1279 qt_native_sigaction(SIGPIPE, &noaction, 0); 1280 1281 ::setsid(); 1282 1283 qt_native_close(startedPipe[0]); 1284 qt_native_close(pidPipe[0]); 1285 1286 pid_t doubleForkPid = qt_fork(); 1287 if (doubleForkPid == 0) { 1288 ::fcntl(startedPipe[1], F_SETFD, FD_CLOEXEC); 1289 qt_native_close(pidPipe[1]); 1290 1291 if (!encodedWorkingDirectory.isEmpty()) 1292 qt_native_chdir(encodedWorkingDirectory.constData()); 1293 1294 char **argv = new char *[arguments.size() + 2]; 1295 for (int i = 0; i < arguments.size(); ++i) { 1296 #ifdef Q_OS_MAC 1297 argv[i + 1] = ::strdup(arguments.at(i).toUtf8().constData()); 1298 #else 1299 argv[i + 1] = ::strdup(arguments.at(i).toLocal8Bit().constData()); 1300 #endif 1301 } 1302 argv[arguments.size() + 1] = 0; 1303 1304 if (!program.contains(QLatin1Char('/'))) { 1305 const QString path = QString::fromLocal8Bit(::getenv("PATH")); 1306 if (!path.isEmpty()) { 1307 QStringList pathEntries = path.split(QLatin1Char(':')); 1308 for (int k = 0; k < pathEntries.size(); ++k) { 1309 QByteArray tmp = QFile::encodeName(pathEntries.at(k)); 1310 if (!tmp.endsWith('/')) tmp += '/'; 1311 tmp += QFile::encodeName(program); 1312 argv[0] = tmp.data(); 1313 qt_native_execv(argv[0], argv); 1314 } 1315 } 1316 } else { 1317 QByteArray tmp = QFile::encodeName(program); 1318 argv[0] = tmp.data(); 1319 qt_native_execv(argv[0], argv); 1320 } 1321 1322 struct sigaction noaction; 1323 memset(&noaction, 0, sizeof(noaction)); 1324 noaction.sa_handler = SIG_IGN; 1325 qt_native_sigaction(SIGPIPE, &noaction, 0); 1326 1327 // '\1' means execv failed 1328 char c = '\1'; 1329 qt_native_write(startedPipe[1], &c, 1); 1330 qt_native_close(startedPipe[1]); 1331 ::_exit(1); 1332 } else if (doubleForkPid == -1) { 1333 struct sigaction noaction; 1334 memset(&noaction, 0, sizeof(noaction)); 1335 noaction.sa_handler = SIG_IGN; 1336 qt_native_sigaction(SIGPIPE, &noaction, 0); 1337 1338 // '\2' means internal error 1339 char c = '\2'; 1340 qt_native_write(startedPipe[1], &c, 1); 1341 } 1342 1343 qt_native_close(startedPipe[1]); 1344 qt_native_write(pidPipe[1], (const char *)&doubleForkPid, sizeof(pid_t)); 1345 qt_native_chdir("/"); 1346 ::_exit(1); 1347 } 1348 1349 qt_native_close(startedPipe[1]); 1350 qt_native_close(pidPipe[1]); 1351 1352 if (childPid == -1) { 1353 qt_native_close(startedPipe[0]); 1354 qt_native_close(pidPipe[0]); 1355 return false; 1356 } 1357 1358 char reply = '\0'; 1359 int startResult = qt_native_read(startedPipe[0], &reply, 1); 1360 int result; 1361 qt_native_close(startedPipe[0]); 1362 while (::waitpid(childPid, &result, 0) == -1 && errno == EINTR) 1363 { } 1364 bool success = (startResult != -1 && reply == '\0'); 1365 if (success && pid) { 1366 pid_t actualPid = 0; 1367 if (qt_native_read(pidPipe[0], (char *)&actualPid, sizeof(pid_t)) == sizeof(pid_t)) { 1368 *pid = actualPid; 1369 } else { 1370 *pid = 0; 1371 } 1372 } 1373 qt_native_close(pidPipe[0]); 1374 return success; 1375 } 1376 1377 void K3bQProcessPrivate::initializeProcessManager() 1378 { 1379 (void) processManager(); 1380 } 1381 1382 //QT_END_NAMESPACE 1383 1384 #include "k3bqprocess_unix.moc" 1385 1386 #endif // QT_NO_PROCESS