Revamp signal handling in eglfs/linuxfb

Go back to the pipe-based signal handling. signalfd() introduces more harm than good
and is a regression for applications that install their own signal handlers.

Simplify the somewhat overcomplicated suspend (Ctrl+Z) logic too. There is no need for
requiring a callback. Just enable/disable the keyboard and cursor on suspend and resume
and emit the signals. Backends (like kms) may then perform additional steps, if they
choose to do so.

Task-number: QTBUG-48384
Change-Id: Ifd52de89c59915a2e0be6bf5ebc6f2ff1728eb50
Reviewed-by: Louai Al-Khanji <louai.al-khanji@theqtcompany.com>
This commit is contained in:
Laszlo Agocs 2015-09-28 17:37:55 +02:00
parent 918f1cd3d8
commit f191ba9d71
3 changed files with 60 additions and 68 deletions

View File

@ -79,87 +79,79 @@ static void setTTYCursor(bool enable)
}
#endif
#ifdef VTH_ENABLED
static QFbVtHandler *vth;
void QFbVtHandler::signalHandler(int sigNo)
{
char a = sigNo;
QT_WRITE(vth->m_sigFd[0], &a, sizeof(a));
}
#endif
QFbVtHandler::QFbVtHandler(QObject *parent)
: QObject(parent),
m_tty(-1),
m_signalFd(-1),
m_signalNotifier(0)
{
#ifdef VTH_ENABLED
setTTYCursor(false);
if (isatty(0)) {
if (isatty(0))
m_tty = 0;
ioctl(m_tty, KDGKBMODE, &m_oldKbdMode);
if (!qEnvironmentVariableIntValue("QT_QPA_ENABLE_TERMINAL_KEYBOARD")) {
// Disable the tty keyboard.
ioctl(m_tty, KDSKBMUTE, 1);
ioctl(m_tty, KDSKBMODE, KBD_OFF_MODE);
}
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, m_sigFd)) {
qErrnoWarning(errno, "QFbVtHandler: socketpair() failed");
return;
}
// SIGSEGV and such cannot safely be blocked. We cannot handle them in an
// async-safe manner either. Restoring the keyboard, video mode, etc. may
// all contain calls that cannot safely be made from a signal handler.
vth = this;
setTTYCursor(false);
setKeyboardEnabled(false);
// Other signals: block them and use signalfd.
sigset_t mask;
sigemptyset(&mask);
m_signalNotifier = new QSocketNotifier(m_sigFd[1], QSocketNotifier::Read, this);
connect(m_signalNotifier, &QSocketNotifier::activated, this, &QFbVtHandler::handleSignal);
// Catch Ctrl+C.
sigaddset(&mask, SIGINT);
// Ctrl+Z. Up to the platform plugins to handle it in a meaningful way.
sigaddset(&mask, SIGTSTP);
sigaddset(&mask, SIGCONT);
// Default signal used by kill. To overcome the common issue of no cleaning
// up when killing a locally started app via a remote session.
sigaddset(&mask, SIGTERM);
m_signalFd = signalfd(-1, &mask, SFD_CLOEXEC);
if (m_signalFd < 0) {
qErrnoWarning(errno, "signalfd() failed");
} else {
m_signalNotifier = new QSocketNotifier(m_signalFd, QSocketNotifier::Read, this);
connect(m_signalNotifier, &QSocketNotifier::activated, this, &QFbVtHandler::handleSignal);
// Block the signals that are handled via signalfd. Applies only to the current
// thread, but new threads will inherit the creator's signal mask.
pthread_sigmask(SIG_BLOCK, &mask, 0);
}
struct sigaction sa;
sa.sa_flags = 0;
sa.sa_handler = signalHandler;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, 0); // Ctrl+C
sigaction(SIGTSTP, &sa, 0); // Ctrl+Z
sigaction(SIGCONT, &sa, 0);
sigaction(SIGTERM, &sa, 0); // default signal used by kill
#endif
}
QFbVtHandler::~QFbVtHandler()
{
#ifdef VTH_ENABLED
restoreKeyboard();
setKeyboardEnabled(true);
setTTYCursor(true);
if (m_signalFd != -1)
close(m_signalFd);
if (m_signalNotifier) {
close(m_sigFd[0]);
close(m_sigFd[1]);
}
#endif
}
void QFbVtHandler::restoreKeyboard()
void QFbVtHandler::setKeyboardEnabled(bool enable)
{
#ifdef VTH_ENABLED
if (m_tty == -1)
return;
ioctl(m_tty, KDSKBMUTE, 0);
ioctl(m_tty, KDSKBMODE, m_oldKbdMode);
#endif
}
// To be called from the slot connected to suspendRequested() in case the
// platform plugin does in fact allow suspending on Ctrl+Z.
void QFbVtHandler::suspend()
{
#ifdef VTH_ENABLED
kill(getpid(), SIGSTOP);
if (enable) {
::ioctl(m_tty, KDSKBMUTE, 0);
::ioctl(m_tty, KDSKBMODE, m_oldKbdMode);
} else {
::ioctl(m_tty, KDGKBMODE, &m_oldKbdMode);
if (!qEnvironmentVariableIntValue("QT_QPA_ENABLE_TERMINAL_KEYBOARD")) {
::ioctl(m_tty, KDSKBMUTE, 1);
::ioctl(m_tty, KDSKBMODE, KBD_OFF_MODE);
}
}
#else
Q_UNUSED(enable);
#endif
}
@ -168,17 +160,22 @@ void QFbVtHandler::handleSignal()
#ifdef VTH_ENABLED
m_signalNotifier->setEnabled(false);
signalfd_siginfo sig;
if (read(m_signalFd, &sig, sizeof(sig)) == sizeof(sig)) {
switch (sig.ssi_signo) {
char sigNo;
if (QT_READ(m_sigFd[1], &sigNo, sizeof(sigNo)) == sizeof(sigNo)) {
switch (sigNo) {
case SIGINT: // fallthrough
case SIGTERM:
handleInt();
break;
case SIGTSTP:
emit suspendRequested();
emit aboutToSuspend();
setKeyboardEnabled(true);
setTTYCursor(true);
::kill(getpid(), SIGSTOP);
break;
case SIGCONT:
setTTYCursor(false);
setKeyboardEnabled(false);
emit resumed();
break;
default:
@ -194,7 +191,7 @@ void QFbVtHandler::handleInt()
{
#ifdef VTH_ENABLED
emit interrupted();
restoreKeyboard();
setKeyboardEnabled(true);
setTTYCursor(true);
_exit(1);
#endif

View File

@ -59,23 +59,22 @@ public:
QFbVtHandler(QObject *parent = 0);
~QFbVtHandler();
void suspend();
signals:
void interrupted();
void suspendRequested();
void aboutToSuspend();
void resumed();
private slots:
void handleSignal();
private:
void restoreKeyboard();
void setKeyboardEnabled(bool enable);
void handleInt();
static void signalHandler(int sigNo);
int m_tty;
int m_oldKbdMode;
int m_signalFd;
int m_sigFd[2];
QSocketNotifier *m_signalNotifier;
};

View File

@ -52,15 +52,11 @@ public:
QEglFSKmsInterruptHandler(QEglFSKmsScreen *screen) : m_screen(screen) {
m_vtHandler = static_cast<QEglFSIntegration *>(QGuiApplicationPrivate::platformIntegration())->vtHandler();
connect(m_vtHandler, &QFbVtHandler::interrupted, this, &QEglFSKmsInterruptHandler::restoreVideoMode);
connect(m_vtHandler, &QFbVtHandler::suspendRequested, this, &QEglFSKmsInterruptHandler::handleSuspendRequest);
connect(m_vtHandler, &QFbVtHandler::aboutToSuspend, this, &QEglFSKmsInterruptHandler::restoreVideoMode);
}
public slots:
void restoreVideoMode() { m_screen->restoreMode(); }
void handleSuspendRequest() {
m_screen->restoreMode();
m_vtHandler->suspend();
}
private:
QFbVtHandler *m_vtHandler;