mirror of
https://github.com/QuasarApp/qca.git
synced 2025-04-28 12:34:31 +00:00
console now supports real tty access. added some notes to the header.
svn path=/trunk/kdesupport/qca/; revision=635004
This commit is contained in:
parent
829ba858c1
commit
f46f72508d
@ -163,11 +163,59 @@ namespace QCA
|
||||
class ConsoleReferencePrivate;
|
||||
class ConsoleReference;
|
||||
|
||||
// note: only one Console object can be created at a time
|
||||
// QCA Console system
|
||||
//
|
||||
// QCA provides an API for asynchronous, event-based access to
|
||||
// the console and stdin/stdout, as these facilities are
|
||||
// otherwise not portable. The primary use of this system within
|
||||
// QCA is for passphrase prompting in command-line applications,
|
||||
// using the tty console type.
|
||||
//
|
||||
// How it works: Create a Console object for the type of console
|
||||
// desired, and then use ConsoleReference to act on the console.
|
||||
// Only one ConsoleReference may operate on a Console at a time.
|
||||
//
|
||||
// A Console object overtakes either the physical console (tty
|
||||
// type) or stdin/stdout (stdio type). Only one of each type
|
||||
// may be created at a time.
|
||||
//
|
||||
// Whenever code is written that needs a tty or stdio object, the
|
||||
// code should first call one of the static methods (ttyInstance
|
||||
// or stdioInstance) to see if a console object for the desired
|
||||
// type exists already. If the object exists, use it. If it does
|
||||
// not exist, the rule is that the relevant code should create the
|
||||
// object, use the object, and then destroy the object when the
|
||||
// operation is completed.
|
||||
//
|
||||
// By following the above rule, you can write code that utilizes
|
||||
// a console without the application having to create some master
|
||||
// console object for you. Of course, if the application has
|
||||
// created a console then it will be used.
|
||||
//
|
||||
// Why make a master console object? The primary reason is that it
|
||||
// is not guaranteed that all I/O will survive creation and
|
||||
// destruction of a console object. If you are using the stdio
|
||||
// type, then you probably want a long-lived console object. It
|
||||
// is possible to capture unprocessed I/O by calling
|
||||
// bytesLeftToRead or bytesLeftToWrite. However, it is not
|
||||
// expected that general console-needing code will call these
|
||||
// functions when utilizing a temporary console. Thus, an
|
||||
// application developer would need to create his own console
|
||||
// object, invoke the console-needing code, and then do his own
|
||||
// extraction of the unprocessed I/O if necessary. Another reason
|
||||
// to extract unprocessed I/O is if you need to switch from
|
||||
// QCA::Console back to standard functions (e.g. fgets).
|
||||
//
|
||||
class QCA_EXPORT Console : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Tty, // physical console
|
||||
Stdio, // stdin/stdout
|
||||
};
|
||||
|
||||
enum ChannelMode
|
||||
{
|
||||
Read, // stdin
|
||||
@ -180,13 +228,17 @@ namespace QCA
|
||||
Interactive // char-by-char input, no echo
|
||||
};
|
||||
|
||||
Console(ChannelMode cmode = Read, TerminalMode tmode = Default, QObject *parent = 0);
|
||||
Console(Type type, ChannelMode cmode, TerminalMode tmode, QObject *parent = 0);
|
||||
~Console();
|
||||
|
||||
static Console *instance();
|
||||
static bool isStdinRedirected();
|
||||
static bool isStdoutRedirected();
|
||||
|
||||
// call shutdown() to get access to unempty buffers
|
||||
void shutdown();
|
||||
static Console *ttyInstance();
|
||||
static Console *stdioInstance();
|
||||
|
||||
// call release() to get access to unempty buffers
|
||||
void release();
|
||||
QByteArray bytesLeftToRead();
|
||||
QByteArray bytesLeftToWrite();
|
||||
|
||||
@ -211,7 +263,7 @@ namespace QCA
|
||||
ConsoleReference(QObject *parent = 0);
|
||||
~ConsoleReference();
|
||||
|
||||
bool start(SecurityMode mode = SecurityDisabled);
|
||||
bool start(Console *console, SecurityMode mode = SecurityDisabled);
|
||||
void stop();
|
||||
|
||||
// normal i/o
|
||||
@ -222,13 +274,17 @@ namespace QCA
|
||||
QSecureArray readSecure(int bytes = -1);
|
||||
void writeSecure(const QSecureArray &a);
|
||||
|
||||
// close write channel (only if writing enabled)
|
||||
void closeOutput();
|
||||
|
||||
int bytesAvailable() const;
|
||||
int bytesToWrite() const;
|
||||
|
||||
signals:
|
||||
void readyRead();
|
||||
void bytesWritten(int bytes);
|
||||
void closed();
|
||||
void inputClosed();
|
||||
void outputClosed();
|
||||
|
||||
private:
|
||||
friend class ConsoleReferencePrivate;
|
||||
|
@ -71,6 +71,7 @@ public:
|
||||
{
|
||||
out.take(out_id, QPipeDevice::Write);
|
||||
connect(&out, SIGNAL(bytesWritten(int)), SLOT(out_bytesWritten(int)));
|
||||
connect(&out, SIGNAL(closed()), SLOT(out_closed()));
|
||||
out.enable();
|
||||
}
|
||||
|
||||
@ -101,7 +102,10 @@ public slots:
|
||||
|
||||
void setSecurityEnabled(bool enabled)
|
||||
{
|
||||
in.setSecurityEnabled(enabled);
|
||||
if(in.isValid())
|
||||
in.setSecurityEnabled(enabled);
|
||||
if(out.isValid())
|
||||
out.setSecurityEnabled(enabled);
|
||||
}
|
||||
|
||||
QByteArray read(int bytes = -1)
|
||||
@ -124,6 +128,11 @@ public slots:
|
||||
out.writeSecure(a);
|
||||
}
|
||||
|
||||
void closeOutput()
|
||||
{
|
||||
out.close();
|
||||
}
|
||||
|
||||
int bytesAvailable() const
|
||||
{
|
||||
return in.bytesAvailable();
|
||||
@ -152,7 +161,8 @@ public:
|
||||
signals:
|
||||
void readyRead();
|
||||
void bytesWritten(int bytes);
|
||||
void closed();
|
||||
void inputClosed();
|
||||
void outputClosed();
|
||||
|
||||
private slots:
|
||||
void in_readyRead()
|
||||
@ -167,12 +177,17 @@ private slots:
|
||||
|
||||
void in_closed()
|
||||
{
|
||||
emit closed();
|
||||
emit inputClosed();
|
||||
}
|
||||
|
||||
void in_error(QCA::QPipeEnd::Error)
|
||||
{
|
||||
emit closed();
|
||||
emit inputClosed();
|
||||
}
|
||||
|
||||
void out_closed()
|
||||
{
|
||||
emit outputClosed();
|
||||
}
|
||||
};
|
||||
|
||||
@ -250,6 +265,11 @@ public:
|
||||
mycall(worker, "writeSecure", QVariantList() << qVariantFromValue<QSecureArray>(a));
|
||||
}
|
||||
|
||||
void closeOutput()
|
||||
{
|
||||
mycall(worker, "closeOutput");
|
||||
}
|
||||
|
||||
int bytesAvailable()
|
||||
{
|
||||
return mycall(worker, "bytesAvailable").toInt();
|
||||
@ -277,7 +297,8 @@ public:
|
||||
signals:
|
||||
void readyRead();
|
||||
void bytesWritten(int);
|
||||
void closed();
|
||||
void inputClosed();
|
||||
void outputClosed();
|
||||
|
||||
protected:
|
||||
virtual void atStart()
|
||||
@ -289,7 +310,8 @@ protected:
|
||||
// signals to avoid having to make slots just to emit.
|
||||
connect(worker, SIGNAL(readyRead()), SIGNAL(readyRead()), Qt::DirectConnection);
|
||||
connect(worker, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)), Qt::DirectConnection);
|
||||
connect(worker, SIGNAL(closed()), SIGNAL(closed()), Qt::DirectConnection);
|
||||
connect(worker, SIGNAL(inputClosed()), SIGNAL(inputClosed()), Qt::DirectConnection);
|
||||
connect(worker, SIGNAL(outputClosed()), SIGNAL(outputClosed()), Qt::DirectConnection);
|
||||
|
||||
worker->start(_in_id, _out_id);
|
||||
}
|
||||
@ -312,9 +334,11 @@ public:
|
||||
Console *q;
|
||||
|
||||
bool started;
|
||||
Console::Type type;
|
||||
Console::TerminalMode mode;
|
||||
ConsoleThread *thread;
|
||||
ConsoleReference *ref;
|
||||
Q_PIPE_ID in_id;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
DWORD old_mode;
|
||||
@ -345,11 +369,11 @@ public:
|
||||
if(m == Console::Interactive)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
HANDLE h = GetStdHandle(in_id);
|
||||
GetConsoleMode(h, &old_mode);
|
||||
SetConsoleMode(h, old_mode & (~ENABLE_LINE_INPUT & ~ENABLE_ECHO_INPUT));
|
||||
#else
|
||||
int fd = 0; // stdin
|
||||
int fd = in_id;
|
||||
struct termios attr;
|
||||
tcgetattr(fd, &attr);
|
||||
old_term_attr = attr;
|
||||
@ -366,10 +390,10 @@ public:
|
||||
else
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
HANDLE h = GetStdHandle(in_id);
|
||||
SetConsoleMode(h, old_mode);
|
||||
#else
|
||||
int fd = 0; // stdin
|
||||
int fd = in_id;
|
||||
tcsetattr(fd, TCSANOW, &old_term_attr);
|
||||
#endif
|
||||
}
|
||||
@ -378,50 +402,128 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static Console *g_console = 0;
|
||||
static Console *g_tty_console = 0, *g_stdio_console = 0;
|
||||
|
||||
Console::Console(ChannelMode cmode, TerminalMode tmode, QObject *parent)
|
||||
Console::Console(Type type, ChannelMode cmode, TerminalMode tmode, QObject *parent)
|
||||
:QObject(parent)
|
||||
{
|
||||
Q_ASSERT(g_console == 0);
|
||||
g_console = this;
|
||||
if(type == Tty)
|
||||
{
|
||||
Q_ASSERT(g_tty_console == 0);
|
||||
g_tty_console = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_ASSERT(g_stdio_console == 0);
|
||||
g_stdio_console = this;
|
||||
}
|
||||
|
||||
d = new ConsolePrivate(this);
|
||||
d->type = type;
|
||||
|
||||
Q_PIPE_ID in = INVALID_Q_PIPE_ID;
|
||||
Q_PIPE_ID out = INVALID_Q_PIPE_ID;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if(type == Tty)
|
||||
{
|
||||
in = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, false,
|
||||
OPEN_EXISTING, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
in = GetStdHandle(STD_INPUT_HANDLE);
|
||||
}
|
||||
#else
|
||||
in = 0;
|
||||
if(type == Tty)
|
||||
{
|
||||
in = open("/dev/tty", O_RDONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
in = 0; // stdin
|
||||
}
|
||||
#endif
|
||||
if(cmode == ReadWrite)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if(type == Tty)
|
||||
{
|
||||
out = CreateFile("CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, false,
|
||||
OPEN_EXISTING, 0, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
}
|
||||
#else
|
||||
out = 1;
|
||||
if(type == Tty)
|
||||
{
|
||||
out = open("/dev/tty", O_WRONLY);
|
||||
}
|
||||
else
|
||||
{
|
||||
out = 1; // stdout
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
d->in_id = in;
|
||||
d->setInteractive(tmode);
|
||||
d->thread->start(in, out);
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
{
|
||||
shutdown();
|
||||
release();
|
||||
Console::Type type = d->type;
|
||||
delete d;
|
||||
g_console = 0;
|
||||
if(type == Tty)
|
||||
g_tty_console = 0;
|
||||
else
|
||||
g_stdio_console = 0;
|
||||
}
|
||||
|
||||
Console *Console::instance()
|
||||
bool Console::isStdinRedirected()
|
||||
{
|
||||
return g_console;
|
||||
#ifdef Q_OS_WIN
|
||||
Handle h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD mode;
|
||||
if(GetConsoleMode(h, &mode))
|
||||
return false;
|
||||
return true;
|
||||
#else
|
||||
return (isatty(0) ? false : true); // 0 == stdin
|
||||
#endif
|
||||
}
|
||||
|
||||
void Console::shutdown()
|
||||
bool Console::isStdoutRedirected()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
Handle h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
DWORD mode;
|
||||
if(GetConsoleMode(h, &mode))
|
||||
return false;
|
||||
return true;
|
||||
#else
|
||||
return (isatty(1) ? false : true); // 1 == stdout
|
||||
#endif
|
||||
}
|
||||
|
||||
Console *Console::ttyInstance()
|
||||
{
|
||||
return g_tty_console;
|
||||
}
|
||||
|
||||
Console *Console::stdioInstance()
|
||||
{
|
||||
return g_stdio_console;
|
||||
}
|
||||
|
||||
void Console::release()
|
||||
{
|
||||
d->thread->stop();
|
||||
}
|
||||
@ -467,7 +569,7 @@ private slots:
|
||||
if(!self)
|
||||
return;
|
||||
if(late_close)
|
||||
emit q->closed();
|
||||
emit q->inputClosed();
|
||||
}
|
||||
};
|
||||
|
||||
@ -483,18 +585,16 @@ ConsoleReference::~ConsoleReference()
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool ConsoleReference::start(SecurityMode mode)
|
||||
bool ConsoleReference::start(Console *console, SecurityMode mode)
|
||||
{
|
||||
// make sure this reference isn't using a console already
|
||||
Q_ASSERT(!d->console);
|
||||
|
||||
Console *c = Console::instance();
|
||||
if(!c)
|
||||
return false;
|
||||
|
||||
// one console reference at a time
|
||||
Q_ASSERT(c->d->ref == 0);
|
||||
Q_ASSERT(console->d->ref == 0);
|
||||
|
||||
d->console = c;
|
||||
// let's take it
|
||||
d->console = console;
|
||||
d->thread = d->console->d->thread;
|
||||
d->console->d->ref = this;
|
||||
|
||||
@ -516,7 +616,8 @@ bool ConsoleReference::start(SecurityMode mode)
|
||||
|
||||
connect(d->thread, SIGNAL(readyRead()), SIGNAL(readyRead()));
|
||||
connect(d->thread, SIGNAL(bytesWritten(int)), SIGNAL(bytesWritten(int)));
|
||||
connect(d->thread, SIGNAL(closed()), SIGNAL(closed()));
|
||||
connect(d->thread, SIGNAL(inputClosed()), SIGNAL(inputClosed()));
|
||||
connect(d->thread, SIGNAL(outputClosed()), SIGNAL(outputClosed()));
|
||||
|
||||
d->late_read = false;
|
||||
d->late_close = false;
|
||||
@ -570,6 +671,11 @@ void ConsoleReference::writeSecure(const QSecureArray &a)
|
||||
d->thread->writeSecure(a);
|
||||
}
|
||||
|
||||
void ConsoleReference::closeOutput()
|
||||
{
|
||||
d->thread->closeOutput();
|
||||
}
|
||||
|
||||
int ConsoleReference::bytesAvailable() const
|
||||
{
|
||||
return d->thread->bytesAvailable();
|
||||
@ -589,37 +695,72 @@ class ConsolePrompt::Private : public QObject
|
||||
public:
|
||||
Synchronizer sync;
|
||||
ConsoleReference console;
|
||||
QString promptStr;
|
||||
QSecureArray result;
|
||||
int at;
|
||||
bool done;
|
||||
bool enter;
|
||||
bool enterMode;
|
||||
|
||||
Private() : sync(this), console(this)
|
||||
{
|
||||
connect(&console, SIGNAL(readyRead()), SLOT(con_readyRead()));
|
||||
connect(&console, SIGNAL(closed()), SLOT(con_closed()));
|
||||
connect(&console, SIGNAL(inputClosed()), SLOT(con_inputClosed()));
|
||||
}
|
||||
|
||||
bool start(bool enterMode)
|
||||
bool start(bool _enterMode)
|
||||
{
|
||||
bool tmp_console = false;
|
||||
Console *tty = Console::ttyInstance();
|
||||
if(!tty)
|
||||
{
|
||||
tty = new Console(Console::Tty, Console::ReadWrite, Console::Interactive);
|
||||
tmp_console = true;
|
||||
}
|
||||
|
||||
result.clear();
|
||||
at = 0;
|
||||
done = false;
|
||||
enter = enterMode;
|
||||
if(!console.start(QCA::ConsoleReference::SecurityEnabled))
|
||||
enterMode = _enterMode;
|
||||
if(!console.start(tty, ConsoleReference::SecurityEnabled))
|
||||
{
|
||||
printf("Console input not available or closed\n");
|
||||
// cleanup
|
||||
if(tmp_console)
|
||||
delete tty;
|
||||
fprintf(stderr, "Console input not available or closed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!enterMode)
|
||||
writeString(promptStr + ": ");
|
||||
|
||||
// reparent the Console under us (for Synchronizer)
|
||||
QObject *orig_parent = tty->parent();
|
||||
tty->setParent(this);
|
||||
|
||||
// block while prompting
|
||||
sync.waitForCondition();
|
||||
|
||||
// restore parent
|
||||
tty->setParent(orig_parent);
|
||||
|
||||
// cleanup
|
||||
console.stop();
|
||||
if(tmp_console)
|
||||
delete tty;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void writeString(const QString &str)
|
||||
{
|
||||
console.writeSecure(str.toLocal8Bit());
|
||||
}
|
||||
|
||||
bool processChar(unsigned char c)
|
||||
{
|
||||
if(c == '\r' || c == '\n')
|
||||
{
|
||||
printf("\n");
|
||||
writeString("\n");
|
||||
if(!done)
|
||||
{
|
||||
sync.conditionMet();
|
||||
@ -628,7 +769,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if(enter)
|
||||
if(enterMode)
|
||||
return true;
|
||||
|
||||
if(c == '\b' || c == 0x7f)
|
||||
@ -636,8 +777,7 @@ public:
|
||||
if(at > 0)
|
||||
{
|
||||
--at;
|
||||
printf("\b \b");
|
||||
fflush(stdout);
|
||||
writeString("\b \b");
|
||||
result.resize(at);
|
||||
}
|
||||
return true;
|
||||
@ -649,12 +789,15 @@ public:
|
||||
result.resize(at + 1);
|
||||
result[at++] = c;
|
||||
|
||||
printf("*");
|
||||
fflush(stdout);
|
||||
writeString("*");
|
||||
return true;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void showPrompt()
|
||||
{
|
||||
}
|
||||
|
||||
void con_readyRead()
|
||||
{
|
||||
while(console.bytesAvailable() > 0)
|
||||
@ -667,9 +810,9 @@ private slots:
|
||||
}
|
||||
}
|
||||
|
||||
void con_closed()
|
||||
void con_inputClosed()
|
||||
{
|
||||
printf("Console closed\n");
|
||||
fprintf(stderr, "Console input closed\n");
|
||||
if(!done)
|
||||
{
|
||||
sync.conditionMet();
|
||||
@ -691,9 +834,8 @@ ConsolePrompt::~ConsolePrompt()
|
||||
|
||||
QSecureArray ConsolePrompt::getHidden(const QString &promptStr)
|
||||
{
|
||||
printf("%s: ", qPrintable(promptStr));
|
||||
fflush(stdout);
|
||||
ConsolePrompt p;
|
||||
p.d->promptStr = promptStr;
|
||||
if(!p.d->start(false))
|
||||
return QSecureArray();
|
||||
return p.d->result;
|
||||
|
Loading…
x
Reference in New Issue
Block a user