Cherry-picking upstream version 0.9.0+20151024.

This commit is contained in:
Alf Gaida 2015-10-24 14:05:30 +02:00
parent c25900cfc6
commit 61e287883b
11 changed files with 366 additions and 351 deletions

View File

@ -4,5 +4,5 @@ Upstream Authors:
Copyright:
Copyright (c) 2015 LXQt team
License: LGPL-2.1
License: LGPL-2.1+
The full text of the licenses can be found in the 'COPYING' file.

View File

@ -16,6 +16,9 @@ find_package(lxqt REQUIRED QUIET)
include(LXQtCompilerSettings NO_POLICY_SCOPE)
include(LXQtTranslate)
set ( LINK_LXSU "lxsu")
set ( LINK_LXSUDO "lxsudo")
set ( HDRS
passworddialog.h
)
@ -23,7 +26,6 @@ set ( HDRS
set ( SRCS
passworddialog.cpp
sudo.cpp
su.cpp
main.cpp
)
@ -66,21 +68,40 @@ target_compile_definitions(lxqt-sudo
PRIVATE "LXQTSUDO_SUDO=\"sudo\""
PRIVATE "LXQTSUDO_SU=\"su\""
PRIVATE "LXQTSUDO=\"lxqt-sudo\""
PRIVATE "LXQTSUDO_LXSU=\"${LINK_LXSU}\""
PRIVATE "LXQTSUDO_LXSUDO=\"${LINK_LXSUDO}\""
PRIVATE "LXQT_VERSION=\"${LXQT_VERSION}\""
)
add_custom_command(TARGET lxqt-sudo POST_BUILD
COMMAND ln -f -s lxqt-sudo "${LINK_LXSU}"
WORKING_DIRECTORY .
COMMENT "Creating ${LINK_LXSU} symlink"
)
add_custom_command(TARGET lxqt-sudo POST_BUILD
COMMAND ln -f -s lxqt-sudo "${LINK_LXSUDO}"
WORKING_DIRECTORY .
COMMENT "Creating ${LINK_LXSUDO} symlink"
)
install(TARGETS
lxqt-sudo
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
COMPONENT Runtime
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/${LINK_LXSU}" "${CMAKE_CURRENT_BINARY_DIR}/${LINK_LXSUDO}"
DESTINATION "${CMAKE_INSTALL_BINDIR}"
COMPONENT Runtime
)
install(FILES
${DESKTOP_FILES}
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications"
COMPONENT Runtime
)
install(FILES
man/lxqt-sudo.1
man/lxqt-sudo.1 "man/${LINK_LXSU}.1" "man/${LINK_LXSUDO}.1"
DESTINATION "${CMAKE_INSTALL_MANDIR}/man1"
COMPONENT Runtime
)

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
lxqt-sudo (0.9.0+20151024-1) experimental; urgency=medium
* Cherry-picked upstream version 0.9.0+20151024.
-- Alf Gaida <agaida@siduction.org> Sat, 24 Oct 2015 14:05:24 +0200
lxqt-sudo (0.9.0+20150908-2) experimental; urgency=medium
* Added minimum version for liblxqt0-dev (>= 0.9.0+20150911)

View File

@ -26,92 +26,12 @@
* END_COMMON_COPYRIGHT_HEADER */
#include <LXQt/Application>
#include "passworddialog.h"
#include <QTextStream>
#include <QMessageBox>
#include <QDebug>
extern const QString app_master;
const QString app_master{QStringLiteral(LXQTSUDO)};
const QString app_version{QStringLiteral(LXQT_VERSION)};
extern const QString sudo_prog;
extern int sudo(QStringList const & args, PasswordDialog & dlg);
extern const QString su_prog;
extern int su(QStringList const & args, PasswordDialog & dlg);
void usage(QString const & err = QString())
{
if (!err.isEmpty())
QTextStream(stderr) << err << '\n';
QTextStream(stdout)
<< QObject::tr("Usage: %1 [option] [command [arguments...]]\n\n"
"GUI frontend for %2/%3\n\n"
"Arguments:\n"
" option:\n"
" -h|--help Print this help.\n"
" -v|--version Print version information.\n"
" -s|--su Use %3(1) as backend (instead of the default %2(8)).\n"
" command Command to run.\n"
" arguments Optional arguments for command.\n\n").arg(app_master).arg(sudo_prog).arg(su_prog);
if (!err.isEmpty())
QMessageBox(QMessageBox::Critical, app_master, err, QMessageBox::Ok).exec();
}
void version()
{
QTextStream(stdout)
<< QObject::tr("%1 version %2\n").arg(app_master).arg(app_version);
}
enum backend_t
{
BACK_SUDO
, BACK_SU
};
#include "sudo.h"
int main(int argc, char **argv)
{
LXQt::Application app(argc, argv, true);
app.setQuitOnLastWindowClosed(false);
backend_t backend = BACK_SUDO;
QStringList args = app.arguments();
args.removeAt(0);
if (1 >= argc)
{
usage(QObject::tr("%1: no command to run provided!").arg(app_master));
return 1;
} else
{
//simple help check
std::string arg1(argv[1]);
if ("-h" == arg1 || "--help" == arg1)
{
usage();
return 0;
} else if ("-v" == arg1 || "--version" == arg1)
{
version();
return 0;
} else if ("-s" == arg1 || "--su" == arg1)
{
backend = BACK_SU;
args.removeAt(0);
}
//any other arguments we simply forward to sudo
}
PasswordDialog dlg(args);
dlg.setModal(true);
lxqtApp->setActiveWindow(&dlg);
switch (backend)
{
case BACK_SUDO:
return sudo(args, dlg);
case BACK_SU:
return su(args, dlg);
}
Sudo s;
return s.main();
}

View File

@ -1,13 +1,18 @@
.TH lxqt-sudo 1 "" "" "LXQt\ Module"
.SH NAME
\fBlxqt-sudo\fR \- execute a command as privileged user
\fBlxqt-sudo\fR, \fBlxsu\fR \- execute a command as privileged user
.SH SYNOPSIS
\fBlxqt-sudo\fR [\fIoption\fR] [\fIcommand\fR [\fIarguments\fR]]
\fBlxqt-sudo\fR \fIoption\fR [\fIcommand\fR [\fIarguments\fR]]
.br
\fBlxsu\fR [\fIoption\fR] [\fIcommand\fR [\fIarguments\fR]]
.br
\fBlxsudo\fR [\fIoption\fR] [\fIcommand\fR [\fIarguments\fR]]
.SH DESCRIPTION
\fBlxqt-sudo\fR is a graphical QT frontend for plain \fBsudo(8)\fR or \fBsu(1)\fR(for requesting optional password in GUI fashion).
\fBlxqt-sudo\fR (and symlinks \fBlxsu\fR, \fBlxsudo\fR) is a graphical QT frontend for plain \fBsudo(8)\fR or \fBsu(1)\fR (for requesting optional password in GUI fashion).
.br
When invoked it simply spawns child \fIsudo\fR or \fIsu\fR process with requested \fIcommand\fR (and optional \fIarguments\fR). If \fIsudo\fR/\fIsu\fR requests user's password,
the GUI password dialog is shown and (after submit) the password is provided to backend.
.br
.SH OPTIONS
\fBoption\fR is one of:
.br
@ -15,11 +20,13 @@ the GUI password dialog is shown and (after submit) the password is provided to
.br
-v|--version Print version information.
.br
-s|--su Use \fBsu\fR as backend (instead of the default \fBsudo\fR).
-s|--su Use \fIsu\fR as backend (default for \fBlxqt-sudo\fR & \fBlxsudo\fR is \fIsudo\fR, for \fBlxsu\fR is \fIsu\fR).
.br
-d|--sudo Use \fIsudo\fR as backend (default for \fBlxqt-sudo\fR & \fBlxsudo\fR is \fIsudo\fR, for \fBlxsu\fR is \fIsu\fR).
.SH "REPORTING BUGS"
Report bugs to https://github.com/lxde/lxqt/issues
.SH "SEE ALSO"
\fBsudo(8)\fR
\fBsudo(8)\fR \fBsu(1)\fR
.SH AUTHOR
This manual page was created by \fBPalo Kisa\fR \fI<palo.kisa@gmail.com>\fR
for \fBLXQt\fR project.

1
man/lxsu.1 Symbolic link
View File

@ -0,0 +1 @@
lxqt-sudo.1

1
man/lxsudo.1 Symbolic link
View File

@ -0,0 +1 @@
lxqt-sudo.1

View File

@ -34,9 +34,6 @@ namespace Ui {
class PasswordDialog;
}
class Communication;
class QProcess;
class PasswordDialog : public QDialog
{
Q_OBJECT

193
su.cpp
View File

@ -1,193 +0,0 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* LXQt - a lightweight, Qt based, desktop toolset
* http://lxqt.org
*
* Copyright: 2015 LXQt team
* Authors:
* Palo Kisa <palo.kisa@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#include <LXQt/Application>
#include "passworddialog.h"
#include <QMessageBox>
#include <QSocketNotifier>
#include <QDebug>
#include <QThread>
#include <pty.h>
#include <unistd.h>
#include <memory>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
extern const QString app_master;
extern const QString su_prog;
const QString su_prog{QStringLiteral(LXQTSUDO_SU)};
static const QString su_pwd_prompt_end{QStringLiteral(": ")};
static const QChar nl{QLatin1Char('\n')};
static void child(QString const & arg, PasswordDialog & dlg)
{
std::string s_prog = su_prog.toStdString()
, s_arg = arg.toStdString();
char const * params[] = {
s_prog.c_str()
, "-c"
, s_arg.c_str()
, nullptr
};
setsid(); //session leader
execvp(params[0], const_cast<char **>(params));
//exec never returns in case of success
QTextStream{stderr, QIODevice::WriteOnly} << QObject::tr("%1: Failed to exec '%2': %3\n").arg(app_master).arg(su_prog).arg(strerror(errno));
exit(1);
}
static void stopChild(int & childPid
, int & pwdFd
, int & ret)
{
close(pwdFd);
kill(childPid, SIGINT);
int res, status;
for (int cnt = 10; 0 == (res = waitpid(childPid, &status, WNOHANG)) && 0 < cnt; --cnt)
QThread::msleep(100);
if (0 == res)
{
kill(childPid, SIGKILL);
ret = 1;
} else
{
ret = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}
childPid = -1;
}
static int parent(int childPid, int pwdFd, PasswordDialog & dlg)
{
//set the FD as non-blocking
if (0 != fcntl(pwdFd, F_SETFL, O_NONBLOCK))
{
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("Failed to set non-block: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
return 1;
}
FILE * pwd_f = fdopen(pwdFd, "r+");
if (nullptr == pwd_f)
{
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("Failed to fdopen: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
return 1;
}
QTextStream child_str{pwd_f};
int ret;
QObject::connect(&dlg, &QDialog::finished, [&] (int result)
{
if (QDialog::Accepted == result)
{
child_str << dlg.password().append(nl);
child_str.flush();
} else
{
stopChild(childPid, pwdFd, ret);
lxqtApp->quit();
}
});
bool check_pwd = true;
QString last_line;
QScopedPointer<QSocketNotifier> pwd_watcher{new QSocketNotifier{pwdFd, QSocketNotifier::Read}};
QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, [&]
{
QString line = child_str.readAll();
if (line.isEmpty())
{
pwd_watcher.reset(nullptr);
if (last_line.startsWith(QStringLiteral("%1:").arg(su_prog)))
{
pwd_watcher.reset(nullptr); //stop the notifications events
stopChild(childPid, pwdFd, ret);
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("Child '%1' process failed!\n%2").arg(su_prog).arg(last_line), QMessageBox::Ok).exec();
}
lxqtApp->quit();
} else
{
if (check_pwd)
{
//check only first output of child(su)
check_pwd = false;
if (!line.contains(nl) && line.endsWith(su_pwd_prompt_end))
{
//if now echo is turned off, su requests password struct termios tios;
struct termios tios;
Q_ASSERT(0 == tcgetattr(pwdFd, &tios));
if (!(ECHO & tios.c_lflag))
{
dlg.show();
return;
}
}
}
QTextStream{stderr, QIODevice::WriteOnly} << line;
//assuming text oriented output
QStringList lines = line.split(nl, QString::SkipEmptyParts);
last_line = lines.isEmpty() ? QString() : lines.back();
}
});
lxqtApp->exec();
if (0 < childPid)
{
int res, status;
res = waitpid(childPid, &status, 0);
ret = (childPid == res && WIFEXITED(status)) ? WEXITSTATUS(status) : 1;
}
return ret;
}
int su(QStringList const & args, PasswordDialog & dlg)
{
int pid, fd;
if (1 != args.size())
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("With %1 backend only one argument/command is supported!").arg(su_prog), QMessageBox::Ok).exec();
else if (0 == (pid = forkpty(&fd, nullptr, nullptr, nullptr)))
child(args[0], dlg); //never returns
else if (-1 == pid)
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("Failed to fork: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
else
return parent(pid, fd, dlg);
return 1;
}

312
sudo.cpp
View File

@ -25,92 +25,276 @@
*
* END_COMMON_COPYRIGHT_HEADER */
#include <LXQt/Application>
#include "sudo.h"
#include "passworddialog.h"
#include <QProcess>
#include <LXQt/Application>
#include <QTextStream>
#include <QMessageBox>
#include <QFileInfo>
#include <QSocketNotifier>
#include <QDebug>
#include <QThread>
#include <pty.h>
#include <unistd.h>
#include <memory>
#include <csignal>
#include <sys/wait.h>
#include <fcntl.h>
extern const QString sudo_prog;
const QString sudo_prog{QStringLiteral(LXQTSUDO_SUDO)};
const QString sudo_pwd_prompt{QStringLiteral("Password:\n")};
int sudo(QStringList const & args, PasswordDialog & dlg)
namespace
{
QScopedPointer<QProcess> sudo{new QProcess};
QObject::connect(&dlg, &QDialog::finished, [&sudo, &dlg] (int result)
const QString app_master{QStringLiteral(LXQTSUDO)};
const QString app_version{QStringLiteral(LXQT_VERSION)};
const QString app_lxsu{QStringLiteral(LXQTSUDO_LXSU)};
const QString app_lxsudo{QStringLiteral(LXQTSUDO_LXSUDO)};
const QString su_prog{QStringLiteral(LXQTSUDO_SU)};
const QString sudo_prog{QStringLiteral(LXQTSUDO_SUDO)};
const QString pwd_prompt_end{QStringLiteral(": ")};
const QChar nl{QLatin1Char('\n')};
void usage(QString const & err = QString())
{
if (!err.isEmpty())
QTextStream(stderr) << err << '\n';
QTextStream(stdout)
<< QObject::tr("Usage: %1 option [command [arguments...]]\n\n"
"GUI frontend for %2/%3\n\n"
"Arguments:\n"
" option:\n"
" -h|--help Print this help.\n"
" -v|--version Print version information.\n"
" -s|--su Use %3(1) as backend.\n"
" -d|--sudo Use %2(8) as backend.\n"
" command Command to run.\n"
" arguments Optional arguments for command.\n\n").arg(app_master).arg(sudo_prog).arg(su_prog);
if (!err.isEmpty())
QMessageBox(QMessageBox::Critical, app_master, err, QMessageBox::Ok).exec();
}
void version()
{
QTextStream(stdout)
<< QObject::tr("%1 version %2\n").arg(app_master).arg(app_version);
}
}
Sudo::Sudo()
: mArgs{lxqtApp->arguments()}
, mBackend{BACK_NONE}
{
QString cmd = QFileInfo(mArgs[0]).fileName();
mArgs.removeAt(0);
if (app_lxsu == cmd)
mBackend = BACK_SU;
else if (app_lxsudo == cmd || app_master == cmd)
mBackend = BACK_SUDO;
}
Sudo::~Sudo()
{
}
int Sudo::main()
{
if (0 < mArgs.size())
{
//simple option check
QString const & arg1 = mArgs[0];
if ("-h" == arg1 || "--help" == arg1)
{
usage();
return 0;
} else if ("-v" == arg1 || "--version" == arg1)
{
version();
return 0;
} else if ("-s" == arg1 || "--su" == arg1)
{
mBackend = BACK_SU;
mArgs.removeAt(0);
} else if ("-d" == arg1 || "--sudo" == arg1)
{
mBackend = BACK_SUDO;
mArgs.removeAt(0);
}
}
//any other arguments we simply forward to su/sudo
if (1 > mArgs.size())
{
usage(tr("%1: no command to run provided!").arg(app_master));
return 1;
}
if (BACK_NONE == mBackend)
{
//we were invoked through unknown link (or renamed binary)
usage(tr("%1: no backend chosen!").arg(app_master));
return 1;
} else if (BACK_SU == mBackend && 1 < mArgs.size())
{
QString cmd = mArgs.replaceInStrings(QRegExp(QStringLiteral("^(.*)$")), "'\\1'").join(QStringLiteral(" "));
QTextStream(stderr) << tr("%1: warning - got multiple arguments for %2 backend, squashing into one: %3")
.arg(app_master).arg(su_prog).arg(cmd);
mArgs.erase(++mArgs.begin(), mArgs.end());
mArgs[0] = std::move(cmd);
}
mDlg.reset(new PasswordDialog{mArgs});
mDlg->setModal(true);
lxqtApp->setActiveWindow(mDlg.data());
mChildPid = forkpty(&mPwdFd, nullptr, nullptr, nullptr);
if (0 == mChildPid)
child(); //never returns
else if (-1 == mChildPid)
QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
, tr("Failed to fork: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
else
return parent();
return 1;
}
void Sudo::child()
{
int params_cnt = 2 //1. su/sudo & last nullptr
+ (BACK_SU == mBackend ? 1 : 0) //-c for su
+ mArgs.size();
std::unique_ptr<char const *[]> params{new char const *[params_cnt]};
const char ** param_arg = params.get() + 1;
std::string program;
if (BACK_SU == mBackend)
{
program = su_prog.toStdString();
*(param_arg++) = "-c";
} else
program = sudo_prog.toStdString();
params[0] = program.c_str();
std::vector<std::string> arguments;
for (const auto & a : mArgs)
arguments.push_back(a.toStdString());
for (const auto & a : arguments)
*(param_arg++) = a.c_str();
*param_arg = nullptr;
setsid(); //session leader
execvp(params[0], const_cast<char **>(params.get()));
//exec never returns in case of success
QTextStream{stderr, QIODevice::WriteOnly} << tr("%1: Failed to exec '%2': %3\n").arg(app_master).arg(params[0]).arg(strerror(errno));
exit(1);
}
void Sudo::stopChild()
{
close(mPwdFd);
kill(mChildPid, SIGINT);
int res, status;
for (int cnt = 10; 0 == (res = waitpid(mChildPid, &status, WNOHANG)) && 0 < cnt; --cnt)
QThread::msleep(100);
if (0 == res)
{
kill(mChildPid, SIGKILL);
mRet = 1;
} else
{
mRet = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}
mChildPid = -1;
}
int Sudo::parent()
{
//set the FD as non-blocking
if (0 != fcntl(mPwdFd, F_SETFL, O_NONBLOCK))
{
QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
, tr("Failed to set non-block: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
return 1;
}
FILE * pwd_f = fdopen(mPwdFd, "r+");
if (nullptr == pwd_f)
{
QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
, tr("Failed to fdopen: %1").arg(strerror(errno)), QMessageBox::Ok).exec();
return 1;
}
QTextStream child_str{pwd_f};
QObject::connect(mDlg.data(), &QDialog::finished, [&] (int result)
{
if (QDialog::Accepted == result)
{
sudo->write(QByteArray{}.append(dlg.password().append('\n')));
child_str << mDlg->password().append(nl);
child_str.flush();
} else
{
sudo->terminate();
if (!sudo->waitForFinished(1000))
sudo->kill();
stopChild();
lxqtApp->quit();
}
});
//start background process -> sudo
sudo->setProcessChannelMode(QProcess::ForwardedOutputChannel);
sudo->setReadChannel(QProcess::StandardError);
QString last_line;
int ret;
QObject::connect(sudo.data(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished)
, [&ret, &last_line, &dlg] (int exitCode, QProcess::ExitStatus exitStatus)
QScopedPointer<QSocketNotifier> pwd_watcher{new QSocketNotifier{mPwdFd, QSocketNotifier::Read}};
QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, [&]
{
ret = QProcess::NormalExit == exitStatus ? exitCode : 255;
if (0 != ret && last_line.startsWith(QStringLiteral("%1:").arg(sudo_prog)))
QMessageBox(QMessageBox::Critical, dlg.windowTitle()
, QObject::tr("Child '%1' process failed!\n%2").arg(sudo_prog).arg(last_line), QMessageBox::Ok).exec();
lxqtApp->quit();
});
QObject::connect(sudo.data(), &QProcess::readyReadStandardError, [&sudo, &dlg, &last_line]
{
QByteArray err = sudo->readAllStandardError();
if (sudo_pwd_prompt == err.constData())
QString line = child_str.readAll();
if (line.isEmpty())
{
dlg.show();
return;
pwd_watcher.reset(nullptr);
QString const & prog = BACK_SU == mBackend ? su_prog : sudo_prog;
if (last_line.startsWith(QStringLiteral("%1:").arg(prog)))
{
pwd_watcher.reset(nullptr); //stop the notifications events
stopChild();
QMessageBox(QMessageBox::Critical, mDlg->windowTitle()
, tr("Child '%1' process failed!\n%2").arg(prog).arg(last_line), QMessageBox::Ok).exec();
}
lxqtApp->quit();
} else
{
if (line.endsWith(pwd_prompt_end))
{
//if now echo is turned off, su/sudo requests password
struct termios tios;
//loop to be sure we don't miss the flag (we can afford such small delay in "normal" output processing)
for (size_t cnt = 10; 0 < cnt && 0 == tcgetattr(mPwdFd, &tios) && (ECHO & tios.c_lflag); --cnt)
QThread::msleep(10);
if (!(ECHO & tios.c_lflag))
{
mDlg->show();
return;
}
}
QTextStream{stderr, QIODevice::WriteOnly} << line;
//assuming text oriented output
QStringList lines = line.split(nl, QString::SkipEmptyParts);
last_line = lines.isEmpty() ? QString() : lines.back();
}
QTextStream{stderr, QIODevice::WriteOnly} << err;
int nl_pos = err.lastIndexOf('\n');
if (-1 == nl_pos)
last_line += err;
else
{
if (err.endsWith('\n'))
err.remove(err.size() - 1, 1);
nl_pos = err.lastIndexOf('\n');
if (-1 != nl_pos)
err.remove(0, nl_pos + 1);
last_line = err;
}
});
//forward all stdin to child
QTextStream std_in{stdin, QIODevice::ReadOnly};
QSocketNotifier stdin_watcher{0/*stdin*/, QSocketNotifier::Read};
QObject::connect(&stdin_watcher, &QSocketNotifier::activated, [&std_in, &sudo]
{
QString line{std_in.readLine()};
if (!std_in.atEnd())
line += QLatin1Char('\n');
sudo->write(line.toStdString().c_str());
if (std_in.atEnd())
sudo->closeWriteChannel();
});
sudo->start(sudo_prog, QStringList() << QStringLiteral("-S")
<< QStringLiteral("-p") << sudo_pwd_prompt
<< args);
lxqtApp->exec();
sudo->waitForFinished(-1);
return ret;
if (0 < mChildPid)
{
int res, status;
res = waitpid(mChildPid, &status, 0);
mRet = (mChildPid == res && WIFEXITED(status)) ? WEXITSTATUS(status) : 1;
}
return mRet;
}

71
sudo.h Normal file
View File

@ -0,0 +1,71 @@
/* BEGIN_COMMON_COPYRIGHT_HEADER
* (c)LGPL2+
*
* LXQt - a lightweight, Qt based, desktop toolset
* http://lxqt.org
*
* Copyright: 2015 LXQt team
* Authors:
* Palo Kisa <palo.kisa@gmail.com>
*
* This program or library is free software; you can redistribute it
* and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA
*
* END_COMMON_COPYRIGHT_HEADER */
#ifndef SUDO_H
#define SUDO_H
#include <QObject>
#include <QScopedPointer>
#include <QStringList>
class PasswordDialog;
class Sudo : public QObject
{
Q_OBJECT
public:
enum backend_t
{
BACK_NONE
, BACK_SUDO
, BACK_SU
};
public:
Sudo();
~Sudo();
int main();
private:
//parent methods
int parent();
void stopChild();
//child methods
void child();
private:
QScopedPointer<PasswordDialog> mDlg;
QStringList mArgs;
backend_t mBackend;
int mChildPid;
int mPwdFd;
int mRet;
};
#endif //SUDO_H