diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2023-03-16 17:24:15 -0700 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2023-05-22 10:43:50 -0700 |
commit | f9c87cfd44bcf4b90cb45354252ef19f647b0469 (patch) | |
tree | 178c478b703821ead52916e2db562ee819c780c2 /tests | |
parent | 6a4afebc5ce8db69a6c9fb398cada31e6bad5e3c (diff) |
QProcess/Unix: add setUnixProcessParameters()
This commit adds those three flags that are either frequent enough or
difficult to do: close all file descriptors above stderr and reset the
signal handlers. Setting SIGPIPE to be ignored isn't critical, but is
required when the ResetSignalHandlers flag is used, as this is run
after the user child process modifier.
[ChangeLog][QtCore][QProcess] Added setUnixProcessParameters() function
that can be used to modify certain settings of the child process,
without the need to provide a callback using setChildProcessModifier().
Change-Id: Icfe44ecf285a480fafe4fffd174d0d1d63840403
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
Diffstat (limited to 'tests')
4 files changed, 185 insertions, 0 deletions
diff --git a/tests/auto/corelib/io/qprocess/CMakeLists.txt b/tests/auto/corelib/io/qprocess/CMakeLists.txt index 016fcf6666..9fbf7657fd 100644 --- a/tests/auto/corelib/io/qprocess/CMakeLists.txt +++ b/tests/auto/corelib/io/qprocess/CMakeLists.txt @@ -28,3 +28,6 @@ if(WIN32) add_subdirectory(testProcessEchoGui) add_subdirectory(testSetNamedPipeHandleState) endif() +if(UNIX) + add_subdirectory(testUnixProcessParameters) +endif() diff --git a/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt new file mode 100644 index 0000000000..9b6c48933c --- /dev/null +++ b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2023 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## testProcessNormal Binary: +##################################################################### + +qt_internal_add_executable(testUnixProcessParameters + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" + CORE_LIBRARY None + SOURCES + main.cpp +) diff --git a/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp new file mode 100644 index 0000000000..e701677403 --- /dev/null +++ b/tests/auto/corelib/io/qprocess/testUnixProcessParameters/main.cpp @@ -0,0 +1,62 @@ +// Copyright (C) 2023 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <string_view> + +#include <errno.h> +#include <fcntl.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/resource.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + if (argc < 2) { + printf("Usage: %s command [extra]\nSee source code for commands\n", + argv[0]); + return EXIT_FAILURE; + } + + std::string_view cmd = argv[1]; + errno = 0; + + if (cmd.size() == 0) { + // just checking that we did get here + return EXIT_SUCCESS; + } + + if (cmd == "reset-sighand") { + // confirm it was not ignored + struct sigaction action; + sigaction(SIGUSR1, nullptr, &action); + if (action.sa_handler == SIG_DFL) + return EXIT_SUCCESS; + fprintf(stderr, "SIGUSR1 is SIG_IGN\n"); + return EXIT_FAILURE; + } + + if (cmd == "ignore-sigpipe") { + // confirm it was ignored + struct sigaction action; + sigaction(SIGPIPE, nullptr, &action); + if (action.sa_handler == SIG_IGN) + return EXIT_SUCCESS; + fprintf(stderr, "SIGPIPE is SIG_DFL\n"); + return EXIT_FAILURE; + } + + if (cmd == "std-file-descriptors") { + int fd = atoi(argv[2]); + if (close(fd) < 0 && errno == EBADF) + return EXIT_SUCCESS; + fprintf(stderr, "%d is a valid file descriptor\n", fd); + return EXIT_FAILURE; + } + + fprintf(stderr, "Unknown command \"%s\"", cmd.data()); + return EXIT_FAILURE; +} diff --git a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp index 285126b826..069440c49b 100644 --- a/tests/auto/corelib/io/qprocess/tst_qprocess.cpp +++ b/tests/auto/corelib/io/qprocess/tst_qprocess.cpp @@ -114,6 +114,9 @@ private slots: void setChildProcessModifier_data(); void setChildProcessModifier(); void throwInChildProcessModifier(); + void unixProcessParameters_data(); + void unixProcessParameters(); + void unixProcessParametersAndChildModifier(); #endif void exitCodeTest(); void systemEnvironment(); @@ -1439,6 +1442,110 @@ void tst_QProcess::createProcessArgumentsModifier() #endif // Q_OS_WIN #ifdef Q_OS_UNIX +void tst_QProcess::unixProcessParameters_data() +{ + QTest::addColumn<QProcess::UnixProcessParameters>("params"); + QTest::addColumn<QString>("cmd"); + QTest::newRow("defaults") << QProcess::UnixProcessParameters{} << QString(); + + auto addRow = [](const char *cmd, QProcess::UnixProcessFlags flags) { + QProcess::UnixProcessParameters params = {}; + params.flags = flags; + QTest::addRow("%s", cmd) << params << cmd; + }; + using P = QProcess::UnixProcessFlag; + addRow("reset-sighand", P::ResetSignalHandlers); + addRow("ignore-sigpipe", P::IgnoreSigPipe); + addRow("std-file-descriptors", P::CloseNonStandardFileDescriptors); +} + +void tst_QProcess::unixProcessParameters() +{ + QFETCH(QProcess::UnixProcessParameters, params); + QFETCH(QString, cmd); + + // set up a few things + struct Scope { + int devnull; + struct sigaction old_sigusr1, old_sigpipe; + Scope() + { + int fd = open("/dev/null", O_RDONLY); + devnull = fcntl(fd, F_DUPFD, 100); + close(fd); + + // we ignore SIGUSR1 and reset SIGPIPE to Terminate + struct sigaction act = {}; + sigemptyset(&act.sa_mask); + act.sa_handler = SIG_IGN; + sigaction(SIGUSR1, &act, &old_sigusr1); + act.sa_handler = SIG_DFL; + sigaction(SIGPIPE, &act, &old_sigpipe); + } + ~Scope() + { + if (devnull != -1) + dismiss(); + } + void dismiss() + { + close(devnull); + sigaction(SIGUSR1, &old_sigusr1, nullptr); + sigaction(SIGPIPE, &old_sigpipe, nullptr); + devnull = -1; + } + } scope; + + QProcess process; + process.setUnixProcessParameters(params); + process.setStandardInputFile(QProcess::nullDevice()); // so we can't mess with SIGPIPE + process.setProgram("testUnixProcessParameters/testUnixProcessParameters"); + process.setArguments({ cmd, QString::number(scope.devnull) }); + process.start(); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.readAllStandardError(), QString()); + QCOMPARE(process.readAll(), QString()); + QCOMPARE(process.exitCode(), 0); + QCOMPARE(process.exitStatus(), QProcess::NormalExit); +} + +void tst_QProcess::unixProcessParametersAndChildModifier() +{ + static constexpr char message[] = "Message from the handler function\n"; + static_assert(std::char_traits<char>::length(message) <= PIPE_BUF); + QProcess process; + int pipes[2]; + + QVERIFY2(pipe(pipes) == 0, qPrintable(qt_error_string())); + auto pipeGuard0 = qScopeGuard([=] { close(pipes[0]); }); + { + auto pipeGuard1 = qScopeGuard([=] { close(pipes[1]); }); + + // verify that our modifier runs before the parameters are applied + process.setChildProcessModifier([=] { + write(pipes[1], message, strlen(message)); + }); + auto flags = QProcess::UnixProcessFlag::CloseNonStandardFileDescriptors; + process.setUnixProcessParameters({ flags }); + process.setProgram("testUnixProcessParameters/testUnixProcessParameters"); + process.setArguments({ "std-file-descriptors", QString::number(pipes[1]) }); + process.start(); + QVERIFY2(process.waitForStarted(5000), qPrintable(process.errorString())); + } // closes the writing end of the pipe + + QVERIFY(process.waitForFinished(5000)); + QCOMPARE(process.readAllStandardError(), QString()); + QCOMPARE(process.readAll(), QString()); + + char buf[2 * sizeof(message)]; + int r = read(pipes[0], buf, sizeof(buf)); + QVERIFY2(r >= 0, qPrintable(qt_error_string())); + QCOMPARE(QByteArrayView(buf, r), message); +} +#endif + +#ifdef Q_OS_UNIX static constexpr char messageFromChildProcess[] = "Message from the child process"; static_assert(std::char_traits<char>::length(messageFromChildProcess) <= PIPE_BUF); static void childProcessModifier(int fd) |