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