Description: Enable bidirectional communication lxqt-sudo shows you what an executed process prints to stdout/stderr, but it doesn't send anything written to lxqt-sudo's stdin to the child process. This patch fixes that and adds some small miscellaneous improvements. Origin: https://github.com/ArrayBolt3/lxqt-sudo/compare/d69855c2900d10c2aae4d39678b71aadf37dc37c...4f430cb0fe240566e74ade15e1657b1f99177c12 Forwarded: https://github.com/lxqt/lxqt-sudo/pull/210 Last-Update: 2024-01-11 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ diff --git a/sudo.cpp b/sudo.cpp index d12f77c..eebcf5c 100644 --- a/sudo.cpp +++ b/sudo.cpp @@ -68,6 +68,7 @@ namespace const QString doas_prog{QStringLiteral(LXQTSUDO_DOAS)}; const QString pwd_prompt_end{QStringLiteral(": ")}; const QChar nl{QLatin1Char('\n')}; + constexpr int term_eol_size = 2; void usage(QString const & err = QString()) { @@ -374,12 +375,14 @@ int Sudo::parent() } QTextStream child_str{pwd_f}; + // pseudoterminal echoes everything written into it's input; we don't want duplicating input + int inhibit_count = 0; QObject::connect(mDlg.data(), &QDialog::finished, [&] (int result) { if (QDialog::Accepted == result) { - child_str << mDlg->password().append(nl); + child_str << mDlg->password() << nl; child_str.flush(); } else { @@ -388,6 +391,8 @@ int Sudo::parent() }); QString last_line; + QString const & error_check = QStringLiteral("%1:").arg(backendName()); + QTextStream stderr_str{stderr, QIODevice::WriteOnly}; QScopedPointer pwd_watcher{new QSocketNotifier{mPwdFd, QSocketNotifier::Read}}; auto reader = [&] { @@ -396,11 +401,10 @@ int Sudo::parent() { pwd_watcher.reset(nullptr); //stop the notifications events - QString const & prog = backendName(); - if (last_line.startsWith(QStringLiteral("%1:").arg(prog))) + if (last_line.startsWith(error_check)) { QMessageBox(QMessageBox::Critical, mDlg->windowTitle() - , tr("Child '%1' process failed!\n%2").arg(prog).arg(last_line), QMessageBox::Ok).exec(); + , tr("Child '%1' process failed!\n%2").arg(backendName()).arg(last_line), QMessageBox::Ok).exec(); } } else { @@ -414,18 +418,56 @@ int Sudo::parent() if (!(ECHO & tios.c_lflag)) { mDlg->show(); - return; } } - QTextStream{stderr, QIODevice::WriteOnly} << line; - //assuming text oriented output - QStringList lines = line.split(nl, Qt::SkipEmptyParts); - last_line = lines.isEmpty() ? QString() : lines.back(); + if (inhibit_count > 0) + { + if (inhibit_count < line.count()) + { + stderr_str << line.right(line.count() - inhibit_count); + stderr_str.flush(); + inhibit_count = 0; + } else + { + inhibit_count -= line.count(); + } + } else + { + stderr_str << line; + stderr_str.flush(); + } + + //assuming text oriented output; find the last non-empty line + auto i = line.crbegin(), i_end = line.crbegin(), i_crend = line.crend(); + do { + i_end = i + 1; + i = std::find(i_end, i_crend, nl); + } while (i != i_crend && std::distance(i, i_end) == 0); + + last_line.clear(); + last_line.reserve(std::distance(i, i_end)); + std::for_each(i.base(), i_end.base(), [&last_line](decltype (*i.base()) val) { last_line.append(val); }); } }; + QTextStream stdin_str{stdin, QIODevice::ReadOnly}; + QScopedPointer stdin_watcher{new QSocketNotifier{STDIN_FILENO, QSocketNotifier::Read}}; + auto writer = [&] + { + QString line = stdin_str.readLine(); + if (line.isEmpty()) { + stdin_watcher.reset(nullptr); //stop the notification events + } else + { + inhibit_count += line.count() + term_eol_size; + child_str << line << nl; + child_str.flush(); + } + }; + QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, reader); + QObject::connect(stdin_watcher.data(), &QSocketNotifier::activated, writer); std::unique_ptr child_waiter; QTimer::singleShot(0, [&child_waiter, this] {