Используйте QProcess для отправки EndOfText (Ctrl-C) в интерактивную оболочку - PullRequest
0 голосов
/ 06 февраля 2019

Я использую QProcess , чтобы открыть /bin/sh или /usr/bin/bash, и можно писать команды в оболочку и считывать вывод в мою программу.

Фактическая проблема возникает, когдапытаюсь послать управляющий сигнал конца текста в оболочку, чтобы прервать запущенный дочерний процесс оболочки.

Что я пробовал:

  • Оболочка запускается в -i nteractive mode
  • Я использую встроенную в оболочку команду set -m, чтобы включить управление заданиями
  • В целях отладки я зачитал переменную $-, похоже, himBHs
  • Отправка произвольных команд обычно работает (например, ls)
  • Отправка \x04 (конец передачи, Ctrl + D) работает и убивает оболочку.

Как можно соответствующим образом уничтожить запущенный процесс, не открывая снова оболочку?

QProcess process;
process.start("/bin/sh", QStringList() << "-i");
process.write("set -m\necho $-\n");                 // returns himBHs
process.waitForBytesWritten();

// start a running program here (E.g. tail -f logfile)
process.write("tail -f logfile\n");

process.write("\x03");
process.write("newcommand\n");
process.waitForBytesWritten();

Выполнение первой команды внутри оболочки возвращает вывод на стандартный вывод, но я больше ничего не получаю после отправки ETX иследующая команда, хотя оболочка все ещевыполняется (process.state() == QProcess::Running)

  1. Есть ли лучший способ посылать контрольные сигналы или общаться с дочерним процессом ребенка?
  2. Что я мог сделать, чтобы запустить новую программу внутри оболочки, не открывая ее снова?(Я спрашиваю об этом потому, что программа, вероятно, будет использовать ssh в качестве оболочки, и я хочу, чтобы она не инициировала совершенно новое соединение для незначительного изменения программы / аргумента)

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Оболочка никогда не видит Ctrl-C.Он интерпретируется (псевдо) -терминалом и преобразуется в SIGINT, после чего выполняется действие.

Локально запускайте программу в под-оболочке, которая сообщает о своем pid, а затем используйте этот PID для его непосредственного уничтожения.

#include <QtCore>
#include <signal.h>
#include <cstdio>

int getPID(const QByteArray &line) {
   int pid = 0;
   char c1, c2;
   if (sscanf(line.data(), "@@@%d@@%c%c", &pid, &c1, &c2) == 3)
      if (c1 == '@' && (c2 == '\r' || c2 == '\n')) return pid;
   return 0;
}

int main(int argc, char *argv[]) {
   auto input = QByteArray(
                    "echo _kill_me_now_ > log\n"
                    "/bin/sh -c 'echo @@@$$@@@>&2; exec tail -f log'\n"
                    "echo done\n"
                    "exit\n")
                    .split('\n');
   // tail -f will block

   QCoreApplication app(argc, argv);
   QProcess process;
   int pid = 0;

   auto const writeInputLine = [&] {
      if (input.isEmpty()) return;
      auto const line = input.takeFirst();
      puts(line.data());
      fflush(stdout);
      process.write(line);
      process.write("\n");
   };

   process.setProcessChannelMode(QProcess::SeparateChannels);
   QObject::connect(&process, &QProcess::stateChanged, [](auto state) {
      auto static const meta = QMetaEnum::fromType<QProcess::ProcessState>();
      fprintf(stderr, "State=%s\n", meta.key(state));
      fflush(stderr);
      if (state == QProcess::NotRunning) QCoreApplication::quit();
   });
   QObject::connect(&process, &QProcess::readyReadStandardError, [&] {
      auto const data = process.readAllStandardError();
      if (auto p = getPID(data)) pid = p; // we could suppress pid output here
      fputs(data.data(), stdout);
      fflush(stdout);
      if (data.endsWith("$ ")) writeInputLine();
   });
   QObject::connect(&process, &QProcess::readyReadStandardOutput, [&] {
      while (process.canReadLine()) {
         auto const line = process.readLine();
         fputs(line.data(), stdout);
         if (line.startsWith("_kill_me_now_") && pid) {
            kill(pid, SIGTERM);
            pid = 0;
         }
      }
      fflush(stdout);
   });

   process.start("/bin/sh", {"--noediting", "-i"});
   return app.exec();
}

С помощью ssh, поскольку вам нужно переслать сигнал удаленному процессу, и, таким образом, вам необходим удаленный управляющий терминал (ssh -t).И для что вы будете отправлять Ctrl-C, что удаленный терминал будет интерпретировать как правильный сигнал.

0 голосов
/ 06 февраля 2019

Вы пытались играть с сигналом и слотами для QProcess?Вот простой пример:

test::test(QObject* p)
    : QObject (p),
      process{}
{

    connect(&process, SIGNAL(finished(int, QProcess::ExitStatus) ),
                    this, SLOT(hFinish()) );

    process.start("/bin/sh", QStringList() << "-i");
    process.write("set -m\necho $-\n");                 // returns himBHs
    process.waitForBytesWritten();

    // start a running program here (E.g. tail -f logfile)
    process.write("tail -f logfile\n");

    process.write("\x03");
    process.write("newcommand\n");
    process.waitForBytesWritten();

}

void test::hFinish()
{
    this->process.kill();
}
...