13#include "ptyprocess.h"
15#include "ptyprocess_p.h"
17#include <config-kdesu.h>
27#include <sys/resource.h>
33#include <sys/select.h>
37#include <QStandardPaths>
39#include <KConfigGroup>
40#include <KSharedConfig>
42extern int kdesuDebugArea();
46using namespace KDESuPrivate;
58 tv.tv_usec = 1000 * ms;
63 return select(
fd + 1, &fds,
nullptr,
nullptr, &tv);
76 KConfigGroup cg(config, QStringLiteral(
"super-user-command"));
82 return kill(
pid, 0) == 0;
97 ret = waitpid(
pid, &state, WNOHANG);
100 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
101 <<
"waitpid():" << strerror(errno);
105 if (WIFEXITED(state)) {
106 return WEXITSTATUS(state);
115PtyProcess::PtyProcess()
120PtyProcess::PtyProcess(PtyProcessPrivate &dd)
127PtyProcess::~PtyProcess() =
default;
129int PtyProcess::init()
135 if (!d->pty->open()) {
136 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
137 <<
"Failed to open PTY.";
140 if (!d->wantLocalEcho) {
143 d->inputBuffer.resize(0);
159 return d->pty ? d->pty->masterFd() : -1;
180 if (!d->inputBuffer.isEmpty()) {
184 ret = d->inputBuffer;
188 int flags = fcntl(
fd(), F_GETFL);
190 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
191 <<
"fcntl(F_GETFL):" << strerror(errno);
196 flags &= ~O_NONBLOCK;
201 if ((flags != oflags) && (fcntl(
fd(), F_SETFL, flags) < 0)) {
209 int nbytes = read(
fd(), ret.
data() + ret.
size(), 0x8000);
211 if (errno == EINTR) {
232 d->inputBuffer =
readAll(block);
236 if (!d->inputBuffer.isEmpty()) {
237 pos = d->inputBuffer.indexOf(
'\n');
240 ret = d->inputBuffer;
243 ret = d->inputBuffer.
left(pos);
244 d->inputBuffer.
remove(0, pos + 1);
257 write(
fd(),
"\n", 1);
270 d->inputBuffer.prepend(tmp);
292 if ((
m_pid = fork()) == -1) {
293 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
294 <<
"fork():" << strerror(errno);
300 d->pty->closeSlave();
305 if (setupTTY() < 0) {
309 for (
const QByteArray &var : std::as_const(d->env)) {
310 putenv(
const_cast<char *
>(var.constData()));
312 unsetenv(
"KDE_FULL_SESSION");
314 unsetenv(
"SESSION_MANAGER");
317 unsetenv(
"DBUS_SESSION_BUS_ADDRESS");
320 const QByteArray old_lc_all = qgetenv(
"LC_ALL");
322 qputenv(
"KDESU_LC_ALL", old_lc_all);
324 unsetenv(
"KDESU_LC_ALL");
326 qputenv(
"LC_ALL",
"C");
336 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] " << command <<
"not found.";
342 const char **argp = (
const char **)malloc((args.
count() + 2) *
sizeof(
char *));
347 argp[i++] = arg.constData();
352 execv(path.
constData(),
const_cast<char **
>(argp));
353 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
354 <<
"execv(" << path <<
"):" << strerror(errno);
375 qCCritical(KSU_LOG) <<
"process has exited while waiting for password.";
378 if (!d->pty->tcGetAttr(&tio)) {
379 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
380 <<
"tcgetattr():" << strerror(errno);
383 if (tio.c_lflag & ECHO) {
397 d->wantLocalEcho = enable;
403 return d->pty->setEcho(enable) ? 0 : -1;
438 timeout.tv_usec = 100000;
439 int ret = select(
fd() + 1, &fds,
nullptr,
nullptr, &timeout);
441 if (errno != EINTR) {
442 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
443 <<
"select():" << strerror(errno);
464 kill(
m_pid, SIGTERM);
467 int off = remainder.
indexOf(
'\n');
471 remainder.
remove(0, off + 1);
479 if (errno == ECHILD) {
484 }
else if (ret ==
Killed) {
500int PtyProcess::setupTTY()
505 for (
int sig = 1; sig < NSIG; sig++) {
506 signal(sig, SIG_DFL);
508 signal(SIGHUP, SIG_IGN);
513 int slave = d->pty->slaveFd();
522 getrlimit(RLIMIT_NOFILE, &rlp);
523 for (
int i = 3; i < (int)rlp.rlim_cur; i++) {
529 struct ::termios tio;
530 if (tcgetattr(0, &tio) < 0) {
531 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
532 <<
"tcgetattr():" << strerror(errno);
535 tio.c_oflag &= ~OPOST;
536 if (tcsetattr(0, TCSANOW, &tio) < 0) {
537 qCCritical(KSU_LOG) <<
"[" << __FILE__ <<
":" << __LINE__ <<
"] "
538 <<
"tcsetattr():" << strerror(errno);
QString readEntry(const char *key, const char *aDefault=nullptr) const
Synchronous communication with tty programs.
int waitSlave()
Waits until the pty has cleared the ECHO flag.
void setExitString(const QByteArray &exit)
Sets the exit string.
int m_pid
PID of child process.
static int waitMS(int fd, int ms)
Wait ms milliseconds (ie.
int pid() const
Returns the pid of the process.
QByteArray readLine(bool block=true)
Reads a line from the program's standard out.
@ NotExited
Child hasn't exited.
@ Killed
Child terminated by signal.
QByteArray m_exitString
String to scan for in output that indicates child has exited.
QByteArray readAll(bool block=true)
Read all available output from the program's standard out.
int exec(const QByteArray &command, const QList< QByteArray > &args)
Forks off and execute a command.
bool m_terminal
Indicates running in a terminal, causes additional newlines to be printed after output.
void setEnvironment(const QList< QByteArray > &env)
Set additinal environment variables.
void setErase(bool erase)
Overwrites the password as soon as it is used.
int fd() const
Returns the filedescriptor of the process.
void writeLine(const QByteArray &line, bool addNewline=true)
Writes a line of text to the program's standard in.
static int checkPidExited(pid_t pid)
Check process exit status for process pid.
static bool checkPid(pid_t pid)
Basic check for the existence of pid.
int waitForChild()
Waits for the child to exit.
QList< QByteArray > environment() const
Returns the additional environment variables set by setEnvironment()
int enableLocalEcho(bool enable=true)
Enables/disables local echo on the pseudo tty.
virtual void virtual_hook(int id, void *data)
Standard hack to add virtual methods in a BC way.
void unreadLine(const QByteArray &line, bool addNewline=true)
Puts back a line of input.
void setTerminal(bool terminal)
Enables/disables terminal output.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
const char * constData() const const
bool contains(QByteArrayView bv) const const
qsizetype indexOf(QByteArrayView bv, qsizetype from) const const
bool isEmpty() const const
QByteArray left(qsizetype len) const const
qsizetype length() const const
QByteArray & remove(qsizetype pos, qsizetype len)
void reserve(qsizetype size)
void resize(qsizetype newSize, char c)
qsizetype size() const const
bool startsWith(QByteArrayView bv) const const
QString decodeName(const QByteArray &localFileName)
QByteArray encodeName(const QString &fileName)
qsizetype count() const const
QString findExecutable(const QString &executableName, const QStringList &paths)
const QChar * constData() const const
bool isEmpty() const const