Compare commits

...

7 Commits

Author SHA1 Message Date
Alf Gaida
7141596f49 Adding upstream version 0.11.0. 2016-09-24 12:43:59 +02:00
Alf Gaida
0a33946684 Adding upstream version 0.10.96~7-gccdc1f6. 2016-07-19 18:43:37 +02:00
Alf Gaida
7382b92b4a Adding upstream version 0.10.0. 2015-11-03 00:50:45 +01:00
Alf Gaida
cb95c92327 Adding upstream version 0.9.0+20151029. 2015-10-31 11:16:51 +01:00
Alf Gaida
588579f00e Adding upstream version 0.9.0+20151024. 2015-10-24 14:29:06 +02:00
Alf Gaida
d286267068 Adding upstream version 0.9.0+20150908. 2015-09-23 00:02:08 +02:00
Alf Gaida
442af30319 Adding upstream version 0.9.0+20150831. 2015-09-04 01:22:16 +02:00
16 changed files with 563 additions and 409 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
build
translations/lxqt-sudo

View File

@ -2,7 +2,7 @@ Upstream Authors:
LXQt team: http://lxqt.org
Copyright:
Copyright (c) 2015 LXQt team
Copyright (c) 2015-2016 LXQt team
License: LGPL-2.1
The full text of the licenses can be found in the 'COPYING' file.
License: LGPL-2.1+
The full text of the licenses can be found in the 'LICENSE' file.

64
CHANGELOG Normal file
View File

@ -0,0 +1,64 @@
lxqt-sudo-0.11.0 / 2016-09-24
=============================
* Add README.md
* build: Forward translations parameters
* ts-files removal (#21)
* Remove translations (will be pulled in build time)
* Bump year
* Italian translation update
* Updated Russian translation Fix language and typos
* Add Russian translation
0.10.0 / 2015-11-02
===================
* Fix LICENSE file
* Fix starting pcmanfm-qt
* Preserve environment with sudo backend
* Update Slovak translation
* Updated translation template, updated german translation.
* Add a plus to AUTHORS license AUTHORS must match the files fixes lxde/lxqt/issues/784 fix typo choosen --> chosen
* lxqt-sudo: partial support for running cmd with arguments using su backend
* lxqt-sudo: unified processing logic for su & sudo into common code
* lxqt-sudo: lxsu & lxsudo symlinks for lxqt-sudo
* lxqt-sudo: added support for su(1) backend
* Add Polish translation
* Rename LxQt to LXQt everywhere
* Remove the country specific from language code.
* Add Greek (el) translation
* Created italian translation, fixed missing line <TS version="2.1" language="XX"> for _de.ts and -sk.ts
* Traditional Chinese translation
* Handles CMake policy CMP0063
* Use the LXQtCompilerSettings CMake module
* main: removed old useless define
* Adds Runtime install COMPONENT
* Coding style change
* Use target_compile_definitions()
* Use GNUInstallDirs
* Use CMAKE_AUTOUIC, drop qt5_wrap_ui()
* Use CMAKE_AUTOMOC, drop qt5_wrap_cpp()
* Use CMAKE_INCLUDE_CURRENT_DIR
* main: use standard liblxqt unix signal handling to quit
* Updates the build system to the Targets infrastructure
* German translation
* lxqt-sudo: licensing
0.9.0 / 2015-06-10
==================
* lxqt-sudo: direct communication with sudo
* lxqt-sudo: use dialog-password icon
* lxqt-sudo: help & version info into stdout (instead of stderr)
* man: bug reporting link fixed
* lxqt-sudo: version parameter support
* lxqt-sudo: added sk translation
* lxqt-sudo: proper communication error messages to stderr
* lxqt-sudo: removed .desktop file
* lxqt-sudo: showing big "security-high" icon
* lxqt-sudo: help/usage added
* lxqt-sudo: information about sudo's failure
* lxqt-sudo: manual page
* lxqt-sudo: do not use -b for sudo (fixes some application starting problems)
* lxqt-sudo: new GUI frontend for sudo

View File

@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
project(lxqt-sudo)
option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF)
option(PULL_TRANSLATIONS "Pull translations" ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
@ -16,12 +17,16 @@ find_package(lxqt REQUIRED QUIET)
include(LXQtCompilerSettings NO_POLICY_SCOPE)
include(LXQtTranslate)
set ( LINK_LXSU "lxsu")
set ( LINK_LXSUDO "lxsudo")
set ( HDRS
passworddialog.h
)
set ( SRCS
passworddialog.cpp
sudo.cpp
main.cpp
)
@ -43,6 +48,10 @@ lxqt_translate_ts(QM_FILES
${UIS}
INSTALL_DIR
"${LXQT_TRANSLATIONS_DIR}/${PROJECT_NAME}"
PULL_TRANSLATIONS ${PULL_TRANSLATIONS}
CLEAN_TRANSLATIONS ${CLEAN_TRANSLATIONS}
TRANSLATIONS_REPO ${TRANSLATIONS_REPO}
TRANSLATIONS_REFSPEC ${TRANSLATIONS_REFSPEC}
)
lxqt_app_translation_loader(SRCS ${PROJECT_NAME})
@ -56,27 +65,48 @@ add_executable(lxqt-sudo
target_link_libraries(lxqt-sudo
Qt5::Widgets
util
lxqt
)
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
)

View File

@ -1,9 +1,8 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@ -23,8 +22,7 @@ specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
@ -89,9 +87,9 @@ libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
@ -138,8 +136,8 @@ included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control
compilation and installation of the library.
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
@ -305,10 +303,10 @@ of these things:
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
@ -386,10 +384,9 @@ all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply, and the section as a whole is intended to apply in other
circumstances.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
@ -407,11 +404,11 @@ be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License
may add an explicit geographical distribution limitation excluding those
countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.

33
README.md Normal file
View File

@ -0,0 +1,33 @@
# lxqt-sudo
## Overview
lxqt-sudo is a graphical front-end of commands `sudo` and `su` respectively. As such it enables regular users to launch applications with permissions of other users including root.
## Installation
### Compiling source code
Runtime dependencies are qtbase, sudo (su should be installed by default on all *ix operating systems) and [liblxqt](https://github.com/lxde/liblxqt).
Installing at least one icon theme according to the [XDG Icon Theme Specification](https://www.freedesktop.org/wiki/Specifications/icon-theme-spec/) like e. g. "Oxygen Icons" is recommended to have the GUI display icons.
Additional build dependencies are CMake and optionally Git to pull latest VCS checkouts. The localization files were outsourced to repository [lxqt-l10n](https://github.com/lxde/lxqt-l10n) so the corresponding dependencies are needed, too. Please refer to this repository's `README.md` for further information.
Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems.
To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual.
### Binary packages
Official binary packages are provided by all major Linux distributions like Arch Linux, Debian (as of Debian stretch), Fedora and openSUSE. Just use your package manager to search for string `lxqt-sudo`.
## Configuration
lxqt-sudo itself does not require any configuration.
In order to use it as front-end of `sudo` the corresponding permissions have to be set, though. Most of the time this is handled by binary `visudo` or editing configuration file `/etc/sudoers` manually which both is beyond this document's scope.
## Usage
lxqt-sudo comes with a man page explaining the syntax very well so running `man 1 lxqt-sudo` should get you started.
By default `sudo` is used as backend, the choice can be enforced by command line options `--su[do]` or by using symbolic links `lxsu` and `lxsudo` which belong to regular installations of lxqt-sudo.

149
main.cpp
View File

@ -26,151 +26,12 @@
* END_COMMON_COPYRIGHT_HEADER */
#include <LXQt/Application>
#include "passworddialog.h"
#include <QFileInfo>
#include <QDir>
#include <QProcess>
#include <QTimer>
#include <QMessageBox>
#include <QSocketNotifier>
#include <sstream>
#include <QDebug>
const QString app_master{QStringLiteral(LXQTSUDO)};
const QString app_version{QStringLiteral(LXQT_VERSION)};
const QString sudo_prog{QStringLiteral(LXQTSUDO_SUDO)};
const QString sudo_pwd_prompt{QStringLiteral("Password:\n")};
void usage(QString const & err = QString())
{
if (!err.isEmpty())
QTextStream(stderr) << err << '\n';
QTextStream(stdout)
<< QObject::tr("Usage: %1 command [arguments...]\n\n"
"GUI frontend for %2\n\n"
"Arguments:\n"
" command Command to run.\n"
" arguments Optional arguments for command.\n\n").arg(app_master).arg(sudo_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);
}
int master(int argc, char **argv)
{
//master
LxQt::Application app(argc, argv, true);
app.setQuitOnLastWindowClosed(false);
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;
}
//any other arguments we simply forward to sudo
}
QStringList args = app.arguments();
//check for provided command is done before
args.removeAt(0);
PasswordDialog dlg(args);
dlg.setModal(true);
app.setActiveWindow(&dlg);
QScopedPointer<QProcess> sudo{new QProcess};
QObject::connect(&dlg, &QDialog::finished, [&sudo, &dlg] (int result)
{
if (QDialog::Accepted == result)
{
sudo->write(QByteArray{}.append(dlg.password().append('\n')));
} else
{
sudo->terminate();
if (!sudo->waitForFinished(1000))
sudo->kill();
}
});
//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)
, [&app, &ret, &last_line, &dlg] (int exitCode, QProcess::ExitStatus exitStatus)
{
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();
app.quit();
});
QObject::connect(sudo.data(), &QProcess::readyReadStandardError, [&sudo, &dlg, &last_line]
{
QByteArray err = sudo->readAllStandardError();
if (sudo_pwd_prompt == err.constData())
{
dlg.show();
return;
}
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);
app.exec();
sudo->waitForFinished(-1);
return ret;
}
#include "sudo.h"
int main(int argc, char **argv)
{
return master(argc, argv);
LXQt::Application app(argc, argv, true);
app.setQuitOnLastWindowClosed(false);
Sudo s;
return s.main();
}

View File

@ -1,17 +1,32 @@
.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 \fIcommand\fR [\fIarguments\fR]
.SH DESCRIPTION
\fBlxqt-sudo\fR is a graphical QT frontend for plain \fBsudo(8)\fR (for requesting optional password in GUI fashion).
\fBlxqt-sudo\fR \fIoption\fR [\fIcommand\fR [\fIarguments\fR]]
.br
When invoked it simply spawns child \fIsudo\fR process with requested \fIcommand\fR (and \fIarguments\fR). If \fIsudo\fR requests user's password,
the GUI password dialog is shown and (after submit) the password is provided to \fIsudo\fR.
\fBlxsu\fR [\fIoption\fR] [\fIcommand\fR [\fIarguments\fR]]
.br
\fBlxsudo\fR [\fIoption\fR] [\fIcommand\fR [\fIarguments\fR]]
.SH DESCRIPTION
\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
-h|--help Print help.
.br
-v|--version Print version information.
.br
-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

312
sudo.cpp Normal file
View File

@ -0,0 +1,312 @@
/* 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 "sudo.h"
#include "passworddialog.h"
#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>
namespace
{
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);
}
inline void env_workarounds()
{
//cleanup environment
//pcmanfm-qt will not start if the DBUS_SESSION_BUS_ADDRESS is preserved
unsetenv("DBUS_SESSION_BUS_ADDRESS");
}
}
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
+ 1 //-c for su | -E for sudo
+ 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"; //run command
} else
{
program = sudo_prog.toStdString();
*(param_arg++) = "-E"; //preserve environment
}
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;
env_workarounds();
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)
{
child_str << mDlg->password().append(nl);
child_str.flush();
} else
{
stopChild();
lxqtApp->quit();
}
});
QString last_line;
QScopedPointer<QSocketNotifier> pwd_watcher{new QSocketNotifier{mPwdFd, QSocketNotifier::Read}};
QObject::connect(pwd_watcher.data(), &QSocketNotifier::activated, [&]
{
QString line = child_str.readAll();
if (line.isEmpty())
{
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();
}
});
lxqtApp->exec();
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

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">
<context>
<name>PasswordDialog</name>
<message>
<location filename="../passworddialog.ui" line="20"/>
<location filename="../build/ui_passworddialog.h" line="129"/>
<source>LXQt sudo</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../passworddialog.ui" line="77"/>
<location filename="../build/ui_passworddialog.h" line="134"/>
<source>Command:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../passworddialog.ui" line="84"/>
<location filename="../build/ui_passworddialog.h" line="135"/>
<source>Password:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="43"/>
<source>&lt;b&gt;%1&lt;/b&gt; needs administrative privileges.
Please enter your password.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="54"/>
<source>Attempt #%1</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="57"/>
<source>Usage: %1 command [arguments...]
GUI frontend for %2
Arguments:
command Command to run.
arguments Optional arguments for command.
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../main.cpp" line="69"/>
<source>%1 version %2
</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../main.cpp" line="80"/>
<source>%1: no command to run provided!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../main.cpp" line="131"/>
<source>Child &apos;%1&apos; process failed!
%2</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">
<context>
<name>PasswordDialog</name>
<message>
<location filename="../passworddialog.ui" line="20"/>
<location filename="../build/ui_passworddialog.h" line="129"/>
<source>LXQt sudo</source>
<translation>LXQt sudo</translation>
</message>
<message>
<location filename="../passworddialog.ui" line="77"/>
<location filename="../build/ui_passworddialog.h" line="134"/>
<source>Command:</source>
<translation>Befehl:</translation>
</message>
<message>
<location filename="../passworddialog.ui" line="84"/>
<location filename="../build/ui_passworddialog.h" line="135"/>
<source>Password:</source>
<translation>Kennwort:</translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="43"/>
<source>&lt;b&gt;%1&lt;/b&gt; needs administrative privileges.
Please enter your password.</source>
<translation>&lt;b&gt;%1&lt;/b&gt; benötigt root-Rechte.
Bitte Kennwort eingeben.</translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="54"/>
<source>Attempt #%1</source>
<translation>Versuch #%1</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="57"/>
<source>Usage: %1 command [arguments...]
GUI frontend for %2
Arguments:
command Command to run.
arguments Optional arguments for command.
</source>
<translation>Verwendung: %1 command [arguments...]
graphische Benutzeroberfläche für %2
Argumente:
command auszuführender Befehl
arguments Argumente zum auszuführenden Befehl
</translation>
</message>
<message>
<location filename="../main.cpp" line="69"/>
<source>%1 version %2
</source>
<translation>%1 Version %2
</translation>
</message>
<message>
<location filename="../main.cpp" line="80"/>
<source>%1: no command to run provided!</source>
<translation>%1: kein auszuführender Befehl angegeben!</translation>
</message>
<message>
<location filename="../main.cpp" line="131"/>
<source>Child &apos;%1&apos; process failed!
%2</source>
<translation>nachgeordneter Prozess &apos;%1&apos; ist fehlgeschlagen!
%2</translation>
</message>
</context>
</TS>

View File

@ -1,80 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">
<context>
<name>PasswordDialog</name>
<message>
<location filename="../passworddialog.ui" line="20"/>
<location filename="../build/ui_passworddialog.h" line="129"/>
<source>LXQt sudo</source>
<translation>LXQt sudo</translation>
</message>
<message>
<location filename="../passworddialog.ui" line="77"/>
<location filename="../build/ui_passworddialog.h" line="134"/>
<source>Command:</source>
<translation>Príkaz:</translation>
</message>
<message>
<location filename="../passworddialog.ui" line="84"/>
<location filename="../build/ui_passworddialog.h" line="135"/>
<source>Password:</source>
<translation>Heslo:</translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="43"/>
<source>&lt;b&gt;%1&lt;/b&gt; needs administrative privileges.
Please enter your password.</source>
<translation>&lt;b&gt;%1&lt;/b&gt; vyžaduje práva administrátora.
Prosím, zadajte svoje heslo.</translation>
</message>
<message>
<location filename="../passworddialog.cpp" line="54"/>
<source>Attempt #%1</source>
<translation>Pokus č. %1</translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="57"/>
<source>Usage: %1 command [arguments...]
GUI frontend for %2
Arguments:
command Command to run.
arguments Optional arguments for command.
</source>
<translation>Použitie: %1 command [arguments...]
GUI frontend pre %2
Parametre:
command Príkaz na spustenie.
arguments Parametre príkazu.
</translation>
</message>
<message>
<location filename="../main.cpp" line="69"/>
<source>%1 version %2
</source>
<translation>%1 verzia %2
</translation>
</message>
<message>
<location filename="../main.cpp" line="80"/>
<source>%1: no command to run provided!</source>
<translation>%1: žiaden príkaz na spustenie!</translation>
</message>
<message>
<location filename="../main.cpp" line="131"/>
<source>Child &apos;%1&apos; process failed!
%2</source>
<translation>Dcérsky &apos;%1&apos; process zlyhal!
%2</translation>
</message>
</context>
</TS>