diff options
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 150 |
1 files changed, 79 insertions, 71 deletions
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index f1c8039b6b7..1a118fc9116 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -174,6 +174,19 @@ struct ChildError char function[8]; }; +// Used for argv and envp arguments to execve() +struct CharPointerList +{ + std::unique_ptr<char *[]> pointers; + + CharPointerList(const QString &argv0, const QStringList &args); + explicit CharPointerList(const QProcessEnvironmentPrivate *env); + +private: + QByteArray data; + void updatePointers(qsizetype count); +}; + struct QProcessPoller { QProcessPoller(const QProcessPrivate &proc); @@ -209,6 +222,62 @@ int QProcessPoller::poll(const QDeadlineTimer &deadline) { return qt_poll_msecs(pfds, n_pfds, deadline.remainingTime()); } + +CharPointerList::CharPointerList(const QString &program, const QStringList &args) +{ + qsizetype count = 1 + args.size(); + pointers.reset(new char *[count + 1]); + pointers[count] = nullptr; + + // we abuse the pointer array to store offsets first (QByteArray will + // reallocate, after all) + pointers[0] = reinterpret_cast<char *>(0); + data = QFile::encodeName(program); + data += '\0'; + + const auto end = args.end(); + auto it = args.begin(); + for (qsizetype i = 1; it != end; ++it, ++i) { + pointers[i] = reinterpret_cast<char *>(data.size()); + data += QFile::encodeName(*it); + data += '\0'; + } + + updatePointers(count); +} + +CharPointerList::CharPointerList(const QProcessEnvironmentPrivate *environment) +{ + if (!environment) + return; + + const QProcessEnvironmentPrivate::Map &env = environment->vars; + qsizetype count = env.size(); + pointers.reset(new char *[count + 1]); + pointers[count] = nullptr; + + const auto end = env.end(); + auto it = env.begin(); + for (qsizetype i = 0; it != end; ++it, ++i) { + // we abuse the pointer array to store offsets first (QByteArray will + // reallocate, after all) + pointers[i] = reinterpret_cast<char *>(data.size()); + + data += it.key(); + data += '='; + data += it->bytes(); + data += '\0'; + } + + updatePointers(count); +} + +void CharPointerList::updatePointers(qsizetype count) +{ + char *const base = const_cast<char *>(data.constBegin()); + for (qsizetype i = 0; i < count; ++i) + pointers[i] = base + qptrdiff(pointers[i]); +} } // anonymous namespace static bool qt_pollfd_check(const pollfd &pfd, short revents) @@ -402,31 +471,6 @@ static QString resolveExecutable(const QString &program) return program; } -static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Map &environment, int *envc) -{ - *envc = 0; - if (environment.isEmpty()) - return nullptr; - - char **envp = new char *[environment.count() + 2]; - envp[environment.count()] = nullptr; - envp[environment.count() + 1] = nullptr; - - auto it = environment.constBegin(); - const auto end = environment.constEnd(); - for ( ; it != end; ++it) { - QByteArray key = it.key(); - QByteArray value = it.value().bytes(); - key.reserve(key.length() + 1 + value.length()); - key.append('='); - key.append(value); - - envp[(*envc)++] = ::strdup(key.constData()); - } - - return envp; -} - void QProcessPrivate::startProcess() { Q_Q(QProcess); @@ -455,25 +499,9 @@ void QProcessPrivate::startProcess() // Start the process (platform dependent) q->setProcessState(QProcess::Starting); - // 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] = nullptr; - - // Resolve the program name - QString resolvedProgram = resolveExecutable(program); - argv[0] = ::strdup(QFile::encodeName(resolvedProgram).constData()); - - // Add every argument to the list - for (int i = 0; i < arguments.count(); ++i) - argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData()); - - // Duplicate the environment. - int envc = 0; - char **envp = nullptr; - if (environment.d.constData()) { - envp = _q_dupEnvironment(environment.d.constData()->vars, &envc); - } + // Prepare the arguments and the environment + const CharPointerList argv(resolveExecutable(program), arguments); + const CharPointerList envp(environment.d.constData()); // Encode the working directory if it's non-empty, otherwise just pass 0. const char *workingDirPtr = nullptr; @@ -493,16 +521,6 @@ void QProcessPrivate::startProcess() pid_t childPid; forkfd = ::forkfd(ffdflags , &childPid); int lastForkErrno = errno; - if (forkfd != FFD_CHILD_PROCESS) { - // Parent process. - // Clean up duplicated memory. - for (int i = 0; i <= arguments.count(); ++i) - free(argv[i]); - for (int i = 0; i < envc; ++i) - free(envp[i]); - delete [] argv; - delete [] envp; - } if (forkfd == -1) { // Cleanup, report error and return @@ -518,7 +536,7 @@ void QProcessPrivate::startProcess() // Start the child. if (forkfd == FFD_CHILD_PROCESS) { - execChild(workingDirPtr, argv, envp); + execChild(workingDirPtr, argv.pointers.get(), envp.pointers.get()); ::_exit(-1); } @@ -915,6 +933,9 @@ bool QProcessPrivate::startDetached(qint64 *pid) return false; } + const CharPointerList argv(resolveExecutable(program), arguments); + const CharPointerList envp(environment.d.constData()); + pid_t childPid = fork(); if (childPid == 0) { ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored @@ -940,23 +961,10 @@ bool QProcessPrivate::startDetached(qint64 *pid) // Render channels configuration. commitChannels(); - char **argv = new char *[arguments.size() + 2]; - for (int i = 0; i < arguments.size(); ++i) - argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData()); - argv[0] = ::strdup(QFile::encodeName(resolveExecutable(program)).constData()); - argv[arguments.size() + 1] = nullptr; - - // Duplicate the environment. - int envc = 0; - char **envp = nullptr; - if (environment.d.constData()) { - envp = _q_dupEnvironment(environment.d.constData()->vars, &envc); - } - - if (envp) - qt_safe_execve(argv[0], argv, envp); + if (envp.pointers) + qt_safe_execve(argv.pointers[0], argv.pointers.get(), envp.pointers.get()); else - qt_safe_execv(argv[0], argv); + qt_safe_execv(argv.pointers[0], argv.pointers.get()); reportFailed("execv: "); } else if (doubleForkPid == -1) { |