/***************************************************************************** * smsub Server Manager Subprocess * Copyright (C) 2020-2024 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * This software is provided as-is, no warranties are given to you, we are not * responsible for anything with use of the software, you are self responsible. *****************************************************************************/ #include #include #include #include "SMSubProcess.h" #include "smsub.h" SMSubProcess::SMSubProcess(const QString &executable, const QStringList &arguments, const QString &workingDirectory, const int &termTimeout, const bool &keepAlive) : termTimeout(termTimeout), keepAlive(keepAlive) { // Set process executable, arguments and working directory process.setProgram(executable); process.setArguments(arguments); process.setWorkingDirectory(workingDirectory); // manage input channel process.setInputChannelMode(QProcess::ManagedInputChannel); // stdout and stderr in same IO stream process.setProcessChannelMode(QProcess::MergedChannels); // Connect process signal handlers QObject::connect(&process, &QProcess::readyRead, this, &SMSubProcess::readyRead); QObject::connect(&process, &QProcess::errorOccurred, this, &SMSubProcess::processError); QObject::connect(&process, &QProcess::started, this, [=]() { QTextStream(stderr) << "Subprocess started!" << smsub_endl; emit statusUpdated(true, QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); }); QObject::connect(&process, QOverload::of(&QProcess::finished), this, [=](int exitCode) { QTextStream(stderr) << "Subprocess exited!" << smsub_endl; // Exit with the same exit code as the process emit statusUpdated(false, QDateTime::currentDateTimeUtc().toSecsSinceEpoch()); if (!keepAlive) QCoreApplication::exit(exitCode); }); } void SMSubProcess::readyRead() { #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O RR!" << smsub_endl; #endif // Read process output and emit event #ifdef SMSUB_BUFFERED_READS #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O W!" << smsub_endl; #endif const QByteArray readData = process.read(1024); if (!readData.isEmpty()) emit outputWritten(readData); #else while (process.canReadLine()) { #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O WL!" << smsub_endl; #endif const QByteArray readData = process.readLine().trimmed(); emit outputWritten(readData + '\n'); } #endif } void SMSubProcess::processError(QProcess::ProcessError error) { // Handle process errors if (Q_LIKELY(error == QProcess::FailedToStart)) { QTextStream(stderr) << "Process failed to start!" << smsub_endl; if (!keepAlive) QCoreApplication::exit(1); } else if (error == QProcess::UnknownError) { QTextStream(stderr) << "Unknown error occurred!" << smsub_endl; if (!keepAlive) QCoreApplication::exit(1); } } void SMSubProcess::aboutToQuit() { // Main process terminated, terminate subprocess with set timeout if (process.state() == QProcess::Running) { process.terminate(); if (!process.waitForFinished(termTimeout)) { QTextStream(stderr) << "Failed to terminate process!" << smsub_endl; } } } void SMSubProcess::startProcess() { // Start process as requested if (process.state() == QProcess::NotRunning) process.start(QIODevice::ReadWrite); } void SMSubProcess::stopProcess() { // Terminate process as requested if (process.state() == QProcess::Running) process.terminate(); } void SMSubProcess::killProcess() { // Kill process as requested if (process.state() == QProcess::Running) process.kill(); } void SMSubProcess::writeInput(const QByteArray &input) { #ifdef SMSUB_IODEBUG QTextStream(stderr) << "Subprocess I/O IN!" << smsub_endl; #endif // Write input from Unix/IPC socket to process if (process.state() == QProcess::Running) process.write(input); }