📄 qprocess_unix.cpp
字号:
QByteArray fname = QFile::encodeName(channel.file); if (&channel == &stdinChannel) { // try to open in read-only mode channel.pipe[1] = -1; if ( (channel.pipe[0] = open(fname, O_RDONLY)) != -1) return true; // success q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Could not open input redirection for reading"))); } else { int mode = O_WRONLY | O_CREAT; if (channel.append) mode |= O_APPEND; else mode |= O_TRUNC; channel.pipe[0] = -1; if ( (channel.pipe[1] = open(fname, mode, 0666)) != -1) return true; // success q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Could not open output redirection for writing"))); } // could not open file processError = QProcess::FailedToStart; emit q->error(processError); cleanup(); return false; } else { Q_ASSERT_X(channel.process, "QProcess::start", "Internal error"); Channel *source; Channel *sink; if (channel.type == Channel::PipeSource) { // we are the source source = &channel; sink = &channel.process->stdinChannel; Q_ASSERT(source == &stdoutChannel); Q_ASSERT(sink->process == this && sink->type == Channel::PipeSink); } else { // we are the sink; source = &channel.process->stdoutChannel; sink = &channel; Q_ASSERT(sink == &stdinChannel); Q_ASSERT(source->process == this && source->type == Channel::PipeSource); } if (source->pipe[1] != INVALID_Q_PIPE || sink->pipe[0] != INVALID_Q_PIPE) { // already created, do nothing return true; } else { Q_ASSERT(source->pipe[0] == INVALID_Q_PIPE && source->pipe[1] == INVALID_Q_PIPE); Q_ASSERT(sink->pipe[0] == INVALID_Q_PIPE && sink->pipe[1] == INVALID_Q_PIPE); Q_PIPE pipe[2] = { -1, -1 }; qt_create_pipe(pipe); sink->pipe[0] = pipe[0]; source->pipe[1] = pipe[1]; return true; } }}static char **_q_dupEnvironment(const QStringList &environment, int *envc){ // if LD_LIBRARY_PATH exists in the current environment, but // not in the environment list passed by the programmer, then // copy it over.#if defined(Q_OS_MAC) static const char libraryPath[] = "DYLD_LIBRARY_PATH";#else static const char libraryPath[] = "LD_LIBRARY_PATH";#endif const QString libraryPathString = QLatin1String(libraryPath); QStringList env = environment; QStringList matches = env.filter( QRegExp(QLatin1Char('^') + libraryPathString + QLatin1Char('='))); const QString envLibraryPath = QString::fromLocal8Bit(::getenv(libraryPath)); if (matches.isEmpty() && !envLibraryPath.isEmpty()) { QString entry = libraryPathString; entry += QLatin1Char('='); entry += envLibraryPath; env << libraryPathString + QLatin1Char('=') + envLibraryPath; } char **envp = new char *[env.count() + 1]; envp[env.count()] = 0; for (int j = 0; j < env.count(); ++j) { QString item = env.at(j); envp[j] = ::strdup(item.toLocal8Bit().constData()); } *envc = env.count(); return envp;}void QProcessPrivate::startProcess(){ Q_Q(QProcess);#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::startProcess()");#endif processManager()->start(); // Initialize pipes qt_create_pipe(childStartedPipe); if (threadData->eventDispatcher) { startupSocketNotifier = new QSocketNotifier(childStartedPipe[0], QSocketNotifier::Read, q); QObject::connect(startupSocketNotifier, SIGNAL(activated(int)), q, SLOT(_q_startupNotification())); } qt_create_pipe(deathPipe); ::fcntl(deathPipe[0], F_SETFD, FD_CLOEXEC); ::fcntl(deathPipe[1], F_SETFD, FD_CLOEXEC); if (threadData->eventDispatcher) { deathNotifier = new QSocketNotifier(deathPipe[0], QSocketNotifier::Read, q); QObject::connect(deathNotifier, SIGNAL(activated(int)), q, SLOT(_q_processDied())); } if (!createChannel(stdinChannel) || !createChannel(stdoutChannel) || !createChannel(stderrChannel)) return; // Start the process (platform dependent) processState = QProcess::Starting; emit q->stateChanged(processState); // Create argument list with right number of elements, and set the final // one to 0. char **argv = new char *[arguments.count() + 2]; argv[arguments.count() + 1] = 0; // Encode the program name. QByteArray encodedProgramName = QFile::encodeName(program);#ifdef Q_OS_MAC // allow invoking of .app bundles on the Mac. QFileInfo fileInfo(QString::fromUtf8(encodedProgramName.constData())); if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) { QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0, QCFString(fileInfo.absoluteFilePath()), kCFURLPOSIXPathStyle, true); QCFType<CFBundleRef> bundle = CFBundleCreate(0, url); url = CFBundleCopyExecutableURL(bundle); if (url) { QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); encodedProgramName += "/Contents/MacOS/" + static_cast<QString>(str).toUtf8(); } }#endif // Add the program name to the argument list. char *dupProgramName = ::strdup(encodedProgramName.constData()); argv[0] = dupProgramName; // Add every argument to the list for (int i = 0; i < arguments.count(); ++i) { QString arg = arguments.at(i);#ifdef Q_OS_MAC // Mac OS X uses UTF8 for exec, regardless of the system locale. argv[i + 1] = ::strdup(arg.toUtf8().constData());#else argv[i + 1] = ::strdup(arg.toLocal8Bit().constData());#endif } // Duplicate the environment. int envc = 0; char **envp = _q_dupEnvironment(environment, &envc); // Encode the working directory if it's non-empty, otherwise just pass 0. const char *workingDirPtr = 0; QByteArray encodedWorkingDirectory; if (!workingDirectory.isEmpty()) { encodedWorkingDirectory = QFile::encodeName(workingDirectory); workingDirPtr = encodedWorkingDirectory.constData(); } // If the program does not specify a path, generate a list of possible // locations for the binary using the PATH environment variable. char **path = 0; int pathc = 0; if (!program.contains(QLatin1Char('/'))) { const QString pathEnv = QString::fromLocal8Bit(::getenv("PATH")); if (!pathEnv.isEmpty()) { QStringList pathEntries = pathEnv.split(QLatin1Char(':'), QString::SkipEmptyParts); if (!pathEntries.isEmpty()) { pathc = pathEntries.size(); path = new char *[pathc + 1]; path[pathc] = 0; for (int k = 0; k < pathEntries.size(); ++k) { QByteArray tmp = QFile::encodeName(pathEntries.at(k)); if (!tmp.endsWith('/')) tmp += '/'; tmp += encodedProgramName; path[k] = ::strdup(tmp.constData()); } } } } // Start the process manager, and fork off the child process. processManager()->lock(); pid_t childPid = fork(); if (childPid != 0) { // Clean up duplicated memory. free(dupProgramName); for (int i = 1; i <= arguments.count(); ++i) free(argv[i]); for (int i = 0; i < envc; ++i) free(envp[i]); for (int i = 0; i < pathc; ++i) free(path[i]); delete [] argv; delete [] envp; delete [] path; } if (childPid < 0) { // Cleanup, report error and return processManager()->unlock(); processState = QProcess::NotRunning; emit q->stateChanged(processState); processError = QProcess::FailedToStart; q->setErrorString(QLatin1String(QT_TRANSLATE_NOOP(QProcess, "Resource error (fork failure)"))); emit q->error(processError); cleanup(); return; } // Start the child. if (childPid == 0) { execChild(workingDirPtr, path, argv, envp); ::_exit(-1); } // Register the child. In the mean time, we can get a SIGCHLD, so we need // to keep the lock held to avoid a race to catch the child. processManager()->add(childPid, q); pid = Q_PID(childPid); processManager()->unlock(); // parent // close the ends we don't use and make all pipes non-blocking ::fcntl(deathPipe[0], F_SETFL, ::fcntl(deathPipe[0], F_GETFL) | O_NONBLOCK); qt_native_close(childStartedPipe[1]); childStartedPipe[1] = -1; if (stdinChannel.pipe[0] != -1) { qt_native_close(stdinChannel.pipe[0]); stdinChannel.pipe[0] = -1; } if (stdinChannel.pipe[1] != -1) ::fcntl(stdinChannel.pipe[1], F_SETFL, ::fcntl(stdinChannel.pipe[1], F_GETFL) | O_NONBLOCK); if (stdoutChannel.pipe[1] != -1) { qt_native_close(stdoutChannel.pipe[1]); stdoutChannel.pipe[1] = -1; } if (stdoutChannel.pipe[0] != -1) ::fcntl(stdoutChannel.pipe[0], F_SETFL, ::fcntl(stdoutChannel.pipe[0], F_GETFL) | O_NONBLOCK); if (stderrChannel.pipe[1] != -1) { qt_native_close(stderrChannel.pipe[1]); stderrChannel.pipe[1] = -1; } if (stderrChannel.pipe[0] != -1) ::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);}void QProcessPrivate::execChild(const char *workingDir, char **path, char **argv, char **envp){ ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored Q_Q(QProcess); // copy the stdin socket qt_native_dup2(stdinChannel.pipe[0], fileno(stdin)); // copy the stdout and stderr if asked to if (processChannelMode != QProcess::ForwardedChannels) { qt_native_dup2(stdoutChannel.pipe[1], fileno(stdout)); // merge stdout and stderr if asked to if (processChannelMode == QProcess::MergedChannels) { qt_native_dup2(fileno(stdout), fileno(stderr)); } else { qt_native_dup2(stderrChannel.pipe[1], fileno(stderr)); } } // make sure this fd is closed if execvp() succeeds qt_native_close(childStartedPipe[0]); ::fcntl(childStartedPipe[1], F_SETFD, FD_CLOEXEC); // enter the working directory if (workingDir) qt_native_chdir(workingDir); // this is a virtual call, and it base behavior is to do nothing. q->setupChildProcess(); // execute the process if (environment.isEmpty()) { qt_native_execvp(argv[0], argv); } else { if (path) { char **arg = path; while (*arg) { argv[0] = *arg;#if defined (QPROCESS_DEBUG) fprintf(stderr, "QProcessPrivate::execChild() searching / starting %s\n", argv[0]);#endif qt_native_execve(argv[0], argv, envp); ++arg; } } else {#if defined (QPROCESS_DEBUG) fprintf(stderr, "QProcessPrivate::execChild() starting %s\n", argv[0]);#endif qt_native_execve(argv[0], argv, envp); } } // notify failure#if defined (QPROCESS_DEBUG) fprintf(stderr, "QProcessPrivate::execChild() failed, notifying parent process\n");#endif qt_native_write(childStartedPipe[1], "", 1); qt_native_close(childStartedPipe[1]); childStartedPipe[1] = -1;}bool QProcessPrivate::processStarted(){ char c; int i = qt_native_read(childStartedPipe[0], &c, 1); if (startupSocketNotifier) { startupSocketNotifier->setEnabled(false); delete startupSocketNotifier; startupSocketNotifier = 0; } qt_native_close(childStartedPipe[0]); childStartedPipe[0] = -1;#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::processStarted() == %s", i <= 0 ? "true" : "false");#endif return i <= 0;}qint64 QProcessPrivate::bytesAvailableFromStdout() const{ size_t nbytes = 0; qint64 available = 0; if (::ioctl(stdoutChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) available = (qint64) *((int *) &nbytes);#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::bytesAvailableFromStdout() == %lld", available);#endif return available;}qint64 QProcessPrivate::bytesAvailableFromStderr() const{ size_t nbytes = 0; qint64 available = 0; if (::ioctl(stderrChannel.pipe[0], FIONREAD, (char *) &nbytes) >= 0) available = (qint64) *((int *) &nbytes);#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::bytesAvailableFromStderr() == %lld", available);#endif return available;}qint64 QProcessPrivate::readFromStdout(char *data, qint64 maxlen){ qint64 bytesRead = qt_native_read(stdoutChannel.pipe[0], data, maxlen);#if defined QPROCESS_DEBUG qDebug("QProcessPrivate::readFromStdout(%p \"%s\", %lld) == %lld", data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);#endif return bytesRead;}qint64 QProcessPrivate::readFromStderr(char *data, qint64 maxlen){ qint64 bytesRead = qt_native_read(stderrChannel.pipe[0], data, maxlen);#if defined QPROCESS_DEBUG qDebug("QProcessPrivate::readFromStderr(%p \"%s\", %lld) == %lld", data, qt_prettyDebug(data, bytesRead, 16).constData(), maxlen, bytesRead);#endif return bytesRead;}static void qt_ignore_sigpipe(){ // Set to ignore SIGPIPE once only. static QBasicAtomic atom = Q_ATOMIC_INIT(0); if (atom.testAndSet(0, 1)) { struct sigaction noaction; memset(&noaction, 0, sizeof(noaction)); noaction.sa_handler = SIG_IGN; qt_native_sigaction(SIGPIPE, &noaction, 0); }}qint64 QProcessPrivate::writeToStdin(const char *data, qint64 maxlen){ qt_ignore_sigpipe(); qint64 written = qt_native_write(stdinChannel.pipe[1], data, maxlen);#if defined QPROCESS_DEBUG qDebug("QProcessPrivate::writeToStdin(%p \"%s\", %lld) == %lld", data, qt_prettyDebug(data, maxlen, 16).constData(), maxlen, written);#endif return written;}void QProcessPrivate::terminateProcess(){#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::killProcess()");#endif if (pid) ::kill(pid_t(pid), SIGTERM);}void QProcessPrivate::killProcess(){#if defined (QPROCESS_DEBUG) qDebug("QProcessPrivate::killProcess()");#endif if (pid) ::kill(pid_t(pid), SIGKILL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -