summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorFabian Schillinger2010-11-01 17:35:27 +0100
committerFabian Schillinger2010-11-01 17:35:27 +0100
commitea3fb17345e5f82db9f2e98a8062e95797700ace (patch)
tree1da0d1a8ec9455364386af78762d0f6fed187824 /src
parentProcess start/stop/view functionality (diff)
parent[PVSGUI] No X required for --help and --version (diff)
downloadpvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.tar.gz
pvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.tar.xz
pvs-ea3fb17345e5f82db9f2e98a8062e95797700ace.zip
Merge branch 'master' of openslx.org:pvs
Conflicts: CMakeLists.txt src/core/pvsConnectionManager.cpp src/pvs.cpp src/pvs.h
Diffstat (limited to 'src')
-rw-r--r--src/core/pvsClient.cpp1
-rw-r--r--src/core/pvsConnectionManager.cpp19
-rw-r--r--src/gui/clientConfigDialog.cpp28
-rw-r--r--src/gui/clientConfigDialog.h3
-rw-r--r--src/gui/clientFileReceiveDialog.cpp95
-rw-r--r--src/gui/clientFileReceiveDialog.h14
-rw-r--r--src/gui/clientFileSendDialog.cpp201
-rw-r--r--src/gui/clientFileSendDialog.h21
-rw-r--r--src/gui/connectionWindow.h3
-rw-r--r--src/gui/frame.cpp304
-rw-r--r--src/gui/frame.h29
-rw-r--r--src/gui/mainWindow.cpp54
-rw-r--r--src/gui/mainWindow.h1
-rw-r--r--src/gui/multicastConfigDialog.cpp170
-rw-r--r--src/gui/multicastConfigDialog.h38
-rw-r--r--src/gui/ui/clientConfigDialog.ui64
-rw-r--r--src/gui/ui/clientFileReceiveDialog.ui11
-rw-r--r--src/gui/ui/clientFileSendDialog.ui15
-rw-r--r--src/gui/ui/clientNicklistDialog.ui7
-rw-r--r--src/gui/ui/mainwindow.ui15
-rw-r--r--src/gui/ui/mainwindowtouch.ui11
-rw-r--r--src/gui/ui/multicastConfigDialog.ui156
-rw-r--r--src/input/CMakeLists.txt114
-rw-r--r--src/input/inputEvent.cpp99
-rw-r--r--src/input/inputEvent.h219
-rw-r--r--src/input/inputEventHandler.cpp36
-rw-r--r--src/input/inputEventHandler.h295
-rw-r--r--src/input/inputEventNonQt.cpp17
-rw-r--r--src/input/inputHandlerChain.h49
-rw-r--r--src/input/killX11Handler.cpp89
-rw-r--r--src/input/killX11Handler.h34
-rw-r--r--src/input/magicSysRqHandler.cpp28
-rw-r--r--src/input/magicSysRqHandler.h72
-rw-r--r--src/input/org.openslx.pvs.input.policy18
-rw-r--r--src/input/privilegedHandlerForwarder.cpp82
-rw-r--r--src/input/privilegedHandlerForwarder.h32
-rw-r--r--src/input/pvsCheckPrivileges.cpp550
-rw-r--r--src/input/pvsCheckPrivileges.h151
-rw-r--r--src/input/pvsPrivInputHandler.cpp108
-rw-r--r--src/input/pvsPrivInputHandler.h45
-rw-r--r--src/input/pvsPrivInputSignalHandler.cpp69
-rw-r--r--src/input/pvsPrivInputSignalHandler.h47
-rw-r--r--src/input/pvsPrivInputSocket.cpp233
-rw-r--r--src/input/pvsPrivInputSocket.h37
-rw-r--r--src/input/pvsSyslog.cpp133
-rw-r--r--src/input/pvsSyslog.h63
-rw-r--r--src/input/pvsprivinputd.conf28
-rw-r--r--src/input/pvsprivinputd.cpp262
-rw-r--r--src/input/rebootSystemHandler.cpp32
-rw-r--r--src/input/rebootSystemHandler.h34
-rw-r--r--src/input/sayHelloHandler.cpp25
-rw-r--r--src/input/sayHelloHandler.h34
-rw-r--r--src/input/x11FakeKeyboardHandler.cpp906
-rw-r--r--src/input/x11FakeKeyboardHandler.h29
-rw-r--r--src/input/x11FakeMouseHandler.cpp59
-rw-r--r--src/input/x11FakeMouseHandler.h34
-rw-r--r--src/input/x11InputUtils.cpp29
-rw-r--r--src/input/x11InputUtils.h27
-rw-r--r--src/net/mcast/CMakeLists.txt61
-rw-r--r--src/net/mcast/McastConfiguration.cpp57
-rw-r--r--src/net/mcast/McastConfiguration.h204
-rw-r--r--src/net/mcast/McastConstants.h36
-rw-r--r--src/net/mcast/McastPGMSocket.cpp666
-rw-r--r--src/net/mcast/McastPGMSocket.h81
-rw-r--r--src/net/mcast/McastReceiver.cpp179
-rw-r--r--src/net/mcast/McastReceiver.h80
-rw-r--r--src/net/mcast/McastSender.cpp127
-rw-r--r--src/net/mcast/McastSender.h73
-rw-r--r--src/net/mcast/trial_programs/CMakeLists.txt38
-rw-r--r--src/net/mcast/trial_programs/McastConfigArgParser.cpp165
-rw-r--r--src/net/mcast/trial_programs/McastConfigArgParser.h26
-rw-r--r--src/net/mcast/trial_programs/mcastreceive.cpp150
-rw-r--r--src/net/mcast/trial_programs/mcastreceive.h44
-rw-r--r--src/net/mcast/trial_programs/mcastsend.cpp122
-rw-r--r--src/net/mcast/trial_programs/mcastsend.h42
-rw-r--r--src/net/pvsIncomingMulticastTransfer.cpp133
-rw-r--r--src/net/pvsIncomingMulticastTransfer.h71
-rw-r--r--src/net/pvsListenServer.cpp60
-rw-r--r--src/net/pvsListenServer.h10
-rw-r--r--src/net/pvsNetworkInterfaceListModel.cpp81
-rw-r--r--src/net/pvsNetworkInterfaceListModel.h35
-rw-r--r--src/net/pvsOutgoingMulticastTransfer.cpp209
-rw-r--r--src/net/pvsOutgoingMulticastTransfer.h92
-rw-r--r--src/net/pvsServerConnection.h7
-rwxr-xr-xsrc/pvs.cpp261
-rwxr-xr-xsrc/pvs.h43
-rw-r--r--src/pvsDaemon.cpp15
-rw-r--r--src/pvsgui.cpp44
-rw-r--r--src/pvsgui.h1
-rw-r--r--src/util/clientGUIUtils.cpp3
-rwxr-xr-xsrc/util/clientGUIUtils.h12
-rwxr-xr-xsrc/util/clientGUIUtils_X11.cpp11
-rw-r--r--src/version.h4
93 files changed, 8528 insertions, 94 deletions
diff --git a/src/core/pvsClient.cpp b/src/core/pvsClient.cpp
index f048b54..0448f97 100644
--- a/src/core/pvsClient.cpp
+++ b/src/core/pvsClient.cpp
@@ -316,6 +316,5 @@ void PVSClient::processesVectorAdd(QString msg)
else if (msg.indexOf("vector ready") == 0)
{
emit processVectorReady(true);
- ConsoleLog writeError("processVectorReady(true)!");
}
}
diff --git a/src/core/pvsConnectionManager.cpp b/src/core/pvsConnectionManager.cpp
index d0f1479..db302c4 100644
--- a/src/core/pvsConnectionManager.cpp
+++ b/src/core/pvsConnectionManager.cpp
@@ -260,6 +260,25 @@ void PVSConnectionManager::onCommand(PVSMsg command)
else tmp->processesVectorAdd(message);
}
}
+ else if (ident == "MCASTFTANNOUNCE")
+ {
+ _pvsServer.sendToAll(command);
+ }
+ else if (ident == "MCASTFTRETRY")
+ {
+ QStringList fields = message.split(':');
+ if (!fields.size() == 2)
+ {
+ qDebug() << "Malformed MCASTFTRETRY message:" << message;
+ return;
+ }
+
+ PVSClient* client = getClientFromUsername(fields[0]);
+ if (client)
+ {
+ client->sendMessage(command);
+ }
+ }
}
void PVSConnectionManager::onChat(PVSMsg chatMsg)
{
diff --git a/src/gui/clientConfigDialog.cpp b/src/gui/clientConfigDialog.cpp
index 0ee5908..b7ba407 100644
--- a/src/gui/clientConfigDialog.cpp
+++ b/src/gui/clientConfigDialog.cpp
@@ -16,20 +16,33 @@
# -----------------------------------------------------------------------------
*/
+#include <QtDebug>
+#include <QNetworkInterface>
+#include <QStandardItemModel>
#include "clientConfigDialog.h"
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <src/net/pvsNetworkInterfaceListModel.h>
+
+using namespace std;
ClientConfigDialog::ClientConfigDialog(QWidget *parent) :
- QDialog(parent)
+ QDialog(parent),
+ _interfaceListModel(0)
{
setupUi(this);
connect(this, SIGNAL(accepted()), this, SLOT(writeSettings()));
connect(radioButtonOtherRO, SIGNAL(clicked()), this,
SLOT(checkPermissions()));
+ _interfaceListModel = new PVSNetworkInterfaceListModel(this);
+ interfaceList->setModel(_interfaceListModel);
+ interfaceList->setModelColumn(0);
+ connect(reloadInterfaceListButton, SIGNAL(clicked()), _interfaceListModel, SLOT(reloadInterfaceList()));
// connect to D-Bus and get interface
QDBusConnection dbus = QDBusConnection::sessionBus();
_ifaceDBus = new OrgOpenslxPvsInterface("org.openslx.pvs", "/", dbus, this);
-
}
ClientConfigDialog::~ClientConfigDialog()
@@ -53,7 +66,14 @@ void ClientConfigDialog::readSettings()
else
comboBox->setCurrentIndex(_settings.value("Display/location").toInt());
- QDBusPendingReply<QString> reply = _ifaceDBus->getConfigValue("Permissions/vnc_lecturer");
+ QDBusPendingReply<QString> reply = _ifaceDBus->getConfigValue("multicast/interface");
+ reply.waitForFinished();
+ if (reply.isValid())
+ {
+ interfaceList->setEditText(reply.value());
+ }
+
+ reply = _ifaceDBus->getConfigValue("Permissions/vnc_lecturer");
reply.waitForFinished();
if (reply.isValid())
{
@@ -86,7 +106,6 @@ void ClientConfigDialog::readSettings()
reply.waitForFinished();
if (reply.isValid())
checkBoxAllowFiletransfer->setChecked(reply.value() == "T");
-
}
void ClientConfigDialog::writeSettings()
@@ -110,6 +129,7 @@ void ClientConfigDialog::writeSettings()
QString(checkBoxAllowChat->isChecked() ? "T" : "F"));
_ifaceDBus->setConfigValue("Permissions/allow_filetransfer",
QString(checkBoxAllowFiletransfer->isChecked() ? "T" : "F"));
+ _ifaceDBus->setConfigValue("multicast/interface", interfaceList->currentText());
_settings.sync();
emit configChanged();
diff --git a/src/gui/clientConfigDialog.h b/src/gui/clientConfigDialog.h
index 98da54f..fd7529e 100644
--- a/src/gui/clientConfigDialog.h
+++ b/src/gui/clientConfigDialog.h
@@ -18,6 +18,8 @@
#include "pvsinterface.h"
#include "ui_clientConfigDialog.h"
+class QAbstractItemModel;
+
class ClientConfigDialog: public QDialog, private Ui::ClientConfigDialogClass
{
Q_OBJECT
@@ -40,6 +42,7 @@ private Q_SLOTS:
private:
OrgOpenslxPvsInterface *_ifaceDBus;
QSettings _settings;
+ QAbstractItemModel* _interfaceListModel;
};
diff --git a/src/gui/clientFileReceiveDialog.cpp b/src/gui/clientFileReceiveDialog.cpp
index 669ca81..fc6a1a3 100644
--- a/src/gui/clientFileReceiveDialog.cpp
+++ b/src/gui/clientFileReceiveDialog.cpp
@@ -16,6 +16,9 @@
*/
#include "clientFileReceiveDialog.h"
+#include <pvsinterface.h>
+#include <QFileInfo>
+#include <limits.h>
ClientFileReceiveDialog::ClientFileReceiveDialog(QTcpSocket *socket, QWidget *parent) :
QDialog(parent)
@@ -37,9 +40,44 @@ ClientFileReceiveDialog::ClientFileReceiveDialog(QTcpSocket *socket, QWidget *pa
connect(this, SIGNAL(finished(int)), this, SLOT(deleteLater()));
}
+ClientFileReceiveDialog::ClientFileReceiveDialog(QString const& sender, qulonglong transferID,
+ QString const& filename, qulonglong size, OrgOpenslxPvsInterface* ifaceDBus, QWidget* parent)
+{
+ setupUi(this);
+
+ _transferID = transferID;
+ _filename = filename;
+ _ifaceDBus = ifaceDBus;
+ _bytesToRead = size;
+ _socket = 0;
+ _file = 0;
+ div = 1;
+ while((size / div) > INT_MAX)
+ {
+ div <<= 1;
+ }
+
+ connect(ifaceDBus, SIGNAL(incomingMulticastTransferStarted(qulonglong)), SLOT(mcastTransferStarted(qulonglong)));
+ connect(ifaceDBus, SIGNAL(incomingMulticastTransferProgress(qulonglong, qulonglong, qulonglong)),
+ SLOT(mcastTransferProgress(qulonglong, qulonglong, qulonglong)));
+ connect(ifaceDBus, SIGNAL(incomingMulticastTransferFinished(qulonglong)), SLOT(mcastTransferFinished(qulonglong)));
+ connect(ifaceDBus, SIGNAL(incomingMulticastTransferFailed(qulonglong, QString)), SLOT(mcastTransferFailed(qulonglong, QString)));
+ connect(cancelButton, SIGNAL(clicked()), SLOT(cancelTransfer()));
+
+ qDebug("[%s] New multicast incoming transfer: %s from %s", metaObject()->className(),
+ filename.toLocal8Bit().constData(), sender.toLocal8Bit().constData());
+
+
+ filenameLabel->setText(QFileInfo(filename).baseName());
+ labelNick->setText(sender);
+ progressBar->setRange(0, 0);
+}
+
+
ClientFileReceiveDialog::~ClientFileReceiveDialog()
{
- _socket->deleteLater();
+ if(_socket)
+ _socket->deleteLater();
qDebug("[%s] Deleted!", metaObject()->className());
}
@@ -168,6 +206,61 @@ void ClientFileReceiveDialog::error(QAbstractSocket::SocketError error)
close();
}
+void ClientFileReceiveDialog::mcastTransferStarted(qulonglong transferID)
+{
+ if(transferID != _transferID)
+ return;
+}
+
+void ClientFileReceiveDialog::mcastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of)
+{
+ if(transferID != _transferID)
+ return;
+
+ progressBar->setRange(0, of);
+ progressBar->setValue(bytes);
+
+ labelA->setText(formatSize(bytes));
+ labelB->setText(formatSize(of));
+}
+
+void ClientFileReceiveDialog::mcastTransferFinished(qulonglong transferID)
+{
+ if(transferID != _transferID)
+ return;
+
+ QString filename = QFileDialog::getSaveFileName(this, tr("Where should I save %1?").arg(_filename), _filename);
+ QFile* file = new QFile(_filename);
+ if(filename.isNull() || filename.isEmpty())
+ {
+ file->remove();
+ }
+ else
+ {
+ if(!file->rename(filename))
+ {
+ QMessageBox::warning(this, tr("Could not rename file"), tr("Failed to rename %1 to %2").arg(_filename).arg(filename));
+ }
+ }
+ accept();
+ deleteLater();
+}
+
+void ClientFileReceiveDialog::mcastTransferFailed(qulonglong transferID, QString reason)
+{
+ if(transferID != _transferID)
+ return;
+
+ QMessageBox::warning(this, tr("File transfer failed"), tr("File transfer failed for the following reason:\n%1").arg(reason));
+ reject();
+ deleteLater();
+}
+
+void ClientFileReceiveDialog::cancelTransfer()
+{
+ _ifaceDBus->cancelIncomingMulticastTransfer(_transferID);
+}
+
QString ClientFileReceiveDialog::formatSize(qint64 size)
{
if (size >= 1000000000) // GB
diff --git a/src/gui/clientFileReceiveDialog.h b/src/gui/clientFileReceiveDialog.h
index c13d7b7..c9ed220 100644
--- a/src/gui/clientFileReceiveDialog.h
+++ b/src/gui/clientFileReceiveDialog.h
@@ -18,6 +18,8 @@
#include <QtNetwork>
#include "ui_clientFileReceiveDialog.h"
+class OrgOpenslxPvsInterface;
+
class ClientFileReceiveDialog: public QDialog,
private Ui::ClientFileReceiveDialogClass
{
@@ -25,6 +27,7 @@ Q_OBJECT
public:
ClientFileReceiveDialog(QTcpSocket *socket, QWidget *parent = 0);
+ ClientFileReceiveDialog(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, OrgOpenslxPvsInterface* ifaceDBus, QWidget* parent = 0);
~ClientFileReceiveDialog();
private Q_SLOTS:
@@ -33,6 +36,13 @@ private Q_SLOTS:
void close();
void error(QAbstractSocket::SocketError error);
+ // multicast:
+ void mcastTransferStarted(qulonglong transferID);
+ void mcastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of);
+ void mcastTransferFinished(qulonglong transferID);
+ void mcastTransferFailed(qulonglong transferID, QString reason);
+ void cancelTransfer();
+
private:
void sendAck(bool b);
QString formatSize(qint64 size);
@@ -42,6 +52,10 @@ private:
qint64 _bytesToRead;
int div;
+ // multicast:
+ OrgOpenslxPvsInterface* _ifaceDBus;
+ QString _filename;
+ qulonglong _transferID;
};
#endif /* CLIENTFILERECEIVEDIALOG_H_ */
diff --git a/src/gui/clientFileSendDialog.cpp b/src/gui/clientFileSendDialog.cpp
index b4512c0..93da725 100644
--- a/src/gui/clientFileSendDialog.cpp
+++ b/src/gui/clientFileSendDialog.cpp
@@ -22,6 +22,10 @@ ClientFileSendDialog::ClientFileSendDialog(QWidget *parent) :
{
setupUi(this);
+ _transferID = 0;
+ _error = false;
+ _isMulticast = false;
+
_file = NULL;
_socket = NULL;
_clientNicklistDialog = new ClientNicklistDialog(this);
@@ -58,7 +62,15 @@ void ClientFileSendDialog::open()
reject();
return;
}
- open(_clientNicklistDialog->getNick());
+
+ if (_clientNicklistDialog->isSendToAll())
+ {
+ sendToAll();
+ }
+ else
+ {
+ open(_clientNicklistDialog->getNick());
+ }
}
void ClientFileSendDialog::open(QString nick)
@@ -73,6 +85,70 @@ void ClientFileSendDialog::open(QString nick)
open(nick, filename);
}
+void ClientFileSendDialog::sendToAll()
+{
+ QString filename = QFileDialog::getOpenFileName(this, tr("Send File"), QDir::homePath(), "");
+ if (filename == "")
+ {
+ reject();
+ return;
+ }
+ sendToAll(filename);
+}
+
+void ClientFileSendDialog::sendToAll(QString filename)
+{
+ _isMulticast = true;
+
+ connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferStarted(qulonglong)), SLOT(multicastTransferStarted(qulonglong)));
+ connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferProgress(qulonglong,qulonglong,qulonglong)), SLOT(multicastTransferProgress(qulonglong, qulonglong, qulonglong)));
+ connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFinished(qulonglong)), SLOT(multicastTransferFinished(qulonglong)));
+ connect(_ifaceDBus, SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const&)), SLOT(multicastTransferFailed(qulonglong, QString const&)));
+
+ filenameLabel->setText(filename);
+ progressBar->setRange(0, 0);
+ labelNick->setText(tr("all"));
+ labelStatus->setText(tr("Waiting to start"));
+
+ QString errorMessage("Backend error");
+
+ // We need to jump through a lot of hoops because this call is prone to time out, and
+ // QtDBus does not support specifying timeouts in generated interfaces.
+ QDBusMessage call = QDBusMessage::createMethodCall("org.openslx.pvs", "/", "org.openslx.pvs", "createMulticastTransfer");
+ call << filename;
+
+ QDBusMessage reply = _ifaceDBus->connection().call(call, QDBus::Block, 5000);
+ if (reply.type() == QDBusMessage::ErrorMessage)
+ {
+ QMessageBox::critical(this, tr("File Send error"), tr("Error communicating with backend: %1: %2").arg(reply.errorName()).arg(reply.errorMessage()));
+ reject();
+ return;
+ }
+ else if (reply.type() == QDBusMessage::InvalidMessage)
+ {
+ QMessageBox::critical(this, tr("File Send error"), tr("Something went wrong while communicating with backend, but I don't know what."));
+ reject();
+ return;
+ }
+ else if (reply.type() == QDBusMessage::ReplyMessage)
+ {
+ QList<QVariant> args = reply.arguments();
+ bool created = args.at(0).toBool();
+ _transferID = args.at(1).toULongLong();
+ QString errorMessage = args.at(2).toString();
+ if (!created)
+ {
+ QMessageBox::critical(this, tr("File Send error"), tr("Could not create a multicast transfer: %1").arg(errorMessage));
+ reject();
+ return;
+ }
+ }
+
+ connect(cancelButton, SIGNAL(clicked()), SLOT(canceled()));
+
+ show();
+}
+
void ClientFileSendDialog::open(QString nick, QString filename)
{
// open file
@@ -127,6 +203,8 @@ void ClientFileSendDialog::receiveAck()
QString ack = QString::fromUtf8(_socket->readLine());
if (ack != "ok\n")
{
+ _error = true;
+ _reason = tr("Receiver declined");
qDebug("[%s] Received nack!", metaObject()->className());
close();
return;
@@ -158,27 +236,30 @@ void ClientFileSendDialog::sendFile()
void ClientFileSendDialog::close()
{
- if (_file && _file->isOpen())
- {
- _file->close();
- qDebug("[%s] File closed.", metaObject()->className());
- }
-
- if (_socket && _socket->isOpen())
+ if (!_isMulticast)
{
- disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck()));
- disconnect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile()));
- disconnect(_socket, SIGNAL(disconnected()), this, SLOT(close()));
- disconnect(_socket, SIGNAL(connected()), this, SLOT(sendHeader()));
- disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)),
- this, SLOT(error(QAbstractSocket::SocketError)));
- _socket->disconnectFromHost();
- qDebug("[%s] Connection closed.", metaObject()->className());
+ if (_file && _file->isOpen())
+ {
+ _file->close();
+ qDebug("[%s] File closed.", metaObject()->className());
+ }
+
+ if (_socket && _socket->isOpen())
+ {
+ disconnect(_socket, SIGNAL(readyRead()), this, SLOT(receiveAck()));
+ disconnect(_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(sendFile()));
+ disconnect(_socket, SIGNAL(disconnected()), this, SLOT(close()));
+ disconnect(_socket, SIGNAL(connected()), this, SLOT(sendHeader()));
+ disconnect(_socket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(error(QAbstractSocket::SocketError)));
+ _socket->disconnectFromHost();
+ qDebug("[%s] Connection closed.", metaObject()->className());
+ }
}
- disconnect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
+ disconnect(cancelButton, SIGNAL(clicked()), this, SLOT(canceled()));
- if (_bytesToWrite == 0)
+ if (!_error)
{
accept();
QMessageBox::information(0, tr("PVS - File Transfer"),
@@ -188,12 +269,26 @@ void ClientFileSendDialog::close()
{
reject();
QMessageBox::warning(0, tr("PVS - File Transfer"),
- tr("File Transfer canceled!"));
+ tr("File Transfer canceled: %1").arg(_reason));
}
}
+void ClientFileSendDialog::canceled()
+{
+ if(_isMulticast)
+ {
+ _ifaceDBus->cancelOutgoingMulticastTransfer(_transferID);
+ }
+
+ _error = true;
+ _reason = tr("You clicked 'Cancel'");
+ close();
+}
+
void ClientFileSendDialog::error(QAbstractSocket::SocketError error)
{
+ _error = true;
+ _reason = tr("Socket Error");
qDebug("[%s] Socket error: %i", metaObject()->className(), error);
close();
}
@@ -210,6 +305,55 @@ QString ClientFileSendDialog::formatSize(qint64 size)
return QString("%1B").arg((qreal)size, 0, 'f',1);
}
+void ClientFileSendDialog::multicastTransferStarted(qulonglong transferID)
+{
+ qDebug() << "multicastTransferStarted(" << transferID << ")";
+ if (transferID != _transferID)
+ {
+ return;
+ }
+ labelStatus->setText("Started");
+}
+
+void ClientFileSendDialog::multicastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of)
+{
+ qDebug() << "multicastTransferProgress(" << transferID << bytes << of << ")";
+ if (transferID != _transferID)
+ {
+ return;
+ }
+
+ if(bytes < of)
+ {
+ labelStatus->setText("Transferring");
+ progressBar->setRange(0, of);
+ progressBar->setValue(bytes);
+ }
+ else
+ {
+ labelStatus->setText("Waiting to finish");
+ progressBar->setRange(0, 0);
+ }
+
+ labelA->setText(formatSize(bytes));
+ labelB->setText(formatSize(of));
+}
+
+void ClientFileSendDialog::multicastTransferFinished(quint64 transferID)
+{
+ qDebug() << "multicastTransferFinished(" << transferID << ")";
+ qDebug("[%s] MulticastTransfer finished", metaObject()->className());
+ close();
+}
+
+void ClientFileSendDialog::multicastTransferFailed(quint64 transferID, QString const& reason)
+{
+ qDebug() << "multicastTransferFailed(" << transferID << reason << ")";
+ _error = true;
+ _reason = reason;
+ close();
+}
+
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -233,6 +377,25 @@ ClientNicklistDialog::ClientNicklistDialog(QWidget *parent) :
listWidget->addItems(nicknames);
listWidget->setCurrentRow(0);
+
+ connect(sendToAllCheckBox, SIGNAL(stateChanged(int)), SLOT(sendToAllStateChanged(int)));
+
+ sendToAllCheckBox->setCheckState(Qt::Unchecked);
+ _isSendToAll = false;
+}
+
+void ClientNicklistDialog::sendToAllStateChanged(int state)
+{
+ if (state)
+ {
+ listWidget->setEnabled(false);
+ _isSendToAll = true;
+ }
+ else
+ {
+ listWidget->setEnabled(true);
+ _isSendToAll = false;
+ }
}
ClientNicklistDialog::~ClientNicklistDialog()
diff --git a/src/gui/clientFileSendDialog.h b/src/gui/clientFileSendDialog.h
index d8afc3a..7abdcc7 100644
--- a/src/gui/clientFileSendDialog.h
+++ b/src/gui/clientFileSendDialog.h
@@ -30,9 +30,17 @@ public:
~ClientNicklistDialog();
QString getNick();
+ bool isSendToAll() const
+ {
+ return _isSendToAll;
+ }
+
+private Q_SLOTS:
+ void sendToAllStateChanged(int state);
private:
OrgOpenslxPvsInterface *_ifaceDBus;
+ bool _isSendToAll;
};
@@ -51,14 +59,22 @@ public:
void open();
void open(QString nick);
void open(QString nick, QString filename);
+ void sendToAll();
+ void sendToAll(QString filename);
private Q_SLOTS:
void sendHeader();
void receiveAck();
void sendFile();
void close();
+ void canceled();
void error(QAbstractSocket::SocketError error);
+ void multicastTransferStarted(qulonglong transferID);
+ void multicastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of);
+ void multicastTransferFinished(qulonglong transferID);
+ void multicastTransferFailed(qulonglong transferID, QString const& reason);
+
private:
QString formatSize(qint64 size);
@@ -71,6 +87,11 @@ private:
OrgOpenslxPvsInterface *_ifaceDBus;
QString _nickname;
+ quint64 _transferID;
+ bool _error;
+ QString _reason;
+ bool _isMulticast;
+
};
#endif /* CLIENTFILESENDDIALOG_H_ */
diff --git a/src/gui/connectionWindow.h b/src/gui/connectionWindow.h
index e7d0177..55442db 100644
--- a/src/gui/connectionWindow.h
+++ b/src/gui/connectionWindow.h
@@ -9,6 +9,7 @@
#include <iostream>
#include <math.h>
#include <QSettings>
+#include <QPointer>
#define FRAME_DELAY 1000 // to comply with the standard value in the gui
@@ -110,7 +111,7 @@ protected:
private:
ConnectionFrame* newConFrame(PVSClient* newConnection); // returns a new frame for the given connection
ConnectionFrame* currentSingleFrame; // pointer to the frame thats currently in FullScreen
- ConnectionFrame* _closeupFrame;
+ QPointer<ConnectionFrame> _closeupFrame;
QPoint currentPosition (ConnectionFrame* cF);
QMenu *menu;
QAction *newDummy;
diff --git a/src/gui/frame.cpp b/src/gui/frame.cpp
index cb79643..1b80c3d 100644
--- a/src/gui/frame.cpp
+++ b/src/gui/frame.cpp
@@ -18,11 +18,16 @@
# -----------------------------------------------------------------------------
*/
+#include <src/input/inputEvent.h>
+#include <src/input/inputHandlerChain.h>
#include "frame.h"
#include <src/gui/mainWindow.h>
#include <iostream>
#include <QPixmap>
+#define MOUSE_MOTION_SEND_INTERVAL 100 /* msecs */
+#define SPECIAL_EVENT_WAIT_TIME 5000 /* msecs */
+
Frame::Frame(const QString & text, QWidget * parent) :
QLabel(parent), _clientVNCThread(0)
{
@@ -51,10 +56,30 @@ Frame::Frame(const QString & text, QWidget * parent) :
button_lock = createToolButton(tr("Lock this client"), QIcon(":/lock"),SLOT(setLock()));
//button_unlock = createToolButton(tr("Unlock this client"), QIcon(":/lock"),SLOT(setLock()));
button_dozent = createToolButton(tr("Set as Superclient"), QIcon(":/dozent2"),SLOT(setDozent()));
+ button_control = createToolButton(tr("Enable Remote Control"), QIcon(":/remotecontrol"), SLOT(remoteControlClicked()));
+ button_control->setCheckable(true);
+ button_control_all = createToolButton(tr("Remote Control All Clients"), QIcon(":/remotecontrolall"), SLOT(remoteControlAllClicked()));
+ button_control_all->setCheckable(true);
connect(this, SIGNAL(clicked()), this, SLOT(slotClicked()));
ip = "";
setToolButtonListVisible(false);
+
+ _remoteControlEnabled = false;
+ _remoteControlToAll = false;
+
+ _mouseMotionEventTimer = new QTimer(this);
+ _mouseMotionEventTimer->setInterval(MOUSE_MOTION_SEND_INTERVAL);
+ _mouseMotionEventTimer->setSingleShot(false);
+ connect(_mouseMotionEventTimer, SIGNAL(timeout()), this, SLOT(sendMouseMotionEvent()));
+
+ _mousePositionChanged = true;
+
+ _specialEventTimer = new QTimer(this);
+ _specialEventTimer->setInterval(SPECIAL_EVENT_WAIT_TIME);
+ _specialEventTimer->setSingleShot(true);
+ connect(_specialEventTimer, SIGNAL(timeout()), this, SLOT(showSpecialEventMenu()));
+
}
Frame::~Frame()
@@ -216,23 +241,45 @@ void Frame::slotClicked()
void Frame::mousePressEvent(QMouseEvent* event)
{
- emit clicked();
- if (event->button() == Qt::RightButton)
- {
- /*if (!_dummy)
- DelDummy->setDisabled(true);
- menu->exec(QCursor::pos());*/
- }
- else
- {
+ if(!_remoteControlEnabled)
+ {
+ emit clicked();
+ if (event->button() == Qt::RightButton)
+ {
+ /*if (!_dummy)
+ DelDummy->setDisabled(true);
+ menu->exec(QCursor::pos());*/
+ }
+ else
+ {
- }
- QLabel::mousePressEvent(event);
+ }
+ QLabel::mousePressEvent(event);
+ }
+ else
+ {
+ event->accept();
+ ConsoleLog writeLine("Captured remote control mousePressEvent");
+
+ updateMousePosition(event);
+ sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons()));
+ }
}
void Frame::mouseReleaseEvent ( QMouseEvent * event )
{
- QLabel::mouseReleaseEvent(event);
+ if(!_remoteControlEnabled)
+ {
+ QLabel::mouseReleaseEvent(event);
+ }
+ else
+ {
+ event->accept();
+ ConsoleLog writeLine("Captured remote control mouseReleaseEvent");
+
+ updateMousePosition(event);
+ sendInputEvent(InputEvent::mousePressRelease(event->button(), event->buttons()));
+ }
}
QToolButton* Frame::createToolButton(const QString &toolTip, const QIcon &icon, const char *member)
@@ -330,3 +377,236 @@ void Frame::setDozent()
getConFrame()->setDozent(true);
}
}
+
+void Frame::remoteControlClicked()
+{
+ if(_remoteControlEnabled)
+ {
+ setMouseTracking(false);
+ _mouseMotionEventTimer->stop();
+ button_control->setToolTip(tr("Enable Remote Control"));
+ _remoteControlEnabled = false;
+ button_control->setChecked(false);
+ releaseKeyboard();
+ }
+ else
+ {
+ button_control->setToolTip(tr("Disable Remote Control"));
+ _remoteControlEnabled = true;
+ button_control->setChecked(true);
+ _mouseMotionEventTimer->start();
+ setMouseTracking(true);
+ if(_mouseOver)
+ grabKeyboard();
+ }
+}
+
+void Frame::remoteControlAllClicked()
+{
+ if(_remoteControlToAll)
+ {
+ button_control_all->setToolTip(tr("Remote Control only this Client"));
+ button_control_all->setChecked(false);
+ _remoteControlToAll = false;
+ }
+ else
+ {
+ button_control_all->setToolTip(tr("Remote Control All Clients"));
+ button_control_all->setChecked(true);
+ _remoteControlToAll = true;
+ }
+}
+
+
+
+void Frame::sendMouseMotionEvent()
+{
+ InputEvent evt = InputEvent::mouseMotion(_lastRecordedMousePosition.x(), _lastRecordedMousePosition.y());
+
+ if(!_mousePositionChanged)
+ return;
+
+ _mousePositionChanged = false;
+ sendInputEvent(evt);
+}
+
+void Frame::sendInputEvent(InputEvent const& evt)
+{
+ QString str;
+ eventToString(evt, str);
+ std::string evtStr = evt.toString();
+ PVSMsg msg(PVSCOMMAND, "INPUTEVENT", str);
+
+ if(_remoteControlEnabled)
+ {
+ if(_remoteControlToAll)
+ {
+ ConsoleLog writeLine(QString("sendInputEvent(%1) to one").arg(evtStr.c_str()));
+ PVSConnectionManager::getManager()->getServer()->sendToAll(msg);
+ }
+ else
+ {
+ ConsoleLog writeLine(QString("sendInputEvent(%1) to all").arg(evtStr.c_str()));
+ _cFrame->getConnection()->sendMessage(msg);
+ }
+ }
+ else
+ {
+ ConsoleLog writeLine("sendMouseMotionEvent() disabled");
+ }
+}
+
+void Frame::mouseMoveEvent(QMouseEvent* event)
+{
+ QPoint newPosition = rescalePosition(event->posF());
+ if(newPosition != _lastRecordedMousePosition) {
+ _lastRecordedMousePosition = newPosition;
+ _mousePositionChanged = true;
+ ConsoleLog writeLine(QString("Mouse moved to (%1,%2)").arg(_lastRecordedMousePosition.x()).arg(_lastRecordedMousePosition.y()));
+ }
+}
+
+QPoint Frame::rescalePosition(QPointF guipos)
+{
+ if(!_clientVNCThread)
+ return QPoint();
+
+ QSize s = size();
+ QSize t = _clientVNCThread->getSize();
+ qreal px, py;
+ px = guipos.x() * t.width() / (qreal)s.width();
+ py = guipos.y() * t.height() / (qreal)s.height();
+ return QPoint((int)px, (int)py);
+}
+
+void Frame::updateMousePosition(QMouseEvent* event)
+{
+ QPoint oldPosition = _lastRecordedMousePosition;
+ _lastRecordedMousePosition = rescalePosition(event->posF());
+ _mousePositionChanged = oldPosition != _lastRecordedMousePosition;
+ sendMouseMotionEvent();
+}
+
+void Frame::enterEvent(QEvent* event)
+{
+ _mouseOver = true;
+ if(_remoteControlEnabled)
+ {
+ grabKeyboard();
+ }
+}
+
+void Frame::leaveEvent(QEvent* event)
+{
+ _mouseOver = false;
+ if(_remoteControlEnabled)
+ {
+ releaseKeyboard();
+ }
+}
+
+void Frame::keyPressEvent(QKeyEvent* event)
+{
+ if(_remoteControlEnabled)
+ {
+ if(event->key() == Qt::Key_Menu)
+ {
+ qDebug("Menu has been pressed");
+ if(!event->isAutoRepeat())
+ _specialEventTimer->start();
+ }
+ else
+ {
+ // The action of the keyboard may depend on the position of the pointer
+ sendMouseMotionEvent();
+ sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers()));
+ }
+ }
+ else
+ {
+ QLabel::keyPressEvent(event);
+ }
+}
+
+void Frame::keyReleaseEvent(QKeyEvent* event)
+{
+ if(_remoteControlEnabled)
+ {
+ sendMouseMotionEvent();
+ if(event->key() == Qt::Key_Menu)
+ {
+ if(!event->isAutoRepeat())
+ {
+ qDebug("Menu has been released");
+ if(_specialEventTimer->isActive())
+ {
+ qDebug("Pressing key on client");
+ // Pressing the key has been deferred, so do it now:
+ sendInputEvent(InputEvent::keyboardPress(event->key(), event->modifiers()));
+ }
+ sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers()));
+ _specialEventTimer->stop();
+ }
+ }
+ else
+ {
+ // The action of the keyboard may depend on the position of the pointer
+ sendInputEvent(InputEvent::keyboardRelease(event->key(), event->modifiers()));
+ }
+ }
+ else
+ {
+ QLabel::keyReleaseEvent(event);
+ }
+}
+
+bool Frame::event(QEvent* event)
+{
+ if(_remoteControlEnabled)
+ {
+ bool recognized;
+ switch(event->type())
+ {
+ case QEvent::ShortcutOverride:
+ recognized = true;
+ event->accept();
+ break;
+ case QEvent::KeyPress:
+ recognized = true;
+ keyPressEvent(static_cast<QKeyEvent*>(event));
+ break;
+ case QEvent::KeyRelease:
+ recognized = true;
+ keyReleaseEvent(static_cast<QKeyEvent*>(event));
+ break;
+ default:
+ recognized = false;
+ }
+ if(recognized && event->isAccepted())
+ return true;
+ }
+ return QLabel::event(event);
+}
+
+void Frame::showSpecialEventMenu()
+{
+ qDebug("Trying to show menu...");
+ QMenu* menu = new QMenu(this);
+ QList<SpecialInputEventDescription> specialEvents = privileged_handler_chain::describe();
+ QList<SpecialInputEventDescription>::iterator iter;
+ int i;
+ for(i = 0, iter = specialEvents.begin();
+ iter != specialEvents.end();
+ iter++, i++)
+ {
+ QAction* act = menu->addAction((*iter).descriptionString);
+ act->setData(i);
+ }
+ QAction* selected = menu->exec(QCursor::pos());
+ if(selected)
+ {
+ int index = selected->data().toInt();
+ sendInputEvent(specialEvents.at(index).toEvent());
+ }
+ delete menu;
+}
diff --git a/src/gui/frame.h b/src/gui/frame.h
index 3004e0c..8271670 100644
--- a/src/gui/frame.h
+++ b/src/gui/frame.h
@@ -8,6 +8,7 @@ class VNCClientThread;
class ConnectionWindow;
class ConnectionFrame;
class MainWindow;
+class InputEvent;
class Frame: public QLabel
{
@@ -48,8 +49,13 @@ public:
QToolButton* button_lock;
QToolButton* button_unlock;
QToolButton* button_dozent;
+ QToolButton* button_control;
+ QToolButton* button_control_all;
QList<QToolButton*> toolButtonList;
+ bool _remoteControlEnabled;
+ bool _remoteControlToAll;
+
public Q_SLOTS:
void updateImage(int x, int y, int w, int h);
void iamDown();
@@ -68,13 +74,24 @@ public Q_SLOTS:
void setLock();
//void unlock();
void setDozent();
+private Q_SLOTS:
+ void remoteControlClicked();
+ void remoteControlAllClicked();
+ void sendMouseMotionEvent();
+ void showSpecialEventMenu();
signals:
- void clicked();
+ void clicked();
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent ( QMouseEvent * event );
void mouseReleaseEvent ( QMouseEvent * event );
+ void mouseMoveEvent ( QMouseEvent * event );
+ void enterEvent(QEvent* event);
+ void leaveEvent(QEvent* event);
+ void keyPressEvent(QKeyEvent* event);
+ void keyReleaseEvent(QKeyEvent* event);
+ bool event(QEvent* event);
private:
QToolButton* createToolButton(const QString &toolTip, const QIcon &icon, const char *member);
@@ -85,6 +102,16 @@ private:
bool _isLocked;
bool _dozent;
int _ux, _uy;
+
+ // for remote control:
+ QPoint _lastRecordedMousePosition;
+ bool _mousePositionChanged;
+ QTimer* _mouseMotionEventTimer;
+ bool _mouseOver;
+ QTimer* _specialEventTimer;
+ QPoint rescalePosition(QPointF guiPosition);
+ void updateMousePosition(QMouseEvent* event);
+ void sendInputEvent(InputEvent const&);
};
#endif /* FRAME_H_ */
diff --git a/src/gui/mainWindow.cpp b/src/gui/mainWindow.cpp
index 815f001..b6878e8 100644
--- a/src/gui/mainWindow.cpp
+++ b/src/gui/mainWindow.cpp
@@ -16,7 +16,10 @@
#include <QtGui>
#include <QFileDialog>
+#include <QBuffer>
+#include <QDataStream>
#include <src/gui/mainWindow.h>
+#include <src/net/mcast/McastConfiguration.h>
using namespace std;
// setting the IF-DEF Block for the touchgui and the normal gui, for later use
@@ -35,6 +38,7 @@ using namespace std;
#include <src/gui/processesDialog.h>
//#include <src/gui/dialog.h>
#include <src/core/pvsConnectionManager.h>
+#include <src/gui/multicastConfigDialog.h>
#include <iostream>
MainWindow::MainWindow(QWidget *parent) :
@@ -133,6 +137,7 @@ MainWindow::MainWindow(QWidget *parent) :
ui->actionFoto->setStatusTip(tr("Make a screenshot for the selected client(s)"));
ui->actionLock->setStatusTip(tr("Lock or Unlock all Clients"));
connect(ui->actionCreate_profile, SIGNAL(triggered()), this, SLOT(createProfile()));
+ connect(ui->actionConfigure_Network, SIGNAL(triggered()), this, SLOT(configureNetwork()));
connect(ui->actionShowProcesses, SIGNAL(triggered()), this, SLOT(showProcesses()));
ui->actionShowProcesses->setStatusTip(tr("Show Processes of the selected Client"));
connect(ui->actionStartProcess, SIGNAL(triggered()), this, SLOT(startProcess()));
@@ -163,7 +168,6 @@ MainWindow::MainWindow(QWidget *parent) :
_serverSocket = new QTcpServer();
_serverSocket->listen(QHostAddress::Any, 29481);
connect(_serverSocket, SIGNAL(newConnection()), this, SLOT(incomingFile()));
-
}
MainWindow::~MainWindow()
@@ -270,24 +274,24 @@ void MainWindow::loadSettings()
if (current.compare("default") == 0)
{
setWindowTitle("PVSmgr - Default");
- QPoint pos1 = settings.value("default/1", QPoint(0, 0)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("1", pos1);
- QPoint pos2 = settings.value("default/2", QPoint(194, 0)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("2", pos2);
- QPoint pos3 = settings.value("default/3", QPoint(388, 0)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("3", pos3);
- QPoint pos4 = settings.value("default/4", QPoint(582, 0)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("4", pos4);
- QPoint pos5 = settings.value("default/5", QPoint(0, 173)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("5", pos5);
- QPoint pos6 = settings.value("default/6", QPoint(194, 173)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("6", pos6);
- QPoint pos7 = settings.value("default/7", QPoint(388, 173)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("7", pos7);
- QPoint pos8 = settings.value("default/8", QPoint(582, 173)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("8", pos8);
- QPoint pos9 = settings.value("default/9", QPoint(293, 346)).toPoint();
- MainWindow::getConnectionWindow()->addFrameBySettings("9", pos9);
+// QPoint pos1 = settings.value("default/1", QPoint(0, 0)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("1", pos1);
+// QPoint pos2 = settings.value("default/2", QPoint(194, 0)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("2", pos2);
+// QPoint pos3 = settings.value("default/3", QPoint(388, 0)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("3", pos3);
+// QPoint pos4 = settings.value("default/4", QPoint(582, 0)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("4", pos4);
+// QPoint pos5 = settings.value("default/5", QPoint(0, 173)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("5", pos5);
+// QPoint pos6 = settings.value("default/6", QPoint(194, 173)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("6", pos6);
+// QPoint pos7 = settings.value("default/7", QPoint(388, 173)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("7", pos7);
+// QPoint pos8 = settings.value("default/8", QPoint(582, 173)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("8", pos8);
+// QPoint pos9 = settings.value("default/9", QPoint(293, 346)).toPoint();
+// MainWindow::getConnectionWindow()->addFrameBySettings("9", pos9);
QString title = "PVSmgr - ";
title.append(_profilName);
@@ -1277,6 +1281,18 @@ void MainWindow::startChatDialog()
sChatDialog.raise();//show the chat dialog on top level
}
+void MainWindow::configureNetwork()
+{
+ PVSServer* server = PVSConnectionManager::getManager()->getServer();
+ McastConfiguration mc(*(server->getMulticastConfiguration()));
+ MulticastConfigDialog* mcd = new MulticastConfigDialog(&mc, this);
+ int result = mcd->exec();
+ if(result == QDialog::Accepted)
+ {
+ server->multicastReconfigure(&mc);
+ }
+}
+
MainWindow* MainWindow::myself = NULL;
ConnectionList* MainWindow::conList = NULL;
ConnectionWindow* MainWindow::conWin = NULL;
diff --git a/src/gui/mainWindow.h b/src/gui/mainWindow.h
index 2ab62f8..28f82f7 100644
--- a/src/gui/mainWindow.h
+++ b/src/gui/mainWindow.h
@@ -204,6 +204,7 @@ private slots:
void setPasswordForConnection(int enabled);
void combobox1(int menuindex1); // Funktion um index der combobox auszulesen und weiterzuverarbeiten s. Ticker 671
//void combobox2(int menuindex2); // Funktion um index der combobox auszulesen und weiterzuverarbeiten
+ void configureNetwork();
};
diff --git a/src/gui/multicastConfigDialog.cpp b/src/gui/multicastConfigDialog.cpp
new file mode 100644
index 0000000..ff370c7
--- /dev/null
+++ b/src/gui/multicastConfigDialog.cpp
@@ -0,0 +1,170 @@
+#include <QValidator>
+#include <QIntValidator>
+#include <QHostAddress>
+#include <QPushButton>
+#include "multicastConfigDialog.h"
+#include <src/net/mcast/McastConfiguration.h>
+// #include "multicastValidators.h"
+
+MulticastConfigDialog::MulticastConfigDialog(QWidget* parent) :
+ QDialog(parent)
+{
+ setupUi();
+}
+
+MulticastConfigDialog::MulticastConfigDialog(McastConfiguration* config,
+ QWidget *parent) :
+ QDialog(parent)
+{
+ setupUi();
+ _config = config;
+
+ _ui.groupAddressEdit->setText(config->multicastAddress());
+ _ui.dataPortEdit->setText(QString::number(config->multicastUDPPortBase()));
+
+ connect(_ui.buttonBox, SIGNAL(accepted()), this, SLOT(dialogAccepted()));
+ connect(_ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+}
+
+void MulticastConfigDialog::setupUi()
+{
+ _ui.setupUi(this);
+
+ QIntValidator* portValidator = new QIntValidator(1024, 65535, this);
+ _ui.dataPortEdit->setValidator(portValidator);
+
+ connect(_ui.groupAddressEdit, SIGNAL(textChanged(QString const&)), this,
+ SLOT(validateGroupAddress(QString const&)));
+ connect(_ui.dataPortEdit, SIGNAL(textChanged(QString const&)), this,
+ SLOT(validateDataPort(QString const&)));
+
+ connect(_ui.buttonBox, SIGNAL(accepted()), this, SLOT(dialogAccepted()));
+ connect(_ui.buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
+
+ validateGroupAddress(_ui.groupAddressEdit->text());
+ validateDataPort(_ui.dataPortEdit->text());
+}
+
+MulticastConfigDialog::~MulticastConfigDialog()
+{
+}
+
+void MulticastConfigDialog::dialogAccepted()
+{
+ QHostAddress addr;
+ bool addressParses = addr.setAddress(_ui.groupAddressEdit->text());
+ _config->multicastAddress(_ui.groupAddressEdit->text());
+ quint16 port = _ui.dataPortEdit->text().toInt();
+ _config->multicastUDPPortBase(port);
+ _config->multicastDPort(port + 1);
+ _config->multicastSPort(port + 2);
+ _config->multicastRate(_ui.rateSpinbox->value() * 1024);
+ accept();
+}
+
+void MulticastConfigDialog::setError(QWidget* widget,
+ QLabel* errorMessageLabel, QString text)
+{
+ if (errorMessageLabel)
+ errorMessageLabel->setText(QString(
+ "<span style=\"font-weight: 600; color: #880000;\">") + text
+ + "</span>");
+ if (widget)
+ widget->setStyleSheet("background-color: #ffcccc;");
+}
+
+void MulticastConfigDialog::setOK(QWidget* widget, QLabel* errorMessageLabel)
+{
+ if (errorMessageLabel)
+ errorMessageLabel->setText(QString(
+ "<span style=\"font-weight: 600; color: #008800;\">")
+ + tr("OK") + "</span>");
+ if (widget)
+ widget->setStyleSheet("background-color: #ccffcc;");
+}
+
+void MulticastConfigDialog::validateGroupAddress(QString const& input)
+{
+ QHostAddress a;
+
+ _isAddressValid = false;
+
+ if (!a.setAddress(input))
+ {
+ setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr(
+ "Not a valid IP Address"));
+ revalidateButtons();
+ return;
+ }
+
+ // check if it is IPv4
+ if (a.protocol() != QAbstractSocket::IPv4Protocol)
+ {
+ setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr(
+ "Not a valid IPv4 Address"));
+ revalidateButtons();
+ return;
+ }
+
+ // check if it is a valid multicast address
+ quint32 addr = a.toIPv4Address();
+ if ((addr & 0xf0000000) != 0xe0000000)
+ {
+ setError(_ui.groupAddressEdit, _ui.groupAddressMessage, tr(
+ "Not an IPv4 multicast address"));
+ revalidateButtons();
+ return;
+ }
+
+ _isAddressValid = true;
+ setOK(_ui.groupAddressEdit, _ui.groupAddressMessage);
+ revalidateButtons();
+}
+
+void MulticastConfigDialog::validateDataPort(QString const& input)
+{
+ bool ok;
+ int p = input.toInt(&ok, 0);
+
+ _isPortValid = false;
+
+ if (!ok)
+ {
+ setError(_ui.dataPortEdit, _ui.dataPortMessage, tr("Not a number"));
+ revalidateButtons();
+ return;
+ }
+
+ if (p < 0)
+ {
+ setError(_ui.dataPortEdit, _ui.dataPortMessage, tr("Must be positive"));
+ revalidateButtons();
+ return;
+ }
+
+ if (p < 1024)
+ {
+ setError(_ui.dataPortEdit, _ui.dataPortMessage, tr(
+ "Must not be a privileged port"));
+ revalidateButtons();
+ return;
+ }
+
+ if (p > 65535)
+ {
+ setError(_ui.dataPortEdit, _ui.dataPortMessage, tr(
+ "Port number too large"));
+ revalidateButtons();
+ return;
+ }
+
+ _isPortValid = true;
+ setOK(_ui.dataPortEdit, _ui.dataPortMessage);
+ revalidateButtons();
+}
+
+void MulticastConfigDialog::revalidateButtons()
+{
+ _ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(_isAddressValid
+ && _isPortValid);
+}
diff --git a/src/gui/multicastConfigDialog.h b/src/gui/multicastConfigDialog.h
new file mode 100644
index 0000000..6421813
--- /dev/null
+++ b/src/gui/multicastConfigDialog.h
@@ -0,0 +1,38 @@
+#ifndef MULTICASTCONFIGDIALOG_H
+#define MULTICASTCONFIGDIALOG_H
+
+#include <QtGui/QDialog>
+#include <QPointer>
+#include "ui_multicastConfigDialog.h"
+#include <pvsinterface.h>
+
+class McastConfiguration;
+
+class MulticastConfigDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ MulticastConfigDialog(QWidget* parent = 0);
+ MulticastConfigDialog(McastConfiguration* dbusIface, QWidget *parent = 0);
+ ~MulticastConfigDialog();
+
+private:
+ Ui::MulticastConfigDialogClass _ui;
+ McastConfiguration* _config;
+ bool _isAddressValid;
+ bool _isPortValid;
+
+ void setupUi();
+
+ void setError(QWidget* input, QLabel* messageLabel, QString text);
+ void setOK(QWidget* input, QLabel* messageLabel);
+ void revalidateButtons();
+
+private slots:
+ void dialogAccepted();
+ void validateGroupAddress(QString const&);
+ void validateDataPort(QString const&);
+};
+
+#endif // MULTICASTCONFIGDIALOG_H
diff --git a/src/gui/ui/clientConfigDialog.ui b/src/gui/ui/clientConfigDialog.ui
index 3262b6b..bb4bdc9 100644
--- a/src/gui/ui/clientConfigDialog.ui
+++ b/src/gui/ui/clientConfigDialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>438</width>
- <height>257</height>
+ <width>445</width>
+ <height>266</height>
</rect>
</property>
<property name="windowTitle">
@@ -27,7 +27,7 @@
<enum>QTabWidget::North</enum>
</property>
<property name="currentIndex">
- <number>0</number>
+ <number>2</number>
</property>
<widget class="QWidget" name="tabPermissions">
<attribute name="title">
@@ -197,6 +197,64 @@
</item>
</layout>
</widget>
+ <widget class="QWidget" name="networkTab">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <attribute name="title">
+ <string>Network</string>
+ </attribute>
+ <layout class="QFormLayout" name="formLayout">
+ <item row="2" column="1">
+ <widget class="QComboBox" name="interfaceList">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ <property name="insertPolicy">
+ <enum>QComboBox::NoInsert</enum>
+ </property>
+ <property name="frame">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Network Interface</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QPushButton" name="reloadInterfaceListButton">
+ <property name="text">
+ <string>Reload List</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</widget>
</item>
<item>
diff --git a/src/gui/ui/clientFileReceiveDialog.ui b/src/gui/ui/clientFileReceiveDialog.ui
index af3a135..a137def 100644
--- a/src/gui/ui/clientFileReceiveDialog.ui
+++ b/src/gui/ui/clientFileReceiveDialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>208</width>
- <height>108</height>
+ <width>528</width>
+ <height>117</height>
</rect>
</property>
<property name="windowTitle">
@@ -61,6 +61,13 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QLabel" name="labelStatus">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
diff --git a/src/gui/ui/clientFileSendDialog.ui b/src/gui/ui/clientFileSendDialog.ui
index d2d9c75..85462ba 100644
--- a/src/gui/ui/clientFileSendDialog.ui
+++ b/src/gui/ui/clientFileSendDialog.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>186</width>
- <height>108</height>
+ <width>528</width>
+ <height>144</height>
</rect>
</property>
<property name="windowTitle">
@@ -33,7 +33,7 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
@@ -61,10 +61,17 @@
</property>
</spacer>
</item>
+ <item>
+ <widget class="QLabel" name="labelStatus">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelA">
<property name="text">
diff --git a/src/gui/ui/clientNicklistDialog.ui b/src/gui/ui/clientNicklistDialog.ui
index afd84f1..3679b55 100644
--- a/src/gui/ui/clientNicklistDialog.ui
+++ b/src/gui/ui/clientNicklistDialog.ui
@@ -29,6 +29,13 @@
<widget class="QListWidget" name="listWidget"/>
</item>
<item>
+ <widget class="QCheckBox" name="sendToAllCheckBox">
+ <property name="text">
+ <string>Send to &amp;all</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
diff --git a/src/gui/ui/mainwindow.ui b/src/gui/ui/mainwindow.ui
index 285d0f2..d444092 100644
--- a/src/gui/ui/mainwindow.ui
+++ b/src/gui/ui/mainwindow.ui
@@ -243,9 +243,16 @@
</property>
<addaction name="actionAbout_pvs"/>
</widget>
+ <widget class="QMenu" name="menuNetwork">
+ <property name="title">
+ <string>Network</string>
+ </property>
+ <addaction name="actionConfigure_Network"/>
+ </widget>
<addaction name="menu_File"/>
<addaction name="menuClients"/>
<addaction name="menuLogging"/>
+ <addaction name="menuNetwork"/>
<addaction name="menu_Help"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
@@ -533,6 +540,14 @@
<string>-</string>
</property>
</action>
+ <action name="actionConfigure_Network">
+ <property name="text">
+ <string>&amp;Configure...</string>
+ </property>
+ <property name="toolTip">
+ <string>Configure Network Parameters</string>
+ </property>
+ </action>
<action name="actionShowProcesses">
<property name="icon">
<iconset resource="../../../pvsmgr.qrc">
diff --git a/src/gui/ui/mainwindowtouch.ui b/src/gui/ui/mainwindowtouch.ui
index ae1d0cf..ea6f01e 100644
--- a/src/gui/ui/mainwindowtouch.ui
+++ b/src/gui/ui/mainwindowtouch.ui
@@ -365,6 +365,8 @@
<addaction name="actionresetall"/>
<addaction name="separator"/>
<addaction name="actionDozent"/>
+ <addaction name="separator"/>
+ <addaction name="actionConfigure_Network"/>
<addaction name="actionShowProcesses"/>
<addaction name="actionStartProcess"/>
</widget>
@@ -638,6 +640,15 @@
<string>Ctrl+D</string>
</property>
</action>
+ <action name="actionConfigure_Network">
+ <property name="icon">
+ <iconset resource="../../../pvsmgr.qrc">
+ <normaloff>:/netconf</normaloff>:/netconf</iconset>
+ </property>
+ <property name="text">
+ <string>Configure Network...</string>
+ </property>
+ </action>
<action name="actionShowProcesses">
<property name="icon">
<iconset resource="../../../pvsmgr.qrc">
diff --git a/src/gui/ui/multicastConfigDialog.ui b/src/gui/ui/multicastConfigDialog.ui
new file mode 100644
index 0000000..1ddf02c
--- /dev/null
+++ b/src/gui/ui/multicastConfigDialog.ui
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MulticastConfigDialogClass</class>
+ <widget class="QDialog" name="MulticastConfigDialogClass">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>331</width>
+ <height>314</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>PVS - Multicast Configuration</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;table style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
+&lt;tr&gt;
+&lt;td style=&quot;border: none;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;You need to specify connection parameters for multicast messaging on your network. These parameters will automatically be distributed to client computers, so you need to assign them only once.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;You will probably want to assign an address from the &lt;span style=&quot; text-decoration: underline;&quot;&gt;239.0.0.0/8&lt;/span&gt; &amp;quot;Administratively Scoped&amp;quot; range.&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Do not assign arbitrary numbers without checking with your network administrator!&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Multicast Group Address</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLineEdit" name="groupAddressEdit">
+ <property name="inputMask">
+ <string notr="true">009.009.009.009; </string>
+ </property>
+ <property name="text">
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QLabel" name="groupAddressMessage">
+ <property name="text">
+ <string>&lt;span style=&quot; font-weight:600; color:#008800;&quot;&gt;OK&lt;/span&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Data Port (1024-65535)</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="dataPortEdit">
+ <property name="inputMask">
+ <string notr="true">00009; </string>
+ </property>
+ <property name="maxLength">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" colspan="2">
+ <widget class="QLabel" name="dataPortMessage">
+ <property name="text">
+ <string>&lt;span style=&quot; font-weight:600; color:#008800;&quot;&gt;OK&lt;/span&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Transmission Rate</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QSpinBox" name="rateSpinbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="suffix">
+ <string> KiB/s</string>
+ </property>
+ <property name="maximum">
+ <number>10240</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/input/CMakeLists.txt b/src/input/CMakeLists.txt
new file mode 100644
index 0000000..20fd531
--- /dev/null
+++ b/src/input/CMakeLists.txt
@@ -0,0 +1,114 @@
+include(${QT_USE_FILE})
+
+set(pvsinput_SRCS
+ inputEvent.cpp
+ )
+
+if(UNIX)
+ find_file(XINPUT2_HDR X11/extensions/XInput2.h)
+ if(XINPUT2_HDR)
+ set_property(SOURCE x11FakeKeyboardHandler.cpp
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS HAVE_XINPUT2_H
+ )
+ endif()
+
+ set(pvsprivinputd_SRCS
+ pvsprivinputd.cpp
+ pvsPrivInputHandler.cpp
+ pvsCheckPrivileges.cpp
+ pvsSyslog.cpp
+ pvsPrivInputSignalHandler.cpp
+ rebootSystemHandler.cpp
+ killX11Handler.cpp
+ magicSysRqHandler.cpp
+ sayHelloHandler.cpp
+ inputEventHandler.cpp
+ )
+
+ set(pvsprivinputd_MOC_HDRS
+ pvsPrivInputHandler.h
+ pvsCheckPrivileges.h
+ pvsSyslog.h
+ pvsPrivInputSignalHandler.h
+ )
+
+ qt4_wrap_cpp(pvsprivinputd_MOC_SRCS
+ ${pvsprivinputd_MOC_HDRS}
+ )
+
+ add_executable(pvsprivinputd
+ ${pvsprivinputd_SRCS}
+ ${pvsprivinputd_MOC_SRCS}
+ )
+
+ set_property(SOURCE ${pvsprivinputd_SRCS} ${pvsprivinputd_MOC_SRCS}
+ APPEND
+ PROPERTY COMPILE_FLAGS " -I${QT_QTCORE_INCLUDE_DIR} -I${QT_QTDBUS_INCLUDE_DIR} -I${QT_QTNETWORK_INCLUDE_DIR}")
+
+ target_link_libraries(pvsprivinputd
+ pvsinput
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTDBUS_LIBRARY}
+ ${QT_QTNETWORK_LIBRARY}
+ )
+
+ list(APPEND pvsinput_SRCS
+ pvsPrivInputSocket.cpp
+ x11InputUtils.cpp
+ x11FakeKeyboardHandler.cpp
+ x11FakeMouseHandler.cpp
+ privilegedHandlerForwarder.cpp
+ )
+
+ # we need pkg-config to find out where to install the action file:
+ find_package(PkgConfig)
+ if(NOT PKG_CONFIG_FOUND)
+ # we will try to make a best effort and put our policy file into
+ # ${PREFIX}/share/polkit-1, but only if we can find
+ # the pkexec executable.
+ find_program(PKEXEC_LOCATION pkexec)
+ if(PKEXEC_LOCATION OR ENABLE_POLKIT)
+ set(POLKIT_FOUND ON)
+ set(POLKIT_PREFIX ${CMAKE_INSTALL_PREFIX})
+ set(POLKIT_POLICY_DIR ${POLKIT_PREFIX}/share/polkit-1/actions)
+ endif()
+ else()
+ pkg_check_modules(POLKIT "polkit-gobject-1")
+ if(POLKIT_FOUND)
+ execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} polkit-gobject-1 --variable=policydir
+ OUTPUT_VARIABLE POLKIT_POLICY_DIR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(NOT POLKIT_POLICY_DIR)
+ set(POLKIT_FOUND OFF)
+ endif()
+ endif()
+ endif()
+
+ # now, arrange for policykit integration:
+ if(POLKIT_FOUND OR ENABLE_POLKIT)
+ if(NOT POLKIT_POLICY_DIR)
+ message(SEND_ERROR "PolicyKit integration is enabled, but cannot find PolicyKit's actions directory. Please set POLKIT_POLICY_DIR to the right value")
+ endif()
+
+ install(FILES org.openslx.pvs.input.policy
+ DESTINATION ${POLKIT_POLICY_DIR})
+ set_property(SOURCE ${pvsprivinputd_SRCS}
+ APPEND
+ PROPERTY COMPILE_DEFINITIONS HAVE_POLKIT)
+ message(STATUS "PolicyKit integration: enabled")
+ else()
+ message(STATUS "PolicyKit integration: disabled")
+ endif()
+
+ # Install a pvsprivinputd.conf template
+ install(FILES pvsprivinputd.conf
+ DESTINATION /etc
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+endif()
+
+add_library(
+ pvsinput
+ STATIC
+ ${pvsinput_SRCS}
+)
diff --git a/src/input/inputEvent.cpp b/src/input/inputEvent.cpp
new file mode 100644
index 0000000..9b69a3a
--- /dev/null
+++ b/src/input/inputEvent.cpp
@@ -0,0 +1,99 @@
+/*
+ * inputEvent.cpp
+ *
+ * Created on: 06.09.2010
+ * Author: brs
+ */
+
+#include <QBuffer>
+#include <QByteArray>
+#include <QDataStream>
+#include <QKeyEvent>
+#include <QMouseEvent>
+#include <QString>
+#include "inputEvent.h"
+#include <src/util/consoleLogger.h>
+
+// We implement operators to serialize and load an event:
+QDataStream& operator <<(QDataStream& ostrm, InputEvent const& evt)
+{
+ ostrm << evt.type_ << evt.code_ << evt.value_;
+ return ostrm;
+}
+
+QDataStream& operator >>(QDataStream& istrm, InputEvent& evt)
+{
+ istrm >> evt.type_ >> evt.code_ >> evt.value_;
+ return istrm;
+}
+
+void eventToString(InputEvent const& evt, QString& str)
+{
+ QByteArray ba;
+ QBuffer buf(&ba);
+ buf.open(QIODevice::WriteOnly);
+ QDataStream out(&buf);
+ out << evt;
+ str = QString::fromAscii(ba.toBase64());
+}
+
+bool eventFromString(QString const& str, InputEvent& evt)
+{
+ // TODO This does not do proper error checking. Only use from trusted sources!
+ QByteArray ba = QByteArray::fromBase64(str.toAscii());
+ QBuffer buf(&ba);
+ buf.open(QIODevice::ReadOnly);
+ QDataStream in(&buf);
+ in >> evt;
+ return true;
+}
+
+quint16 InputEvent::mouseButtonsFromQt(int b)
+{
+ quint16 ret = 0;
+ if(b & Qt::LeftButton)
+ {
+ ret |= EB_LEFT;
+ }
+ if(b & Qt::RightButton)
+ {
+ ret |= EB_RIGHT;
+ }
+ if(b & Qt::MidButton)
+ {
+ ret |= EB_MIDDLE;
+ }
+ return ret;
+}
+
+InputEvent InputEvent::mousePressRelease(int qt_button, int qt_buttons)
+{
+ quint16 button = mouseButtonsFromQt(qt_button);
+ quint16 buttons = mouseButtonsFromQt(qt_buttons);
+ quint16 code;
+
+ if(buttons & button)
+ {
+ code = EC_PRESS;
+ }
+ else
+ {
+ code = EC_RELEASE;
+ }
+
+ quint32 value = ((quint32)button << 16) | buttons;
+ return InputEvent(ET_BUTTON, code, value);
+}
+
+InputEvent InputEvent::keyboardPress(int key, int mods)
+{
+ quint32 value = key | mods;
+ return InputEvent(ET_KEY, EC_PRESS, value);
+}
+
+InputEvent InputEvent::keyboardRelease(int key, int mods)
+{
+ quint32 value = key | mods;
+ return InputEvent(ET_KEY, EC_RELEASE, value);
+}
+
diff --git a/src/input/inputEvent.h b/src/input/inputEvent.h
new file mode 100644
index 0000000..7a64bfc
--- /dev/null
+++ b/src/input/inputEvent.h
@@ -0,0 +1,219 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEvent.h:
+ # - Definition of an input event
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTEVENT_H_
+#define INPUTEVENT_H_
+
+#include <cassert>
+#include <string>
+#include <sstream>
+#include <stdint.h>
+
+#ifndef __linux
+# error "This will only run on a Linux system. Porting is required for other systems."
+#endif
+
+struct QDataStream;
+struct QString;
+struct QMouseEvent;
+struct QKeyEvent;
+
+class InputEvent
+{
+private:
+ friend QDataStream& operator<<(QDataStream&, InputEvent const&);
+ friend QDataStream& operator>>(QDataStream&, InputEvent&);
+
+ friend void eventToString(InputEvent const& evt, QString& str);
+ friend bool eventFromString(QString const& str, InputEvent& evt);
+
+ uint16_t type_;
+ uint16_t code_;
+ uint32_t value_;
+
+ // InputEvents are immutable. Prohibit assignment:
+ InputEvent& operator=(InputEvent const&); // There intentionally is no implementation.
+public:
+ InputEvent(uint16_t type, uint16_t code, uint32_t value) : type_(type), code_(code), value_(value)
+ {
+ }
+
+ InputEvent()
+ {
+ }
+
+ static InputEvent mouseMotion(uint16_t x, uint16_t y)
+ {
+ return InputEvent(ET_POINTER, 0, ((uint32_t)x << 16) | y);
+ }
+
+ static uint16_t mouseButtonsFromQt(int b);
+
+ static InputEvent mousePressRelease(int button, int buttons);
+ static InputEvent keyboardPress(int key, int mods);
+ static InputEvent keyboardRelease(int key, int mods);
+
+ static const uint16_t ET_KEY = 0;
+ static const uint16_t ET_BUTTON = 1;
+ static const uint16_t ET_POINTER = 2;
+ static const uint16_t ET_SPECIAL = 3;
+
+ static const uint16_t EC_PRESS = 0;
+ static const uint16_t EC_RELEASE = 1;
+ static const uint16_t EC_REBOOT = 2;
+ static const uint16_t EC_SYSRQ = 3;
+ static const uint16_t EC_KILL_X = 4;
+ static const uint16_t EC_SAY_HELLO = 5; //< for debugging purposes
+
+ typedef uint32_t event_key;
+
+ typedef uint32_t event_key_modifiers;
+
+ static const uint16_t EB_LEFT = 1;
+ static const uint16_t EB_MIDDLE = 2;
+ static const uint16_t EB_RIGHT = 4;
+
+ static const uint32_t MODIFIER_MASK =
+ 0x7e000000;
+
+ uint16_t type() const
+ {
+ return type_;
+ }
+
+ uint16_t code() const
+ {
+ return code_;
+ }
+
+ uint32_t value() const
+ {
+ return value_;
+ }
+
+ bool isKeyboard() const
+ {
+ return type_ == ET_KEY;
+ }
+
+ bool isButton() const
+ {
+ return type_ == ET_BUTTON;
+ }
+
+ bool isPointer() const
+ {
+ return type_ == ET_POINTER;
+ }
+
+ bool isSpecial() const
+ {
+ return type_ == ET_SPECIAL;
+ }
+
+ bool isPress() const
+ {
+ return code_ == EC_PRESS;
+ }
+
+ bool isRelease() const
+ {
+ return code_ == EC_RELEASE;
+ }
+
+ uint16_t pressedButton() const
+ {
+ assert(type_ == ET_BUTTON);
+ return (value_ >> 16);
+ }
+
+ uint16_t heldButtons() const
+ {
+ assert(type_ == ET_BUTTON);
+ return (value_ & 0xffff);
+ }
+
+ uint16_t xCoord() const
+ {
+ assert(type_ == ET_POINTER);
+ return (value_ >> 16);
+ }
+
+ uint16_t yCoord() const
+ {
+ assert(type_ == ET_POINTER);
+ return (value_ & 0xffff);
+ }
+
+ static std::string typeToString(uint16_t type)
+ {
+ switch(type)
+ {
+ case ET_BUTTON:
+ return "BUTTON";
+ case ET_KEY:
+ return "KEY";
+ case ET_POINTER:
+ return "POINTER";
+ case ET_SPECIAL:
+ return "SPECIAL";
+ default:
+ std::ostringstream s;
+ s << std::hex << type;
+ return s.str();
+ }
+ }
+
+ static std::string codeToString(uint16_t code)
+ {
+ switch(code)
+ {
+ case EC_PRESS:
+ return "PRESS";
+ case EC_RELEASE:
+ return "RELEASE";
+ case EC_SYSRQ:
+ return "SYSRQ";
+ case EC_REBOOT:
+ return "REBOOT";
+ case EC_KILL_X:
+ return "KILL_X";
+ default:
+ std::ostringstream s;
+ s << std::hex << code;
+ return s.str();
+ }
+ }
+
+ std::string toString() const
+ {
+ std::ostringstream s;
+ s << typeToString(type_) << ':' << codeToString(code_) << ':' << std::hex << value_;
+ return s.str();
+ }
+
+ uint32_t qt_keysym() const
+ {
+ return value_ & ~MODIFIER_MASK;
+ }
+
+ uint32_t qt_modifiers() const
+ {
+ return value_ & MODIFIER_MASK;
+ }
+};
+
+#endif /* INPUTEVENT_H_ */
diff --git a/src/input/inputEventHandler.cpp b/src/input/inputEventHandler.cpp
new file mode 100644
index 0000000..c16c358
--- /dev/null
+++ b/src/input/inputEventHandler.cpp
@@ -0,0 +1,36 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include "inputEventHandler.h"
+#include "pvsCheckPrivileges.h"
+
+bool policy::allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_UNKNOWN, PVSCheckPrivileges::USER_PRIVILEGED,
+ ctx);
+ else
+ return false;
+}
+
+bool policy::allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx)
+{
+ if(ctx)
+ return PVSCheckPrivileges::instance()->require(PVSCheckPrivileges::SESSION_LOCAL, PVSCheckPrivileges::USER_UNKNOWN,
+ ctx);
+ else
+ return false;
+}
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h
new file mode 100644
index 0000000..52e3338
--- /dev/null
+++ b/src/input/inputEventHandler.h
@@ -0,0 +1,295 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTEVENTHANDLER_H_
+#define INPUTEVENTHANDLER_H_
+
+#include <QtGlobal>
+#include <QtDebug>
+#include <QList>
+#include <QString>
+#include <QCoreApplication>
+#include <boost/mpl/contains.hpp>
+#include <boost/mpl/if.hpp>
+#include <boost/mpl/vector.hpp>
+#include <src/input/inputEvent.h>
+
+#define HANDLER_TYPE_DONT_CARE 0xffff
+#define HANDLER_CODE_DONT_CARE 0xffff
+#define HANDLER_VALUE_DONT_CARE 0xffffffff
+
+class InputEventContext
+{
+public:
+ virtual pid_t getSenderPid() const = 0;
+ virtual uid_t getSenderUid() const = 0;
+ virtual gid_t getSenderGid() const = 0;
+};
+
+struct SpecialInputEventDescription
+{
+ SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0)
+ : descriptionString(d), evtType(t), evtCode(c), evtValue(v)
+ {
+ }
+
+ QString descriptionString;
+ quint16 evtType;
+ quint16 evtCode;
+ quint32 evtValue;
+
+ InputEvent toEvent() const
+ {
+ return InputEvent(evtType, evtCode, evtValue);
+ }
+};
+
+template<quint16 Type = HANDLER_TYPE_DONT_CARE,
+ quint16 Code = HANDLER_CODE_DONT_CARE,
+ quint32 Value = HANDLER_VALUE_DONT_CARE>
+class DefaultInputEventHandler {
+protected:
+ static QString tr(char const* string)
+ {
+ return QCoreApplication::translate("InputEventHandler", string);
+ }
+
+public:
+ virtual bool matches(InputEvent const& evt, InputEventContext const*) {
+ if(Type != HANDLER_TYPE_DONT_CARE) {
+ if(evt.type() != Type)
+ return false;
+ }
+ if(Code != HANDLER_CODE_DONT_CARE) {
+ if(evt.code() != Code)
+ return false;
+ }
+ if(Value != HANDLER_VALUE_DONT_CARE) {
+ if(evt.value() != Value)
+ return false;
+ }
+ return true;
+ }
+
+ virtual void initialize()
+ {
+ }
+
+ virtual void handle(InputEvent const& evt, InputEventContext const*) = 0;
+
+ static void describeInto(QList<SpecialInputEventDescription>& description)
+ {
+ }
+};
+
+namespace policy {
+
+enum SecurityFlags {
+ SEC_FREE_FOR_ALL,
+ SEC_PHYSICAL_OR_PRIVILEGED
+};
+
+bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx);
+bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx);
+
+struct SecurityAllowAny
+{
+ bool allow(InputEvent const& evt, InputEventContext const* ctx)
+ {
+ return true;
+ }
+};
+
+struct SecurityAllowPhysicalOrPrivileged
+{
+ bool allow(InputEvent const& evt, InputEventContext const* ctx)
+ {
+ if(allowPhysicalSeat(evt, ctx))
+ return true;
+ else if(allowPrivilegedUser(evt, ctx))
+ return true;
+ return false;
+ }
+};
+
+struct UnixLike;
+struct Linux;
+struct Windows;
+
+#if defined(__linux)
+typedef boost::mpl::vector2<UnixLike,Linux>::type Systems;
+#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__)
+typedef boost::mpl::vector1<Windows>::type Systems;
+#else
+# error "Porting is needed!"
+#endif
+
+struct SystemEnabled;
+struct SystemDisabled;
+
+template<typename System>
+struct RequireSystem
+{
+ typedef typename boost::mpl::contains<Systems, System>::type enabled_type;
+ static const bool enabled = enabled_type::value;
+};
+
+struct RequireNoSystem
+{
+ typedef boost::mpl::bool_<true>::type enabled_type;
+ static const bool enabled = enabled_type::value;
+};
+
+}
+
+template<bool Enabled, typename Delegate, typename SecurityPolicy>
+class HandlerHelper
+{
+public:
+ bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ if(!securityPolicy.allow(evt, context))
+ {
+ std::string evtStr = evt.toString();
+ qWarning("Input Event %s has been denied by security policy", evtStr.c_str());
+ return true;
+ }
+ if(delegate.matches(evt, context)) {
+ delegate.handle(evt, context);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ void initialize()
+ {
+ delegate.initialize();
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ Delegate::describeInto(list);
+ }
+
+private:
+ Delegate delegate;
+ SecurityPolicy securityPolicy;
+};
+
+template<typename Delegate, typename SecurityPolicy>
+class HandlerHelper<false, Delegate, SecurityPolicy>
+{
+public:
+ bool handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ return false;
+ }
+
+ void initialize()
+ {
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>&)
+ {
+ }
+};
+
+template<typename Delegate, typename SystemPolicy = policy::RequireNoSystem, typename SecurityPolicy = void>
+struct Handler : public HandlerHelper<SystemPolicy::enabled, Delegate, SecurityPolicy>
+{
+};
+
+template<typename DefaultSecurityPolicy, typename HandlerType>
+struct ApplyDefaultSecurityPolicy
+{
+ typedef HandlerType type;
+};
+
+template<typename DefaultSecurityPolicy, typename Delegate, typename SystemPolicy>
+struct ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, Handler<Delegate, SystemPolicy, void> >
+{
+ typedef Handler<Delegate, SystemPolicy, DefaultSecurityPolicy> type;
+};
+
+template<typename DefaultSecurityPolicy, typename Begin, typename End>
+struct InputEventHandlerChainHelper
+{
+private:
+ typedef typename boost::mpl::next<Begin>::type next_iterator_type;
+ typedef InputEventHandlerChainHelper<DefaultSecurityPolicy, next_iterator_type, End> next_in_chain;
+
+ typedef typename boost::mpl::deref<Begin>::type handler_entry_type;
+ typedef typename ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, handler_entry_type>::type handler_type;
+
+ handler_type _handler;
+ next_in_chain _next;
+
+public:
+ void handle(InputEvent const& evt, InputEventContext const* context = 0) {
+ if(!_handler.handle(evt, context)) {
+ _next.handle(evt, context);
+ }
+ }
+
+ void initialize() {
+ _handler.initialize();
+ _next.initialize();
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ handler_type::describeInto(list);
+ next_in_chain::describeInto(list);
+ }
+
+ static QList<SpecialInputEventDescription> describe()
+ {
+ QList<SpecialInputEventDescription> list;
+ describeInto(list);
+ return list;
+ }
+};
+
+template<typename DefaultSecurityPolicy, typename End>
+struct InputEventHandlerChainHelper<DefaultSecurityPolicy, End, End>
+{
+ void handle(InputEvent const&, InputEventContext const* context = 0) {
+ // do nothing
+ }
+
+ void initialize() {
+ // do nothing
+ }
+
+ static void describeInto(QList<SpecialInputEventDescription>&)
+ {
+ // do nothing
+ }
+
+ static QList<SpecialInputEventDescription> describe()
+ {
+ return QList<SpecialInputEventDescription>();
+ }
+};
+
+template<typename DefaultSecurityPolicy, typename Collection>
+struct InputEventHandlerChain :
+ public InputEventHandlerChainHelper<DefaultSecurityPolicy,
+ typename boost::mpl::begin<Collection>::type,
+ typename boost::mpl::end<Collection>::type>
+{
+};
+
+#endif /* INPUTEVENTHANDLER_H_ */
diff --git a/src/input/inputEventNonQt.cpp b/src/input/inputEventNonQt.cpp
new file mode 100644
index 0000000..a7455fd
--- /dev/null
+++ b/src/input/inputEventNonQt.cpp
@@ -0,0 +1,17 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEvent.h:
+ # - Input Event routines that need not be linked against Qt
+ # --------------------------------------------------------------------------
+ */
+
+
diff --git a/src/input/inputHandlerChain.h b/src/input/inputHandlerChain.h
new file mode 100644
index 0000000..3c9446c
--- /dev/null
+++ b/src/input/inputHandlerChain.h
@@ -0,0 +1,49 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.h:
+ # - Definition of the input handler chain
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef INPUTHANDLERCHAIN_H_
+#define INPUTHANDLERCHAIN_H_
+
+#include <boost/mpl/list.hpp>
+#include "inputEventHandler.h"
+
+#include "x11FakeKeyboardHandler.h"
+#include "x11FakeMouseHandler.h"
+#include "privilegedHandlerForwarder.h"
+#include "rebootSystemHandler.h"
+#include "sayHelloHandler.h"
+#include "killX11Handler.h"
+#include "magicSysRqHandler.h"
+
+typedef boost::mpl::list<
+ Handler<X11FakeKeyboardHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<X11FakeMouseButtonHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<X11FakeMouseMovementHandler, policy::RequireSystem<policy::UnixLike> >,
+ Handler<PrivilegedHandlerForwarder>
+>::type unprivileged_handler_list;
+
+typedef InputEventHandlerChain<policy::SecurityAllowAny, unprivileged_handler_list> unprivileged_handler_chain;
+
+typedef boost::mpl::list<
+ Handler<SayHelloHandler, policy::RequireNoSystem, policy::SecurityAllowAny >,
+ Handler<KillX11Handler, policy::RequireSystem<policy::Linux> >,
+ Handler<RebootLinuxSystemHandler, policy::RequireSystem<policy::Linux> >,
+ Handler<MagicSysRqHandler, policy::RequireSystem<policy::Linux> >
+>::type privileged_handler_list;
+
+typedef InputEventHandlerChain<policy::SecurityAllowPhysicalOrPrivileged, privileged_handler_list> privileged_handler_chain;
+
+#endif /* INPUTHANDLERCHAIN_H_ */
diff --git a/src/input/killX11Handler.cpp b/src/input/killX11Handler.cpp
new file mode 100644
index 0000000..7ac75a1
--- /dev/null
+++ b/src/input/killX11Handler.cpp
@@ -0,0 +1,89 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # killX11Handler.h
+ # - Kill the X11 Server - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include <QDir>
+#include <QFileInfo>
+#include <QStringList>
+#include "pvsCheckPrivileges.h"
+#include "killX11Handler.h"
+
+using namespace std;
+
+void KillX11Handler::handle(InputEvent const&, InputEventContext const* ctx)
+{
+ // Find out which display device is used:
+ QString displayDevice = PVSCheckPrivileges::instance()->getX11DisplayDevice(ctx);
+ QString displayDeviceAbs = QFileInfo(displayDevice).canonicalFilePath();
+
+ if(displayDevice.isNull())
+ {
+ qWarning("Can not kill X server for %d,%d,%d: No Display Device", ctx->getSenderPid(), ctx->getSenderUid(), ctx->getSenderGid());
+ return;
+ }
+
+ QList<pid_t> pids;
+
+ // Find all processes that have it opened:
+ QDir proc("/proc");
+ QFileInfoList entries = proc.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+ foreach(QFileInfo fi, entries)
+ {
+ // We are only interested in numerical ids:
+ bool ok;
+ pid_t pid = fi.fileName().toUInt(&ok);
+ if(!ok)
+ continue;
+
+ // We have a pid. Now check open files:
+ QDir fds(QString("/proc/%1/fd").arg(pid));
+ qDebug("Searching for X server in %s", fds.absolutePath().toLocal8Bit().constData());
+ QFileInfoList fdfiles = fds.entryInfoList(QDir::Files);
+ foreach(QFileInfo fdfile, fdfiles)
+ {
+ qDebug(" Looking for terminal in %s", fdfile.absoluteFilePath().toLocal8Bit().constData());
+ QFileInfo fdTarget(fdfile.readLink());
+ if(fdTarget.canonicalFilePath() == displayDeviceAbs)
+ {
+ qDebug(" ... found");
+ pids << pid;
+ }
+ }
+ }
+
+ // If everything is well, we have exactly one pid:
+ if(pids.size() != 1)
+ {
+ QStringList pidStrs;
+ foreach(pid_t pid, pids)
+ {
+ pidStrs << QString::number(pid);
+ }
+
+ qWarning("Display device %s is open by multiple or zero processes (%s). We don't know which is X. Aborting.",
+ displayDeviceAbs.toLocal8Bit().constData(),
+ pidStrs.join(", ").toLocal8Bit().constData());
+ return;
+ }
+
+ // We found the PID for the X server. Now kill it.
+ QString exe = QFileInfo(QString("/proc/%1/exe").arg(pids[0])).readLink();
+ qDebug("Killing X server, PID %d, exe name %s with SIGTERM", pids[0], exe.toLocal8Bit().constData());
+
+// kill(pids[0], SIGTERM);
+}
diff --git a/src/input/killX11Handler.h b/src/input/killX11Handler.h
new file mode 100644
index 0000000..2f3ef44
--- /dev/null
+++ b/src/input/killX11Handler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # killX11Handler.h
+ # - Kill the X11 Server - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef KILLX11HANDLER_H_
+#define KILLX11HANDLER_H_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class KillX11Handler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Kill X Server")), InputEvent::ET_SPECIAL, InputEvent::EC_KILL_X);
+ }
+};
+
+#endif /* KILLX11HANDLER_H_ */
diff --git a/src/input/magicSysRqHandler.cpp b/src/input/magicSysRqHandler.cpp
new file mode 100644
index 0000000..108bfca
--- /dev/null
+++ b/src/input/magicSysRqHandler.cpp
@@ -0,0 +1,28 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputEventHandler.h:
+ # - Common definitions for input event handlers
+ # --------------------------------------------------------------------------
+ */
+
+#include <QFile>
+#include "magicSysRqHandler.h"
+
+void MagicSysRqHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ QFile trigger("/proc/sysrq-trigger");
+ trigger.open(QIODevice::WriteOnly);
+ char c = (char)(evt.value() & 0xff);
+ trigger.write(&c, 1);
+ trigger.flush();
+ trigger.close();
+}
diff --git a/src/input/magicSysRqHandler.h b/src/input/magicSysRqHandler.h
new file mode 100644
index 0000000..563d091
--- /dev/null
+++ b/src/input/magicSysRqHandler.h
@@ -0,0 +1,72 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # magicSysRqHandler.h
+ # - Trigger Magic-SysRq functions - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef MAGICSYSRQHANDLER_H_
+#define MAGICSYSRQHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class MagicSysRqHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ>
+{
+public:
+ void handle(InputEvent const& evt, InputEventContext const* ctx);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Reboot immediately"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'b');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Crash system"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'c');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show all held logs"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'd');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGTERM to all"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'e');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Activate OOM killer"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'f');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Send SIGKILL to all"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'i');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Force thaw filesystems"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'j');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Kill all on terminal"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'k');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Show stack traces"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'l');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump memory info"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'm');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Make real-time tasks niceable"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'n');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Power off immediately"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'o');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump registers and flags"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'p');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump timers and clockevents"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'q');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Turn off raw keyboard mode"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'r');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Sync all mounted filesystems"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 's');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump task list"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 't');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Remount all read-only"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'u');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump uninterruptible tasks"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'w');
+ list << SpecialInputEventDescription(QCoreApplication::translate("InputEventHandler", "Dump ftrace buffer"),
+ InputEvent::ET_SPECIAL, InputEvent::EC_SYSRQ, 'z');
+ }
+};
+
+#endif /* MAGICSYSRQHANDLER_H_ */
diff --git a/src/input/org.openslx.pvs.input.policy b/src/input/org.openslx.pvs.input.policy
new file mode 100644
index 0000000..f0de9f2
--- /dev/null
+++ b/src/input/org.openslx.pvs.input.policy
@@ -0,0 +1,18 @@
+<?xml version="1.0" ?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+<policyconfig>
+ <vendor>The OpenSLX project</vendor>
+ <vendor_url>http://lab.openslx.org/pvs</vendor_url>
+
+ <action id="org.openslx.pvs.privilegedinput">
+ <description>Use privileged input actions in PVS</description>
+ <message>Authentication is required to let PVS use privileged input actions</message>
+ <defaults>
+ <allow_inactive>auth_self_keep</allow_inactive>
+ <allow_active>auth_self_keep</allow_active>
+ <allow_any>auth_admin_keep</allow_any>
+ </defaults>
+ </action>
+</policyconfig>
diff --git a/src/input/privilegedHandlerForwarder.cpp b/src/input/privilegedHandlerForwarder.cpp
new file mode 100644
index 0000000..3c0b3bd
--- /dev/null
+++ b/src/input/privilegedHandlerForwarder.cpp
@@ -0,0 +1,82 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.cpp:
+ # - Forward privileged input events to a special handler process - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <string>
+#include <QSettings>
+#include <QtDebug>
+#include "pvsPrivInputSocket.h"
+#include "privilegedHandlerForwarder.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+void PrivilegedHandlerForwarder::initialize()
+{
+ QSettings settings(QSettings::NativeFormat, QSettings::SystemScope, "openslx", "pvsinputd");
+
+ QString defaultPath = "/tmp/pvsprivinputd.sock";
+ QByteArray socketPath = settings.value("socketpath", defaultPath).toString().toLocal8Bit();
+
+ _socket = pvsPrivInputMakeClientSocket();
+ if(_socket < 0)
+ {
+ return;
+ }
+}
+
+void PrivilegedHandlerForwarder::handle(InputEvent const& evt, InputEventContext const*)
+{
+ qDebug("Trying to handle %s in PrivilegedHandlerForwarder", evt.toString().c_str());
+ if(_socket < 0)
+ {
+ initialize();
+ }
+
+ QByteArray data;
+ QDataStream strm(&data, QIODevice::WriteOnly);
+ strm.setByteOrder(QDataStream::BigEndian);
+ strm << evt;
+
+ assert(data.size() == 8);
+
+ int delta = 0;
+ int count = 0;
+ do {
+ delta = write(_socket, data.constData(), data.size());
+ if(delta < 0)
+ {
+ qWarning("Error while communicating with pvsprivinputd: %s", strerror(errno));
+ close(_socket);
+
+ // Try again:
+ initialize();
+ }
+ else if(delta != 8)
+ {
+ // This should normally not happen.
+ qWarning("Could not send a complete packet. Only %d bytes sent", delta);
+ }
+ count++;
+ } while(delta != 8 && count < 3);
+}
diff --git a/src/input/privilegedHandlerForwarder.h b/src/input/privilegedHandlerForwarder.h
new file mode 100644
index 0000000..37e8f24
--- /dev/null
+++ b/src/input/privilegedHandlerForwarder.h
@@ -0,0 +1,32 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # inputHandlerChain.h:
+ # - Forward privileged input events to a special handler process.
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PRIVILEGEDHANDLERFORWARDER_H_
+#define PRIVILEGEDHANDLERFORWARDER_H_
+
+#include "inputEventHandler.h"
+
+class PrivilegedHandlerForwarder : public DefaultInputEventHandler<InputEvent::ET_SPECIAL>
+{
+public:
+ void handle(InputEvent const& evt, InputEventContext const* = 0);
+ void initialize();
+
+private:
+ int _socket;
+};
+
+#endif /* PRIVILEGEDHANDLERFORWARDER_H_ */
diff --git a/src/input/pvsCheckPrivileges.cpp b/src/input/pvsCheckPrivileges.cpp
new file mode 100644
index 0000000..2026c0a
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.cpp
@@ -0,0 +1,550 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsCheckPrivileges.cpp:
+ # - A small program that checks whether or not it is called from
+ # a physical seat and conditionally executes the pvs input daemon.
+ # Additional security-relevant conditions should be checked here.
+ #
+ # The program is called with exactly one parameter, specifying the
+ # number of the file descriptor which is to be passed to its child.
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <iostream>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <QCoreApplication>
+#include <QDir>
+#include <QFileInfo>
+#include <QFileSystemWatcher>
+#include <QSettings>
+#include <QTimer>
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <QtDBus/QDBusInterface>
+#include <QtDBus/QDBusMetaType>
+#include <QtDBus/QDBusReply>
+#include <QtGlobal>
+#include <QDebug>
+#include <QUuid>
+#include "pvsPrivInputSocket.h"
+#include "pvsCheckPrivileges.h"
+
+using namespace std;
+
+#define TIMEOUT_VALUE 10000 /* Wait for max. 10 seconds */
+
+// We need these classes for PolicyKit access:
+struct PolKitSubject {
+ QString subject_kind;
+ QMap<QString, QVariant> subject_details;
+};
+Q_DECLARE_METATYPE(PolKitSubject);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitSubject const& subj)
+{
+ arg.beginStructure();
+ arg << subj.subject_kind << subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitSubject& subj)
+{
+ arg.beginStructure();
+ arg >> subj.subject_kind >> subj.subject_details;
+ arg.endStructure();
+ return arg;
+}
+
+struct PolKitAuthReply {
+ bool isAuthorized;
+ bool isChallenge;
+ QMap<QString, QString> details;
+};
+Q_DECLARE_METATYPE(PolKitAuthReply);
+
+QDBusArgument& operator<<(QDBusArgument& arg, PolKitAuthReply const& reply)
+{
+ arg.beginStructure();
+ arg << reply.isAuthorized << reply.isChallenge << reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+QDBusArgument const& operator>>(QDBusArgument const& arg, PolKitAuthReply& reply)
+{
+ arg.beginStructure();
+ arg >> reply.isAuthorized >> reply.isChallenge >> reply.details;
+ arg.endStructure();
+ return arg;
+}
+
+// We need to pass QMap<QString, QString> to QVariant:
+typedef QMap<QString, QString> QStringStringMap;
+Q_DECLARE_METATYPE(QStringStringMap)
+
+PVSCheckPrivileges* PVSCheckPrivileges::instance()
+{
+ static PVSCheckPrivileges* static_instance = 0;
+ if(!static_instance)
+ {
+ static_instance = new PVSCheckPrivileges();
+ }
+ return static_instance;
+}
+
+void PVSCheckPrivileges::deleteInstance()
+{
+ delete instance();
+}
+
+PVSCheckPrivileges::PVSCheckPrivileges(QObject* parent)
+ : QObject(parent)
+{
+ // initialise our cache:
+ updateCachedUserDatabase();
+ rereadConfiguration();
+
+ // and make it update itself:
+ QStringList paths;
+ paths << "/etc/passwd" << "/etc/group" << pvsPrivInputGetSettingsPath();
+ _watcher = new QFileSystemWatcher(paths, this);
+ connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(updateCachedUserDatabase()));
+ connect(_watcher, SIGNAL(fileChanged(QString const&)), this, SLOT(rereadConfiguration()));
+}
+
+PVSCheckPrivileges::~PVSCheckPrivileges()
+{
+}
+
+QString PVSCheckPrivileges::getSessionReference(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return QString();
+ }
+
+ QString sessionReference = _savedConsoleKitSession.value(sender, QString());
+ if(sessionReference.isNull())
+ {
+ QDBusConnection conn = QDBusConnection::systemBus();
+ // Find the name of the current session:
+ QDBusInterface manager("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", conn);
+ QDBusReply<QDBusObjectPath> replyGetSession = manager.call(QDBus::Block, "GetSessionForUnixProcess", (quint32)sender.pid);
+ if(!replyGetSession.isValid())
+ {
+ qWarning("Reply to GetSessionForUnixProcess is invalid: %s: %s", replyGetSession.error().name().toLocal8Bit().constData(), replyGetSession.error().message().toLocal8Bit().constData());
+ return QString();
+ }
+ _savedConsoleKitSession[sender] = sessionReference = replyGetSession.value().path();
+ }
+ return sessionReference;
+}
+
+PVSCheckPrivileges::SessionKind PVSCheckPrivileges::getSessionKind(CachedInputContext const& sender)
+{
+ if(!sender.isValid())
+ {
+ return SESSION_UNKNOWN;
+ }
+
+ // if the sender is root, we always suppose he works locally.
+ if(sender.uid == 0)
+ {
+ return SESSION_LOCAL;
+ }
+
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return SESSION_LOOKUP_FAILURE;
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface session("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<bool> replyIsLocal = session.call(QDBus::Block, "IsLocal");
+
+ if(!replyIsLocal.isValid())
+ {
+ qWarning("Unable to find out whether the current session is local: %s: %s", replyIsLocal.error().name().toLocal8Bit().constData(), replyIsLocal.error().message().toLocal8Bit().constData());
+ return SESSION_LOOKUP_FAILURE;
+ }
+ return replyIsLocal.value() ? SESSION_LOCAL : SESSION_NONLOCAL;
+}
+
+PVSCheckPrivileges::UserPrivilege PVSCheckPrivileges::getUserPrivilege(CachedInputContext const& sender)
+{
+ // Always allow root:
+ if(sender.uid == 0)
+ {
+ return USER_PRIVILEGED;
+ }
+
+ // Or if the user is one of those enumerated in the privileged-users configuration value:
+ if(_privilegedUsers.contains(sender.uid))
+ {
+ return USER_PRIVILEGED;
+ }
+
+ // Or if the user is a member of one of the groups enumerated in the privileged-groups configuration value:
+ foreach(gid_t gid, _privilegedGroups)
+ {
+ if(_userGroupMap.contains(sender.uid, gid))
+ {
+ return USER_PRIVILEGED;
+ }
+ }
+
+ // User is not trivially privileged, so try to check with PolicyKit.
+#ifdef HAVE_POLKIT // but only if it is present
+ // For PolKit, we need the start-time of the process.
+ // On Linux, we can get it from /proc:
+ QString procStat = QString("/proc/%1/stat").arg(sender.pid);
+ QFile procStatFile(procStat);
+ if(!procStatFile.exists())
+ {
+ qWarning("Could not look up any info on process %d, its %s file does not exist", sender.pid, procStat.toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ procStatFile.open(QIODevice::ReadOnly);
+ QByteArray procStatBytes = procStatFile.readAll();
+ qDebug() << "Read stat file: " << procStatBytes;
+ QString procStatLine = QString::fromLocal8Bit(procStatBytes.constData(), procStatBytes.length());
+ QStringList procStatFields = procStatLine.split(QRegExp("\\s+"));
+ qDebug() << "Found stat fields: " << procStatFields;
+ bool ok;
+ quint64 startTime = procStatFields[21].toULongLong(&ok);
+ if(!ok)
+ {
+ qWarning("Could not find out start time for process %d", (int)sender.pid);
+ return USER_LOOKUP_FAILURE;
+ }
+
+ // Okay, we got the startTime. Now ask PolicyKit:
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", conn);
+ PolKitSubject subj;
+ subj.subject_kind = "unix-process";
+ subj.subject_details["pid"] = (quint32)sender.pid;
+ subj.subject_details["start-time"] = startTime;
+ QDBusReply<PolKitAuthReply> reply = intf.call(QDBus::Block,
+ QLatin1String("CheckAuthorization"),
+ QVariant::fromValue(subj),
+ "org.openslx.pvs.privilegedinput",
+ QVariant::fromValue(QMap<QString, QString>()) /* No details */,
+ (quint32)1 /* Allow Interaction */,
+ QUuid::createUuid().toString() /* Cancellation ID */);
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to CheckAuthorization is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return USER_LOOKUP_FAILURE;
+ }
+ return reply.value().isAuthorized ? USER_PRIVILEGED : USER_UNPRIVILEGED;
+#else
+ return USER_UNPRIVILEGED;
+#endif
+}
+
+QString PVSCheckPrivileges::getX11SessionName(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11Display"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11Display is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+QString PVSCheckPrivileges::getX11DisplayDevice(CachedInputContext const& sender)
+{
+ QString sessionReference = getSessionReference(sender);
+ if(sessionReference.isNull())
+ {
+ return QString();
+ }
+
+ QDBusConnection conn = QDBusConnection::systemBus();
+ QDBusInterface intf("org.freedesktop.ConsoleKit", sessionReference, "org.freedesktop.ConsoleKit.Session", conn);
+ QDBusReply<QString> reply = intf.call(QDBus::Block, QLatin1String("GetX11DisplayDevice"));
+ if(!reply.isValid())
+ {
+ QDBusError err = reply.error();
+
+ qWarning("Reply to GetX11DisplayDevice is invalid: %s: %s", err.name().toLocal8Bit().constData(), err.message().toLocal8Bit().constData());
+ return QString();
+ }
+
+ return reply.value();
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind, CachedInputContext const& sender)
+{
+ SessionKind cachedSessionKind;
+
+ if(sessionKind == SESSION_NONLOCAL)
+ {
+ // All sessions are at least non-local
+ return true;
+ }
+ else if(sessionKind == SESSION_LOCAL)
+ {
+ if((cachedSessionKind = _savedSessionKind.value(sender, SESSION_UNKNOWN)) == SESSION_UNKNOWN)
+ {
+ cachedSessionKind = getSessionKind(sender);
+ if(cachedSessionKind != SESSION_LOOKUP_FAILURE)
+ _savedSessionKind[sender] = cachedSessionKind;
+ qDebug("Got session kind: %s", toString(cachedSessionKind).toLocal8Bit().constData());
+ }
+
+ switch(cachedSessionKind)
+ {
+ case SESSION_LOOKUP_FAILURE:
+ case SESSION_UNKNOWN:
+ {
+ // If we cannot find out the correct session kind, look up what we should do in
+ // the configuration:
+ QSettings* config = pvsPrivInputGetSettings();
+ QVariant assumeLocal = config->value("assume-session-local", false);
+ if(!assumeLocal.canConvert(QVariant::Bool))
+ {
+ qWarning("There is an assume-session-local setting, but cannot convert it to boolean");
+ return false;
+ }
+ return assumeLocal.toBool();
+ }
+ case SESSION_LOCAL:
+ return true;
+ case SESSION_NONLOCAL:
+ return false;
+ default:
+ qWarning("Internal error: Undefined session kind %d", (int)cachedSessionKind);
+ return false;
+ }
+ }
+ else
+ {
+ qWarning("Internal error: It does not make sense to require an unknown session or undefined session kind %d", (int)sessionKind);
+ return false;
+ }
+}
+
+bool PVSCheckPrivileges::require(UserPrivilege userPrivilege, CachedInputContext const& sender)
+{
+ UserPrivilege cachedUserPrivilege;
+
+ if(userPrivilege == USER_UNPRIVILEGED)
+ {
+ // All users are unprivileged
+ return true;
+ }
+ else if(userPrivilege == USER_PRIVILEGED)
+ {
+ if((cachedUserPrivilege = _savedUserPrivilege.value(sender, USER_UNKNOWN)) == USER_UNKNOWN)
+ {
+ cachedUserPrivilege = getUserPrivilege(sender);
+ if(cachedUserPrivilege != USER_LOOKUP_FAILURE)
+ _savedUserPrivilege[sender] = cachedUserPrivilege;
+ qDebug("Got user privilege: %s", toString(cachedUserPrivilege).toLocal8Bit().constData());
+ }
+
+ switch(cachedUserPrivilege)
+ {
+ case USER_LOOKUP_FAILURE:
+ case USER_UNKNOWN:
+ {
+ // If we cannot find out the correct user privilege level, look up what we should do in
+ // the configuration:
+ QSettings* config = pvsPrivInputGetSettings();
+ QVariant assumePrivileged = config->value("assume-user-privileged", false);
+ if(!assumePrivileged.canConvert(QVariant::Bool))
+ {
+ qWarning("There is an assume-session-local setting, but cannot convert it to boolean");
+ return false;
+ }
+ return assumePrivileged.toBool();
+ }
+ case USER_PRIVILEGED:
+ return true;
+ case USER_UNPRIVILEGED:
+ return false;
+ default:
+ qWarning("Internal error: Found undefined user privilege level %d", (int)cachedUserPrivilege);
+ _savedUserPrivilege.remove(sender);
+ return false;
+ }
+ }
+ else
+ {
+ qWarning("Internal error: It does not make sense to require an unknown or undefined user privilege level %d", (int)userPrivilege);
+ return false;
+ }
+}
+
+bool PVSCheckPrivileges::require(SessionKind sessionKind,
+ UserPrivilege userPrivilege,
+ CachedInputContext const& sender)
+{
+ if(!require(sessionKind, sender))
+ return false;
+ if(!require(userPrivilege, sender))
+ return false;
+ return true;
+}
+
+uint qHash(CachedInputContext const& p)
+{
+ return qHash(qMakePair(p.pid, qMakePair(p.uid, p.gid)));
+}
+
+void PVSCheckPrivileges::updateCachedUserDatabase()
+{
+ QHash<QString, uid_t> userNames;
+
+ _userGroupMap.clear();
+
+ // assemble a list of known users and their primary groups:
+ setpwent(); // open the user database
+ struct passwd* userrec;
+ while((userrec = getpwent()))
+ {
+ userNames.insert(userrec->pw_name, userrec->pw_uid);
+ _userGroupMap.insert(userrec->pw_uid, userrec->pw_gid);
+ }
+ endpwent(); // close the database
+
+ // augment with secondary groups:
+ setgrent(); // open the group database
+ struct group* grouprec;
+ while((grouprec = getgrent()))
+ {
+ char** membername = grouprec->gr_mem;
+ while(*membername)
+ {
+ uid_t uid = userNames.value(*membername, (uid_t)-1);
+ if(uid != (uid_t)-1)
+ {
+ _userGroupMap.insert(uid, grouprec->gr_gid);
+ }
+ membername++;
+ }
+ }
+ endgrent();
+
+ // decisions may have changed, so clear the caches:
+ _savedUserPrivilege.clear();
+}
+
+void PVSCheckPrivileges::rereadConfiguration()
+{
+ QSettings* settings = pvsPrivInputReopenSettings();
+
+ _privilegedGroups.clear();
+ QVariant groupList = settings->value("privileged-groups");
+ if(groupList.isValid())
+ {
+ if(!groupList.canConvert(QVariant::StringList))
+ {
+ qWarning("There is a privileged-groups setting, but it cannot be converted to a list of strings.");
+ goto endGroupListScan;
+ }
+
+ QStringList groups = groupList.toStringList();
+ foreach(QString groupName, groups)
+ {
+ bool ok;
+ gid_t gid = groupName.toUInt(&ok);
+ if(ok)
+ {
+ _privilegedGroups.append(gid);
+ }
+ else
+ {
+ // lookup the name:
+ QByteArray groupNameBytes = groupName.toLocal8Bit();
+ struct group* group = getgrnam(groupNameBytes.constData());
+ if(group)
+ {
+ _privilegedGroups.append(group->gr_gid);
+ }
+ else
+ {
+ qWarning("privileged-groups setting contains %s which is neither a numeric GID "
+ "nor a valid group name. Skipping.",
+ groupNameBytes.constData());
+ }
+ }
+ }
+ }
+
+endGroupListScan:
+ _privilegedUsers.clear();
+ QVariant userList = settings->value("privileged-users");
+ if(userList.isValid())
+ {
+ if(!userList.canConvert(QVariant::StringList))
+ {
+ qWarning("There is a privileged-users setting, but it cannot be converted to a list of strings.");
+ goto endUserListScan;
+ }
+
+ QStringList users = userList.toStringList();
+ foreach(QString userName, users)
+ {
+ bool ok;
+ uid_t uid = userName.toUInt(&ok);
+ if(ok)
+ {
+ _privilegedUsers.append(uid);
+ }
+ else
+ {
+ // lookup the name:
+ QByteArray userNameBytes = userName.toLocal8Bit();
+ struct passwd* user = getpwnam(userNameBytes.constData());
+ if(user)
+ {
+ _privilegedUsers.append(user->pw_uid);
+ }
+ else
+ {
+ qWarning("privileged-users setting contains %s which is neither a numeric UID "
+ "nor a valid user name. Skipping.",
+ userNameBytes.constData());
+ }
+ }
+ }
+ }
+
+endUserListScan:
+ return;
+}
diff --git a/src/input/pvsCheckPrivileges.h b/src/input/pvsCheckPrivileges.h
new file mode 100644
index 0000000..62b463c
--- /dev/null
+++ b/src/input/pvsCheckPrivileges.h
@@ -0,0 +1,151 @@
+/*
+ # Copyright (c) 2009,2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # -----------------------------------------------------------------------------
+ # src/net/pvsCheckPrivileges_linux.h
+ # - Linux implementation of privilege checking
+ # -----------------------------------------------------------------------------
+ */
+
+#ifndef PVSCHECKPRIVILEGES_H_
+#define PVSCHECKPRIVILEGES_H_
+
+#include <sys/types.h>
+#include <QObject>
+#include <QString>
+#include <QList>
+#include <QMultiMap>
+#include <QHash>
+#include "inputEventHandler.h"
+
+struct CachedInputContext
+{
+ CachedInputContext(InputEventContext const* source)
+ {
+ if(source)
+ {
+ pid = source->getSenderPid();
+ uid = source->getSenderUid();
+ gid = source->getSenderGid();
+ }
+ else
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+ }
+
+ CachedInputContext()
+ {
+ pid = (pid_t)-1;
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ }
+
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+
+ bool isValid() const
+ {
+ return (pid != (pid_t)-1) && (uid != (uid_t)-1) && (gid != (gid_t)-1);
+ }
+
+ bool operator==(CachedInputContext const& other) const
+ {
+ return (other.pid == pid) && (other.uid == uid) && (other.gid == gid);
+ }
+};
+uint qHash(CachedInputContext const& p);
+
+class QFileSystemWatcher;
+
+class PVSCheckPrivileges : public QObject
+{
+ Q_OBJECT
+public:
+ typedef enum {
+ SESSION_LOCAL,
+ SESSION_NONLOCAL,
+ SESSION_LOOKUP_FAILURE,
+ SESSION_UNKNOWN
+ } SessionKind;
+ static QString toString(SessionKind k)
+ {
+ switch(k)
+ {
+ case SESSION_LOOKUP_FAILURE: return "SESSION_LOOKUP_FAILURE";
+ case SESSION_LOCAL: return "SESSION_LOCAL";
+ case SESSION_NONLOCAL: return "SESSION_NONLOCAL";
+ case SESSION_UNKNOWN: return "SESSION_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ typedef enum {
+ USER_PRIVILEGED,
+ USER_UNPRIVILEGED,
+ USER_LOOKUP_FAILURE,
+ USER_UNKNOWN
+ } UserPrivilege;
+ static QString toString(UserPrivilege k)
+ {
+ switch(k)
+ {
+ case USER_PRIVILEGED: return "USER_PRIVILEGED";
+ case USER_UNPRIVILEGED: return "USER_UNPRIVILEGED";
+ case USER_LOOKUP_FAILURE: return "USER_LOOKUP_FAILURE";
+ case USER_UNKNOWN: return "USER_UNKNOWN";
+ default: return QString("unknown value (%1)").arg(k);
+ }
+ }
+
+ static PVSCheckPrivileges* instance();
+ static void deleteInstance();
+
+ bool require(SessionKind sessionKind, CachedInputContext const& sender);
+ bool require(UserPrivilege userPrivilege, CachedInputContext const& sender);
+ bool require(SessionKind sessionKind, UserPrivilege userPrivilege, CachedInputContext const& sender);
+ QString getX11SessionName(CachedInputContext const& sender);
+ QString getX11DisplayDevice(CachedInputContext const& sender);
+
+public slots:
+ void updateCachedUserDatabase();
+ void rereadConfiguration();
+
+private:
+ PVSCheckPrivileges(QObject* parent = 0);
+ virtual ~PVSCheckPrivileges();
+
+ typedef QPair<pid_t, QPair<uid_t, gid_t> > piduidgid;
+ piduidgid makePidUidGid(pid_t pid, uid_t uid, gid_t gid)
+ {
+ return qMakePair(pid, qMakePair(uid, gid));
+ }
+
+ QString getSessionReference(CachedInputContext const& sender);
+ SessionKind getSessionKind(CachedInputContext const& sender);
+ UserPrivilege getUserPrivilege(CachedInputContext const& sender);
+
+ static PVSCheckPrivileges* _instance;
+
+ QHash<CachedInputContext, UserPrivilege> _savedUserPrivilege;
+ QHash<CachedInputContext, SessionKind> _savedSessionKind;
+ QHash<CachedInputContext, QString> _savedConsoleKitSession;
+
+ QList<uid_t> _privilegedUsers;
+ QList<gid_t> _privilegedGroups;
+ QMultiMap<uid_t, gid_t> _userGroupMap;
+ QFileSystemWatcher* _watcher;
+};
+
+#endif /* PVSCHECKPRIVILEGES_H_ */
diff --git a/src/input/pvsPrivInputHandler.cpp b/src/input/pvsPrivInputHandler.cpp
new file mode 100644
index 0000000..70ed1bc
--- /dev/null
+++ b/src/input/pvsPrivInputHandler.cpp
@@ -0,0 +1,108 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputHandler.h
+ # - Handle privileged input connection requests - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <cerrno>
+#include <QtDebug>
+#include <QSocketNotifier>
+#include "inputEvent.h"
+#include "inputEventHandler.h"
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputHandler.h"
+
+using namespace std;
+
+class PrivInputContext : public InputEventContext
+{
+public:
+ PrivInputContext(pid_t pid, uid_t uid, gid_t gid)
+ : _pid(pid), _uid(uid), _gid(gid)
+ {
+ }
+
+ pid_t getSenderPid() const
+ {
+ return _pid;
+ }
+
+ uid_t getSenderUid() const
+ {
+ return _uid;
+ }
+
+ gid_t getSenderGid() const
+ {
+ return _gid;
+ }
+
+private:
+ pid_t _pid;
+ uid_t _uid;
+ gid_t _gid;
+};
+
+PVSPrivInputHandler::PVSPrivInputHandler(int fd, QObject* parent) :
+ QObject(parent)
+{
+ _fd = fd;
+ _notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+ _notifier->setEnabled(true);
+ connect(_notifier, SIGNAL(activated(int)), this, SLOT(canRead()));
+}
+
+PVSPrivInputHandler::~PVSPrivInputHandler()
+{
+ delete _notifier;
+}
+
+void PVSPrivInputHandler::canRead()
+{
+ _notifier->setEnabled(false);
+
+ // We need to retrieve the credentials of the process:
+ size_t siz = 8;
+ _messageAssembly.clear();
+ _messageAssembly.resize(8);
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+ int err;
+
+ if(!pvsPrivInputRecvMessage(_fd, _messageAssembly.data(), siz, pid, uid, gid, &err))
+ {
+ close(_fd);
+ deleteLater();
+ return;
+ }
+ else
+ {
+ if(siz != 8)
+ {
+ qWarning("Malformed PVS Input Event packet");
+ return;
+ }
+ QDataStream strm(&_messageAssembly, QIODevice::ReadOnly);
+ InputEvent evt;
+ strm.setByteOrder(QDataStream::BigEndian);
+ strm >> evt;
+ PrivInputContext ctx(pid, uid, gid);
+ _handlerChain.handle(evt, &ctx);
+ }
+
+ _notifier->setEnabled(true);
+}
diff --git a/src/input/pvsPrivInputHandler.h b/src/input/pvsPrivInputHandler.h
new file mode 100644
index 0000000..9980cdf
--- /dev/null
+++ b/src/input/pvsPrivInputHandler.h
@@ -0,0 +1,45 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputHandler.h
+ # - Handle privileged input connection requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTHANDLER_H_
+#define PVSPRIVINPUTHANDLER_H_
+
+#include <QObject>
+#include <QHash>
+#include <QPointer>
+#include "inputHandlerChain.h"
+
+class QSocketNotifier;
+
+class PVSPrivInputHandler : public QObject
+{
+ Q_OBJECT
+public:
+ PVSPrivInputHandler(int fd, QObject* parent = 0);
+ virtual ~PVSPrivInputHandler();
+
+private slots:
+ void canRead();
+
+private:
+ QSocketNotifier* _notifier;
+ QByteArray _messageAssembly;
+ int _bytes;
+ int _fd;
+ privileged_handler_chain _handlerChain;
+};
+
+#endif /* PVSPRIVINPUTHANDLER_H_ */
diff --git a/src/input/pvsPrivInputSignalHandler.cpp b/src/input/pvsPrivInputSignalHandler.cpp
new file mode 100644
index 0000000..b09c335
--- /dev/null
+++ b/src/input/pvsPrivInputSignalHandler.cpp
@@ -0,0 +1,69 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSignalHandler.cpp:
+ # - Handle signals forwarded over a file descriptor - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputSignalHandler.h"
+
+void PVSPrivInputSignalHandler::signalReceived(int sigfd)
+{
+ int signum;
+
+ pid_t pid;
+ uid_t uid;
+ gid_t gid;
+ int err;
+ size_t siz = sizeof(signum);
+
+ if(!pvsPrivInputRecvMessage(sigfd, &signum, siz, pid, uid, gid, &err))
+ {
+ qWarning("Should have received a signal but could not read message: %s", strerror(err));
+ return;
+ }
+
+ if(siz != sizeof(signum))
+ {
+ qWarning("Sould have received a message of size %d bytes but got %d bytes instead. Discarding message.", (int)sizeof(signum), (int)siz);
+ return;
+ }
+
+ if(!_allowUnauthenticatedKilling && pid != getpid())
+ {
+ qCritical("SOMETHING STRANGE IS GOING ON!\nReceived signal %d from PID %d\nPlease kill me instead of sending me signal notifications.", signum, (int)pid);
+ return;
+ }
+
+ switch(signum)
+ {
+ case SIGINT:
+ qDebug("Caught SIGINT. Quitting.");
+ emit terminate();
+ break;
+ case SIGTERM:
+ qDebug("Caught SIGTERM. Quitting.");
+ emit terminate();
+ break;
+ case SIGHUP:
+ qDebug("Caught SIGHUP. Reloading configuration.");
+ emit reloadConfiguration();
+ break;
+ default:
+ qWarning("Received signal %d, but don't know how to handle it.", signum);
+ }
+}
diff --git a/src/input/pvsPrivInputSignalHandler.h b/src/input/pvsPrivInputSignalHandler.h
new file mode 100644
index 0000000..cb75c86
--- /dev/null
+++ b/src/input/pvsPrivInputSignalHandler.h
@@ -0,0 +1,47 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSignalHandler.h:
+ # - Handle signals forwarded over a file descriptor - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTSIGNALHANDLER_H_
+#define PVSPRIVINPUTSIGNALHANDLER_H_
+
+#include <QObject>
+
+class PVSPrivInputSignalHandler : public QObject
+{
+ Q_OBJECT
+public:
+ PVSPrivInputSignalHandler(QObject* parent = 0)
+ : QObject(parent)
+ {
+ }
+
+ void allowUnauthenticatedKilling(bool value)
+ {
+ _allowUnauthenticatedKilling = value;
+ }
+
+public slots:
+ void signalReceived(int sigfd);
+
+signals:
+ void terminate();
+ void reloadConfiguration();
+
+private:
+ bool _allowUnauthenticatedKilling;
+};
+
+#endif /* PVSPRIVINPUTSIGNALHANDLER_H_ */
diff --git a/src/input/pvsPrivInputSocket.cpp b/src/input/pvsPrivInputSocket.cpp
new file mode 100644
index 0000000..df5dff5
--- /dev/null
+++ b/src/input/pvsPrivInputSocket.cpp
@@ -0,0 +1,233 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSocket.h:
+ # - Centralize knowledge of socket address and connection options
+ # for pvsprivinputd - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <cerrno>
+#include <QtDebug>
+#include <QSettings>
+#include "pvsPrivInputSocket.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+static QSettings* pvsPrivInputSettings = 0;
+
+QString pvsPrivInputGetSettingsPath()
+{
+ return "/etc/pvsprivinputd.conf";
+}
+
+QSettings* pvsPrivInputGetSettings()
+{
+ if(!pvsPrivInputSettings)
+ {
+ pvsPrivInputSettings = new QSettings(pvsPrivInputGetSettingsPath(), QSettings::IniFormat);
+ }
+ return pvsPrivInputSettings;
+}
+
+QSettings* pvsPrivInputReopenSettings()
+{
+ if(pvsPrivInputSettings)
+ {
+ delete pvsPrivInputSettings;
+ pvsPrivInputSettings = 0;
+ }
+ return pvsPrivInputGetSettings();
+}
+
+QString pvsPrivInputGetSocketAddress()
+{
+ return pvsPrivInputGetSettings()->value("socketpath", "/tmp/pvsprivinputd.sock").toString();
+}
+
+bool pvsPrivInputEnableReceiveCredentials(int sock)
+{
+ int passcred = 1;
+ if(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &passcred, sizeof(passcred)) < 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+int pvsPrivInputMakeClientSocket()
+{
+ int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if(sock < 0)
+ {
+ qWarning("Could not create a socket: %s", strerror(errno));
+ return -1;
+ }
+
+ QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1);
+ if(connect(sock, reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)) < 0)
+ {
+ qWarning("Could not connect to pvsprivinputd at %s: %s", socketPath.constData(), strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ return sock;
+}
+
+int pvsPrivInputMakeServerSocket()
+{
+ int sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if(sock < 0)
+ {
+ qCritical("Could not create a socket: %s", strerror(errno));
+ return -1;
+ }
+
+ // Bind to the address:
+ QByteArray socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, socketPath.constData(), UNIX_PATH_MAX - 1);
+ if(bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0)
+ {
+ qCritical("Could not bind socket to %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ // Announce that credentials are requested:
+ if(!pvsPrivInputEnableReceiveCredentials(sock))
+ {
+ // We will not operate without credentials.
+ qCritical("Could not request peer credentials: %s", strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+#if 0 /* Only for SOCK_STREAM: */
+ // Listen for connections:
+ if(listen(sock, 1) < 0)
+ {
+ qCritical("Could not listen for connections to %s: %s", socketPath.constData(), strerror(errno));
+ close(sock);
+ return -1;
+ }
+#endif
+
+ return sock;
+}
+
+bool pvsPrivInputSendMessage(int sock, void* buf, size_t _len, int* err)
+{
+ /*
+ * Portability note: All UNIX-like systems can transmit credentials over UNIX
+ * sockets, but only Linux does it automagically.
+ */
+
+ long len = (long)_len;
+
+ // send(2) does not split messages on a SOCK_DGRAM socket.
+ int e = send(sock, buf, len, 0);
+ if(e < 0)
+ {
+ qWarning("Failed to send message of length %d over socket %d: %s", (unsigned)len, e, strerror(errno));
+ if(err)
+ *err = errno;
+ return false;
+ }
+ else if(e < len)
+ {
+ qWarning("Failed to send a complete message of length %d over socket %d, only %d bytes were sent", (unsigned)len, sock, e);
+ if(err)
+ *err = errno;
+ return false;
+ }
+
+ return true;
+}
+
+bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len,
+ pid_t& pid, uid_t& uid, gid_t& gid, int* err)
+{
+ struct iovec iov;
+ struct msghdr msg;
+ char ctlbuf[1024];
+ iov.iov_base = buf;
+ iov.iov_len = len;
+ msg.msg_name = 0;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = &ctlbuf;
+ msg.msg_controllen = sizeof(ctlbuf);
+ msg.msg_flags = 0;
+ int bytes_read = recvmsg(sock, &msg, 0);
+ if(bytes_read < 0)
+ {
+ qWarning("Could not read from socket: %s", strerror(errno));
+ if(err)
+ *err = errno;
+ return false;
+ }
+
+ pid = -1;
+ uid = -1;
+ gid = -1;
+
+ struct cmsghdr* cmsg;
+ for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS)
+ {
+ struct ucred* creds = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg));
+ pid = creds->pid;
+ uid = creds->uid;
+ gid = creds->gid;
+ break;
+ }
+ else if(cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
+ {
+ // We need to close passed file descriptors. If we don't, we
+ // have a denial-of-service vulnerability.
+ int* fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ unsigned num_fds = cmsg->cmsg_len / sizeof(int);
+ for(unsigned i = 0; i < num_fds; i++)
+ {
+ close(fds[i]);
+ }
+ }
+ }
+
+ if(pid == (pid_t)-1 || uid == (uid_t)-1 || gid == (gid_t)-1)
+ {
+ *err = 0;
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/input/pvsPrivInputSocket.h b/src/input/pvsPrivInputSocket.h
new file mode 100644
index 0000000..447360b
--- /dev/null
+++ b/src/input/pvsPrivInputSocket.h
@@ -0,0 +1,37 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsPrivInputSocket.h:
+ # - Centralize knowledge of socket address and connection options
+ # for pvsprivinputd - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSPRIVINPUTSOCKET_H_
+#define PVSPRIVINPUTSOCKET_H_
+
+#include <sys/types.h>
+#include <cstring>
+#include <QString>
+
+class QSettings;
+
+QSettings* pvsPrivInputGetSettings();
+QSettings* pvsPrivInputReopenSettings();
+QString pvsPrivInputGetSettingsPath();
+QString pvsPrivInputGetSocketAddress();
+bool pvsPrivInputEnableReceiveCredentials(int sock);
+int pvsPrivInputMakeClientSocket();
+int pvsPrivInputMakeServerSocket();
+bool pvsPrivInputSendMessage(int sock, void* buf, size_t len, int* err = 0);
+bool pvsPrivInputRecvMessage(int sock, void* buf, size_t& len, pid_t& pid, uid_t& uid, gid_t& gid, int* err = 0);
+
+#endif /* PVSPRIVINPUTSOCKET_H_ */
diff --git a/src/input/pvsSyslog.cpp b/src/input/pvsSyslog.cpp
new file mode 100644
index 0000000..6a9be31
--- /dev/null
+++ b/src/input/pvsSyslog.cpp
@@ -0,0 +1,133 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsSyslog.cpp:
+ # - Send output to syslog or to a file - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <cerrno>
+#include <syslog.h>
+#include <QFile>
+#include <QTextStream>
+#include "pvsSyslog.h"
+
+using namespace std;
+
+#define PVS_LOG_BUFSIZ 8192
+
+void PVSLogRedirector::inputAvailableOn(int fd)
+{
+ QByteArray readbuf;
+ readbuf.resize(PVS_LOG_BUFSIZ);
+ ssize_t siz;
+ bool cont = true;
+
+ do
+ {
+ siz = recv(fd, readbuf.data(), readbuf.size(), MSG_DONTWAIT);
+ if(siz > 0)
+ {
+ _buf.append(readbuf.constData(), siz);
+
+ // Send lines:
+ int oldnpos = -1;
+ int npos = -1;
+ while((npos = _buf.indexOf('\n', oldnpos + 1)) >= 0)
+ {
+ if((oldnpos + 1) == npos)
+ {
+ oldnpos = npos;
+ continue;
+ }
+ QString line = QString::fromLocal8Bit(_buf.constData() + oldnpos + 1, npos - oldnpos - 1);
+ doRedirectInput(line);
+ oldnpos = npos;
+ }
+
+ // All complete lines have been sent. Remove sent bytes from buffer.
+ _buf.remove(0, oldnpos + 1);
+
+ // If line length is too large, send it now.
+ if(_buf.size() >= BUFSIZ)
+ {
+ QString longLine = QString::fromLocal8Bit(_buf.constData(), _buf.size());
+ doRedirectInput(longLine);
+ _buf.clear();
+ }
+ }
+ else if(siz == 0)
+ {
+ // Socket closed on other end.
+ break;
+ }
+ else if(siz < 0)
+ {
+ int err = errno;
+ switch(err)
+ {
+ case EAGAIN:
+#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
+ case EWOULDBLOCK:
+#endif
+ // We read all there is to read.
+ cont = false;
+ break;
+ default:
+ // Something happened.
+ // We can't inform the user via qDebug() and friends as that would loop right back to us again,
+ // so we emit a log line.
+ doRedirectInput(QString("Error in logging code: Could not read log input: %s").arg(strerror(err)));
+ cont = false;
+ }
+ }
+ }
+ while(cont);
+}
+
+PVSSyslogRedirector::PVSSyslogRedirector()
+{
+ openlog("pvsprivinputd", LOG_PID, LOG_USER);
+}
+
+PVSSyslogRedirector::~PVSSyslogRedirector()
+{
+ closelog();
+}
+
+void PVSSyslogRedirector::doRedirectInput(QString const& line)
+{
+ QByteArray bytes = line.toLocal8Bit();
+ syslog(LOG_NOTICE, "%s", bytes.data());
+}
+
+PVSLogfileRedirector::PVSLogfileRedirector(QString const& filename)
+{
+ _file = new QFile(filename, this);
+ _file->open(QIODevice::Append);
+ _stream = new QTextStream(_file);
+}
+
+PVSLogfileRedirector::~PVSLogfileRedirector()
+{
+ _stream->flush();
+ _file->close();
+ delete _stream;
+ delete _file;
+}
+
+void PVSLogfileRedirector::doRedirectInput(QString const& line)
+{
+ *_stream << line << flush;
+}
diff --git a/src/input/pvsSyslog.h b/src/input/pvsSyslog.h
new file mode 100644
index 0000000..8c9591a
--- /dev/null
+++ b/src/input/pvsSyslog.h
@@ -0,0 +1,63 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsSyslog.h:
+ # - Send output to syslog or to a file - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef PVSSYSLOG_H_
+#define PVSSYSLOG_H_
+
+#include <QObject>
+#include <QString>
+
+class PVSLogRedirector : public QObject
+{
+ Q_OBJECT
+public slots:
+ void inputAvailableOn(int fd);
+
+protected:
+ virtual void doRedirectInput(QString const& line) = 0;
+
+private:
+ QByteArray _buf;
+};
+
+class PVSSyslogRedirector : public PVSLogRedirector
+{
+public:
+ PVSSyslogRedirector();
+ virtual ~PVSSyslogRedirector();
+
+protected:
+ void doRedirectInput(QString const& line);
+};
+
+class QFile;
+class QTextStream;
+
+class PVSLogfileRedirector : public PVSLogRedirector
+{
+public:
+ PVSLogfileRedirector(QString const& filename);
+ virtual ~PVSLogfileRedirector();
+
+protected:
+ void doRedirectInput(QString const& line);
+
+private:
+ QFile* _file;
+ QTextStream* _stream;
+};
+
+#endif /* PVSSYSLOG_H_ */
diff --git a/src/input/pvsprivinputd.conf b/src/input/pvsprivinputd.conf
new file mode 100644
index 0000000..a62a922
--- /dev/null
+++ b/src/input/pvsprivinputd.conf
@@ -0,0 +1,28 @@
+;; Lines starting with `;' are comments.
+;;
+;; socketpath:
+;; Where pvsprivinputd should put its listening socket.
+socketpath = /tmp/pvsprivinputd.sock
+
+;; privileged-users:
+;; Comma-separated list of users that are allowed to run privileged actions
+privileged-users = root
+
+;; privileged-groups:
+;; Comma-separated list of user groups that are allowed to run privileged actions
+; privileged-groups = wheel
+
+;; assume-session-local:
+;; Assume that a session is local if it can not be looked up in ConsoleKit,
+;; for example, if you are not running ConsoleKit.
+;;
+;; WARNING: Setting this to true may be a security risk. Running ConsoleKit is
+;; really recommended.
+; assume-session-local = false
+
+;; assume-user-privileged:
+;; Assume that a user is privileged if he/she can not be looked up in
+;; the user database or PolicyKit fails to deliver an answer.
+;;
+;; WARNING: Setting this to true is most definitely a security risk.
+; assume-user-privileged = false \ No newline at end of file
diff --git a/src/input/pvsprivinputd.cpp b/src/input/pvsprivinputd.cpp
new file mode 100644
index 0000000..e6ae155
--- /dev/null
+++ b/src/input/pvsprivinputd.cpp
@@ -0,0 +1,262 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # pvsprivinputd.cpp:
+ # - Handle privileged input events - daemon
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <signal.h>
+#include <unistd.h>
+#include <cerrno>
+#include <cstdlib>
+#include <cstdio>
+#include <cstring>
+#include <QtDebug>
+#include <QFileInfo>
+#include <QSettings>
+#include <QCoreApplication>
+#include <QStringList>
+#include <QSocketNotifier>
+#include "pvsCheckPrivileges.h"
+#include "pvsPrivInputSocket.h"
+#include "pvsPrivInputHandler.h"
+#include "pvsPrivInputSignalHandler.h"
+#include "pvsSyslog.h"
+
+using namespace std;
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX 108 /* according to unix(7) */
+#endif
+
+QByteArray socketPath;
+int signalFds[2];
+
+QTextStream qout(stdout, QIODevice::WriteOnly);
+QTextStream qerr(stderr, QIODevice::WriteOnly);
+
+static void unlinkSocket()
+{
+ if(!socketPath.isNull())
+ {
+ unlink(socketPath.constData());
+ }
+}
+
+static void onCaughtSignal(int signum)
+{
+ pvsPrivInputSendMessage(signalFds[0], &signum, sizeof(signum), 0);
+}
+
+static void usage()
+{
+ qout << QObject::tr(
+ "Usage: %1 [--help|-h] [--daemon|-d] [--log=<Logger>|-l<Logger>]\n"
+ "\n"
+ "Options:\n"
+ " --help, -h Show this message\n"
+ " --daemon, -d Run in background\n"
+ " --log=<Logger>,\n"
+ " -l<Logger> Redirect all output to <Logger>\n"
+ " valid values are:\n"
+ " - any file name\n"
+ " - `syslog' to redirect output to the system log\n"
+ " - `null' to discard output (default)\n"
+ " (without quotes)\n"
+ "\n"
+ "Signals:\n"
+ " SIGINT, SIGTERM (or press Ctrl+C when run in foreground)\n"
+ " Quit\n"
+ " SIGHUP Reload configuration and cached user data\n").arg(qApp->arguments().at(0)) << flush;
+}
+
+int main(int argc, char** argv)
+{
+ QCoreApplication app(argc, argv);
+ app.setApplicationName("pvsprivinputd");
+ app.setOrganizationName("openslx");
+
+ bool no_fork = true;
+ QString logTarget = "null";
+
+ foreach(QString arg, app.arguments().mid(1))
+ {
+ if(arg == "--daemon" || arg == "-d")
+ {
+ no_fork = false;
+ }
+ else if(arg.startsWith("-l"))
+ {
+ logTarget = arg.mid(2);
+ }
+ else if(arg.startsWith("--log="))
+ {
+ logTarget = arg.mid(6);
+ }
+ else if(arg == "--help" || arg == "-h")
+ {
+ usage();
+ return EXIT_SUCCESS;
+ }
+ else
+ {
+ qout << "Unexpected argument: " << arg << endl;
+ usage();
+ return EXIT_FAILURE;
+ }
+ }
+
+ if(logTarget != "null" && logTarget != "syslog")
+ {
+ logTarget = QFileInfo(logTarget).absoluteFilePath();
+ qout << "Writing log to " << logTarget << endl;
+ }
+
+ if(!no_fork)
+ {
+ pid_t pid;
+ pid_t sid;
+
+ pid = fork();
+ if(pid < 0)
+ {
+ qCritical("Could not fork: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if(pid > 0)
+ {
+ exit(EXIT_SUCCESS);
+ }
+
+ // We are now inside a child process.
+ // Detach from parent:
+ sid = setsid();
+ if(sid < 0)
+ {
+ qCritical("Could not detach from parent process: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ // Change to some benign directory:
+ if(chdir("/") < 0)
+ {
+ qCritical("Could not change directory: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // Okay, we are running as a daemon. Defer reopening standard file descriptors
+ // so we can report errors.
+
+ // Set the file creation mask to allow all processes to connect to
+ // the socket (access control is handled differently):
+ umask(0);
+
+ // Store the socket path before the signal handler is installed so it does not risk segfaulting
+ // due to bad timing.
+ socketPath = pvsPrivInputGetSocketAddress().toLocal8Bit();
+
+ // Ignore SIGPIPE. Connection errors are handled internally.
+ // According to signal(2), the only error is that the signal number
+ // is invalid. This cannot happen.
+ signal(SIGPIPE, SIG_IGN);
+
+ // Create our socket:
+ int sock = pvsPrivInputMakeServerSocket();
+ if(sock < 0)
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ // Install our main object
+ PVSPrivInputHandler handler(sock);
+
+ // set up signal handling:
+ if(socketpair(AF_UNIX, SOCK_DGRAM, 0, signalFds) < 0)
+ {
+ qCritical("Could not set up signal handling. Giving up.");
+ exit(EXIT_FAILURE);
+ }
+ PVSPrivInputSignalHandler sigHandler;
+ QSocketNotifier sigintWatcher(signalFds[1], QSocketNotifier::Read);
+ sigHandler.allowUnauthenticatedKilling(!pvsPrivInputEnableReceiveCredentials(signalFds[1]));
+ QObject::connect(&sigintWatcher, SIGNAL(activated(int)), &sigHandler, SLOT(signalReceived(int)));
+ QObject::connect(&sigHandler, SIGNAL(terminate()), &app, SLOT(quit()));
+ QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(updateCachedUserDatabase()));
+ QObject::connect(&sigHandler, SIGNAL(reloadConfiguration()), PVSCheckPrivileges::instance(), SLOT(rereadConfiguration()));
+ signal(SIGINT, onCaughtSignal);
+ signal(SIGTERM, onCaughtSignal);
+ signal(SIGHUP, onCaughtSignal);
+
+ PVSLogRedirector* logRedir = 0;
+ QSocketNotifier* logNotif = 0;
+ // Our setup is complete.
+ if(!no_fork)
+ {
+ // Reopen standard file descriptors:
+ freopen("/dev/null", "r", stdin);
+
+ if(logTarget == "null")
+ {
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ }
+ else if(logTarget == "syslog")
+ {
+ logRedir = new PVSSyslogRedirector();
+ }
+ else
+ {
+ logRedir = new PVSLogfileRedirector(logTarget);
+ }
+
+ if(logRedir)
+ {
+ int logFds[2];
+ if(socketpair(AF_UNIX, SOCK_STREAM, 0, logFds) < 0)
+ {
+ qWarning("Could not open a socket pair: %s", strerror(errno));
+ return EXIT_FAILURE;
+ }
+
+ logNotif = new QSocketNotifier(logFds[1], QSocketNotifier::Read);
+ QObject::connect(logNotif, SIGNAL(activated(int)), logRedir, SLOT(inputAvailableOn(int)));
+
+ // redirect stdout and stderr:
+ dup2(logFds[0], 1);
+ dup2(logFds[0], 2);
+ }
+ }
+
+ atexit(unlinkSocket);
+
+ qout << "PVS Privileged Input Daemon initialization complete. Entering main loop." << endl;
+
+ // and run the main loop.
+ int ret = app.exec();
+
+ if(logRedir)
+ {
+ delete logNotif;
+ delete logRedir;
+ }
+
+ PVSCheckPrivileges::deleteInstance();
+
+ qout << "PVS Privileged Input Daemon deinitialization complete. Exiting." << endl;
+
+ return ret;
+}
diff --git a/src/input/rebootSystemHandler.cpp b/src/input/rebootSystemHandler.cpp
new file mode 100644
index 0000000..b5b8f8a
--- /dev/null
+++ b/src/input/rebootSystemHandler.cpp
@@ -0,0 +1,32 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.cpp
+ # - Handle system reboot requests - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <cerrno>
+#include <QtDebug>
+#include "rebootSystemHandler.h"
+
+using namespace std;
+
+void RebootLinuxSystemHandler::handle(InputEvent const& evt, InputEventContext const* ctx)
+{
+ // Rebooting a linux system is particulary easy:
+ if(kill(1, SIGINT) < 0)
+ {
+ qWarning("Could not kill /sbin/init: %s", strerror(errno));
+ }
+}
diff --git a/src/input/rebootSystemHandler.h b/src/input/rebootSystemHandler.h
new file mode 100644
index 0000000..34fa8ae
--- /dev/null
+++ b/src/input/rebootSystemHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle system reboot requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef REBOOTSYSTEMHANDLER_H_
+#define REBOOTSYSTEMHANDLER_H_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class RebootLinuxSystemHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Reboot")), InputEvent::ET_SPECIAL, InputEvent::EC_REBOOT);
+ }
+};
+
+#endif /* REBOOTSYSTEMHANDLER_H_ */
diff --git a/src/input/sayHelloHandler.cpp b/src/input/sayHelloHandler.cpp
new file mode 100644
index 0000000..fc6f668
--- /dev/null
+++ b/src/input/sayHelloHandler.cpp
@@ -0,0 +1,25 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle "say hello" requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#include "sayHelloHandler.h"
+#include <iostream>
+
+using namespace std;
+
+void SayHelloHandler::handle(InputEvent const&, InputEventContext const* ctx)
+{
+ cerr << "I'm right here! You sent this message from pid " << ctx->getSenderPid() << " as user " << ctx->getSenderUid() << " with gid " << ctx->getSenderGid() << endl;
+}
diff --git a/src/input/sayHelloHandler.h b/src/input/sayHelloHandler.h
new file mode 100644
index 0000000..b0d4c7e
--- /dev/null
+++ b/src/input/sayHelloHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # rebootSystemHandler.h
+ # - Handle "say hello" requests - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef SAYHELLOHANDLER_CPP_
+#define SAYHELLOHANDLER_CPP_
+
+#include <QCoreApplication>
+#include "inputEventHandler.h"
+
+class SayHelloHandler : public DefaultInputEventHandler<InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const*);
+
+ static void describeInto(QList<SpecialInputEventDescription>& list)
+ {
+ list << SpecialInputEventDescription(tr(QT_TRANSLATE_NOOP("InputEventHandler", "Say Hello")), InputEvent::ET_SPECIAL, InputEvent::EC_SAY_HELLO);
+ }
+};
+
+#endif /* SAYHELLOHANDLER_CPP_ */
diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp
new file mode 100644
index 0000000..0c32b66
--- /dev/null
+++ b/src/input/x11FakeKeyboardHandler.cpp
@@ -0,0 +1,906 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeKeyboardHandler.h:
+ # - Handle keyboard events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#include <map>
+#include <set>
+#include <cassert>
+// Qt headers need to be included before X11 headers
+#include <QApplication>
+#include <QtCore>
+#include "x11FakeKeyboardHandler.h"
+// #include <multimap>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/keysymdef.h>
+#include <X11/extensions/XTest.h>
+#ifdef HAVE_XINPUT2_H
+# include <X11/extensions/XInput2.h>
+#else
+# include <X11/extensions/XInput.h>
+#endif
+#include <X11/XKBlib.h>
+#include <src/util/consoleLogger.h>
+#include "x11InputUtils.h"
+
+//////////////////////// INPUT EVENT TRANSLATION /////////////////////////////////
+
+typedef unsigned char xmodifier_type;
+
+char modifiernames[][8] = {
+ "SHIFT",
+ "LOCK",
+ "CONTROL",
+ "MOD1",
+ "MOD2",
+ "MOD3",
+ "MOD4",
+ "MOD5"
+};
+
+QString modifiers_to_string(xmodifier_type mods)
+{
+ QString s;
+ for(int i = 0; i < 8; i++)
+ {
+ if(mods & (1<<i))
+ {
+ s += modifiernames[i];
+ s += ", ";
+ }
+ }
+ s.chop(2);
+ return s;
+}
+
+
+typedef std::map<quint32, KeySym> lookup_table_type;
+typedef lookup_table_type::const_iterator lookup_table_const_iterator;
+typedef lookup_table_type::iterator lookup_table_iterator;
+lookup_table_type keysyms;
+void initialize_keysyms() {
+ keysyms[Qt::Key_Escape] = XK_Escape;
+ keysyms[Qt::Key_Tab] = XK_Tab;
+// keysyms[Qt::Key_Backtab] = XK_Backtab;
+ keysyms[Qt::Key_Backspace] = XK_BackSpace;
+ keysyms[Qt::Key_Return] = XK_Return;
+ keysyms[Qt::Key_Enter] = XK_KP_Enter;
+ keysyms[Qt::Key_Insert] = XK_Insert;
+ keysyms[Qt::Key_Delete] = XK_Delete;
+ keysyms[Qt::Key_Pause] = XK_Pause;
+ keysyms[Qt::Key_Print] = XK_Print;
+ keysyms[Qt::Key_SysReq] = XK_Sys_Req;
+ keysyms[Qt::Key_Clear] = XK_Clear;
+ keysyms[Qt::Key_Home] = XK_Home;
+ keysyms[Qt::Key_End] = XK_End;
+ keysyms[Qt::Key_Left] = XK_Left;
+ keysyms[Qt::Key_Up] = XK_Up;
+ keysyms[Qt::Key_Right] = XK_Right;
+ keysyms[Qt::Key_Down] = XK_Down;
+ keysyms[Qt::Key_PageUp] = XK_Page_Up;
+ keysyms[Qt::Key_PageDown] = XK_Page_Down;
+ keysyms[Qt::Key_Shift] = XK_Shift_L;
+ keysyms[Qt::Key_Control] = XK_Control_L;
+ keysyms[Qt::Key_Meta] = XK_Meta_L;
+ keysyms[Qt::Key_Alt] = XK_Alt_L;
+ keysyms[Qt::Key_CapsLock] = XK_Caps_Lock;
+ keysyms[Qt::Key_NumLock] = XK_Num_Lock;
+ keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock;
+ keysyms[Qt::Key_F1] = XK_F1;
+ keysyms[Qt::Key_F2] = XK_F2;
+ keysyms[Qt::Key_F3] = XK_F3;
+ keysyms[Qt::Key_F4] = XK_F4;
+ keysyms[Qt::Key_F5] = XK_F5;
+ keysyms[Qt::Key_F6] = XK_F6;
+ keysyms[Qt::Key_F7] = XK_F7;
+ keysyms[Qt::Key_F8] = XK_F8;
+ keysyms[Qt::Key_F9] = XK_F9;
+ keysyms[Qt::Key_F10] = XK_F10;
+ keysyms[Qt::Key_F11] = XK_F11;
+ keysyms[Qt::Key_F12] = XK_F12;
+ keysyms[Qt::Key_F13] = XK_F13;
+ keysyms[Qt::Key_F14] = XK_F14;
+ keysyms[Qt::Key_F15] = XK_F15;
+ keysyms[Qt::Key_F16] = XK_F16;
+ keysyms[Qt::Key_F17] = XK_F17;
+ keysyms[Qt::Key_F18] = XK_F18;
+ keysyms[Qt::Key_F19] = XK_F19;
+ keysyms[Qt::Key_F20] = XK_F20;
+ keysyms[Qt::Key_F21] = XK_F21;
+ keysyms[Qt::Key_F22] = XK_F22;
+ keysyms[Qt::Key_F23] = XK_F23;
+ keysyms[Qt::Key_F24] = XK_F24;
+ keysyms[Qt::Key_F25] = XK_F25;
+ keysyms[Qt::Key_F26] = XK_F26;
+ keysyms[Qt::Key_F27] = XK_F27;
+ keysyms[Qt::Key_F28] = XK_F28;
+ keysyms[Qt::Key_F29] = XK_F29;
+ keysyms[Qt::Key_F30] = XK_F30;
+ keysyms[Qt::Key_F31] = XK_F31;
+ keysyms[Qt::Key_F32] = XK_F32;
+ keysyms[Qt::Key_F33] = XK_F33;
+ keysyms[Qt::Key_F34] = XK_F34;
+ keysyms[Qt::Key_F35] = XK_F35;
+ keysyms[Qt::Key_Super_L] = XK_Super_L;
+ keysyms[Qt::Key_Super_R] = XK_Super_R;
+ keysyms[Qt::Key_Menu] = XK_Menu;
+ keysyms[Qt::Key_Hyper_L] = XK_Hyper_L;
+ keysyms[Qt::Key_Hyper_R] = XK_Hyper_R;
+ keysyms[Qt::Key_Help] = XK_Help;
+// keysyms[Qt::Key_Direction_L] = XK_Direction_L;
+// keysyms[Qt::Key_Direction_R] = XK_Direction_R;
+ keysyms[Qt::Key_Space] = XK_space;
+ keysyms[Qt::Key_Exclam] = XK_exclam;
+ keysyms[Qt::Key_QuoteDbl] = XK_quotedbl;
+ keysyms[Qt::Key_NumberSign] = XK_numbersign;
+ keysyms[Qt::Key_Dollar] = XK_dollar;
+ keysyms[Qt::Key_Percent] = XK_percent;
+ keysyms[Qt::Key_Ampersand] = XK_ampersand;
+ keysyms[Qt::Key_Apostrophe] = XK_apostrophe;
+ keysyms[Qt::Key_ParenLeft] = XK_parenleft;
+ keysyms[Qt::Key_ParenRight] = XK_parenright;
+ keysyms[Qt::Key_Asterisk] = XK_asterisk;
+ keysyms[Qt::Key_Plus] = XK_plus;
+ keysyms[Qt::Key_Comma] = XK_comma;
+ keysyms[Qt::Key_Minus] = XK_minus;
+ keysyms[Qt::Key_Period] = XK_period;
+ keysyms[Qt::Key_Slash] = XK_slash;
+ keysyms[Qt::Key_0] = XK_0;
+ keysyms[Qt::Key_1] = XK_1;
+ keysyms[Qt::Key_2] = XK_2;
+ keysyms[Qt::Key_3] = XK_3;
+ keysyms[Qt::Key_4] = XK_4;
+ keysyms[Qt::Key_5] = XK_5;
+ keysyms[Qt::Key_6] = XK_6;
+ keysyms[Qt::Key_7] = XK_7;
+ keysyms[Qt::Key_8] = XK_8;
+ keysyms[Qt::Key_9] = XK_9;
+ keysyms[Qt::Key_Colon] = XK_colon;
+ keysyms[Qt::Key_Semicolon] = XK_semicolon;
+ keysyms[Qt::Key_Less] = XK_less;
+ keysyms[Qt::Key_Equal] = XK_equal;
+ keysyms[Qt::Key_Greater] = XK_greater;
+ keysyms[Qt::Key_Question] = XK_question;
+ keysyms[Qt::Key_At] = XK_at;
+ keysyms[Qt::Key_A] = XK_A;
+ keysyms[Qt::Key_B] = XK_B;
+ keysyms[Qt::Key_C] = XK_C;
+ keysyms[Qt::Key_D] = XK_D;
+ keysyms[Qt::Key_E] = XK_E;
+ keysyms[Qt::Key_F] = XK_F;
+ keysyms[Qt::Key_G] = XK_G;
+ keysyms[Qt::Key_H] = XK_H;
+ keysyms[Qt::Key_I] = XK_I;
+ keysyms[Qt::Key_J] = XK_J;
+ keysyms[Qt::Key_K] = XK_K;
+ keysyms[Qt::Key_L] = XK_L;
+ keysyms[Qt::Key_M] = XK_M;
+ keysyms[Qt::Key_N] = XK_N;
+ keysyms[Qt::Key_O] = XK_O;
+ keysyms[Qt::Key_P] = XK_P;
+ keysyms[Qt::Key_Q] = XK_Q;
+ keysyms[Qt::Key_R] = XK_R;
+ keysyms[Qt::Key_S] = XK_S;
+ keysyms[Qt::Key_T] = XK_T;
+ keysyms[Qt::Key_U] = XK_U;
+ keysyms[Qt::Key_V] = XK_V;
+ keysyms[Qt::Key_W] = XK_W;
+ keysyms[Qt::Key_X] = XK_X;
+ keysyms[Qt::Key_Y] = XK_Y;
+ keysyms[Qt::Key_Z] = XK_Z;
+ keysyms[Qt::Key_BracketLeft] = XK_bracketleft;
+ keysyms[Qt::Key_Backslash] = XK_backslash;
+ keysyms[Qt::Key_BracketRight] = XK_bracketright;
+ keysyms[Qt::Key_AsciiCircum] = XK_asciicircum;
+ keysyms[Qt::Key_Underscore] = XK_underscore;
+ keysyms[Qt::Key_QuoteLeft] = XK_quoteleft;
+ keysyms[Qt::Key_BraceLeft] = XK_braceleft;
+ keysyms[Qt::Key_Bar] = XK_bar;
+ keysyms[Qt::Key_BraceRight] = XK_braceright;
+ keysyms[Qt::Key_AsciiTilde] = XK_asciitilde;
+ keysyms[Qt::Key_nobreakspace] = XK_nobreakspace;
+ keysyms[Qt::Key_exclamdown] = XK_exclamdown;
+ keysyms[Qt::Key_cent] = XK_cent;
+ keysyms[Qt::Key_sterling] = XK_sterling;
+ keysyms[Qt::Key_currency] = XK_currency;
+ keysyms[Qt::Key_yen] = XK_yen;
+ keysyms[Qt::Key_brokenbar] = XK_brokenbar;
+ keysyms[Qt::Key_section] = XK_section;
+ keysyms[Qt::Key_diaeresis] = XK_diaeresis;
+ keysyms[Qt::Key_copyright] = XK_copyright;
+ keysyms[Qt::Key_ordfeminine] = XK_ordfeminine;
+ keysyms[Qt::Key_guillemotleft] = XK_guillemotleft;
+ keysyms[Qt::Key_notsign] = XK_notsign;
+ keysyms[Qt::Key_hyphen] = XK_hyphen;
+ keysyms[Qt::Key_registered] = XK_registered;
+ keysyms[Qt::Key_macron] = XK_macron;
+ keysyms[Qt::Key_degree] = XK_degree;
+ keysyms[Qt::Key_plusminus] = XK_plusminus;
+ keysyms[Qt::Key_twosuperior] = XK_twosuperior;
+ keysyms[Qt::Key_threesuperior] = XK_threesuperior;
+ keysyms[Qt::Key_acute] = XK_acute;
+ keysyms[Qt::Key_mu] = XK_mu;
+ keysyms[Qt::Key_paragraph] = XK_paragraph;
+ keysyms[Qt::Key_periodcentered] = XK_periodcentered;
+ keysyms[Qt::Key_cedilla] = XK_cedilla;
+ keysyms[Qt::Key_onesuperior] = XK_onesuperior;
+ keysyms[Qt::Key_masculine] = XK_masculine;
+ keysyms[Qt::Key_guillemotright] = XK_guillemotright;
+ keysyms[Qt::Key_onequarter] = XK_onequarter;
+ keysyms[Qt::Key_onehalf] = XK_onehalf;
+ keysyms[Qt::Key_threequarters] = XK_threequarters;
+ keysyms[Qt::Key_questiondown] = XK_questiondown;
+ keysyms[Qt::Key_Agrave] = XK_Agrave;
+ keysyms[Qt::Key_Aacute] = XK_Aacute;
+ keysyms[Qt::Key_Acircumflex] = XK_Acircumflex;
+ keysyms[Qt::Key_Atilde] = XK_Atilde;
+ keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis;
+ keysyms[Qt::Key_Aring] = XK_Aring;
+ keysyms[Qt::Key_AE] = XK_AE;
+ keysyms[Qt::Key_Ccedilla] = XK_Ccedilla;
+ keysyms[Qt::Key_Egrave] = XK_Egrave;
+ keysyms[Qt::Key_Eacute] = XK_Eacute;
+ keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex;
+ keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis;
+ keysyms[Qt::Key_Igrave] = XK_Igrave;
+ keysyms[Qt::Key_Iacute] = XK_Iacute;
+ keysyms[Qt::Key_Icircumflex] = XK_Icircumflex;
+ keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis;
+ keysyms[Qt::Key_ETH] = XK_ETH;
+ keysyms[Qt::Key_Ntilde] = XK_Ntilde;
+ keysyms[Qt::Key_Ograve] = XK_Ograve;
+ keysyms[Qt::Key_Oacute] = XK_Oacute;
+ keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex;
+ keysyms[Qt::Key_Otilde] = XK_Otilde;
+ keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis;
+ keysyms[Qt::Key_multiply] = XK_multiply;
+ keysyms[Qt::Key_Ooblique] = XK_Ooblique;
+ keysyms[Qt::Key_Ugrave] = XK_Ugrave;
+ keysyms[Qt::Key_Uacute] = XK_Uacute;
+ keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex;
+ keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis;
+ keysyms[Qt::Key_Yacute] = XK_Yacute;
+ keysyms[Qt::Key_THORN] = XK_THORN;
+ keysyms[Qt::Key_ssharp] = XK_ssharp;
+ keysyms[Qt::Key_Agrave] = XK_Agrave;
+ keysyms[Qt::Key_Aacute] = XK_Aacute;
+ keysyms[Qt::Key_Acircumflex] = XK_Acircumflex;
+ keysyms[Qt::Key_Atilde] = XK_Atilde;
+ keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis;
+ keysyms[Qt::Key_Aring] = XK_Aring;
+ keysyms[Qt::Key_AE] = XK_AE;
+ keysyms[Qt::Key_Ccedilla] = XK_Ccedilla;
+ keysyms[Qt::Key_Egrave] = XK_Egrave;
+ keysyms[Qt::Key_Eacute] = XK_Eacute;
+ keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex;
+ keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis;
+ keysyms[Qt::Key_Igrave] = XK_Igrave;
+ keysyms[Qt::Key_Iacute] = XK_Iacute;
+ keysyms[Qt::Key_Icircumflex] = XK_Icircumflex;
+ keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis;
+ keysyms[Qt::Key_ETH] = XK_ETH;
+ keysyms[Qt::Key_Ntilde] = XK_Ntilde;
+ keysyms[Qt::Key_Ograve] = XK_Ograve;
+ keysyms[Qt::Key_Oacute] = XK_Oacute;
+ keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex;
+ keysyms[Qt::Key_Otilde] = XK_Otilde;
+ keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis;
+ keysyms[Qt::Key_division] = XK_division;
+ keysyms[Qt::Key_Ooblique] = XK_Ooblique;
+ keysyms[Qt::Key_Ugrave] = XK_Ugrave;
+ keysyms[Qt::Key_Uacute] = XK_Uacute;
+ keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex;
+ keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis;
+ keysyms[Qt::Key_Yacute] = XK_Yacute;
+ keysyms[Qt::Key_THORN] = XK_THORN;
+ keysyms[Qt::Key_ydiaeresis] = XK_ydiaeresis;
+ keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift;
+ keysyms[Qt::Key_Multi_key] = XK_Multi_key;
+ keysyms[Qt::Key_Codeinput] = XK_Codeinput;
+ keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate;
+ keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate;
+ keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate;
+ keysyms[Qt::Key_Mode_switch] = XK_Mode_switch;
+// keysyms[Qt::Key_script_switch] = XK_script_switch;
+ keysyms[Qt::Key_Kanji] = XK_Kanji;
+ keysyms[Qt::Key_Muhenkan] = XK_Muhenkan;
+// keysyms[Qt::Key_Henkan_Mode] = XK_Henkan_Mode;
+ keysyms[Qt::Key_Henkan] = XK_Henkan;
+ keysyms[Qt::Key_Romaji] = XK_Romaji;
+ keysyms[Qt::Key_Hiragana] = XK_Hiragana;
+ keysyms[Qt::Key_Katakana] = XK_Katakana;
+ keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana;
+ keysyms[Qt::Key_Zenkaku] = XK_Zenkaku;
+ keysyms[Qt::Key_Hankaku] = XK_Hankaku;
+ keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku;
+ keysyms[Qt::Key_Touroku] = XK_Touroku;
+ keysyms[Qt::Key_Massyo] = XK_Massyo;
+ keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock;
+ keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift;
+ keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift;
+ keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle;
+// keysyms[Qt::Key_Kanji_Bangou] = XK_Kanji_Bangou;
+// keysyms[Qt::Key_Zen_Koho] = XK_Zen_Koho;
+// keysyms[Qt::Key_Mae_Koho] = XK_Mae_Koho;
+ keysyms[Qt::Key_Hangul] = XK_Hangul;
+ keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
+ keysyms[Qt::Key_Hangul] = XK_Hangul;
+ keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start;
+ keysyms[Qt::Key_Hangul_End] = XK_Hangul_End;
+ keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja;
+ keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo;
+ keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja;
+// keysyms[Qt::Key_Hangul_Codeinput] = XK_Hangul_Codeinput;
+ keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja;
+ keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja;
+ keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja;
+ keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja;
+// keysyms[Qt::Key_Hangul_SingleCandidate] = XK_Hangul_SingleCandidate;
+// keysyms[Qt::Key_Hangul_MultipleCandidate] = XK_Hangul_MultipleCandidate;
+// keysyms[Qt::Key_Hangul_PreviousCandidate] = XK_Hangul_PreviousCandidate;
+ keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special;
+// keysyms[Qt::Key_Hangul_switch] = XK_Hangul_switch;
+ keysyms[Qt::Key_Dead_Grave] = XK_dead_grave;
+ keysyms[Qt::Key_Dead_Acute] = XK_dead_acute;
+ keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex;
+ keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde;
+ keysyms[Qt::Key_Dead_Macron] = XK_dead_macron;
+ keysyms[Qt::Key_Dead_Breve] = XK_dead_breve;
+ keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot;
+ keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis;
+ keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering;
+ keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute;
+ keysyms[Qt::Key_Dead_Caron] = XK_dead_caron;
+ keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla;
+ keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek;
+ keysyms[Qt::Key_Dead_Iota] = XK_dead_iota;
+ keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound;
+ keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound;
+ keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot;
+ keysyms[Qt::Key_Dead_Hook] = XK_dead_hook;
+ keysyms[Qt::Key_Dead_Horn] = XK_dead_horn;
+// keysyms[Qt::Key_Back] = XK_Back;
+// keysyms[Qt::Key_Forward] = XK_Forward;
+// keysyms[Qt::Key_Stop] = XK_Stop;
+// keysyms[Qt::Key_Refresh] = XK_Refresh;
+// keysyms[Qt::Key_VolumeDown] = XK_VolumeDown;
+// keysyms[Qt::Key_VolumeMute] = XK_VolumeMute;
+// keysyms[Qt::Key_VolumeUp] = XK_VolumeUp;
+// keysyms[Qt::Key_BassBoost] = XK_BassBoost;
+// keysyms[Qt::Key_BassUp] = XK_BassUp;
+// keysyms[Qt::Key_BassDown] = XK_BassDown;
+// keysyms[Qt::Key_TrebleUp] = XK_TrebleUp;
+// keysyms[Qt::Key_TrebleDown] = XK_TrebleDown;
+// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay;
+// keysyms[Qt::Key_MediaStop] = XK_MediaStop;
+// keysyms[Qt::Key_MediaPrevious] = XK_MediaPrevious;
+// keysyms[Qt::Key_MediaNext] = XK_MediaNext;
+// keysyms[Qt::Key_MediaRecord] = XK_MediaRecord;
+// keysyms[Qt::Key_HomePage] = XK_HomePage;
+// keysyms[Qt::Key_Favorites] = XK_Favorites;
+// keysyms[Qt::Key_Search] = XK_Search;
+// keysyms[Qt::Key_Standby] = XK_Standby;
+// keysyms[Qt::Key_OpenUrl] = XK_OpenUrl;
+// keysyms[Qt::Key_LaunchMail] = XK_LaunchMail;
+// keysyms[Qt::Key_LaunchMedia] = XK_LaunchMedia;
+// keysyms[Qt::Key_Launch0] = XK_Launch0;
+// keysyms[Qt::Key_Launch1] = XK_Launch1;
+// keysyms[Qt::Key_Launch2] = XK_Launch2;
+// keysyms[Qt::Key_Launch3] = XK_Launch3;
+// keysyms[Qt::Key_Launch4] = XK_Launch4;
+// keysyms[Qt::Key_Launch5] = XK_Launch5;
+// keysyms[Qt::Key_Launch6] = XK_Launch6;
+// keysyms[Qt::Key_Launch7] = XK_Launch7;
+// keysyms[Qt::Key_Launch8] = XK_Launch8;
+// keysyms[Qt::Key_Launch9] = XK_Launch9;
+// keysyms[Qt::Key_LaunchA] = XK_LaunchA;
+// keysyms[Qt::Key_LaunchB] = XK_LaunchB;
+// keysyms[Qt::Key_LaunchC] = XK_LaunchC;
+// keysyms[Qt::Key_LaunchD] = XK_LaunchD;
+// keysyms[Qt::Key_LaunchE] = XK_LaunchE;
+// keysyms[Qt::Key_LaunchF] = XK_LaunchF;
+// keysyms[Qt::Key_Display] = XK_Display;
+// keysyms[Qt::Key_MediaLast] = XK_MediaLast;
+ keysyms[Qt::Key_Select] = XK_Select;
+// keysyms[Qt::Key_Yes] = XK_Yes;
+// keysyms[Qt::Key_No] = XK_No;
+ keysyms[Qt::Key_Cancel] = XK_Cancel;
+// keysyms[Qt::Key_Printer] = XK_Printer;
+ keysyms[Qt::Key_Execute] = XK_Execute;
+// keysyms[Qt::Key_Sleep] = XK_Sleep;
+// keysyms[Qt::Key_MediaPlay] = XK_MediaPlay;
+// keysyms[Qt::Key_Zoom] = XK_Zoom;
+// keysyms[Qt::Key_Jisho] = XK_Jisho;
+// keysyms[Qt::Key_Oyayubi_Left] = XK_Oyayubi_Left;
+// keysyms[Qt::Key_Oyayubi_Right] = XK_Oyayubi_Right;
+// keysyms[Qt::Key_Context1] = XK_Context1;
+// keysyms[Qt::Key_Context2] = XK_Context2;
+// keysyms[Qt::Key_Context3] = XK_Context3;
+// keysyms[Qt::Key_Context4] = XK_Context4;
+// keysyms[Qt::Key_Call] = XK_Call;
+// keysyms[Qt::Key_Hangup] = XK_Hangup;
+// keysyms[Qt::Key_Flip] = XK_Flip;
+ keysyms[Qt::Key_unknown] = XK_VoidSymbol;
+}
+
+/* Store Keycodes for "normal" Latin1 keys: */
+int basic_keycodes[0x100];
+int basic_modifiers[0x100];
+
+int modifier_keycodes[8];
+Qt::KeyboardModifier modifier_meaning[8];
+
+int xmodifier_from_qtmodifier(Qt::KeyboardModifier m) {
+ for(int i = 0; i < 8; i++) {
+ if(m == modifier_meaning[i])
+ return i;
+ }
+ return -1;
+}
+
+typedef std::map<Qt::KeyboardModifier, xmodifier_type> qt_to_xmodifier_type;
+typedef std::map<xmodifier_type, Qt::KeyboardModifier> x_to_qtmodifier_type;
+qt_to_xmodifier_type qt_to_xmodifier;
+x_to_qtmodifier_type x_to_qtmodifier;
+
+typedef std::multimap<xmodifier_type, int> modifier_to_keycode_map;
+modifier_to_keycode_map modifier_to_keycode;
+typedef std::map<int, xmodifier_type> keycode_to_modifier_map;
+keycode_to_modifier_map keycode_to_modifier;
+
+// We need to query the input devices, preferrable through XInput2, but
+// if that is not available we will contend ourselves with XInput1:
+#ifndef HAVE_XINPUT2_H
+# define XIAllDevices 1 /* does not matter */
+# define XIDeviceInfo XDeviceInfo
+# define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos)
+# define XIFreeDeviceInfo(infos) XFreeDeviceList(infos)
+# define XIMasterKeyboard IsXKeyboard
+# define XISlaveKeyboard IsXExtensionKeyboard
+ static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
+ {
+ if(devinfo->use == IsXKeyboard)
+ {
+ // According to XOpenDevice(3X11) you cannot open the Core Keyboard.
+ *nprops = 0;
+ return 0;
+ }
+
+ XDevice* dev = XOpenDevice(dpy, devinfo->id);
+ Atom* props = XListDeviceProperties(dpy, dev, nprops);
+ XCloseDevice(dpy, dev);
+ return props;
+ }
+#else
+ static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops)
+ {
+ return XIListProperties(dpy, devinfo->deviceid, nprops);
+ }
+#endif
+
+
+void initialize_basic_keycodes()
+{
+ for(int i = 0; i < 8; i++) {
+ modifier_keycodes[i] = -1;
+ }
+ for(int i = 0; i < 0x100; i++) {
+ basic_keycodes[i] = -1;
+ }
+
+ Display* dpy = X11InputUtils::display();
+ int min_keycode, max_keycode;
+ XDisplayKeycodes(dpy, &min_keycode, &max_keycode);
+
+ int xkb_opcode, xkb_event, xkb_error, xkb_major, xkb_minor;
+ bool xkb_present = XkbQueryExtension(dpy, &xkb_opcode, &xkb_event, &xkb_error, &xkb_major, &xkb_minor);
+ if(!xkb_present) {
+ int keysyms_per_code;
+ const int count = max_keycode - min_keycode + 1;
+ KeySym* mapping = XGetKeyboardMapping(dpy, min_keycode, count, &keysyms_per_code);
+ for(int i = 0; i < count; i++)
+ {
+ for(int j = 0; j < keysyms_per_code; j++)
+ {
+ const int idx = i * keysyms_per_code + j;
+ const KeySym ks = mapping[idx];
+ const int keycode = min_keycode + i;
+
+ if(ks >= ' ' && ks < 0x100)
+ {
+ if(ks == XK_at) {
+ ConsoleLog writeLine(QString("Keycode %1 (%2) gives `@' with modifiers `%3', but it is really `%4'").arg(keycode).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, 0))).arg(modifiers_to_string(j)).arg(XKeysymToString(XKeycodeToKeysym(dpy, keycode, j))));
+
+ }
+
+ if(basic_keycodes[ks] != -1)
+ continue; // already found
+
+ basic_keycodes[ks] = keycode;
+ basic_modifiers[ks] = j;
+ }
+ }
+ }
+ XFree(mapping);
+ }
+ else
+ {
+ // Try to find out what device XTest will send events on:
+ unsigned int xkbDeviceId = XkbUseCoreKbd;
+ Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false);
+ int ndevinfos;
+ XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos);
+ if(devinfos)
+ {
+#ifndef HAVE_XINPUT2_H
+# define deviceid id
+#endif
+ for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++)
+ {
+ XIDeviceInfo* devinfo = devinfos + i;
+ qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name);
+
+ // We want it to be a keyboard.
+ if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard)
+ continue;
+
+ int nprops;
+ Atom* props = getDeviceProperties(dpy, devinfo, &nprops);
+ if(props)
+ {
+ for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++)
+ {
+ Atom prop = props[j];
+ if(prop == xtestDeviceProp)
+ {
+ // The device is the XTest Keyboard:
+ xkbDeviceId = devinfo->deviceid;
+ }
+ }
+ XFree(props);
+ }
+ }
+ XIFreeDeviceInfo(devinfos);
+#ifdef deviceid /* XInput1 */
+# undef deviceid
+#endif
+ }
+
+ // Okay, we know which device to query. Now get its keymap:
+ XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId);
+ if(!keybDesc)
+ {
+ qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId);
+ }
+
+ for(int i = min_keycode; i <= max_keycode; i++)
+ {
+ for(int j = 0; j <= 0xff; j++)
+ {
+ KeySym ks = 0;
+ unsigned int unconsumed;
+ if(keybDesc)
+ {
+ if(!XkbTranslateKeyCode(keybDesc, i, j, &unconsumed, &ks) || ks == NoSymbol)
+ continue;
+ }
+ else
+ {
+ if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol)
+ continue;
+ }
+
+ if(ks && (ks < 0x100))
+ {
+// ConsoleLog writeLine(QString("Keycode %1 (%2) seems to give `%3' with modifiers `%4', of which `%5' are unconsumed")
+// .arg(i)
+// .arg(XKeysymToString(XKeycodeToKeysym(dpy, i, 0)))
+// .arg(XKeysymToString(ks))
+// .arg(modifiers_to_string(j))
+// .arg(modifiers_to_string(unconsumed)));
+ if(basic_keycodes[ks] != -1)
+ continue;
+
+ basic_keycodes[ks] = i;
+ basic_modifiers[ks] = unconsumed & j;
+ }
+ }
+ }
+
+ // Free the keyboard description:
+ if(keybDesc)
+ {
+ XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true);
+ }
+ }
+
+ // find out which keycodes cause the modifier state bits:
+ XModifierKeymap* modkm;
+ modkm = XGetModifierMapping(dpy);
+
+ const int shift_kc = XKeysymToKeycode(dpy, XK_Shift_L);
+ const int control_kc = XKeysymToKeycode(dpy, XK_Control_L);
+ const int alt_kc = XKeysymToKeycode(dpy, XK_Alt_L);
+ const int meta_kc = XKeysymToKeycode(dpy, XK_Meta_L);
+ const int switch_kc = XKeysymToKeycode(dpy, XK_Mode_switch);
+
+ for(int i = 0; i < 8; i++) {
+ for(int j = 0; j < modkm->max_keypermod; j++) {
+ const int idx = i * modkm->max_keypermod + j;
+ const int kc = modkm->modifiermap[idx];
+
+ modifier_to_keycode.insert(std::make_pair((1<<i), kc));
+ keycode_to_modifier[kc] = (1<<i);
+#define remember_modifier(qmod) do { \
+ qt_to_xmodifier[Qt::qmod##Modifier] = 1<<i; \
+ x_to_qtmodifier[1<<i] = Qt::qmod##Modifier; \
+} while(false)
+ if(kc == shift_kc) {
+ remember_modifier(Shift);
+ } else if(kc == control_kc) {
+ remember_modifier(Control);
+ } else if(kc == alt_kc) {
+ remember_modifier(Alt);
+ } else if(kc == meta_kc) {
+ remember_modifier(Meta);
+ } else if(kc == switch_kc) {
+ remember_modifier(GroupSwitch);
+ }
+#undef remember_modifier
+
+ if(modifier_keycodes[i] != -1) {
+ continue; // already found
+ }
+
+ // select one arbitrarily
+ modifier_keycodes[i] = kc;
+ }
+ }
+
+ XFreeModifiermap(modkm);
+}
+
+xmodifier_type translate_modifiers(quint32 mods)
+{
+ xmodifier_type ret = 0;
+
+ for(int j = 1; j < 8; j++) {
+ xmodifier_type i = 1<<j;
+
+ if(mods & x_to_qtmodifier[i])
+ {
+ ret |= i;
+ }
+ }
+
+ return ret;
+}
+
+typedef std::set<int> int_set_type;
+int_set_type pressed_modifier_keys;
+xmodifier_type current_modifiers;
+
+void trackModifiers(int keycode, bool down)
+{
+ // is this a modifier key?
+ const bool is_modifier = keycode_to_modifier.find(keycode) != keycode_to_modifier.end();
+
+ if(!is_modifier)
+ {
+ return;
+ }
+
+ if(down) {
+ pressed_modifier_keys.insert(keycode);
+ } else {
+ pressed_modifier_keys.erase(keycode);
+ }
+
+ int_set_type::iterator i, end = pressed_modifier_keys.end();
+ xmodifier_type modifs = 0;
+ for(i = pressed_modifier_keys.begin(); i != end; i++)
+ {
+ keycode_to_modifier_map::iterator found_kc = keycode_to_modifier.find(*i);
+ if(found_kc != keycode_to_modifier.end())
+ {
+ modifs |= (*found_kc).second;
+ }
+ }
+ current_modifiers = modifs;
+
+ ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiers_to_string(modifs)));
+}
+
+typedef std::pair<int, bool> modifier_tweak_type;
+typedef std::vector<modifier_tweak_type> tweak_sequence_type;
+
+void tweakModifiers(Display* dpy, xmodifier_type neededState, xmodifier_type actualState, tweak_sequence_type& tracker)
+{
+ ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiers_to_string(neededState)).arg(modifiers_to_string(actualState)));
+ for(int j = 0; j < 8; j++)
+ {
+ xmodifier_type i = 1<<j;
+
+ ConsoleLog writeLine(QString("Checking for %1...").arg(modifiers_to_string(i)));
+
+ if((i & neededState) && !(i & actualState))
+ {
+ ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be pressed").arg(modifiernames[j]));
+
+ // needs to be pressed
+
+ //find the keycode:
+ int kc;
+ modifier_to_keycode_map::iterator iter = modifier_to_keycode.find(i);
+
+ if((iter == modifier_to_keycode.end()) || ((*iter).first != i))
+ {
+ continue; // we don't know a key that triggers this modifier
+ }
+
+ ConsoleLog writeLine(QString(" pressing key %1").arg((*iter).second));
+ XTestFakeKeyEvent(dpy, (*iter).second, 1, CurrentTime);
+
+ tracker.push_back(std::make_pair((*iter).second, true));
+ }
+ else if((!(i & neededState)) && (i & actualState))
+ {
+ ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be released").arg(modifiernames[j]));
+
+ // needs to be released
+
+ int kc = -1;
+ // first, check whether any of the currently pressed keys has triggered this modifier:
+
+ int_set_type::iterator iter, end = pressed_modifier_keys.end();
+ for(iter = pressed_modifier_keys.begin(); iter != end; iter++)
+ {
+ keycode_to_modifier_map::iterator modmap_iter = keycode_to_modifier.find(*iter);
+ if(modmap_iter != keycode_to_modifier.end()) {
+ if(modmap_iter->second == i) {
+ kc = *iter;
+
+ // release this key:
+ ConsoleLog writeLine(QString(" releasing key %1").arg(kc));
+ XTestFakeKeyEvent(dpy, kc, 0, CurrentTime);
+ tracker.push_back(std::make_pair(kc, false));
+ }
+ }
+ }
+
+ if(kc == -1) {
+ // strange, but we need to release some other key:
+ // we don't know which one, so we abort this and hope for the best
+ continue;
+ }
+ }
+ }
+}
+
+void untweakModifiers(Display* dpy, tweak_sequence_type& tracker)
+{
+ tweak_sequence_type::iterator i, end = tracker.end();
+ for(i = tracker.begin(); i != end; i++) {
+ modifier_tweak_type& t = *i;
+ XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime);
+ }
+}
+
+void X11FakeKeyboardHandler::initialize()
+{
+ initialize_keysyms();
+ initialize_basic_keycodes();
+ pressed_modifier_keys.clear();
+ current_modifiers = 0;
+}
+
+void X11FakeKeyboardHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ Display* dpy = X11InputUtils::display();
+
+ // find out which keysym caused this event:
+ lookup_table_const_iterator i = keysyms.find(evt.qt_keysym());
+ if(i == keysyms.end()) {
+ // Special cases. We don't know how to directly translate those, so we will try to emulate them.
+ switch(evt.qt_keysym())
+ {
+ case Qt::Key_Backtab:
+ handle(InputEvent(evt.type(), evt.code(), evt.qt_modifiers() | Qt::ShiftModifier | Qt::Key_Tab));
+ break;
+ default:
+ ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qt_keysym(), 8, 16));
+ }
+ } else {
+ KeySym ks = (*i).second;
+
+ // is it a "normal" key?
+ if(ks >= ' ' && ks < 0x100)
+ {
+ if(basic_keycodes[ks] != -1)
+ {
+ if(evt.isPress())
+ {
+ // Try the simple way first:
+ // Does the keycode with current modifiers yield the requested symbol?
+ KeySym unmod_ks = XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers);
+ ConsoleLog writeLine(QString("Pressing the key for %1 would yield %2 right now.").arg(XKeysymToString(ks)).arg(XKeysymToString(unmod_ks)));
+
+ if(ks == XKeycodeToKeysym(dpy, basic_keycodes[ks], current_modifiers))
+ {
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime);
+ }
+ else
+ {
+ // what modifier keys do we need to press?
+ xmodifier_type mods = translate_modifiers(evt.qt_modifiers());
+
+ // we may need to press additional modifiers to generate this keysym:
+ if(QChar(ks, 0).isLetter())
+ {
+ // but, since we do not differentiate upper and lower case,
+ // and instead let the sender handle those modifiers,
+ // we need to AND ShiftModifier and LockModifier out.
+ mods |= basic_modifiers[ks] & ~(1<<ShiftMapIndex) & ~(1<<LockMapIndex);
+ }
+ else
+ {
+ // Not an alpha character. We do need to respect Shift and Lock for those:
+ mods |= basic_modifiers[ks];
+ }
+
+ // now, tweak the modifiers
+ tweak_sequence_type tweaks;
+ tweakModifiers(dpy, mods, current_modifiers, tweaks);
+
+ // press the key:
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 1, CurrentTime);
+
+ // and release the modifiers:
+ untweakModifiers(dpy, tweaks);
+ }
+ }
+ else
+ {
+ // just release the key.
+ XTestFakeKeyEvent(dpy, basic_keycodes[ks], 0, CurrentTime);
+ }
+ }
+ else
+ {
+ ConsoleLog writeLine(QString("No keycode is mapped to `%1'").arg(XKeysymToString(ks)));
+ }
+ }
+ else
+ {
+ // It is some kind of "special" key, so we just fake that, then:
+ KeyCode kc = XKeysymToKeycode(dpy, ks);
+ if(kc != 0)
+ {
+ ConsoleLog writeLine(QString("%1 special keycode %2, hopefully giving keysym %3").arg(evt.isPress() ? "Pressed" : "Released").arg(kc).arg(XKeysymToString(ks)));
+ XTestFakeKeyEvent(dpy, kc, evt.isPress(), CurrentTime);
+ // and track it if it is a modifier key:
+ trackModifiers(kc, evt.isPress());
+ }
+ else
+ {
+ ConsoleLog writeLine(QString("No keycode is mapped to %1").arg(XKeysymToString(ks)));
+ }
+ }
+ }
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
diff --git a/src/input/x11FakeKeyboardHandler.h b/src/input/x11FakeKeyboardHandler.h
new file mode 100644
index 0000000..3dde7cc
--- /dev/null
+++ b/src/input/x11FakeKeyboardHandler.h
@@ -0,0 +1,29 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeKeyboardHandler.h:
+ # - Handle keyboard events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef X11FAKEKEYBOARDHANDLER_H_
+#define X11FAKEKEYBOARDHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class X11FakeKeyboardHandler : public DefaultInputEventHandler<InputEvent::ET_KEY>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+ void initialize();
+};
+
+#endif /* X11FAKEKEYBOARDHANDLER_H_ */
diff --git a/src/input/x11FakeMouseHandler.cpp b/src/input/x11FakeMouseHandler.cpp
new file mode 100644
index 0000000..58415d5
--- /dev/null
+++ b/src/input/x11FakeMouseHandler.cpp
@@ -0,0 +1,59 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeMouseHandler.h:
+ # - Handle mouse events on X11 - implementation
+ # --------------------------------------------------------------------------
+ */
+
+#include "x11FakeMouseHandler.h" // need to include before X headers
+#include <src/util/consoleLogger.h>
+#include <X11/extensions/XTest.h>
+#include "x11InputUtils.h"
+
+void X11FakeMouseButtonHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ quint16 pressedButton = evt.pressedButton();
+
+ Display* dpy = X11InputUtils::display();
+
+ XTestGrabControl(dpy, 1);
+ for(int i = 0; i < 16; i++) {
+ if((1<<i) == pressedButton)
+ {
+ ConsoleLog writeLine(QString("Got mouse button event: button %1 %2").arg(i + 1).arg(evt.isPress() ? "pressed" : "released"));
+ if(!XTestFakeButtonEvent(dpy, i + 1, evt.isPress(), CurrentTime))
+ {
+ ConsoleLog writeLine("[ERROR] XTestFakeButtonEvent failed");
+ }
+ }
+ }
+ XTestGrabControl(dpy, 0);
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
+
+void X11FakeMouseMovementHandler::handle(InputEvent const& evt, InputEventContext const*)
+{
+ ConsoleLog writeLine(QString("Received mouse motion event (%1,%2)").arg(evt.xCoord()).arg(evt.yCoord()));
+ Display* dpy = X11InputUtils::display();
+ int screen = 0 /* DefaultScreen(dpy) */;
+ XTestGrabControl(dpy, 1);
+ if(!XTestFakeMotionEvent(dpy, screen, evt.xCoord(), evt.yCoord(), CurrentTime))
+ {
+ ConsoleLog writeLine("[ERROR] XTestFakeMotionEvent failed");
+ }
+ XTestGrabControl(dpy, 0);
+
+ // Since there may not be a mainloop running, we need to manually flush the event queue
+ XFlush(dpy);
+}
diff --git a/src/input/x11FakeMouseHandler.h b/src/input/x11FakeMouseHandler.h
new file mode 100644
index 0000000..0e32256
--- /dev/null
+++ b/src/input/x11FakeMouseHandler.h
@@ -0,0 +1,34 @@
+/*
+ # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # --------------------------------------------------------------------------
+ # x11FakeMouseHandler.h:
+ # - Handle mouse events on X11 - interface
+ # --------------------------------------------------------------------------
+ */
+
+#ifndef X11FAKEMOUSEHANDLER_H_
+#define X11FAKEMOUSEHANDLER_H_
+
+#include "inputEventHandler.h"
+
+class X11FakeMouseButtonHandler : public DefaultInputEventHandler<InputEvent::ET_BUTTON>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+};
+
+class X11FakeMouseMovementHandler : public DefaultInputEventHandler<InputEvent::ET_POINTER>
+{
+public:
+ void handle(InputEvent const&, InputEventContext const* = 0);
+};
+
+#endif /* X11FAKEMOUSEHANDLER_H_ */
diff --git a/src/input/x11InputUtils.cpp b/src/input/x11InputUtils.cpp
new file mode 100644
index 0000000..b589bd6
--- /dev/null
+++ b/src/input/x11InputUtils.cpp
@@ -0,0 +1,29 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # ------------------------------------------------------------------------
+ # src/input/x11InputUtils.h:
+ # Utilities for input handling under X11 - implementation
+ */
+
+#include "x11InputUtils.h"
+
+static Display* _dpy = 0;
+
+void X11InputUtils::setDisplay(Display* dpy)
+{
+ _dpy = dpy;
+}
+
+Display* X11InputUtils::display()
+{
+ return _dpy;
+}
diff --git a/src/input/x11InputUtils.h b/src/input/x11InputUtils.h
new file mode 100644
index 0000000..9c85d09
--- /dev/null
+++ b/src/input/x11InputUtils.h
@@ -0,0 +1,27 @@
+/*
+ # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of
+ # Freiburg
+ #
+ # This program is free software distributed under the GPL version 2.
+ # See http://openslx.org/COPYING
+ #
+ # If you have any feedback please consult http://openslx.org/feedback and
+ # send your suggestions, praise, or complaints to feedback@openslx.org
+ #
+ # General information about OpenSLX can be found at http://openslx.org/
+ # ------------------------------------------------------------------------
+ # src/input/x11InputUtils.h:
+ # Utilities for input handling under X11 - interface
+ */
+
+#ifndef X11INPUTUTILS_H_
+#define X11INPUTUTILS_H_
+
+#include <X11/Xlib.h>
+
+struct X11InputUtils {
+ static void setDisplay(Display*);
+ static Display* display();
+};
+
+#endif /* X11INPUTUTILS_H_ */
diff --git a/src/net/mcast/CMakeLists.txt b/src/net/mcast/CMakeLists.txt
new file mode 100644
index 0000000..e92b090
--- /dev/null
+++ b/src/net/mcast/CMakeLists.txt
@@ -0,0 +1,61 @@
+INCLUDE(../../../OpenPGMConfig.cmake)
+
+ADD_DEFINITIONS(
+ ${LIBPGM_CXXFLAGS}
+ -D__STDC_CONSTANT_MACROS
+ -D__STDC_LIMIT_MACROS
+)
+
+# OpenPGM uses the C99 restrict keyword which g++ does not recognize:
+#IF(CMAKE_COMPILER_IS_GNUCXX)
+# ADD_DEFINITIONS(${LIBPGM_CXXFLAGS})
+#ENDIF(CMAKE_COMPILER_IS_GNUCXX)
+
+INCLUDE(${QT_USE_FILE})
+
+SET(pvsmcast_MOC_HDRS
+ McastConfiguration.h
+ McastPGMSocket.h
+ McastReceiver.h
+ McastSender.h
+)
+
+SET(pvsmcast_HDRS
+ McastConfiguration.h
+ McastPGMSocket.h
+ McastReceiver.h
+ McastSender.h
+)
+
+SET(pvsmcast_SRCS
+ McastConfiguration.cpp
+ McastPGMSocket.cpp
+ McastReceiver.cpp
+ McastSender.cpp
+)
+
+QT4_WRAP_CPP(
+ pvsmcast_MOC_SRCS
+ ${pvsmcast_MOC_HDRS}
+)
+
+SET_SOURCE_FILES_PROPERTIES(${pvsmcast_SRCS} ${pvsmcast_MOC_SRCS}
+ PROPERTIES
+ OBJECT_DEPENDS "3rdparty/libpgm.a" # Make sure libpgm gets unpacked before building C++ files
+)
+
+ADD_LIBRARY(
+ pvsmcast
+ STATIC
+ ${pvsmcast_HDRS}
+ ${pvsmcast_SRCS}
+ ${pvsmcast_MOC_SRCS}
+)
+
+TARGET_LINK_LIBRARIES(
+ pvsmcast
+ pgm
+ ${QT_LIBRARIES}
+)
+
+ADD_SUBDIRECTORY(trial_programs)
diff --git a/src/net/mcast/McastConfiguration.cpp b/src/net/mcast/McastConfiguration.cpp
new file mode 100644
index 0000000..6c5e620
--- /dev/null
+++ b/src/net/mcast/McastConfiguration.cpp
@@ -0,0 +1,57 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastConfiguration.cpp
+# - hold Multicast protocol configuration data
+# -----------------------------------------------------------------------------
+*/
+
+#include <QSettings>
+
+#include "McastConfiguration.h"
+
+void McastConfiguration::loadFrom(QSettings* _settings, char const* group)
+{
+ if (group)
+ _settings->beginGroup(group);
+
+ _multicastAddress = _settings->value("groupAddress", DEFAULT_MULTICAST_ADDRESS).toString();
+ _multicastInterface = _settings->value("interface", DEFAULT_MULTICAST_INTERFACE).toString();
+ _multicastMTU = _settings->value("mtu", DEFAULT_MULTICAST_MTU).value<quint16>();
+ _multicastRate = _settings->value("rate", DEFAULT_MULTICAST_RATE).value<quint32>();
+ _multicastUseUDP = _settings->value("use-udp", DEFAULT_MULTICAST_USEUDP).toBool();
+ _multicastWinSize = _settings->value("winsize", DEFAULT_MULTICAST_WSIZ).value<quint16>();
+ _multicastUDPPortBase = _settings->value("portbase", DEFAULT_MULTICAST_UDPPORT).value<quint16>();
+ _multicastDPort = _settings->value("dport", DEFAULT_MULTICAST_DPORT).value<quint16>();
+ _multicastSPort = _settings->value("sport", DEFAULT_MULTICAST_SPORT).value<quint16>();
+
+ if (group)
+ _settings->endGroup();
+}
+
+void McastConfiguration::writeTo(QSettings* _settings, char const* group) const
+{
+ if (group)
+ _settings->beginGroup(group);
+
+ _settings->setValue("groupAddress", _multicastAddress);
+ _settings->setValue("interface", _multicastInterface);
+ _settings->setValue("mtu", _multicastMTU);
+ _settings->setValue("rate", _multicastRate);
+ _settings->setValue("use-udp", _multicastUseUDP);
+ _settings->setValue("winsize", _multicastWinSize);
+ _settings->setValue("portbase", _multicastUDPPortBase);
+ _settings->setValue("dport", _multicastDPort);
+ _settings->setValue("sport", _multicastSPort);
+
+ if (group)
+ _settings->endGroup();
+}
diff --git a/src/net/mcast/McastConfiguration.h b/src/net/mcast/McastConfiguration.h
new file mode 100644
index 0000000..53f7a54
--- /dev/null
+++ b/src/net/mcast/McastConfiguration.h
@@ -0,0 +1,204 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastConfiguration.h
+# - hold Multicast protocol configuration data
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTCONFIGURATION_H_
+#define MCASTCONFIGURATION_H_
+
+#include <QObject>
+#include <QString>
+#include <QtGlobal>
+
+#include "McastConstants.h"
+
+class QSettings;
+
+class McastConfiguration: public QObject
+{
+ Q_OBJECT
+public:
+ McastConfiguration(QObject* parent = 0) :
+ QObject(parent),
+ _multicastInterface(DEFAULT_MULTICAST_INTERFACE),
+ _multicastAddress(DEFAULT_MULTICAST_ADDRESS),
+ _multicastRate(DEFAULT_MULTICAST_RATE),
+ _multicastSPort(DEFAULT_MULTICAST_SPORT),
+ _multicastDPort(DEFAULT_MULTICAST_DPORT),
+ _multicastWinSize(DEFAULT_MULTICAST_WSIZ),
+ _multicastMTU(DEFAULT_MULTICAST_MTU),
+ _multicastUDPPortBase(DEFAULT_MULTICAST_UDPPORT),
+ _multicastUseUDP(DEFAULT_MULTICAST_USEUDP)
+ {
+ }
+
+ McastConfiguration(McastConfiguration const& other, QObject* parent = 0) :
+ QObject(parent),
+ _multicastInterface(other._multicastInterface),
+ _multicastAddress(other._multicastAddress),
+ _multicastRate(other._multicastRate),
+ _multicastSPort(other._multicastSPort),
+ _multicastDPort(other._multicastDPort),
+ _multicastWinSize(other._multicastWinSize),
+ _multicastMTU(other._multicastMTU),
+ _multicastUDPPortBase(other._multicastUDPPortBase),
+ _multicastUseUDP(other._multicastUseUDP)
+ {
+ }
+
+ virtual ~McastConfiguration()
+ {
+ }
+
+ QString multicastAddress() const
+ {
+ return _multicastAddress;
+ }
+ McastConfiguration* multicastAddress(QString const& address)
+ {
+ _multicastAddress = address;
+ return this;
+ }
+
+ quint16 multicastSPort() const
+ {
+ return _multicastSPort;
+ }
+ McastConfiguration* multicastSPort(quint16 port)
+ {
+ _multicastSPort = port;
+ return this;
+ }
+
+ quint16 multicastDPort() const
+ {
+ return _multicastDPort;
+ }
+ McastConfiguration* multicastDPort(quint16 port)
+ {
+ _multicastDPort = port;
+ return this;
+ }
+
+ quint32 multicastRate() const
+ {
+ return _multicastRate;
+ }
+ McastConfiguration* multicastRate(quint32 rate)
+ {
+ _multicastRate = rate;
+ return this;
+ }
+
+ quint16 multicastWinSize() const
+ {
+ return _multicastWinSize;
+ }
+ McastConfiguration* multicastWinSize(quint16 size)
+ {
+ _multicastWinSize = size;
+ return this;
+ }
+
+ quint16 multicastMTU() const
+ {
+ return _multicastMTU;
+ }
+ McastConfiguration* multicastMTU(quint16 mtu)
+ {
+ _multicastMTU = mtu;
+ return this;
+ }
+
+ bool multicastUseUDP() const
+ {
+ return _multicastUseUDP;
+ }
+ McastConfiguration* multicastUseUDP(bool useUDP)
+ {
+ _multicastUseUDP = useUDP;
+ return this;
+ }
+
+ quint16 multicastUDPPortBase() const
+ {
+ return _multicastUDPPortBase;
+ }
+ McastConfiguration* multicastUDPPortBase(quint16 port)
+ {
+ _multicastUDPPortBase = port;
+ return this;
+ }
+
+ QString multicastInterface() const
+ {
+ return _multicastInterface;
+ }
+ McastConfiguration* multicastInterface(QString const& interface)
+ {
+ _multicastInterface = interface;
+ return this;
+ }
+
+ quint16 multicastUDPUPort() const
+ {
+ return _multicastUDPPortBase;
+ }
+
+ quint16 multicastUDPMPort() const
+ {
+ return _multicastUDPPortBase;
+ }
+
+ void commit()
+ {
+ emit changed();
+ }
+
+ McastConfiguration& operator=(McastConfiguration const& source)
+ {
+ if(this != &source)
+ {
+ _multicastInterface = source._multicastInterface;
+ _multicastAddress = source._multicastAddress;
+ _multicastRate = source._multicastRate;
+ _multicastSPort = source._multicastSPort;
+ _multicastDPort = source._multicastDPort;
+ _multicastWinSize = source._multicastWinSize;
+ _multicastMTU = source._multicastMTU;
+ _multicastUDPPortBase = source._multicastUDPPortBase;
+ _multicastUseUDP = source._multicastUseUDP;
+ }
+ return *this;
+ }
+
+ void loadFrom(QSettings* settings, char const* group = 0);
+ void writeTo(QSettings* settings, char const* group = 0) const;
+
+signals:
+ void changed();
+
+private:
+ QString _multicastInterface;
+ QString _multicastAddress;
+ quint32 _multicastRate;
+ quint16 _multicastSPort;
+ quint16 _multicastDPort;
+ quint16 _multicastWinSize;
+ quint16 _multicastMTU;
+ quint16 _multicastUDPPortBase;
+ bool _multicastUseUDP;
+};
+
+#endif /* MCASTCONFIGURATION_H_ */
diff --git a/src/net/mcast/McastConstants.h b/src/net/mcast/McastConstants.h
new file mode 100644
index 0000000..624e195
--- /dev/null
+++ b/src/net/mcast/McastConstants.h
@@ -0,0 +1,36 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastMagic.h
+# - Specify the magic numbers for the McastFT protocol
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTMAGIC_H_
+#define MCASTMAGIC_H_
+
+#include <stdint.h>
+
+#define MCASTFT_MAGIC UINT64_C(0x6d60ad83825fb7f9)
+#define DEFAULT_MULTICAST_INTERFACE ""
+#define DEFAULT_MULTICAST_ADDRESS "239.255.220.207"
+#define DEFAULT_MULTICAST_SPORT 6964
+#define DEFAULT_MULTICAST_DPORT 6965
+#define DEFAULT_MULTICAST_USEUDP true
+#define DEFAULT_MULTICAST_UDPPORT 6966
+#define DEFAULT_MULTICAST_WSIZ 30
+#define DEFAULT_MULTICAST_RATE (100*1024)
+#define DEFAULT_MULTICAST_MTU 1400
+#define DEFAULT_MULTICAST_APDU 1200
+#define DEFAULT_MULTICAST_CHUNK 1024
+#define DEFAULT_MULTICAST_SHUTDOWN_TIMEOUT 10000
+
+#endif /* MCASTMAGIC_H_ */
diff --git a/src/net/mcast/McastPGMSocket.cpp b/src/net/mcast/McastPGMSocket.cpp
new file mode 100644
index 0000000..731fc13
--- /dev/null
+++ b/src/net/mcast/McastPGMSocket.cpp
@@ -0,0 +1,666 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastPGMSocket.cpp
+# - wrap OpenPGM Sockets in a nicer interface -- implementation
+# -----------------------------------------------------------------------------
+*/
+
+#include <cstdlib>
+
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include <QByteArray>
+#include <QList>
+#include <QtDebug>
+#include <QPointer>
+#include <QSocketNotifier>
+#include <QTimer>
+
+#include <pgm/pgm.h>
+
+#include "McastPGMSocket.h"
+
+using namespace std;
+
+class McastPGMSocket_priv
+{
+public:
+ McastPGMSocket_priv() :
+ socket(0),
+ send_notif(0)
+ {
+ }
+ ~McastPGMSocket_priv()
+ {
+ if (socket)
+ pgm_close(socket, 0);
+ Q_FOREACH(QSocketNotifier* notif, _notifs)
+ {
+ delete notif;
+ }
+ if (send_notif)
+ delete send_notif;
+ }
+
+ pgm_sock_t* socket;
+ McastPGMSocket::Direction direction;
+ QSocketNotifier* send_notif;
+ QList<QSocketNotifier*> _notifs;
+
+ QSocketNotifier* notifier_for(int fd) {
+ Q_FOREACH(QSocketNotifier* notif, _notifs)
+ {
+ if(notif->socket() == fd)
+ {
+ return notif;
+ }
+ }
+ return 0;
+ }
+};
+
+static void _ensurePGMInited()
+{
+ if (!pgm_supported())
+ {
+ pgm_error_t* err;
+ int good = pgm_init(&err);
+ if (!good)
+ {
+ qCritical() << "Could not init OpenPGM library: PGM Error: " << (err->message ? err->message : "(null)");
+ std::exit(1);
+ }
+ }
+}
+
+McastPGMSocket::McastPGMSocket(QObject* parent) :
+ QObject(parent),
+ _priv(new McastPGMSocket_priv),
+ _opened(false),
+ _finished(false),
+ _nakTimeout(new QTimer()),
+ _dataTimeout(new QTimer()),
+ _sendTimeout(new QTimer()),
+ _shutdownTimer(0),
+ _shutdown_timeout(0)
+{
+ _ensurePGMInited();
+
+ _nakTimeout->setSingleShot(true);
+ _dataTimeout->setSingleShot(true);
+ _sendTimeout->setSingleShot(true);
+
+ connect(_nakTimeout, SIGNAL(timeout()), this, SLOT(handleNakTimeout()));
+ connect(_dataTimeout, SIGNAL(timeout()), this, SLOT(handleDataTimeout()));
+ connect(_sendTimeout, SIGNAL(timeout()), this, SLOT(canSend()));
+}
+
+McastPGMSocket::~McastPGMSocket()
+{
+ delete _priv;
+ delete _nakTimeout;
+ delete _dataTimeout;
+ delete _sendTimeout;
+}
+
+bool McastPGMSocket::open(McastConfiguration const* config, Direction direction)
+{
+ _priv->direction = direction;
+
+ pgm_error_t* err = 0;
+ int good;
+
+ pgm_addrinfo_t* addrinfo;
+ // parse the address string
+ good = pgm_getaddrinfo((config->multicastInterface() + ";" + config->multicastAddress()).toLatin1().constData(),
+ 0, &addrinfo, &err);
+ if (!good)
+ {
+ qCritical() << "Could not parse address info: PGM Error: "
+ << err->message;
+ return false;
+ }
+
+ sa_family_t family = addrinfo->ai_send_addrs[0].gsr_group.ss_family;
+
+ if(config->multicastUseUDP())
+ {
+ good = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_UDP, &err);
+ }
+ else
+ {
+ good = pgm_socket(&_priv->socket, family, SOCK_SEQPACKET, IPPROTO_PGM, &err);
+ }
+
+ if (!good)
+ {
+ qCritical() << "Could not open socket: PGM Error: " << err->message;
+ pgm_error_free(err);
+ return false;
+ }
+
+ unsigned const ambient_spm = 2000 * 1000; // every one hundred milliseconds (approx.)
+
+ /* Options for sending data */
+ const int spm_heartbeat[] =
+ { 512 * 1000,
+ 1024 * 1000,
+ 2048 * 1000,
+ 4096 * 1000 },
+ max_rate = 0,
+ max_window = config->multicastWinSize() * config->multicastRate() / config->multicastMTU();
+ // const int max_window_sqns = 3000;
+ qDebug() << "Computed window size " << max_window << " packets";
+
+// pgm_setsockopt(_priv->socket, PGM_SEND_ONLY, &send_only,
+// sizeof(send_only));
+
+ // SPM messages
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_AMBIENT_SPM, &ambient_spm, sizeof(ambient_spm));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_HEARTBEAT_SPM, &spm_heartbeat, sizeof(spm_heartbeat));
+
+ // Transmit window
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_TXW_MAX_RTE, &max_rate, sizeof(max_rate));
+// pgm_setsockopt(_priv->socket, PGM_TXW_SECS, &max_window, sizeof(max_window));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_TXW_SQNS, &max_window, sizeof(max_window));
+
+ /* Options for receiving data */
+ const int passive = 0,
+ spmr_expiry = 500 * 1000,
+ nak_bo_ivl = 200 * 1000,
+ nak_rpt_ivl = 500 * 1000,
+ nak_rdata_ivl = 500 * 1000,
+ nak_data_retries = 50,
+ nak_ncf_retries = 50;
+ qDebug() << "Computed window size " << max_window << " packets";
+
+// pgm_setsockopt(_priv->socket, PGM_RECV_ONLY, &recv_only, sizeof(recv_only));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_PASSIVE, &passive, sizeof(passive));
+// pgm_setsockopt(_priv->socket, PGM_RXW_MAX_RTE, &max_rate, sizeof(max_rate));
+// pgm_setsockopt(_priv->socket, PGM_RXW_SECS, &max_window, sizeof(max_window));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_SPMR_EXPIRY, &spmr_expiry, sizeof(spmr_expiry));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_BO_IVL, &nak_bo_ivl, sizeof(nak_bo_ivl));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_RPT_IVL, &nak_rpt_ivl, sizeof(nak_rpt_ivl));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_RDATA_IVL, &nak_rdata_ivl, sizeof(nak_rdata_ivl));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_DATA_RETRIES, &nak_data_retries, sizeof(nak_data_retries));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NAK_NCF_RETRIES, &nak_ncf_retries, sizeof(nak_ncf_retries));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_RXW_SQNS, &max_window, sizeof(max_window));
+
+ /* Try using PGMCC */
+ const struct pgm_pgmccinfo_t pgmccinfo = {
+ 100 /* usecs */ * 1000 /* msecs */,
+ 75 /* from OpenPGM examples */,
+ 500 /* from PGMCC internet-draft */
+ };
+ good = pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_USE_PGMCC, &pgmccinfo, sizeof(pgmccinfo));
+ if(!good)
+ {
+ qCritical() << "Could not enable PGMCC";
+ return false;
+ }
+
+// /* Forward Error Correction */
+// const struct pgm_fecinfo_t pgmfecinfo = {
+// 255 /* from OpenPGM examples */,
+// 2 /* send two proactive packets */,
+// 8 /* from OpenPGM examples */,
+// 1 /* enable on-demand parity */,
+// 1 /* enable variable packet length */
+// };
+// good = pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_USE_FEC, &pgmfecinfo, sizeof(pgmfecinfo));
+// if(!good)
+// {
+// qCritical() << "Could not enable FEC";
+// return false;
+// }
+
+ // Peer Expiry: We will give 1 minute.
+ int const peer_expiry = 60 /* seconds */ * 1000000 /* microseconds */;
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_PEER_EXPIRY, &peer_expiry, sizeof(peer_expiry));
+
+ // MTU
+ int const mtu = config->multicastMTU();
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MTU, &mtu, sizeof(mtu));
+
+ pgm_sockaddr_t addr;
+ addr.sa_addr.sport = config->multicastSPort();
+ addr.sa_port = config->multicastDPort();
+ good = pgm_gsi_create_from_hostname(&addr.sa_addr.gsi, &err);
+ if (!good)
+ {
+ qCritical() << "Could not generate a GSI: PGM Error: " << err->message;
+ pgm_error_free(err);
+ return false;
+ }
+
+ struct pgm_interface_req_t ifreq;
+ ifreq.ir_interface = addrinfo->ai_send_addrs[0].gsr_interface;
+ ifreq.ir_scope_id = 0;
+ if (AF_INET6 == family)
+ {
+ ifreq.ir_scope_id = ((struct sockaddr_in6*)&addrinfo->ai_send_addrs[0])->sin6_scope_id;
+ }
+
+ // UDP Encapsulation
+ if(config->multicastUseUDP())
+ {
+ const int uport = config->multicastUDPUPort();
+ const int mport = config->multicastUDPMPort();
+
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_UDP_ENCAP_MCAST_PORT, &mport, sizeof(mport));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_UDP_ENCAP_UCAST_PORT, &uport, sizeof(uport));
+ }
+
+ good = pgm_bind3(_priv->socket, &addr, sizeof(addr), &ifreq , sizeof(ifreq), &ifreq, sizeof(ifreq), &err);
+ if (!good)
+ {
+ qCritical() << "Could not bind socket: PGM Error: " << err->message;
+ pgm_error_free(err);
+ return false;
+ }
+
+ // qDebug() << "Max APDU is " << _priv->socket->max_apdu;
+ // qDebug() << "Max TPDU is " << _priv->socket->max_tpdu;
+ // qDebug() << "Max TSDU Fragment is " << _priv->socket->max_tsdu_fragment;
+ // qDebug() << "TXW_SQNS is " << _priv->socket->txw_sqns;
+
+ // join the group
+ for (unsigned i = 0; i < addrinfo->ai_recv_addrs_len; i++)
+ {
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_JOIN_GROUP,
+ &addrinfo->ai_recv_addrs[i], sizeof(struct group_req));
+ }
+
+ // set send address
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_SEND_GROUP, &addrinfo->ai_send_addrs[0],
+ sizeof(struct group_req));
+
+ // IP parameters
+ const int nonblocking = 1, multicast_loop = 0, multicast_hops = 16;
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MULTICAST_LOOP, &multicast_loop,
+ sizeof(multicast_loop));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_MULTICAST_HOPS, &multicast_hops,
+ sizeof(multicast_hops));
+ pgm_setsockopt(_priv->socket, IPPROTO_PGM, PGM_NOBLOCK, &nonblocking,
+ sizeof(nonblocking));
+
+ good = pgm_connect(_priv->socket, &err);
+ if (!good)
+ {
+ qCritical() << "Could not connect socket: PGM Error: " << err->message;
+ pgm_error_free(err);
+ return false;
+ }
+
+ _opened = true;
+
+ setupNotifiers();
+
+ pgm_freeaddrinfo(addrinfo);
+
+ /* Prime the generation of SPM packets during the waiting period */
+ if(_priv->direction == PSOCK_WRITE)
+ QTimer::singleShot(0, this, SLOT(handleNak()));
+
+ return true;
+}
+
+void McastPGMSocket::setupNotifiers()
+{
+ int recv_sock, repair_sock, pending_sock;
+ char const* slotname = (_priv->direction == PSOCK_WRITE) ? SLOT(handleNak(int)) : SLOT(handleData(int));
+
+ struct pollfd pollin[10];
+ int in_nfds = 10;
+ pgm_poll_info(_priv->socket, pollin, &in_nfds, POLLIN);
+ for(int i = 0; i < in_nfds; i++)
+ {
+ QSocketNotifier* notif = new QSocketNotifier(pollin[i].fd, QSocketNotifier::Read, this);
+ _priv->_notifs.append(notif);
+ connect(notif, SIGNAL(activated(int)), this, slotname);
+ }
+
+ if(_priv->direction == PSOCK_WRITE)
+ {
+ struct pollfd pfd;
+ int nfds = 1;
+ pgm_poll_info(_priv->socket, &pfd, &nfds, POLLOUT);
+ _priv->send_notif = new QSocketNotifier(pfd.fd, QSocketNotifier::Write, this);
+ connect(_priv->send_notif, SIGNAL(activated(int)), this, SLOT(canSend()));
+ }
+}
+
+void McastPGMSocket::handleNak(int fd)
+{
+ qDebug() << "handleNak(" << fd << ")";
+
+ QSocketNotifier* notif = _priv->notifier_for(fd);
+ notif->setEnabled(false);
+
+ if (_shutdownTimer)
+ {
+ _shutdownTimer->start(_shutdown_timeout);
+ qDebug() << "Started shutdown timer";
+ }
+
+ handleNak();
+
+ notif->setEnabled(true);
+}
+
+void McastPGMSocket::handleNak()
+{
+ if (_finished)
+ return;
+
+ // QTimer::singleShot(1000, this, SLOT(handleNakTimeout()));
+
+ // to handle NAKs in OpenPGM, we need to pgm_recv:
+ char buf[4096];
+ pgm_error_t* err = 0;
+
+ int status;
+ // while we don't block:
+ do
+ {
+ status = pgm_recv(_priv->socket, buf, sizeof(buf), MSG_DONTWAIT, 0, &err);
+
+ if(status == PGM_IO_STATUS_TIMER_PENDING)
+ {
+ struct timeval tv;
+ socklen_t size = sizeof(tv);
+ pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &size);
+ const long usecs = tv.tv_sec * 1000000 + tv.tv_usec;
+ int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if(msecs == 0)
+ msecs = 1;
+ qDebug() << " timer pending: " << usecs << "us (rounded to " << msecs << "ms)";
+ _nakTimeout->start(msecs);
+ break;
+ }
+ else if(status == PGM_IO_STATUS_RATE_LIMITED)
+ {
+ struct timeval tv;
+ socklen_t size = sizeof(tv);
+ pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size);
+ int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if(msecs == 0)
+ msecs = 1;
+ qDebug() << " rate limited: " << msecs << "ms";
+ _nakTimeout->start(msecs);
+ break;
+ }
+ else if(status == PGM_IO_STATUS_WOULD_BLOCK)
+ {
+ qDebug() << " wouldblock";
+ break;
+ }
+ else
+ {
+ if(err)
+ {
+ qCritical() << "Could not handle NAKs: PGM Error: " << err->message;
+ pgm_error_free(err);
+ err = 0;
+ }
+ }
+ }
+ while (true);
+}
+
+void McastPGMSocket::handleNakTimeout()
+{
+ qDebug() << "handleNakTimeout()";
+
+ handleNak();
+}
+
+void McastPGMSocket::handleData(int fd)
+{
+ // need to guard against destruction in finish() via signals/slots
+ QPointer<QSocketNotifier> notif(_priv->notifier_for(fd));
+ notif->setEnabled(false);
+
+ handleData();
+
+ if (notif)
+ notif->setEnabled(true);
+}
+
+void McastPGMSocket::handleData()
+{
+ if (_finished) {
+ return;
+ }
+
+ int status;
+ do
+ {
+ char buf[4096];
+ size_t size;
+ pgm_error_t* err = 0;
+
+ status = pgm_recv(_priv->socket, buf, sizeof(buf), MSG_DONTWAIT, &size, &err);
+
+ if (status == PGM_IO_STATUS_NORMAL)
+ {
+ qDebug() << " normally received";
+ if(size > 0)
+ {
+ QByteArray bytes(buf, size);
+ emit receivedPacket(bytes);
+ }
+ }
+ else if (status == PGM_IO_STATUS_WOULD_BLOCK)
+ {
+ qDebug() << " would block";
+ // nothing more to do this time
+ break;
+ }
+ else if (status == PGM_IO_STATUS_TIMER_PENDING)
+ {
+ struct timeval tv;
+ socklen_t size = sizeof(tv);
+ pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_TIME_REMAIN, &tv, &size);
+ const long usecs = tv.tv_sec * 1000000 + tv.tv_usec;
+ int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if(msecs == 0)
+ msecs = 1;
+ qDebug() << " timer pending: " << usecs << "us (rounded to " << msecs << "ms)";
+ _dataTimeout->start(msecs);
+ break;
+ }
+ else if (status == PGM_IO_STATUS_RATE_LIMITED)
+ {
+ struct timeval tv;
+ socklen_t size = sizeof(tv);
+ pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size);
+ int msecs = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ if(msecs == 0)
+ msecs = 1;
+ qDebug() << " rate limit pending: " << msecs << "ms";
+ _dataTimeout->start(msecs);
+ break;
+ }
+ else if (status == PGM_IO_STATUS_RESET)
+ {
+ qDebug() << " connection reset";
+ emit connectionReset();
+ qCritical() << "Connection Reset: PGM Error: " << (err ? err->message : "(null)");
+ pgm_error_free(err);
+ break;
+ }
+ else if (status == PGM_IO_STATUS_FIN)
+ {
+ qDebug() << " connection finished";
+ emit connectionFinished();
+ break;
+ }
+ else
+ {
+ if(err)
+ {
+ qCritical() << "Could not read packet: PGM Error: " << (err ? err->message: "(null)");
+ break;
+ }
+ }
+
+ // the socket might have been closed from under us
+ if (!_priv->socket)
+ break;
+ }
+ while (true);
+}
+
+void McastPGMSocket::handleDataTimeout()
+{
+ qDebug() << "handleDataTimeout()";
+
+ handleData();
+}
+
+void McastPGMSocket::canSend()
+{
+ if (_finished)
+ return;
+
+
+ if (_priv->send_notif)
+ {
+ _priv->send_notif->setEnabled(false);
+ }
+
+ if(_q.isEmpty())
+ {
+ emit readyToSend();
+ }
+ else
+ {
+ QByteArray const packet(_q.head());
+ int status;
+
+ status = pgm_send(_priv->socket, packet.constData(), packet.size(), 0);
+
+ if(status == PGM_IO_STATUS_NORMAL)
+ {
+ _q.dequeue();
+ if(!_q.isEmpty())
+ {
+ _priv->send_notif->setEnabled(true);
+ }
+ else
+ {
+ emit readyToSend();
+ }
+ }
+ else if(status == PGM_IO_STATUS_WOULD_BLOCK)
+ {
+ _priv->send_notif->setEnabled(true);
+ }
+ else if(status == PGM_IO_STATUS_CONGESTION)
+ {
+ qDebug() << " congested...";
+ // wait a short time (10ms?)
+ _sendTimeout->start(10);
+ }
+ else if(status == PGM_IO_STATUS_RATE_LIMITED)
+ {
+ struct timeval tv;
+ socklen_t size = sizeof(tv);
+ pgm_getsockopt(_priv->socket, IPPROTO_PGM, PGM_RATE_REMAIN, &tv, &size);
+ int msecs = (tv.tv_sec * 1000) + ((tv.tv_usec + 999) / 1000);
+ if(msecs == 0)
+ msecs = 1;
+ qDebug() << " rate_limited, waiting" << msecs << "ms";
+ _sendTimeout->start(msecs);
+ }
+ else
+ {
+ qCritical() << "Unhandled status in canSend():" << status;
+ }
+ }
+
+ if (_shutdownTimer)
+ _shutdownTimer->start(_shutdown_timeout);
+}
+
+void McastPGMSocket::sendPacket(QByteArray const& bytes)
+{
+ if(_shutdownTimer)
+ {
+ qCritical() << "Logic error: sendPacket() after shutdown()";
+ }
+
+ _q.enqueue(bytes);
+ _priv->send_notif->setEnabled(true);
+}
+
+void McastPGMSocket::finish()
+{
+ if (_finished)
+ {
+ return;
+ }
+
+ qDebug() << "finish()";
+
+ Q_FOREACH(QSocketNotifier* notif, _priv->_notifs)
+ {
+ notif->setEnabled(false);
+ delete notif;
+ }
+ _priv->_notifs.clear();
+
+ if(_priv->send_notif)
+ {
+ delete _priv->send_notif;
+ _priv->send_notif = 0;
+ }
+
+ if (_priv->socket)
+ {
+ pgm_close(_priv->socket, 1);
+ _priv->socket = 0;
+ }
+
+ _finished = true;
+
+ emit connectionFinished();
+
+ qDebug() << "Socket finished";
+}
+
+bool McastPGMSocket::finished() const
+{
+ return _finished;
+}
+
+bool McastPGMSocket::isOpen() const
+{
+ return _opened && !_finished;
+}
+
+void McastPGMSocket::shutdown(int interval)
+{
+ if(_priv->direction == PSOCK_READ)
+ return;
+
+ _shutdown_timeout = interval;
+ _shutdownTimer = new QTimer(this);
+ connect(_shutdownTimer, SIGNAL(timeout()), this, SLOT(finish()));
+ if (_q.isEmpty())
+ {
+ _shutdownTimer->start(_shutdown_timeout);
+ qDebug() << "Started shutdown timer";
+ }
+}
diff --git a/src/net/mcast/McastPGMSocket.h b/src/net/mcast/McastPGMSocket.h
new file mode 100644
index 0000000..4ccf931
--- /dev/null
+++ b/src/net/mcast/McastPGMSocket.h
@@ -0,0 +1,81 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastPGMSocket.h
+# - wrap OpenPGM Sockets in a nicer interface -- interface
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTPGMSOCKET_H_
+#define MCASTPGMSOCKET_H_
+
+#include <QByteArray>
+#include <QObject>
+#include <QQueue>
+
+#include <src/net/mcast/McastConfiguration.h>
+#include <src/net/mcast/McastConstants.h>
+
+class McastPGMSocket_priv;
+class QTimer;
+
+class McastPGMSocket : public QObject
+{
+ Q_OBJECT
+public:
+ enum Direction {
+ PSOCK_READ,
+ PSOCK_WRITE
+ };
+
+ McastPGMSocket(QObject* parent = 0);
+ virtual ~McastPGMSocket();
+
+ bool open(McastConfiguration const* config, Direction direction);
+ bool finished() const;
+ bool isOpen() const;
+ void shutdown(int interval = DEFAULT_MULTICAST_SHUTDOWN_TIMEOUT);
+
+signals:
+ void readyToSend();
+ void receivedPacket(QByteArray const& bytes);
+ void connectionReset();
+ void connectionFinished();
+ void shutdownComplete();
+
+public slots:
+ void sendPacket(QByteArray const& bytes);
+ void finish();
+
+private slots:
+ void handleNak(int fd);
+ void handleData(int fd);
+ void handleNak();
+ void handleData();
+ void handleNakTimeout();
+ void handleDataTimeout();
+ void canSend();
+
+private:
+ McastPGMSocket_priv* _priv;
+ QQueue<QByteArray> _q;
+ bool _finished;
+ bool _opened;
+ QTimer* _nakTimeout;
+ QTimer* _dataTimeout;
+ QTimer* _sendTimeout;
+ QTimer* _shutdownTimer;
+ int _shutdown_timeout;
+
+ void setupNotifiers();
+};
+
+#endif /* MCASTPGMSOCKET_H_ */
diff --git a/src/net/mcast/McastReceiver.cpp b/src/net/mcast/McastReceiver.cpp
new file mode 100644
index 0000000..1f27127
--- /dev/null
+++ b/src/net/mcast/McastReceiver.cpp
@@ -0,0 +1,179 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastReceiver.h
+# - implement the receiver-side multicast file transfer protocol -- implementation
+# -----------------------------------------------------------------------------
+*/
+
+#include <QDataStream>
+#include <QtDebug>
+#include <QtGlobal>
+
+#include <pgm/pgm.h>
+// OpenPGM #defines bool. This is bad in C++.
+#undef bool
+
+#include "McastConstants.h"
+#include "McastReceiver.h"
+
+McastReceiver::McastReceiver(QIODevice* iodev, McastConfiguration* config, QObject* parent) :
+ QObject(parent),
+ _config(config ? new McastConfiguration(*config) : 0),
+ _socket(0),
+ _curoffs(0),
+ _closed(false),
+ _hash(QCryptographicHash::Md5),
+ _iodev(iodev)
+{
+ _config->setParent(this);
+}
+
+McastReceiver::~McastReceiver()
+{
+ if (_config)
+ delete _config;
+}
+
+void McastReceiver::config(McastConfiguration const* config)
+{
+ if (_config)
+ delete _config;
+ _config = new McastConfiguration(*config, this);
+}
+
+bool McastReceiver::start()
+{
+ McastConfiguration *config = _config;
+ if (!config)
+ config = new McastConfiguration();
+
+ if (_socket)
+ {
+ delete _socket;
+ }
+ _socket = new McastPGMSocket(this);
+
+ connect(_socket, SIGNAL(receivedPacket(QByteArray)), SLOT(receivedPacket(QByteArray)));
+ connect(_socket, SIGNAL(connectionReset()), SLOT(connectionReset()));
+ // connect(_socket, SIGNAL(connectionFinished()), this, SLOT(connectionFinished()));
+ if (_socket->open(_config, McastPGMSocket::PSOCK_READ))
+ {
+ return true;
+ }
+ else
+ {
+ disconnect(_socket, SIGNAL(receivedPacket(QByteArray)), this, SLOT(receivedPacket(QByteArray)));
+ disconnect(_socket, SIGNAL(connectionReset()), this, SLOT(connectionReset()));
+ return false;
+ }
+}
+
+void McastReceiver::abort()
+{
+ if (_socket)
+ {
+ delete _socket;
+ _socket = 0;
+ }
+
+ if (_iodev)
+ {
+ _iodev->close();
+ }
+}
+
+void McastReceiver::receivedPacket(QByteArray const& bytes)
+{
+ if(_closed)
+ return;
+
+ quint16 checksum_should = qChecksum(bytes.constData(), bytes.size() - 2);
+
+ QDataStream strm(bytes);
+ strm.setByteOrder(QDataStream::BigEndian);
+
+ // read the packet
+ quint64 magic;
+ quint64 offset;
+ quint16 checksum;
+
+
+ strm >> magic;
+ if(magic != MCASTFT_MAGIC)
+ {
+ qCritical() << "Received packet whose magic number does not match. Ignoring.";
+ return;
+ }
+
+ strm >> offset;
+ qDebug() << " Received packet for offset" << offset;
+
+ if (offset == UINT64_C(0xffffffffffffffff))
+ {
+ // this is the end of the data stream.
+ QByteArray md5;
+ strm >> md5;
+
+ quint16 fchecksum;
+ strm >> fchecksum;
+
+ // compare the hash value
+ if ((fchecksum != checksum_should) || (md5 != _hash.result()))
+ {
+ _close(RES_MD5_MISMATCH);
+ }
+ else
+ {
+ _close(RES_OK);
+ }
+
+ return;
+ }
+ else if (offset != _curoffs)
+ {
+ qCritical() << "Packet loss or double delivery. PGM should have prevented this. Bailing out.";
+ _close(RES_OFFSET_MISMATCH);
+ return;
+ }
+
+ QByteArray contents;
+ strm >> contents;
+ _curoffs += contents.size();
+
+ strm >> checksum;
+ if(checksum != checksum_should)
+ {
+ qCritical() << "Checksum does not match. Bailing out.";
+ _close(RES_CHECKSUM_MISMATCH);
+ return;
+ }
+
+ _hash.addData(contents);
+
+ _iodev->write(contents);
+
+ emit progress(_curoffs);
+}
+
+void McastReceiver::connectionReset()
+{
+ _close(RES_CONNECTION_RESET);
+}
+
+void McastReceiver::_close(Result result)
+{
+ _iodev->close();
+ _socket->finish();
+
+ _closed = true;
+ emit finished(result);
+}
diff --git a/src/net/mcast/McastReceiver.h b/src/net/mcast/McastReceiver.h
new file mode 100644
index 0000000..247733d
--- /dev/null
+++ b/src/net/mcast/McastReceiver.h
@@ -0,0 +1,80 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastReceiver.h
+# - implement the receiver-side multicast file transfer protocol -- interface
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTRECEIVER_H_
+#define MCASTRECEIVER_H_
+
+#include <QByteArray>
+#include <QCryptographicHash>
+#include <QIODevice>
+#include <QObject>
+#include <QtGlobal>
+
+#include <src/net/mcast/McastConfiguration.h>
+#include <src/net/mcast/McastPGMSocket.h>
+
+class McastReceiver : public QObject
+{
+ Q_OBJECT
+public:
+ enum Result {
+ RES_OK,
+ RES_ABORTED,
+ RES_OFFSET_MISMATCH,
+ RES_CHECKSUM_MISMATCH,
+ RES_MD5_MISMATCH,
+ RES_CONNECTION_RESET
+ };
+
+ McastReceiver(QIODevice* iodev, McastConfiguration* config = 0, QObject* parent = 0);
+ virtual ~McastReceiver();
+
+ McastConfiguration* config()
+ {
+ return _config;
+ }
+
+ void config(McastConfiguration const* config);
+
+ static inline bool is_error(Result result)
+ {
+ return result != RES_OK;
+ }
+
+signals:
+ void finished(int result);
+ void progress(quint64 offset);
+
+public slots:
+ bool start();
+ void abort();
+
+private:
+ McastConfiguration* _config;
+ McastPGMSocket* _socket;
+ quint64 _curoffs;
+ bool _closed;
+ QCryptographicHash _hash;
+ QIODevice* _iodev;
+
+private slots:
+ void receivedPacket(QByteArray const& bytes);
+ void connectionReset();
+
+ void _close(Result result);
+};
+
+#endif /* MCASTRECEIVER_H_ */
diff --git a/src/net/mcast/McastSender.cpp b/src/net/mcast/McastSender.cpp
new file mode 100644
index 0000000..3fec6a4
--- /dev/null
+++ b/src/net/mcast/McastSender.cpp
@@ -0,0 +1,127 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastReceiver.h
+# - implement the receiver-side multicast file transfer protocol -- implementation
+# -----------------------------------------------------------------------------
+*/
+
+#include "McastSender.h"
+#include "McastConstants.h"
+
+#include <QDataStream>
+#include <QTimer>
+
+#include <pgm/pgm.h>
+// OpenPGM #defines bool. This is bad in C++.
+#undef bool
+
+#define MCASTFT_START_DEFER_TIME 2000 /* msec */
+
+McastSender::McastSender(QIODevice* iodev, McastConfiguration const* config, QObject* parent) :
+ QObject(parent),
+ _config(config ? new McastConfiguration(*config) : new McastConfiguration()),
+ _socket(0),
+ _iodev(iodev),
+ _curoffs(0),
+ _hash(QCryptographicHash::Md5),
+ _finished(false)
+{
+}
+
+McastSender::~McastSender()
+{
+ delete _config;
+}
+
+void McastSender::start()
+{
+ _socket = new McastPGMSocket(this);
+ connect(_socket, SIGNAL(readyToSend()), this, SLOT(deferredStart()));
+ _socket->open(_config, McastPGMSocket::PSOCK_WRITE);
+}
+
+void McastSender::start(McastPGMSocket* socket)
+{
+ _socket = socket;
+ Q_ASSERT(_socket->isOpen());
+ deferredStart();
+}
+
+void McastSender::deferredStart()
+{
+ // Wait some time, to give the PGM library the chance to generate some
+ // undisturbed SPM messages:
+ QTimer::singleShot(MCASTFT_START_DEFER_TIME, this, SLOT(readyToSend()));
+ disconnect(_socket, SIGNAL(readyToSend()), this, SLOT(deferredStart()));
+ connect(_socket, SIGNAL(readyToSend()), this, SLOT(readyToSend()));
+}
+
+void McastSender::readyToSend()
+{
+ if(_finished)
+ return;
+
+ if(_iodev->atEnd())
+ {
+ QByteArray fpdu;
+ QDataStream strm(&fpdu, QIODevice::WriteOnly);
+ strm.setByteOrder(QDataStream::BigEndian);
+
+ strm << (quint64)MCASTFT_MAGIC << (quint64)UINT64_C(0xffffffffffffffff) << _hash.result();
+ strm << qChecksum(fpdu.constData(), fpdu.size());
+
+ _socket->sendPacket(fpdu);
+ connect(_socket, SIGNAL(connectionFinished()), this, SLOT(socketFinished()));
+ _socket->shutdown();
+
+ _finished = true;
+
+ _iodev->close();
+
+ emit allSent();
+ }
+ else
+ {
+ QByteArray barr(DEFAULT_MULTICAST_APDU, '\0');
+ qint64 len_read;
+ len_read = _iodev->read(barr.data(), barr.capacity());
+ barr.resize((int)len_read);
+
+ _hash.addData(barr);
+
+ QByteArray pdu;
+ QDataStream strm(&pdu, QIODevice::WriteOnly);
+ strm.setByteOrder(QDataStream::BigEndian);
+
+ strm << (quint64)MCASTFT_MAGIC << _curoffs;
+ strm << barr;
+ quint16 checksum = qChecksum(pdu.constData(), pdu.size());
+ strm << checksum;
+
+ _curoffs += len_read;
+
+ _socket->sendPacket(pdu);
+
+ emit progress(_curoffs);
+ }
+}
+
+void McastSender::close()
+{
+ _socket->finish();
+}
+
+void McastSender::socketFinished()
+{
+ _socket->deleteLater();
+ emit finished();
+}
diff --git a/src/net/mcast/McastSender.h b/src/net/mcast/McastSender.h
new file mode 100644
index 0000000..0c5e29f
--- /dev/null
+++ b/src/net/mcast/McastSender.h
@@ -0,0 +1,73 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/McastReceiver.h
+# - implement the sender-side multicast file transfer protocol -- interface
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTSENDER_H_
+#define MCASTSENDER_H_
+
+#include <QCryptographicHash>
+#include <QIODevice>
+#include <QObject>
+
+#include "McastConfiguration.h"
+#include "McastPGMSocket.h"
+
+class McastSender : public QObject
+{
+ Q_OBJECT
+public:
+ McastSender(QIODevice* iodev = 0, McastConfiguration const* config = 0, QObject* parent = 0);
+ virtual ~McastSender();
+
+ McastConfiguration* config()
+ {
+ return _config;
+ }
+
+ QIODevice* iodevice() const
+ {
+ return _iodev;
+ }
+
+ void setIODevice(QIODevice* iodevice)
+ {
+ _iodev = iodevice;
+ }
+
+signals:
+ void finished();
+ void progress(quint64 offset);
+ void allSent();
+
+public slots:
+ void start();
+ void start(McastPGMSocket* openSocket);
+ void close();
+
+private slots:
+ void deferredStart();
+ void readyToSend();
+ void socketFinished();
+
+private:
+ McastConfiguration* _config;
+ McastPGMSocket* _socket;
+ QIODevice* _iodev;
+ quint64 _curoffs;
+ QCryptographicHash _hash;
+ bool _finished;
+};
+
+#endif /* MCASTSENDER_H_ */
diff --git a/src/net/mcast/trial_programs/CMakeLists.txt b/src/net/mcast/trial_programs/CMakeLists.txt
new file mode 100644
index 0000000..d0f68fa
--- /dev/null
+++ b/src/net/mcast/trial_programs/CMakeLists.txt
@@ -0,0 +1,38 @@
+INCLUDE(${QT_USE_FILE})
+
+QT4_WRAP_CPP(
+ mcastsend_MOC
+ mcastsend.h
+)
+
+QT4_WRAP_CPP(
+ mcastreceive_MOC
+ mcastreceive.h
+)
+
+SET(argparser_SRC
+ McastConfigArgParser.h
+ McastConfigArgParser.cpp
+)
+
+ADD_EXECUTABLE(mcastsend
+ mcastsend.cpp
+ mcastsend.h
+ ${argparser_SRC}
+ ${mcastsend_MOC}
+)
+
+ADD_EXECUTABLE(mcastreceive
+ mcastreceive.cpp
+ mcastreceive.h
+ ${argparser_SRC}
+ ${mcastreceive_MOC}
+)
+
+TARGET_LINK_LIBRARIES(mcastsend
+ pvsmcast
+)
+
+TARGET_LINK_LIBRARIES(mcastreceive
+ pvsmcast
+) \ No newline at end of file
diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.cpp b/src/net/mcast/trial_programs/McastConfigArgParser.cpp
new file mode 100644
index 0000000..881f728
--- /dev/null
+++ b/src/net/mcast/trial_programs/McastConfigArgParser.cpp
@@ -0,0 +1,165 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/McastConfigArgParser.cpp
+# - Parse common Multicast Configuration CLI arguments
+# -----------------------------------------------------------------------------
+*/
+
+#include <iostream>
+
+#include <QCoreApplication>
+
+#include "McastConfigArgParser.h"
+
+using namespace std;
+
+bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& end, McastConfiguration* config)
+{
+ QString arg = *i;
+
+ if (arg == "-addr")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ config->multicastAddress(*i);
+ }
+ else if (arg == "-dport")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint16 dport = (quint16)i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: dport is not an integer" << endl;
+ return false;
+ }
+ config->multicastDPort(dport);
+ }
+ else if (arg == "-sport")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint16 sport = (quint16)i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: sport is not an integer" << endl;
+ return false;
+ }
+ config->multicastSPort(sport);
+ }
+ else if (arg == "-mtu")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint16 mtu = (quint16)i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: MTU is not an integer" << endl;
+ return false;
+ }
+ config->multicastMTU(mtu);
+ }
+ else if (arg == "-rate")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint32 rate = i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: Rate is not an integer" << endl;
+ return false;
+ }
+ config->multicastRate(rate);
+ }
+ else if (arg == "-winsize")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint16 winsize = (quint16)i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: Winsize is not an integer" << endl;
+ return false;
+ }
+ config->multicastWinSize(winsize);
+ }
+ else if (arg == "-udp")
+ {
+ config->multicastUseUDP(true);
+ }
+ else if (arg == "-no-udp")
+ {
+ config->multicastUseUDP(false);
+ }
+ else if (arg == "-udp-port")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ return false;
+ }
+ bool ok;
+ quint16 udpport = (quint16)i->toInt(&ok);
+ if (!ok)
+ {
+ cerr << "Error: UDP-Port is not an integer" << endl;
+ return false;
+ }
+ config->multicastUDPPortBase(udpport);
+ }
+ else if (arg == "-intf")
+ {
+ i++;
+ if (i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << "is missing argument" << endl;
+ return false;
+ }
+ config->multicastInterface(*i);
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/net/mcast/trial_programs/McastConfigArgParser.h b/src/net/mcast/trial_programs/McastConfigArgParser.h
new file mode 100644
index 0000000..4fb18a7
--- /dev/null
+++ b/src/net/mcast/trial_programs/McastConfigArgParser.h
@@ -0,0 +1,26 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/McastConfigArgParser.h
+# - Parse common Multicast Configuration CLI arguments
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTCONFIGARGPARSER_H_
+#define MCASTCONFIGARGPARSER_H_
+
+#include <QStringList>
+
+#include "../McastConfiguration.h"
+
+bool parseMcastConfigArg(QStringList::iterator& i, QStringList::iterator const& end, McastConfiguration* config);
+
+#endif /* MCASTCONFIGARGPARSER_H_ */
diff --git a/src/net/mcast/trial_programs/mcastreceive.cpp b/src/net/mcast/trial_programs/mcastreceive.cpp
new file mode 100644
index 0000000..48a0f10
--- /dev/null
+++ b/src/net/mcast/trial_programs/mcastreceive.cpp
@@ -0,0 +1,150 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/mcastsend.cpp
+# - Receive a file via the PVS Mcast protocol
+# -----------------------------------------------------------------------------
+*/
+
+#include <iostream>
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QStringList>
+#include <QTimer>
+
+#include "mcastreceive.h"
+#include "McastConfigArgParser.h"
+#include "../McastConfiguration.h"
+#include "../McastReceiver.h"
+
+using namespace std;
+
+int main(int argc, char** argv)
+{
+ QCoreApplication app(argc, argv);
+ McastReceive me;
+
+ QTimer::singleShot(0, &me, SLOT(run()));
+
+ return app.exec();
+}
+
+void McastReceive::run()
+{
+ QStringList args = QCoreApplication::arguments();
+ QStringList::iterator i = args.begin();
+ QStringList::iterator const end = args.end();
+
+ QString filename("");
+
+ McastConfiguration config;
+
+ ++i;
+ while (i != end)
+ {
+ QString arg = *i;
+
+ cerr << "Arg: " << arg.toLatin1().constData() << endl;
+
+ if (arg == "-file")
+ {
+ ++i;
+ if (i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing its argument" << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+ filename = *i;
+ }
+ else if (arg == "-help")
+ {
+ cerr << "Options:" << endl << endl
+ << " -file <FILE> Receive to file FILE" << endl
+ << " -addr <ADDR> Use ADDR as address specification" << endl
+ << " -dport <PORT> Send to port PORT" << endl
+ << " -sport <PORT> Send from port PORT" << endl
+ << " -mtu <BYTES> Set MTU to BYTES" << endl
+ << " -rate <BYTES> Send BYTES per second" << endl
+ << " -winsize <SECONDS> Set Window Size to SECONDS" << endl
+ << " -udp Use UDP encapsulation" << endl
+ << " -udp-port PORT Use UDP port PORT" << endl;
+ QCoreApplication::quit();
+ return;
+ }
+ else
+ {
+ if (!parseMcastConfigArg(i, end, &config))
+ {
+ cerr << "Unknown argument: " << arg.toLatin1().constData() << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+ }
+
+ ++i;
+ }
+
+ if (filename == "")
+ {
+ cerr << "No Filename given" << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+
+ _target = new QFile(filename, this);
+ _target->open(QIODevice::WriteOnly);
+
+ McastReceiver* recv = new McastReceiver(_target, &config, this);
+
+ connect(recv, SIGNAL(finished(int)), this, SLOT(finished(int)));
+
+ QTimer::singleShot(0, recv, SLOT(start()));
+}
+
+void McastReceive::finished(int state)
+{
+ cerr << "finished: ";
+
+ switch(state)
+ {
+ case McastReceiver::RES_OK:
+ cerr << "OK." << endl;
+ break;
+ case McastReceiver::RES_ABORTED:
+ cerr << "Aborted." << endl;
+ goto failed;
+ case McastReceiver::RES_CHECKSUM_MISMATCH:
+ cerr << "Checksum mismatch." << endl;
+ goto failed;
+ case McastReceiver::RES_CONNECTION_RESET:
+ cerr << "Connection reset." << endl;
+ goto failed;
+ case McastReceiver::RES_MD5_MISMATCH:
+ cerr << "MD5 mismatch." << endl;
+ goto failed;
+ case McastReceiver::RES_OFFSET_MISMATCH:
+ cerr << "Offset mismatch. Undetected packet loss?" << endl;
+ goto failed;
+ default:
+ cerr << "Unknown error code!" << endl;
+ goto failed;
+ }
+
+ QCoreApplication::quit();
+ return;
+failed:
+ cerr << "Deleting file." << endl;
+ _target->remove();
+ QCoreApplication::exit(1);
+ return;
+}
diff --git a/src/net/mcast/trial_programs/mcastreceive.h b/src/net/mcast/trial_programs/mcastreceive.h
new file mode 100644
index 0000000..3e72d4c
--- /dev/null
+++ b/src/net/mcast/trial_programs/mcastreceive.h
@@ -0,0 +1,44 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/mcastsend.cpp
+# - Receive a file via the PVS Mcast protocol
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTRECEIVE_H_
+#define MCASTRECEIVE_H_
+
+#include <QObject>
+
+class QFile;
+class McastReceiver;
+
+class McastReceive : public QObject
+{
+ Q_OBJECT
+public:
+ McastReceive() :
+ QObject(),
+ _receiver(0)
+ {
+ }
+
+public slots:
+ void run();
+ void finished(int state);
+
+private:
+ McastReceiver* _receiver;
+ QFile* _target;
+};
+
+#endif /* MCASTRECEIVE_H_ */
diff --git a/src/net/mcast/trial_programs/mcastsend.cpp b/src/net/mcast/trial_programs/mcastsend.cpp
new file mode 100644
index 0000000..f78a9ce
--- /dev/null
+++ b/src/net/mcast/trial_programs/mcastsend.cpp
@@ -0,0 +1,122 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/mcastsend.cpp
+# - Send a file via the PVS Mcast protocol
+# -----------------------------------------------------------------------------
+*/
+
+#include <iostream>
+
+#include <QCoreApplication>
+#include <QFile>
+#include <QStringList>
+#include <QTimer>
+
+#include <src/net/mcast/McastSender.h>
+#include "mcastsend.h"
+#include "McastConfigArgParser.h"
+#include "../McastConstants.h"
+#include "../McastConfiguration.h"
+
+using namespace std;
+
+int
+main(int argc, char**argv)
+{
+ QCoreApplication app(argc, argv);
+ McastSend me;
+
+ QTimer::singleShot(0, &me, SLOT(run()));
+
+ return app.exec();
+}
+
+void McastSend::run()
+{
+ QStringList args = QCoreApplication::arguments();
+ QStringList::iterator i = args.begin();
+ QStringList::iterator const end = args.end();
+ QString filename("");
+ McastConfiguration config;
+
+ ++i;
+ while(i != end)
+ {
+ // parse command line arguments
+
+ QString arg = *i;
+
+ cerr << "Arg: " << arg.toLatin1().constData() << endl;
+
+ if (arg == "-file")
+ {
+ i++;
+ if(i == end)
+ {
+ cerr << "Option " << arg.toLatin1().constData() << " is missing argument" << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+ filename = *i;
+ }
+ else if (arg == "-help")
+ {
+ cerr << "Options:" << endl << endl
+ << " -file <FILE> Send FILE to the listeners" << endl
+ << " -addr <ADDR> Use ADDR as address specification" << endl
+ << " -dport <PORT> Send to port PORT" << endl
+ << " -sport <PORT> Send from port PORT" << endl
+ << " -mtu <BYTES> Set MTU to BYTES" << endl
+ << " -rate <BYTES> Send BYTES per second" << endl
+ << " -winsize <SECONDS> Set Window Size to SECONDS" << endl
+ << " -udp Use UDP encapsulation" << endl
+ << " -udp-port PORT Use UDP port PORT" << endl;
+ QCoreApplication::quit();
+ return;
+ }
+ else
+ {
+ if (!parseMcastConfigArg(i, end, &config))
+ {
+ cerr << "Unknown command line argument: " << arg.toLatin1().constData() << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+ }
+
+ ++i;
+ }
+
+ if(filename == "")
+ {
+ cerr << "No filename given" << endl;
+ QCoreApplication::exit(1);
+ return;
+ }
+
+ // now, do it.
+ QFile* file = new QFile(filename);
+ file->open(QIODevice::ReadOnly);
+
+ McastSender* sender = new McastSender(file, &config, this);
+ file->setParent(sender);
+
+ connect(sender, SIGNAL(finished()), this, SLOT(finished()));
+
+ QTimer::singleShot(0, sender, SLOT(start()));
+}
+
+void McastSend::finished()
+{
+ cerr << "finished." << endl;
+ QCoreApplication::quit();
+}
diff --git a/src/net/mcast/trial_programs/mcastsend.h b/src/net/mcast/trial_programs/mcastsend.h
new file mode 100644
index 0000000..ae15eb4
--- /dev/null
+++ b/src/net/mcast/trial_programs/mcastsend.h
@@ -0,0 +1,42 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/mcast/trial_programs/mcastsend.cpp
+# - Send a file via the PVS Mcast protocol
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef MCASTSEND_H_
+#define MCASTSEND_H_
+
+#include <QObject>
+
+#include "../McastSender.h"
+
+class McastSend : public QObject
+{
+ Q_OBJECT
+public:
+ McastSend() :
+ QObject(),
+ _sender(0)
+ {
+ }
+
+public slots:
+ void run();
+ void finished();
+
+private:
+ McastSender* _sender;
+};
+
+#endif /* MCASTSEND_H_ */
diff --git a/src/net/pvsIncomingMulticastTransfer.cpp b/src/net/pvsIncomingMulticastTransfer.cpp
new file mode 100644
index 0000000..10b5307
--- /dev/null
+++ b/src/net/pvsIncomingMulticastTransfer.cpp
@@ -0,0 +1,133 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/pcsIncomingMulticastTransfer.h
+# - wrap McastReceiver functionality in PVS daemon
+# -----------------------------------------------------------------------------
+*/
+
+#include <QDir>
+#include <QTemporaryFile>
+#include <QTimer>
+
+#include "pvsIncomingMulticastTransfer.h"
+#include <src/net/mcast/McastReceiver.h>
+
+PVSIncomingMulticastTransfer::PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, QString const& filename,
+ ushort port, McastConfiguration const* configTemplate, QObject* parent) :
+ QObject(parent),
+ _sender(sender),
+ _transferID(transferID),
+ _bytes(0),
+ _size(size),
+ _port(port),
+ _file(new QFile(filename, this)),
+ _receiver(0),
+ _config(configTemplate ?
+ new McastConfiguration(*configTemplate) :
+ new McastConfiguration()),
+ _progressTimer(new QTimer(this))
+{
+ _file->open(QIODevice::WriteOnly);
+
+ _config->multicastUDPPortBase(port);
+ // _config->multicastDPort(port+1);
+ // _config->multicastSPort(port+2);
+
+ connect(_progressTimer, SIGNAL(timeout()), SLOT(updateProgress()));
+ connect(this, SIGNAL(failed(qulonglong, QString const&)), SLOT(removeFile()));
+}
+
+PVSIncomingMulticastTransfer::~PVSIncomingMulticastTransfer()
+{
+ // TODO Auto-generated destructor stub
+}
+
+bool PVSIncomingMulticastTransfer::start()
+{
+ _file->open(QIODevice::WriteOnly);
+ _receiver = new McastReceiver(_file, new McastConfiguration(*_config), this);
+ connect(_receiver, SIGNAL(finished(int)), SLOT(receiverFinished(int)));
+ connect(_receiver, SIGNAL(progress(quint64)), SLOT(receiverProgressed(quint64)));
+
+ if (!_receiver->start())
+ {
+ emit retry(_sender, _transferID);
+ return false;
+ }
+ else
+ {
+ _progressTimer->start(333);
+ return true;
+ }
+}
+
+void PVSIncomingMulticastTransfer::abort()
+{
+ delete _receiver;
+ _receiver = 0;
+
+ delete _progressTimer;
+ _progressTimer = 0;
+
+ if(_file)
+ delete _file;
+}
+
+void PVSIncomingMulticastTransfer::updatePort(ushort port)
+{
+ _config->multicastUDPPortBase(port);
+ _config->multicastSPort(port);
+ _config->multicastDPort(port);
+}
+
+void PVSIncomingMulticastTransfer::receiverProgressed(quint64 bytes)
+{
+ _bytes = bytes;
+}
+
+void PVSIncomingMulticastTransfer::receiverFinished(int how)
+{
+ switch(how)
+ {
+ case McastReceiver::RES_OK:
+ emit finished(_transferID);
+ break;
+ case McastReceiver::RES_ABORTED:
+ emit failed(_transferID, tr("Aborted"));
+ break;
+ case McastReceiver::RES_MD5_MISMATCH:
+ case McastReceiver::RES_CHECKSUM_MISMATCH:
+ emit failed(_transferID, tr("Unrecoverable data corruption"));
+ break;
+ case McastReceiver::RES_CONNECTION_RESET:
+ emit failed(_transferID, tr("Connection was reset"));
+ break;
+ case McastReceiver::RES_OFFSET_MISMATCH:
+ emit failed(_transferID, tr("Unrecoverable data loss. Try a lower transfer rate"));
+ break;
+ }
+}
+
+void PVSIncomingMulticastTransfer::removeFile()
+{
+ if(_file)
+ _file->remove();
+}
+
+void PVSIncomingMulticastTransfer::updateProgress()
+{
+ if (!_started)
+ {
+ emit started(_transferID);
+ }
+ emit progress(_transferID, _bytes, _size);
+}
diff --git a/src/net/pvsIncomingMulticastTransfer.h b/src/net/pvsIncomingMulticastTransfer.h
new file mode 100644
index 0000000..f96e176
--- /dev/null
+++ b/src/net/pvsIncomingMulticastTransfer.h
@@ -0,0 +1,71 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/pcsIncomingMulticastTransfer.h
+# - wrap McastReceiver functionality in PVS daemon
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef PVSINCOMINGMULTICASTTRANSFER_H_
+#define PVSINCOMINGMULTICASTTRANSFER_H_
+
+#include <QObject>
+#include <QString>
+
+#include <src/net/pvsMsg.h>
+
+class McastConfiguration;
+class McastReceiver;
+class QFile;
+class QTimer;
+
+class PVSIncomingMulticastTransfer : public QObject
+{
+ Q_OBJECT
+public:
+ PVSIncomingMulticastTransfer(QString const& sender, qulonglong transferID, qulonglong size, QString const& filename, ushort port,
+ McastConfiguration const* configTemplate, QObject* parent = 0);
+ virtual ~PVSIncomingMulticastTransfer();
+
+ void setFinalFile(QString const& filename);
+
+signals:
+ void retry(QString const& sender, qulonglong transferID);
+ void started(qulonglong transferID);
+ void progress(qulonglong transferID, qulonglong bytes, qulonglong of);
+ void finished(qulonglong transferID);
+ void failed(qulonglong transferID, QString const& reason);
+
+public slots:
+ void updatePort(ushort port);
+ bool start();
+ void abort();
+
+private slots:
+ void receiverProgressed(quint64 bytes);
+ void receiverFinished(int reason);
+ void updateProgress();
+ void removeFile();
+
+private:
+ QString _sender;
+ qulonglong _transferID;
+ qulonglong _bytes;
+ qulonglong _size;
+ ushort _port;
+ QFile* _file;
+ McastReceiver* _receiver;
+ McastConfiguration* _config;
+ bool _started;
+ QTimer* _progressTimer;
+};
+
+#endif /* PVSINCOMINGMULTICASTTRANSFER_H_ */
diff --git a/src/net/pvsListenServer.cpp b/src/net/pvsListenServer.cpp
index 58d4aee..90c2dfb 100644
--- a/src/net/pvsListenServer.cpp
+++ b/src/net/pvsListenServer.cpp
@@ -21,9 +21,13 @@
#include "pvsClientConnection.h"
#include "src/util/consoleLogger.h"
#include <QtNetwork/QSslSocket>
+#include <QBuffer>
+#include <QByteArray>
+#include <QSettings>
#include "SslServer.h"
#include <cassert>
//#define verbose
+#include "mcast/McastConfiguration.h"
// Create listener
PVSListenServer::PVSListenServer(int port, int clients)
@@ -36,6 +40,7 @@ PVSListenServer::PVSListenServer(int port, int clients)
else
_clientsMax = clients;
_port = port;
+ _mcastConfig = 0;
init();
}
@@ -178,6 +183,7 @@ void PVSListenServer::onClientConnected(PVSClientConnection* connected)
{
connected->setServerID(_id);
connected->setID(generateID());
+ connected->push_back_send(mcastConfigMessage());
}
void PVSListenServer::onClientDisconnected(PVSClientConnection* disconnected)
@@ -227,6 +233,11 @@ PVSClientConnection* PVSListenServer::getConnectionFromID(int id)
// Initialize listening socket
bool PVSListenServer::init()
{
+ if (_mcastConfig)
+ delete _mcastConfig;
+ _mcastConfig = new McastConfiguration(this);
+ loadMcastConfig();
+
if (_listenSocket != NULL)
shutdown();
@@ -305,3 +316,52 @@ bool PVSListenServer::isListening()
{
return _listenSocket != NULL && _listenSocket->isListening();
}
+
+void PVSListenServer::loadMcastConfig()
+{
+ QSettings settings;
+ _mcastConfig->loadFrom(&settings, "multicast-filetransfer");
+}
+
+void PVSListenServer::saveMcastConfig()
+{
+ QSettings settings;
+ _mcastConfig->writeTo(&settings, "multicast-filetransfer");
+ settings.sync();
+}
+
+PVSMsg PVSListenServer::mcastConfigMessage()
+{
+ // If anything is changed here, do not forget to
+ // 1. assign a new version number
+ // 2. adapt PVS::onCommand(PVSMsg) in pvs.cpp
+ QByteArray ba;
+ QDataStream strm(&ba, QIODevice::WriteOnly);
+ strm << (quint16)1 // version
+ << _mcastConfig->multicastAddress()
+ << _mcastConfig->multicastUDPPortBase()
+ << _mcastConfig->multicastDPort()
+ << _mcastConfig->multicastSPort()
+ << _mcastConfig->multicastMTU()
+ << _mcastConfig->multicastWinSize()
+ << _mcastConfig->multicastRate()
+ << _mcastConfig->multicastUseUDP();
+
+ QByteArray b64 = ba.toBase64();
+ QString message = QString::fromAscii(b64.constData(), b64.length());
+ PVSMsg msg(PVSCOMMAND, "MCASTFTCONFIG", message);
+ return msg;
+}
+
+void PVSListenServer::multicastReconfigure(McastConfiguration const* source)
+{
+ _mcastConfig->multicastAddress(source->multicastAddress());
+ *_mcastConfig = *source;
+ saveMcastConfig();
+ sendToAll(mcastConfigMessage());
+}
+
+McastConfiguration const* PVSListenServer::getMulticastConfiguration()
+{
+ return _mcastConfig;
+}
diff --git a/src/net/pvsListenServer.h b/src/net/pvsListenServer.h
index b43b730..ca9977c 100644
--- a/src/net/pvsListenServer.h
+++ b/src/net/pvsListenServer.h
@@ -10,6 +10,7 @@ class SslServer;
class PVSClientConnection;
class PVSMsg;
+class McastConfiguration;
class PVSListenServer : public QObject
{
@@ -40,6 +41,12 @@ private:
bool init();
unsigned int generateID();
+ McastConfiguration* _mcastConfig;
+
+ void loadMcastConfig();
+ void saveMcastConfig();
+ PVSMsg mcastConfigMessage();
+
protected:
void timerEvent(QTimerEvent *event);
@@ -60,6 +67,9 @@ public:
bool disconnectClient(PVSClientConnection* delinquent);
void onConnectionRemoved(PVSClientConnection* delinquent);
+ void multicastReconfigure(McastConfiguration const* source);
+ McastConfiguration const* getMulticastConfiguration();
+
std::list<PVSClientConnection*>* getClientListPtr()
{
return &_clients;
diff --git a/src/net/pvsNetworkInterfaceListModel.cpp b/src/net/pvsNetworkInterfaceListModel.cpp
new file mode 100644
index 0000000..67d0c0a
--- /dev/null
+++ b/src/net/pvsNetworkInterfaceListModel.cpp
@@ -0,0 +1,81 @@
+/*
+ * pvsNetworkInterfaceListModel.cpp
+ *
+ * Created on: 04.08.2010
+ * Author: brs
+ */
+
+#include "pvsNetworkInterfaceListModel.h"
+#include <QStringList>
+
+PVSNetworkInterfaceListModel::PVSNetworkInterfaceListModel(QObject* parent) :
+ QAbstractListModel(parent)
+{
+ reloadInterfaceList();
+}
+
+PVSNetworkInterfaceListModel::~PVSNetworkInterfaceListModel()
+{
+}
+
+void PVSNetworkInterfaceListModel::reloadInterfaceList()
+{
+ _interfaces = QNetworkInterface::allInterfaces();
+ reset();
+}
+
+QVariant PVSNetworkInterfaceListModel::data(QModelIndex const& index, int role) const
+{
+ int i = index.row();
+ if(0 > i || i >= _interfaces.size())
+ {
+ return QVariant();
+ }
+ QNetworkInterface intf = _interfaces.at(i);
+
+ switch(role)
+ {
+ case Qt::DisplayRole:
+ {
+ QString name = intf.humanReadableName();
+ QList<QNetworkAddressEntry> addresses = intf.addressEntries();
+ QStringList l;
+
+ foreach(QNetworkAddressEntry addr, addresses)
+ {
+ l.append(addr.ip().toString());
+ }
+
+ return QString("%1 (%2)").arg(name).arg(l.join(", "));
+ }
+ case Qt::EditRole:
+ case Qt::UserRole:
+ return intf.name();
+ default:
+ return QVariant();
+ }
+}
+
+QVariant PVSNetworkInterfaceListModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+ if(section == 0 && orientation == Qt::Vertical && role == Qt::DisplayRole)
+ {
+ return tr("Interface");
+ }
+ else
+ {
+ return QVariant();
+ }
+}
+
+int PVSNetworkInterfaceListModel::rowCount(QModelIndex const& parent) const
+{
+ if(parent.isValid())
+ {
+ return 0;
+ }
+ else
+ {
+ return _interfaces.size();
+ }
+}
diff --git a/src/net/pvsNetworkInterfaceListModel.h b/src/net/pvsNetworkInterfaceListModel.h
new file mode 100644
index 0000000..3a9b95d
--- /dev/null
+++ b/src/net/pvsNetworkInterfaceListModel.h
@@ -0,0 +1,35 @@
+/*
+ * pvsNetworkInterfaceListModel.h
+ *
+ * Created on: 04.08.2010
+ * Author: brs
+ */
+
+#ifndef PVSNETWORKINTERFACELISTMODEL_H_
+#define PVSNETWORKINTERFACELISTMODEL_H_
+
+#include <QAbstractListModel>
+#include <QList>
+#include <QNetworkInterface>
+#include <QVariant>
+
+class PVSNetworkInterfaceListModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+public:
+ PVSNetworkInterfaceListModel(QObject* parent = 0);
+ virtual ~PVSNetworkInterfaceListModel();
+
+ QVariant data(QModelIndex const& index, int role = Qt::DisplayRole) const;
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+ int rowCount(QModelIndex const&) const;
+
+private:
+ QList<QNetworkInterface> _interfaces;
+
+public slots:
+ void reloadInterfaceList();
+};
+
+#endif /* PVSNETWORKINTERFACELISTMODEL_H_ */
diff --git a/src/net/pvsOutgoingMulticastTransfer.cpp b/src/net/pvsOutgoingMulticastTransfer.cpp
new file mode 100644
index 0000000..4df4986
--- /dev/null
+++ b/src/net/pvsOutgoingMulticastTransfer.cpp
@@ -0,0 +1,209 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/pvsOutgoingMulticastTransfer.cpp
+# - wrap McastSender functionality in PVS daemon
+# -----------------------------------------------------------------------------
+*/
+
+#include "pvsOutgoingMulticastTransfer.h"
+
+#include <QDataStream>
+#include <QFile>
+#include <QFileInfo>
+#include <QHostInfo>
+#include <QSettings>
+#include <QTimer>
+
+#include <src/net/pvsMsg.h>
+#include <src/net/mcast/McastConfiguration.h>
+#include <src/net/mcast/McastPGMSocket.h>
+#include <src/net/mcast/McastSender.h>
+#include <src/util/consoleLogger.h>
+
+PVSOutgoingMulticastTransfer::PVSOutgoingMulticastTransfer(QString senderName, quint64 id, QString filename, QObject* parent) :
+ QObject(parent),
+ _file(0),
+ _progress(0),
+ _config(0),
+ _socket(0),
+ _progressTimer(0),
+ _prepareTimer(0),
+ _senderName(senderName),
+ _id(id),
+ _error(false)
+{
+ QFileInfo finfo(filename);
+ if(!finfo.exists())
+ {
+ error("File does not exist");
+ }
+
+ if(!finfo.isReadable())
+ {
+ error("File is not readable");
+ }
+
+ _file = new QFile(filename, this);
+ _length = _file->size();
+}
+
+PVSOutgoingMulticastTransfer::~PVSOutgoingMulticastTransfer()
+{
+ if(_file)
+ delete _file;
+ if(_config)
+ delete _config;
+}
+
+void PVSOutgoingMulticastTransfer::prepare()
+{
+ if (_socket && !_socketInacceptable)
+ {
+ _prepareTimer->stop();
+ delete _prepareTimer;
+ _prepareTimer = 0;
+
+ QTimer::singleShot(0, this, SLOT(doStart()));
+ return;
+ }
+ else if (_socket)
+ {
+ delete _socket;
+ _socket = 0;
+ }
+
+ QSettings settings;
+ quint16 portbase = settings.value("multicast/port-base", "6966").value<quint16>();
+ quint16 portlimit = settings.value("multicast/port-limit", "7966").value<quint16>();
+
+ int tries_remaining = 5;
+ while(tries_remaining > 0)
+ {
+ quint16 port = portbase + (qrand() % (portlimit - portbase + 1));
+
+ if (!_config)
+ {
+ _config = new McastConfiguration();
+ }
+ _config->loadFrom(&settings, "multicast");
+ _config->multicastUDPPortBase(port);
+
+ _socket = new McastPGMSocket(this);
+ if(_socket->open(_config, McastPGMSocket::PSOCK_WRITE))
+ {
+ break;
+ }
+ else
+ {
+ delete _socket;
+ _socket = 0;
+ }
+ }
+
+ if (!_socket)
+ {
+ emit failed(_id, "Could not open socket");
+ delete _prepareTimer;
+ _prepareTimer = 0;
+ return;
+ }
+ else
+ {
+ _socketInacceptable = false;
+ // announce the transfer:
+ QFileInfo info(*_file);
+ QString message = QString("%1:%2:%3:%4:%5").arg(_senderName).arg(_id).arg(info.fileName()).arg(info.size()).arg(_config->multicastUDPPortBase());
+ PVSMsg msg(PVSCOMMAND, "MCASTFTANNOUNCE", message);
+ emit announce(msg);
+ _prepareTimer->start(5000);
+ }
+}
+
+void PVSOutgoingMulticastTransfer::doStart()
+{
+ ConsoleLog writeLine(QString("Starting multicast transfer %1").arg(_id));
+
+ _file->open(QIODevice::ReadOnly);
+
+ _sender = new McastSender(_file, _config, this);
+ connect(_sender, SIGNAL(finished()), SLOT(senderFinished()));
+ connect(_sender, SIGNAL(progress(quint64)), SLOT(senderProgress(quint64)));
+ // connect(_sender, SIGNAL(allSent()), SIGNAL(allSent()));
+ _socket->setParent(_sender);
+ _sender->start(_socket);
+
+ emit started(_id);
+
+ _progressTimer = new QTimer(this);
+ connect(_progressTimer, SIGNAL(timeout()), SLOT(reportTimeout()));
+ _progressTimer->setInterval(333);
+ _progressTimer->start();
+}
+
+void PVSOutgoingMulticastTransfer::senderProgress(quint64 bytes)
+
+{
+ _progress = bytes;
+}
+
+void PVSOutgoingMulticastTransfer::senderFinished()
+{
+ if(_progressTimer)
+ {
+ _progressTimer->stop();
+ delete _progressTimer;
+ _progressTimer = 0;
+ }
+ emit finished(_id);
+ _sender->close();
+ delete _sender;
+}
+
+void PVSOutgoingMulticastTransfer::reportTimeout()
+{
+ emit progress(_id, _progress, _length);
+}
+
+void PVSOutgoingMulticastTransfer::retry()
+{
+ bool first = !_socketInacceptable;
+ _socketInacceptable = true;
+
+ if(first)
+ {
+ _prepareTimer->setInterval(1000);
+ }
+}
+
+void PVSOutgoingMulticastTransfer::start()
+{
+ if (!_prepareTimer)
+ {
+ _prepareTimer = new QTimer(this);
+ _prepareTimer->setSingleShot(true);
+ connect(_prepareTimer, SIGNAL(timeout()), SLOT(prepare()));
+ }
+ QTimer::singleShot(0, this, SLOT(prepare()));
+}
+
+void PVSOutgoingMulticastTransfer::abort()
+{
+ if (_sender)
+ _sender->close();
+}
+
+void PVSOutgoingMulticastTransfer::error(QString const& reason)
+{
+ qCritical() << "Could not create an outgoing mcast transfer: " << reason;
+ _error = true;
+ _reason = reason;
+}
diff --git a/src/net/pvsOutgoingMulticastTransfer.h b/src/net/pvsOutgoingMulticastTransfer.h
new file mode 100644
index 0000000..5fd6a3d
--- /dev/null
+++ b/src/net/pvsOutgoingMulticastTransfer.h
@@ -0,0 +1,92 @@
+/*
+# Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg
+#
+# This program is free software distributed under the GPL version 2.
+# See http://openslx.org/COPYING
+#
+# If you have any feedback please consult http://openslx.org/feedback and
+# send your suggestions, praise, or complaints to feedback@openslx.org
+#
+# General information about OpenSLX can be found at http://openslx.org/
+# -----------------------------------------------------------------------------
+# src/net/pvsOutgoingMulticastTransfer.h
+# - wrap McastSender functionality in PVS daemon
+# -----------------------------------------------------------------------------
+*/
+
+#ifndef PVSOUTGOINGMULTICASTTRANSFER_H_
+#define PVSOUTGOINGMULTICASTTRANSFER_H_
+
+#include <QtGlobal>
+#include <QObject>
+#include <QString>
+
+#include <src/net/pvsMsg.h>
+
+class QFile;
+class QTimer;
+class McastConfiguration;
+class McastPGMSocket;
+class McastSender;
+
+class PVSOutgoingMulticastTransfer : public QObject
+{
+ Q_OBJECT
+public:
+ PVSOutgoingMulticastTransfer(QString senderName, quint64 id, QString filename, QObject* parent = 0);
+ virtual ~PVSOutgoingMulticastTransfer();
+
+ quint64 id() const
+ {
+ return _id;
+ }
+
+ bool isError() const
+ {
+ return _error;
+ }
+
+ QString reason() const
+ {
+ return _reason;
+ }
+
+signals:
+ void started(qulonglong id);
+ void progress(qulonglong id, qulonglong bytes, qulonglong of);
+ void allSent(qulonglong id);
+ void finished(qulonglong id);
+ void failed(qulonglong id, QString const reason);
+ void announce(PVSMsg announcement);
+
+private slots:
+ void senderProgress(quint64 bytes);
+ void senderFinished();
+ void reportTimeout();
+ void doStart();
+ void prepare();
+
+public slots:
+ void start();
+ void abort();
+ void retry();
+
+private:
+ QFile* _file;
+ quint64 _length;
+ quint64 _progress;
+ McastConfiguration* _config;
+ McastPGMSocket* _socket;
+ McastSender* _sender;
+ QTimer* _progressTimer;
+ QTimer* _prepareTimer;
+ QString _senderName;
+ quint64 _id;
+ bool _error;
+ QString _reason;
+ bool _socketInacceptable;
+
+ void error(QString const& reason);
+};
+
+#endif /* PVSOUTGOINGMULTICASTTRANSFER_H_ */
diff --git a/src/net/pvsServerConnection.h b/src/net/pvsServerConnection.h
index 0669d88..c6ef015 100644
--- a/src/net/pvsServerConnection.h
+++ b/src/net/pvsServerConnection.h
@@ -11,9 +11,9 @@
#define _PVSSERVERCONNECTION_H_
#include "src/util/dispatcher.h"
+#include "src/net/pvsMsg.h"
#include <QtNetwork/QSslSocket>
-class PVSMsg;
class PVS;
class PVSDiscoveredServer;
@@ -30,8 +30,6 @@ public:
return _socket != NULL && _socket->state() == QAbstractSocket::ConnectedState;
}
- void sendMessage(PVSMsg newMessage);
-
void ping();
QString getServerName();
@@ -63,6 +61,9 @@ public:
_commandDispatcher.removeListener(ident, who, func);
};
+public Q_SLOTS:
+ void sendMessage(PVSMsg newMessage);
+
protected:
void timerEvent(QTimerEvent *event);
diff --git a/src/pvs.cpp b/src/pvs.cpp
index b343f29..bda16f8 100755
--- a/src/pvs.cpp
+++ b/src/pvs.cpp
@@ -10,11 +10,21 @@
# General information about OpenSLX can be found at http://openslx.org/
*/
+#include <QCryptographicHash>
+#include <QDataStream>
+#include <QDateTime>
+
#include "pvs.h"
#include "src/util/dispatcher.h"
#include "src/net/pvsMsg.h"
#include "src/net/pvsServiceDiscovery.h"
#include "src/net/pvsDiscoveredServer.h"
+#include "src/input/inputEvent.h"
+#include "src/input/inputHandlerChain.h"
+#include "src/input/x11InputUtils.h"
+#include "src/net/mcast/McastConfiguration.h"
+#include "src/net/pvsOutgoingMulticastTransfer.h"
+#include "src/net/pvsIncomingMulticastTransfer.h"
// D-Bus
#include "pvsadaptor.h"
@@ -41,6 +51,9 @@ PVS::PVS() :
/*#endif*/
_vncPort = -1;
+ _masterMcastConfig = new McastConfiguration(this);
+ _masterMcastConfig->loadFrom(&_settings, "multicast");
+
// add a notify to the allow file, so we get informed when the file is changed
QString watchPath(getPolicyDir());
watchPath.append(QString(".allow"));
@@ -80,6 +93,8 @@ PVS::PVS() :
sigaction(SIGHUP, &act, 0);
sigaction(SIGINT, &act, 0);
sigaction(SIGQUIT, &act, 0);
+
+ initializeInputEventHandling();
#endif /*__WIN32__*/
}
@@ -181,6 +196,67 @@ void PVS::onCommand(PVSMsg cmdMessage)
unlock();
return;
}
+ if (ident.compare("INPUTEVENT") == 0)
+ {
+ InputEvent evt;
+ eventFromString(message, evt);
+ handleInputEvent(evt);
+ }
+ if (ident.compare("MCASTFTRETRY") == 0)
+ {
+ QStringList fields = message.split(':');
+ if (fields[0].compare(getUserName()) == 0)
+ {
+ quint64 id = fields[1].toULongLong();
+ PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(id, 0);
+ if (transfer)
+ transfer->retry();
+ }
+ }
+ if (ident.compare("MCASTFTANNOUNCE") == 0)
+ {
+ QStringList fields = message.split(':');
+ bool ok;
+ QString sender;
+ qulonglong transferID;
+ QString basename;
+ qulonglong size;
+ ushort port;
+
+ if (!fields.size() == 5)
+ {
+ goto malformedAnnounce;
+ }
+ sender = fields[0];
+ transferID = fields[1].toULongLong(&ok);
+ if (!ok)
+ {
+ goto malformedAnnounce;
+ }
+ basename = fields[2];
+ size = fields[3].toULongLong(&ok);
+ if (!ok)
+ {
+ goto malformedAnnounce;
+ }
+ port = fields[4].toUShort(&ok);
+ if (!ok)
+ {
+ goto malformedAnnounce;
+ }
+
+ onIncomingMulticastTransfer(sender, transferID, basename, size, port);
+ return;
+
+ malformedAnnounce:
+ qDebug() << "Ignoring malformed MCASTFTANNOUNCE command: " << message;
+ return;
+ }
+ if (ident.compare("MCASTFTCONFIG") == 0)
+ {
+ loadMcastConfig(message);
+ return;
+ }
if (ident.compare("SHOWPROCESSES") == 0)
{
_pvsServerConnection->sendMessage(PVSMsg(PVSCOMMAND, "PROCESSES", "SHOW clear")); //tell the client that we want to clear his process-list
@@ -652,6 +728,191 @@ void PVS::signalHandler(int signal)
}
+bool PVS::createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason)
+{
+ transferID = generateMcastTransferID();
+
+ PVSOutgoingMulticastTransfer* transfer = new PVSOutgoingMulticastTransfer(getUserName(), transferID, objectPath, this);
+ if (transfer->isError())
+ {
+ errorReason = transfer->reason();
+ delete transfer;
+ return false;
+ }
+
+ _outgoingTransfers.insert(transferID, transfer);
+ connect(transfer, SIGNAL(started(qulonglong)), SIGNAL(outgoingMulticastTransferStarted(qulonglong)));
+ connect(transfer, SIGNAL(finished(qulonglong)), SIGNAL(outgoingMulticastTransferFinished(qulonglong)));
+ connect(transfer, SIGNAL(failed(qulonglong, QString const)), SIGNAL(outgoingMulticastTransferFailed(qulonglong, QString const)));
+ connect(transfer, SIGNAL(progress(qulonglong, qulonglong, qulonglong)), SIGNAL(outgoingMulticastTransferProgress(qulonglong, qulonglong, qulonglong)));
+ connect(transfer, SIGNAL(announce(PVSMsg)), _pvsServerConnection, SLOT(sendMessage(PVSMsg)));
+ connect(transfer, SIGNAL(finished(qulonglong)), SLOT(outgoingMulticastTransferDelete(qulonglong)));
+ connect(transfer, SIGNAL(failed(qulonglong, QString const)), SLOT(outgoingMulticastTransferDelete(qulonglong)));
+ QTimer::singleShot(0, transfer, SLOT(start()));
+ errorReason = "";
+ return true;
+}
+
+// Input handling
+
+void PVS::handleInputEvent(InputEvent const& evt)
+{
+ std::string s = evt.toString();
+ ConsoleLog writeLine(QString("Received input event: %1").arg(s.c_str()));
+ _inputEventHandlers.handle(evt);
+}
+
+void PVS::initializeInputEventHandling()
+{
+ X11InputUtils::setDisplay(X11Info::display());
+ _inputEventHandlers.initialize();
+}
+
+void PVS::cancelOutgoingMulticastTransfer(quint64 transferID)
+{
+ PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(transferID, 0);
+
+ if (transfer)
+ {
+ _outgoingTransfers.remove(transferID);
+ delete transfer;
+ }
+}
+
+void PVS::cancelIncomingMulticastTransfer(qulonglong transferID)
+{
+ PVSIncomingMulticastTransfer* transfer = _incomingTransfers.value(transferID, 0);
+
+ if(transfer)
+ {
+ _incomingTransfers.remove(transferID);
+ delete transfer;
+ }
+}
+
+void PVS::onIncomingMulticastTransfer(QString const& sender, qulonglong transferID,
+ QString const& basename, qulonglong size, ushort port)
+{
+ if (_outgoingTransfers.contains(transferID))
+ return;
+
+ PVSIncomingMulticastTransfer* transfer;
+ if (transfer = _incomingTransfers.value(transferID, 0))
+ {
+ transfer->updatePort(port);
+ QTimer::singleShot(0, transfer, SLOT(start()));
+ }
+ else
+ {
+ QString filename = QFileInfo(QDir::home(), QString("%1.part.%2").arg(basename).arg(transferID, 0, 16)).absoluteFilePath();
+ transfer = new PVSIncomingMulticastTransfer(sender, transferID, size, filename, port, _masterMcastConfig, this);
+ _incomingTransfers.insert(transferID, transfer);
+
+ connect(transfer, SIGNAL(retry(QString const&, qulonglong)), SLOT(onIncomingMulticastTransferRetry(QString const&, qulonglong)));
+ connect(transfer, SIGNAL(started(qulonglong)), SIGNAL(incomingMulticastTransferStarted(qulonglong)));
+ connect(transfer, SIGNAL(progress(qulonglong, qulonglong, qulonglong)), SIGNAL(incomingMulticastTransferProgress(qulonglong, qulonglong, qulonglong)));
+ connect(transfer, SIGNAL(finished(qulonglong)), SIGNAL(incomingMulticastTransferFinished(qulonglong)));
+ connect(transfer, SIGNAL(failed(qulonglong, QString const&)), SIGNAL(incomingMulticastTransferFailed(qulonglong, QString)));
+ connect(transfer, SIGNAL(finished(qulonglong)), SLOT(incomingMulticastTransferDelete(qulonglong)));
+ connect(transfer, SIGNAL(failed(qulonglong, QString const&)), SLOT(incomingMulticastTransferDelete(qulonglong)));
+
+ emit incomingMulticastTransferNew(transferID, sender, filename, size);
+ QTimer::singleShot(0, transfer, SLOT(start()));
+ }
+}
+
+void PVS::onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID)
+{
+ PVSMsg retryMessage(PVSCOMMAND, "MCASTFTRETRY", QString("%1:%2").arg(sender).arg(transferID));
+ _pvsServerConnection->sendMessage(retryMessage);
+}
+
+quint64 PVS::generateMcastTransferID()
+{
+ static quint64 nodeID = 0;
+ static quint16 counter = 0;
+
+ if (!nodeID)
+ {
+ QDateTime t = QDateTime::currentDateTime();
+ QCryptographicHash h(QCryptographicHash::Md5);
+ h.addData(getUserName().toLocal8Bit());
+ h.addData(t.toString().toLocal8Bit());
+ QDataStream(h.result()) >> nodeID;
+ }
+
+ return (nodeID & Q_UINT64_C(0xffffffffffff0000)) | (quint64)(++counter);
+}
+
+void PVS::outgoingMulticastTransferDelete(qulonglong transferID)
+{
+ PVSOutgoingMulticastTransfer* transfer = _outgoingTransfers.value(transferID, 0);
+ if (!transfer)
+ return;
+
+ _outgoingTransfers.remove(transferID);
+ transfer->deleteLater();
+}
+
+void PVS::incomingMulticastTransferDelete(qulonglong transferID)
+{
+ PVSIncomingMulticastTransfer* transfer = _incomingTransfers.value(transferID, 0);
+ if (!transfer)
+ {
+ return;
+ }
+
+ _incomingTransfers.remove(transferID);
+ transfer->deleteLater();
+}
+
+void PVS::loadMcastConfig(QString const& message)
+{
+ QByteArray ba = QByteArray::fromBase64(message.toAscii());
+ QDataStream d(&ba, QIODevice::ReadOnly);
+ quint16 ver, udpp, dp, sp, mtu, wsz;
+ quint32 rate;
+ QString addr;
+ bool useudp;
+
+ d >> ver;
+ if(ver != 1)
+ {
+ ConsoleLog writeLine(QString("Unable to decode multicast configuration message: Unknown version %1").arg(ver));
+ return;
+ }
+
+ d >> addr
+ >> udpp
+ >> dp
+ >> sp
+ >> mtu
+ >> wsz
+ >> rate
+ >> useudp;
+ if(d.status() != QDataStream::Ok)
+ {
+ ConsoleLog writeLine(QString("Unable to decode multicast configuration message: There was an error reading"));
+ return;
+ }
+
+ _masterMcastConfig->multicastUDPPortBase(udpp);
+ _masterMcastConfig->multicastDPort(dp);
+ _masterMcastConfig->multicastSPort(sp);
+ _masterMcastConfig->multicastMTU(mtu);
+ _masterMcastConfig->multicastWinSize(wsz);
+ _masterMcastConfig->multicastRate(rate);
+ _masterMcastConfig->multicastAddress(addr);
+ _masterMcastConfig->multicastUseUDP(useudp);
+
+ QSettings settings;
+ _masterMcastConfig->writeTo(&settings, "multicast");
+ settings.sync();
+
+ ConsoleLog writeLine(QString("Reconfigured multicast filetransfer to IP %1, UDP port base %2, destination port %3, source port %4, MTU %5, Window Size %6, rate %7, %8using UDP")
+ .arg(addr).arg(udpp).arg(dp).arg(sp).arg(mtu).arg(wsz).arg(rate).arg(useudp ? "" : "not "));
+}
+
void PVS::setConfigValue(QString key, QString value)
{
_settings.setValue(key, value);
diff --git a/src/pvs.h b/src/pvs.h
index 4ba8e65..f99e497 100755
--- a/src/pvs.h
+++ b/src/pvs.h
@@ -29,9 +29,14 @@
#include <QFile>
#include <QTextStream>
#include <QDir>
+#include "src/input/inputHandlerChain.h"
class PVSServiceDiscovery;
class PVSDiscoveredServer;
+class InputEvent;
+class McastConfiguration;
+class PVSOutgoingMulticastTransfer;
+class PVSIncomingMulticastTransfer;
/**
* PVSClient
@@ -84,6 +89,11 @@ public Q_SLOTS:
QStringList getAvailableHosts();
QString getIpByNick(QString nick);
+ // Multicast File Transfer
+ bool createMulticastTransfer(QString const& objectPath, quint64& transferID, QString& errorReason);
+ void cancelOutgoingMulticastTransfer(quint64 transferID);
+ void cancelIncomingMulticastTransfer(qulonglong transferID);
+
void setConfigValue(QString key, QString value);
QString getConfigValue(QString key);
@@ -100,6 +110,17 @@ Q_SIGNALS:
void addHost(QString host);
void delHost(QString host);
+ // Multicast File Transfer
+ void outgoingMulticastTransferStarted(qulonglong transferID);
+ void outgoingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of);
+ void outgoingMulticastTransferFinished(qulonglong transferID);
+ void outgoingMulticastTransferFailed(qulonglong transferID, QString reason);
+ void incomingMulticastTransferNew(qulonglong transferID, QString sender, QString filename, qulonglong size);
+ void incomingMulticastTransferStarted(qulonglong transferID);
+ void incomingMulticastTransferProgress(qulonglong transferID, qulonglong bytes, qulonglong of);
+ void incomingMulticastTransferFinished(qulonglong transferID);
+ void incomingMulticastTransferFailed(qulonglong transferID, QString reason);
+
protected:
void timerEvent(QTimerEvent *event);
@@ -151,7 +172,27 @@ private:
int _timerLockTest;
int _timerLockDelay;
- QSettings _settings;
+ // input event handling:
+ unprivileged_handler_chain _inputEventHandlers;
+ void handleInputEvent(InputEvent const& evt);
+ void initializeInputEventHandling();
+
+ // multicast transfer
+ McastConfiguration* _masterMcastConfig;
+ QHash<quint64, PVSOutgoingMulticastTransfer*> _outgoingTransfers;
+ QHash<quint64, PVSIncomingMulticastTransfer*> _incomingTransfers;
+ void loadMcastConfig(QString const& encoded);
+ void onIncomingMulticastTransfer(QString const& sender, qulonglong transferID, QString const& basename, qulonglong size, ushort port);
+ static quint64 generateMcastTransferID();
+
+private Q_SLOTS:
+ // housekeeping
+ void outgoingMulticastTransferDelete(qulonglong transferID);
+ void incomingMulticastTransferDelete(qulonglong transferID);
+ void onIncomingMulticastTransferRetry(QString const& sender, qulonglong transferID);
+
+private:
+ QSettings _settings;
};
#endif /* PVSCLIENT_H_ */
diff --git a/src/pvsDaemon.cpp b/src/pvsDaemon.cpp
index 584a65d..1784a14 100644
--- a/src/pvsDaemon.cpp
+++ b/src/pvsDaemon.cpp
@@ -64,6 +64,9 @@ int main(int argc, char** argv)
bool _daemon = false;
int frequency = 5;
int port = -1;
+#ifdef as_daemon
+ bool no_fork = false;
+#endif
QCoreApplication app(argc, argv);
app.setOrganizationName("openslx");
@@ -119,11 +122,14 @@ int main(int argc, char** argv)
{ "freq", required_argument, 0, 'f' },
{ "client", required_argument, 0, 'e' },
{ "script", required_argument, 0, 's' },
+#ifdef as_daemon
+ { "no-fork", no_argument, 0, 'F' },
+#endif
{ 0, 0, 0, 0 },
};
/* getopt_long stores the option index here. */
- int c = getopt_long(argc, argv, "hovdc:f:e:s:p:", long_options,
+ int c = getopt_long(argc, argv, "hvodc:f:e:s:p:", long_options,
&option_index);
option_index++;
if (c == -1)
@@ -226,6 +232,13 @@ int main(int argc, char** argv)
}
break;
}
+#ifdef as_daemon
+ case 'F':
+ {
+ no_fork = true;
+ break;
+ }
+#endif
case '?':
{
ConsoleLog writeError(
diff --git a/src/pvsgui.cpp b/src/pvsgui.cpp
index 8617587..1520c81 100644
--- a/src/pvsgui.cpp
+++ b/src/pvsgui.cpp
@@ -119,6 +119,7 @@ PVSGUI::PVSGUI(QWidget *parent) :
connect(_ifaceDBus, SIGNAL(disconnected()), this, SLOT(disconnected()));
connect(_ifaceDBus, SIGNAL(addHost(QString)), this, SLOT(addHost(QString)));
connect(_ifaceDBus, SIGNAL(delHost(QString)), this, SLOT(delHost(QString)));
+ connect(_ifaceDBus, SIGNAL(incomingMulticastTransferNew(qulonglong, QString, QString, qulonglong)), SLOT(incomingMulticastFile(qulonglong, QString, QString, qulonglong)));
// show toolbar
setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint);
@@ -412,6 +413,12 @@ void PVSGUI::receiveFile()
d->open();
}
+void PVSGUI::incomingMulticastFile(qulonglong transferID, QString sender, QString basename, qulonglong size)
+{
+ ClientFileReceiveDialog *d = new ClientFileReceiveDialog(sender, transferID, basename, size, _ifaceDBus, this);
+ d->open();
+}
+
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -443,24 +450,6 @@ void printVersion()
int main(int argc, char *argv[])
{
- QtSingleApplication app(argc, argv);
- app.setQuitOnLastWindowClosed(false);
- app.setOrganizationName("openslx");
- app.setOrganizationDomain("openslx.org");
- app.setApplicationName("pvsgui");
-
- // only one instance should be allowed
- if (app.sendMessage(""))
- {
- qDebug("[PVSGUI] ERROR: Already running. Exiting");
- return 0;
- }
-
- // use system locale as language to translate gui
- QTranslator translator;
- translator.load(":pvsgui");
- app.installTranslator(&translator);
-
bool visible = false;
int position = -1;
@@ -499,6 +488,25 @@ int main(int argc, char *argv[])
opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
}
+
+ QtSingleApplication app(argc, argv);
+ app.setQuitOnLastWindowClosed(false);
+ app.setOrganizationName("openslx");
+ app.setOrganizationDomain("openslx.org");
+ app.setApplicationName("pvsgui");
+
+ // only one instance should be allowed
+ if (app.sendMessage(""))
+ {
+ qDebug("[PVSGUI] ERROR: Already running. Exiting");
+ return 0;
+ }
+
+ // use system locale as language to translate gui
+ QTranslator translator;
+ translator.load(":pvsgui");
+ app.installTranslator(&translator);
+
PVSGUI pvsgui;
pvsgui.setPosition(position);
pvsgui.setVisible(visible);
diff --git a/src/pvsgui.h b/src/pvsgui.h
index 85a129b..b96d73c 100644
--- a/src/pvsgui.h
+++ b/src/pvsgui.h
@@ -66,6 +66,7 @@ private Q_SLOTS:
void pvsDisconnect();
void sendFile();
void receiveFile();
+ void incomingMulticastFile(qulonglong, QString sender, QString basename, qulonglong size);
private:
void setupMenu();
diff --git a/src/util/clientGUIUtils.cpp b/src/util/clientGUIUtils.cpp
index 4d4cc0d..ff002d3 100644
--- a/src/util/clientGUIUtils.cpp
+++ b/src/util/clientGUIUtils.cpp
@@ -2,7 +2,7 @@
BlankScreen::BlankScreen()
{
- dpy = XOpenDisplay(NULL);
+ dpy = X11Info::display();
scr = DefaultScreen(dpy);
assert(dpy);
blackColor = BlackPixel(dpy, DefaultScreen(dpy));
@@ -142,3 +142,4 @@ bool BlankScreen::unlock()
lockMsg.clear();
return !(locked = false);
}
+
diff --git a/src/util/clientGUIUtils.h b/src/util/clientGUIUtils.h
index b04d3fe..7f7ea47 100755
--- a/src/util/clientGUIUtils.h
+++ b/src/util/clientGUIUtils.h
@@ -25,4 +25,14 @@ private:
BlankScreen_Sysdep* _sysdep;
};
-#endif \ No newline at end of file
+#ifndef __WIN32__
+# include <X11/Xlib.h>
+
+class X11Info
+{
+public:
+ static Display* display();
+};
+#endif /* !__WIN32__ */
+
+#endif
diff --git a/src/util/clientGUIUtils_X11.cpp b/src/util/clientGUIUtils_X11.cpp
index 29c96f0..4b5afe0 100755
--- a/src/util/clientGUIUtils_X11.cpp
+++ b/src/util/clientGUIUtils_X11.cpp
@@ -166,3 +166,14 @@ bool BlankScreen::unlock()
_sysdep->lockMsg.clear();
return !(_sysdep->locked = false);
}
+
+static Display* _dpy = 0;
+
+Display* X11Info::display()
+{
+ if(!_dpy)
+ {
+ _dpy = XOpenDisplay(0);
+ }
+ return _dpy;
+}
diff --git a/src/version.h b/src/version.h
index de5c6aa..a08c819 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,2 +1,2 @@
-#define VERSION_STRING "2.0.5"
-#define VERSION_NUMBER 205
+#define VERSION_STRING "2.8.0"
+#define VERSION_NUMBER 280