#include "ProcessManager.hpp" #include #include #include #include #include ProcessManager::ProcessManager(const QString &newPrefix, const QString &newId, bool useStdErr, QTextCodec* argumentsCodec, QObject *parent) : QObject(parent), prefix(newPrefix), id(newId), useStdErr(useStdErr) { if(argumentsCodec == nullptr) { this->argumentsCodec = QTextCodec::codecForName("UTF-8"); } else { this->argumentsCodec = argumentsCodec; } if(id.isEmpty()) { id = QString::number(qrand()) + "-"; } stdIn.setFileName(prefix + id + "stdin"); stdOut.setFileName(prefix + id + "stdout"); stdErr.setFileName(prefix + id + "stderr"); ctrlIn.setFileName(prefix + id + "ctrlin"); ctrlOut.setFileName(prefix + id + "ctrlout"); bool reattaching = false; if(!ctrlIn.exists()) { if(!QProcess::startDetached("ProcessManager", QStringList() << prefix + id)) { return; } qDebug() << "Started new ProcessManager"; qDebug() << "Waiting for ctrl-files"; while(!ctrlIn.exists() || !ctrlOut.exists()); } else { qDebug() << "Reattaching"; reattaching = true; } if(!ctrlIn.open(QFile::WriteOnly | QFile::Unbuffered)) { qDebug() << "ctrlin not open" << ctrlIn.errorString(); return; } qDebug() << "Opened ctrlin"; ctrlIn.write("HELLO\n"); if(!ctrlOut.open(QFile::ReadOnly)) { qDebug() << "ctrlout not open" << ctrlOut.errorString(); return; } qDebug() << "Opened ctrlout"; QThread::msleep(100); // QFile::waitForReadyRead also seems to doesn't work with fifos ctrlOut.waitForReadyRead(-1); int bytesAvailable = 0; ioctl(ctrlOut.handle(), FIONREAD, &bytesAvailable); // QFile::bytesAvailable always returns 0 on fifos while(bytesAvailable > 0) { processCtrlMessage(QString::fromUtf8(ctrlOut.readLine())); ioctl(ctrlOut.handle(), FIONREAD, &bytesAvailable); } if(!ok) { qDebug() << "not HELLO"; return; } ctrlOutNotifier = new QSocketNotifier(ctrlOut.handle(), QSocketNotifier::Read, this); connect(ctrlOutNotifier, SIGNAL(activated(int)), this, SLOT(ctrlOutReadyRead())); if(reattaching) { ctrlIn.write("STATUS\n"); QByteArray status = ctrlOut.readLine(); if(status != "NOTRUNNING\n") { QRegExp runningExp("^RUNNING: (\\d+)\n$"); if(runningExp.exactMatch(QString::fromUtf8(status))) { pid = runningExp.cap(1).toInt(); running = connectToIO(); } } } } ProcessManager::~ProcessManager() { } bool ProcessManager::isOk() { return ok; } bool ProcessManager::isRunning() { return ok && running; } bool ProcessManager::start(const QString &program, QStringList args) { if(!ok || running) { return false; } for(int i = 0; i < args.length(); ++i) { args[i] = args[i].replace('\\', "\\\\").replace(' ', "\\ "); } ctrlIn.write("START " + program.toUtf8() + " " + argumentsCodec->fromUnicode(args.join(' ')) + "\n"); QRegExp runningExp("^RUNNING: (\\d+)\n$"); if(!runningExp.exactMatch(ctrlOut.readLine())) { return false; } else { pid = runningExp.cap(1).toInt(); return running = connectToIO(); } } QByteArray ProcessManager::readAll() { return stdOut.readAll(); } QByteArray ProcessManager::readLine() { return stdOut.readLine(); } qint64 ProcessManager::write(const QByteArray &data) { if(stdIn.isWritable()) { return stdIn.write(data); } else { return -1; } } bool ProcessManager::isWritable() { return ok && running && stdIn.isOpen() && stdIn.isWritable(); } bool ProcessManager::setWorkingDirectory(const QString &path) { if(!ok || !ctrlIn.isWritable() || running) { return false; } ctrlIn.write("CD " + path.toUtf8() + "\n"); if(ctrlOut.readLine() != "CHANGED\n") { return false; } else { return true; } } void ProcessManager::kill() { if(ok && running && ctrlIn.isWritable()) { ctrlIn.write("KILL\n"); } } void ProcessManager::exit() { ctrlIn.write("EXIT\n"); } void ProcessManager::closeProgFifos() { ctrlIn.write("CLOSE\n"); stdIn.close(); stdOut.close(); stdErr.close(); DELETE_SAVE(stdOutNotifier); DELETE_SAVE(stdErrNotifier); } QString ProcessManager::ID() { return id; } void ProcessManager::ctrlOutReadyRead() { processCtrlMessage(QString::fromUtf8(ctrlOut.readLine())); } void ProcessManager::_stdOutReadyRead() { if(!stdOut.isOpen()) { DELETE_SAVE(stdOutNotifier); } emit readyRead(); } void ProcessManager::_stdErrReadyRead() { if(!stdErr.isOpen()) { DELETE_SAVE(stdErrNotifier); } emit stdErrReadyRead(); } bool ProcessManager::connectToIO() { if(!stdOut.open(QFile::ReadOnly)) { qDebug() << "stdout not open" << stdOut.errorString(); return false; } if(!stdErr.open(QFile::ReadOnly)) { qDebug() << "stderr not open" << stdOut.errorString(); return false; } if(!stdIn.open(QFile::WriteOnly | QFile::Unbuffered)) { qDebug() << "stdin not open" << stdOut.errorString(); return false; } stdOutNotifier = new QSocketNotifier(stdOut.handle(), QSocketNotifier::Read, this); connect(stdOutNotifier, SIGNAL(activated(int)), this, SLOT(_stdOutReadyRead())); if(useStdErr) { stdErrNotifier = new QSocketNotifier(stdErr.handle(), QSocketNotifier::Read, this); connect(stdErrNotifier, SIGNAL(activated(int)), this, SLOT(_stdErrReadyRead())); } return true; } void ProcessManager::processCtrlMessage(const QString& message) { QRegExp quitExp("^QUIT: (\\d+)\n"); if(quitExp.exactMatch(message)) { exitCode = quitExp.cap(1).toInt(); running = false; stdOut.close(); stdErr.close(); stdIn.close(); DELETE_SAVE(stdOutNotifier); DELETE_SAVE(stdErrNotifier); emit finished(exitCode); } else if(message == "HELLO\n") { ok = true; } }