diff options
author | Andrew Christian <andrew.christian@nokia.com> | 2012-02-16 15:18:02 -0500 |
---|---|---|
committer | Chris Craig <ext-chris.craig@nokia.com> | 2012-02-17 19:23:21 +0100 |
commit | 55db246f84514d42e211156c8e7fa28ff4d1b8d4 (patch) | |
tree | 2a3716276b992e3c21fc576c6855bc5cee741b53 | |
parent | 7140c90c4c771f9e027b1922fea167ff102026a9 (diff) |
Schema validation for remote socket protocol.
Change-Id: Ia4981187fb35edde82540042ff60688b8066f725
Reviewed-by: Chris Craig <ext-chris.craig@nokia.com>
-rw-r--r-- | schema/remote/inbound/set.json | 19 | ||||
-rw-r--r-- | schema/remote/inbound/start.json | 9 | ||||
-rw-r--r-- | schema/remote/inbound/stop.json | 9 | ||||
-rw-r--r-- | schema/remote/inbound/write.json | 13 | ||||
-rw-r--r-- | schema/remote/outbound/error.json | 10 | ||||
-rw-r--r-- | schema/remote/outbound/finished.json | 10 | ||||
-rw-r--r-- | schema/remote/outbound/output.json | 10 | ||||
-rw-r--r-- | schema/remote/outbound/started.json | 9 | ||||
-rw-r--r-- | schema/remote/outbound/statechanged.json | 9 | ||||
-rw-r--r-- | src/core/socketlauncher.cpp | 9 | ||||
-rw-r--r-- | src/core/socketlauncher.h | 2 | ||||
-rw-r--r-- | tests/auto/processmanager/testSocketLauncher/main.cpp | 118 | ||||
-rw-r--r-- | tests/auto/processmanager/tst_processmanager.cpp | 24 |
13 files changed, 248 insertions, 3 deletions
diff --git a/schema/remote/inbound/set.json b/schema/remote/inbound/set.json new file mode 100644 index 0000000..da29b4e --- /dev/null +++ b/schema/remote/inbound/set.json @@ -0,0 +1,19 @@ +{ + "title": "Set schema", + "description": "The client would like to change a setting on an existing process", + "properties": { + "command": { "type": "string", "pattern": "set", "required": true }, + "id": { "type": "integer", "required": true }, + + "key": { + "type": "string", + "enum": ["priority", "oomAdjustment"], + "required": true + }, + + "value": { + "type": "integer", + "required": true + } + } +} diff --git a/schema/remote/inbound/start.json b/schema/remote/inbound/start.json new file mode 100644 index 0000000..378e171 --- /dev/null +++ b/schema/remote/inbound/start.json @@ -0,0 +1,9 @@ +{ + "title": "Start schema", + "description": "The client request a new process to be started", + "properties": { + "command": { "type": "string", "pattern": "start", "required": true }, + "id": { "type": "integer", "required": true }, + "info": { "type": "object", "required": true } + } +} diff --git a/schema/remote/inbound/stop.json b/schema/remote/inbound/stop.json new file mode 100644 index 0000000..7a9e6c3 --- /dev/null +++ b/schema/remote/inbound/stop.json @@ -0,0 +1,9 @@ +{ + "title": "Stop schema", + "description": "The client request a process to be stopped", + "properties": { + "command": { "type": "string", "pattern": "stop", "required": true }, + "id": { "type": "integer", "required": true }, + "timeout": { "type": "integer", "required": true } + } +} diff --git a/schema/remote/inbound/write.json b/schema/remote/inbound/write.json new file mode 100644 index 0000000..22e8595 --- /dev/null +++ b/schema/remote/inbound/write.json @@ -0,0 +1,13 @@ +{ + "title": "Write schema", + "description": "The client is writing data to the stdin of an existing process", + "properties": { + "command": { "type": "string", "pattern": "write", "required": true }, + "id": { "type": "integer", "required": true }, + + "data": { + "type": "string", + "required": true + } + } +} diff --git a/schema/remote/outbound/error.json b/schema/remote/outbound/error.json new file mode 100644 index 0000000..1544070 --- /dev/null +++ b/schema/remote/outbound/error.json @@ -0,0 +1,10 @@ +{ + "title": "Error schema", + "description": "Signal the client that a process has sent the error() signal", + "properties": { + "event": { "type": "string", "pattern": "error", "required": true }, + "id": { "type": "integer", "required": true }, + "error": { "type": "integer", "required": true }, + "errorString": { "type": "string", "required": true } + } +} diff --git a/schema/remote/outbound/finished.json b/schema/remote/outbound/finished.json new file mode 100644 index 0000000..4f53729 --- /dev/null +++ b/schema/remote/outbound/finished.json @@ -0,0 +1,10 @@ +{ + "title": "Finished schema", + "description": "Signal the client that a process has sent the finished() signal", + "properties": { + "event": { "type": "string", "pattern": "finished", "required": true }, + "id": { "type": "integer", "required": true }, + "exitCode": { "type": "integer", "required": true }, + "exitStatus": { "type": "integer", "required": true } + } +} diff --git a/schema/remote/outbound/output.json b/schema/remote/outbound/output.json new file mode 100644 index 0000000..ee70cf2 --- /dev/null +++ b/schema/remote/outbound/output.json @@ -0,0 +1,10 @@ +{ + "title": "Output schema", + "description": "Signal the client that a process has written data on stdout or stderr", + "properties": { + "event": { "type": "string", "pattern": "output", "required": true }, + "id": { "type": "integer", "required": true }, + "stdout": { "type": "string" }, + "stderr": { "type": "string" } + } +} diff --git a/schema/remote/outbound/started.json b/schema/remote/outbound/started.json new file mode 100644 index 0000000..cef861a --- /dev/null +++ b/schema/remote/outbound/started.json @@ -0,0 +1,9 @@ +{ + "title": "Started schema", + "description": "Signal the client that a process has sent the started() signal", + "properties": { + "event": { "type": "string", "pattern": "started", "required": true }, + "id": { "type": "integer", "required": true }, + "pid": { "type": "integer", "required": true } + } +} diff --git a/schema/remote/outbound/statechanged.json b/schema/remote/outbound/statechanged.json new file mode 100644 index 0000000..acdddf8 --- /dev/null +++ b/schema/remote/outbound/statechanged.json @@ -0,0 +1,9 @@ +{ + "title": "State Changed schema", + "description": "Signal the client that a process has sent the stateChanged() signal", + "properties": { + "event": { "type": "string", "pattern": "stateChanged", "required": true }, + "id": { "type": "integer", "required": true }, + "stateChanged": { "type": "integer", "required": true }, + } +} diff --git a/src/core/socketlauncher.cpp b/src/core/socketlauncher.cpp index bb6561f..909dc5f 100644 --- a/src/core/socketlauncher.cpp +++ b/src/core/socketlauncher.cpp @@ -92,6 +92,15 @@ bool SocketLauncher::listen(const QString& socketname, QtAddOn::JsonStream::Json } /*! + Return the internal JsonServer object. + */ + +QtAddOn::JsonStream::JsonServer * SocketLauncher::server() const +{ + return m_server; +} + +/*! \internal */ void SocketLauncher::connectionAdded(const QString& identifier) diff --git a/src/core/socketlauncher.h b/src/core/socketlauncher.h index 0fb7ba1..7ee15b7 100644 --- a/src/core/socketlauncher.h +++ b/src/core/socketlauncher.h @@ -58,6 +58,8 @@ public: Q_INVOKABLE bool listen(int port, QtAddOn::JsonStream::JsonAuthority *authority = 0); Q_INVOKABLE bool listen(const QString& socketname, QtAddOn::JsonStream::JsonAuthority *authority=0); + QtAddOn::JsonStream::JsonServer * server() const; + private slots: void connectionAdded(const QString& identifier); void connectionRemoved(const QString& identifier); diff --git a/tests/auto/processmanager/testSocketLauncher/main.cpp b/tests/auto/processmanager/testSocketLauncher/main.cpp index 7d68db0..28bfe54 100644 --- a/tests/auto/processmanager/testSocketLauncher/main.cpp +++ b/tests/auto/processmanager/testSocketLauncher/main.cpp @@ -38,16 +38,132 @@ ****************************************************************************/ #include <QCoreApplication> +#include <QFileInfo> +#include <QDir> +#include <QDebug> + +#include <jsonserver.h> +#include <schemavalidator.h> + #include "socketlauncher.h" #include "standardprocessbackendfactory.h" QT_USE_NAMESPACE_PROCESSMANAGER +QString progname; + +static void usage() +{ + qWarning("Usage: %s [ARGS] <socketname>\n" + "\n" + "The socketname is the name of the Unix local socket to listen on\n" + "\n" + "Valid arguments:\n" + " -validate-inbound PATH Directory where inbound schema are stored\n" + " -validate-outbound PATH Directory where outbound schema are stored\n" + " -warn Warn on invalid messages\n" + " -drop Drop invalid messages\n" + , qPrintable(progname)); + exit(1); +} + +class ValidateMessage : public QObject { + Q_OBJECT +public: + ValidateMessage(QObject *parent=0) : QObject(parent) {} +public slots: + void failed(const QJsonObject& message) { + qDebug() << Q_FUNC_INFO << "Message failed to validate" << message; + } +}; + +static void loadSchemasFromDirectory(QtAddOn::JsonStream::SchemaValidator *validator, const QString& path) +{ + int count = 0; + QDir dir(path); + if (!dir.exists()) + qFatal("Schema directory '%s' does not exist", qPrintable(path)); + + dir.setNameFilters( QStringList() << "*.json" ); + foreach (QString filename, dir.entryList(QDir::Files | QDir::Readable)) { + if (!validator->loadFromFile(dir.filePath(filename))) { + QtAddOn::JsonStream::SchemaError err = validator->getLastError(); + qFatal("Error loading schema file '%s', [%d] %s", + qPrintable(dir.filePath(filename)), err.errorCode(), qPrintable(err.errorString())); + } + count += 1; + } + + if (count == 0) + qFatal("Unable to find any schema files in directory '%s'", qPrintable(path)); + qDebug() << progname << ": loaded" << count << "schemas from" << path; +} + int main(int argc, char **argv) { + QtAddOn::JsonStream::JsonServer::ValidatorFlags flags(QtAddOn::JsonStream::JsonServer::NoValidation); + QString indir, outdir; + QCoreApplication app(argc, argv); + QStringList args = QCoreApplication::arguments(); + progname = args.takeFirst(); + while (args.size()) { + QString arg = args.at(0); + if (!arg.startsWith('-')) + break; + args.removeFirst(); + if (arg == QLatin1String("-help")) + usage(); + else if (arg == QLatin1String("-validate-inbound")) { + if (!args.size()) + usage(); + indir = args.takeFirst(); + QFileInfo fi(indir); + if (!fi.exists() || !fi.isDir()) { + qWarning("Invalid inbound validation directory '%s'", qPrintable(indir)); + exit(1); + } + } + else if (arg == QLatin1String("-validate-outbound")) { + if (!args.size()) + usage(); + outdir = args.takeFirst(); + QFileInfo fi(outdir); + if (!fi.exists() || !fi.isDir()) { + qWarning("Invalid outbound validation directory '%s'", qPrintable(outdir)); + exit(1); + } + } + else if (arg == QLatin1String("-warn")) + flags |= QtAddOn::JsonStream::JsonServer::WarnIfInvalid; + else if (arg == QLatin1String("-drop")) + flags |= QtAddOn::JsonStream::JsonServer::DropIfInvalid; + else { + qWarning("Unexpected argument '%s'", qPrintable(arg)); + usage(); + } + } + + if (args.size() != 1) + usage(); + SocketLauncher launcher; launcher.addFactory(new StandardProcessBackendFactory); - launcher.listen(argv[1]); + if (!indir.isEmpty()) + loadSchemasFromDirectory(launcher.server()->inboundValidator(), indir); + if (!outdir.isEmpty()) + loadSchemasFromDirectory(launcher.server()->outboundValidator(), outdir); + launcher.server()->setValidatorFlags(flags); + + ValidateMessage *vtrap = new ValidateMessage; + + QObject::connect(launcher.server(), SIGNAL(inboundMessageValidationFailed(const QJsonObject&)), + vtrap, SLOT(failed(const QJsonObject&))); + QObject::connect(launcher.server(), SIGNAL(outboundMessageValidationFailed(const QJsonObject&)), + vtrap, SLOT(failed(const QJsonObject&))); + + launcher.listen(args[0]); return app.exec(); } + +#include "main.moc" diff --git a/tests/auto/processmanager/tst_processmanager.cpp b/tests/auto/processmanager/tst_processmanager.cpp index c6863cf..f47d87c 100644 --- a/tests/auto/processmanager/tst_processmanager.cpp +++ b/tests/auto/processmanager/tst_processmanager.cpp @@ -676,12 +676,12 @@ static void pipeLauncherTest( clientFunc func ) } -static void socketLauncherTest( clientFunc func ) +static void socketLauncherTest( clientFunc func, QStringList args=QStringList() ) { QProcess *remote = new QProcess; QString socketName = QLatin1String("/tmp/socketlauncher"); remote->setProcessChannelMode(QProcess::ForwardedChannels); - remote->start("testSocketLauncher/testSocketLauncher", QStringList() << socketName); + remote->start("testSocketLauncher/testSocketLauncher", args << socketName); QVERIFY(remote->waitForStarted()); waitForSocket(socketName); @@ -699,6 +699,15 @@ static void socketLauncherTest( clientFunc func ) delete remote; } +static void socketSchemaTest( clientFunc func ) +{ + QStringList args; + args << "-validate-inbound" << "../../../schema/remote/inbound" + << "-validate-outbound" << "../../../schema/remote/outbound" + << "-warn" << "-drop"; + socketLauncherTest(func, args); +} + /******************************************************************************/ @@ -765,6 +774,17 @@ private slots: void socketLauncherOomChangeAfter() { socketLauncherTest(oomChangeAfterClient); } #endif + void socketSchemaStartAndStop() { socketSchemaTest(startAndStopClient); } + void socketSchemaStartAndKill() { socketSchemaTest(startAndKillClient); } + void socketSchemaStartAndCrash() { socketSchemaTest(startAndCrashClient); } + void socketSchemaEcho() { socketSchemaTest(echoClient); } + void socketSchemaPriorityChangeBefore() { socketSchemaTest(priorityChangeBeforeClient); } + void socketSchemaPriorityChangeAfter() { socketSchemaTest(priorityChangeAfterClient); } +#if defined(Q_OS_LINUX) + void socketSchemaOomChangeBefore() { socketSchemaTest(oomChangeBeforeClient); } + void socketSchemaOomChangeAfter() { socketSchemaTest(oomChangeAfterClient); } +#endif + void prelaunchChildAbort(); void frontend(); |