summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Keller <rainer.keller@digia.com>2013-03-04 13:34:29 +0100
committerRainer Keller <rainer.keller@digia.com>2013-03-04 13:34:29 +0100
commita7f07ef9854b3501e7147e695fe0a7aa88140c1d (patch)
tree2a4cbaad88f1615058ab34a0bb68347d90ed26f2
parent444f75cff164190e2106bdce0f5b0047e2bb0505 (diff)
Make this a terminal application
Network support is removed.
-rw-r--r--.gitignore2
-rw-r--r--README87
-rw-r--r--app.cpp118
-rw-r--r--app.h43
-rw-r--r--appcontroller.pro8
-rw-r--r--appdaemon.pro15
-rw-r--r--daemon.cpp203
-rw-r--r--daemon.h46
-rw-r--r--main.cpp127
-rw-r--r--protocol.cpp90
-rw-r--r--protocol.h29
11 files changed, 133 insertions, 635 deletions
diff --git a/.gitignore b/.gitignore
index 2ebe01e..79b4f32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-appdaemon
+appcontroller
Makefile
*.o
moc_*.cpp
diff --git a/README b/README
deleted file mode 100644
index cb8adc2..0000000
--- a/README
+++ /dev/null
@@ -1,87 +0,0 @@
-General
--------
-
-A transfer packet starts with '@' and ends with '@\n'.
-
- @sample text@\n
-
-Fields are separated by ':'.
-
- @sample text:field1:field2@\n
-
-Fields must not be empty.
-
-To include the characters ':' and '@' in texts these have to be escaped with an additional same character.
-':' becomes "::" and '@' becomes "@@".
-
- @sample text:some::value:some@@value@\n
-
-Will be parsed to
-
- sample text
- some:value
- some@value
-
-Packets must not be handled until they are complete.
-
-Commands
----------
-
-Commands can be issued by QtCreator.
-
-@start:<binary>[:<arg>]@\n
-
-Starts the given process. All running apps will be stopped.
-The binary has to be an absolute path.
-Arguments have to be provided as fields. Multiple fields are possible.
-
-@stop@\n
-
-Kill the running app.
-
-@debug[:<binary>][:<arg>]@\n
-
-Starts debugging. If no binary given it attaches to the running app.
-Otherwise it starts a new app as described in '@start'.
-
-@stdin:<text>@\n
-
-A line to be sent to the apps stdin.
-
-@env:<key>[:<value>]@\n
-
-Set an environment variable.
-
-<key>=<value>
-
-If value is missing <key> will be removed from environment.
-
-Notifications
--------------
-
-Notifications are sent to QtCreator.
-
-@started:<PID>@\n
-
-An app was started successfully.
-
-@stopped:<PID>:<exitstatus>:<exitcode>@\n
-
-An app was/had stopped.
-exitstatus: 0=NormalExit; 1=CrashExit
-exitcode: Exitcode in case the program exits normally. Otherwise undefined.
-
-@stdout:<PID>:<text>@\n
-
-A line of the apps stdout
-
-@stderr:<PID>:<text>@\n
-A line of the apps stderr
-
-@debugging:<PID>:<port>@\n
-
-A debugger started on port.
-
-@error:<text>@\n
-
-Last command failed.
diff --git a/app.cpp b/app.cpp
deleted file mode 100644
index 29bd05e..0000000
--- a/app.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-#include "app.h"
-
-#include <QProcess>
-#include <QProcessEnvironment>
-#include <QDebug>
-
-App::App(QObject *parent)
- : QObject(parent)
- , mProcess(new QProcess(this))
- , mEnv(new QProcessEnvironment(QProcessEnvironment::systemEnvironment()))
- , mPid(0)
-{
- mProcess->setProcessChannelMode(QProcess::SeparateChannels);
-
- connect(mProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
- connect(mProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
- connect(mProcess, SIGNAL(readyReadStandardError()), this, SLOT(processStderr()));
- connect(mProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processStdout()));
- connect(mProcess, SIGNAL(started()), this, SLOT(processStarted()));
-}
-
-App::~App()
-{
- delete mEnv;
-}
-
-void App::start(const QString &binary, const QStringList &args)
-{
- stop();
- mProcess->setProcessEnvironment(*mEnv);
- mProcess->start(binary, args);
-}
-
-void App::stop()
-{
- if (mProcess->state() == QProcess::NotRunning)
- return;
-
- mProcess->terminate();
- if (!mProcess->waitForFinished()) {
- mProcess->kill();
- if (!mProcess->waitForFinished()) {
- emit error("Could not kill");
- }
- }
-}
-
-void App::debug()
-{
- emit error("Debugging not implemented.");
-}
-
-void App::addEnv(const QString &key, const QString &value)
-{
- mEnv->insert(key, value);
-}
-
-void App::delEnv(const QString &key)
-{
- mEnv->remove(key);
-}
-
-void App::write(const QByteArray &data)
-{
- if (mProcess->state() == QProcess::Running)
- mProcess->write(data);
- else
- emit error("Could not write input: Process not running.");
-}
-
-void App::processError(QProcess::ProcessError err)
-{
- switch ( err ) {
- case QProcess::FailedToStart:
- emit error("Process failed to start.");
- break;
- case QProcess::Crashed:
- // no error
- // will be handled by: processFinished(...)
- break;
- case QProcess::Timedout: emit error("Last waitFor... timed out."); break;
- case QProcess::WriteError: emit error("Error during write to process."); break;
- case QProcess::ReadError: emit error("Error during read from process."); break;
- case QProcess::UnknownError: emit error("Process had an unknown error."); break;
- }
-}
-
-void App::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
-{
- if (exitStatus == QProcess::NormalExit) {
- qDebug() << "Process exited with exitcode" << exitCode;
- } else {
- qDebug() << "Process crashed";
- }
- emit stopped(mPid, exitStatus, exitCode);
-}
-
-void App::processStderr()
-{
- QByteArray out = mProcess->readAllStandardError();
- if (!out.isEmpty())
- emit stdErr(mPid, out);
-}
-
-void App::processStdout()
-{
- QByteArray out = mProcess->readAllStandardOutput();
- if (!out.isEmpty())
- emit stdOut(mPid, out);
-}
-
-void App::processStarted()
-{
- mPid = mProcess->pid();
- emit started(mPid);
- qDebug() << "Process started";
-}
-
diff --git a/app.h b/app.h
deleted file mode 100644
index 54a8cd5..0000000
--- a/app.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef APP_H
-#define APP_H
-
-#include <QObject>
-#include <QProcess>
-class QProcessEnvironment;
-
-class App : public QObject
-{
-Q_OBJECT
-
-public:
- App(QObject *parent = 0);
- virtual ~App();
-
- void start(const QString &, const QStringList &);
- void stop();
- void debug();
- void addEnv(const QString &, const QString &);
- void delEnv(const QString &);
- void write(const QByteArray &);
-
-signals:
- void started(pid_t);
- void stopped(pid_t, int, int);
- void stdOut(pid_t, const QByteArray &);
- void stdErr(pid_t, const QByteArray &);
- void debugging(pid_t, quint16);
- void error(const QString &);
-
-private slots:
- void processError(QProcess::ProcessError error);
- void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
- void processStderr();
- void processStdout();
- void processStarted();
-
-private:
- QProcess *mProcess;
- QProcessEnvironment *mEnv;
- pid_t mPid;
-};
-#endif // APP_H
diff --git a/appcontroller.pro b/appcontroller.pro
new file mode 100644
index 0000000..d4d079a
--- /dev/null
+++ b/appcontroller.pro
@@ -0,0 +1,8 @@
+QT-=gui
+HEADERS=\
+
+SOURCES=\
+ main.cpp \
+
+target.path = $$[INSTALL_ROOT]/system/bin
+INSTALLS+=target
diff --git a/appdaemon.pro b/appdaemon.pro
deleted file mode 100644
index aa2b113..0000000
--- a/appdaemon.pro
+++ /dev/null
@@ -1,15 +0,0 @@
-QT-=gui
-QT+=network
-HEADERS=\
- app.h \
- daemon.h \
- protocol.h \
-
-SOURCES=\
- app.cpp \
- daemon.cpp \
- main.cpp \
- protocol.cpp \
-
-target.path = $$[INSTALL_ROOT]/system/bin
-INSTALLS+=target
diff --git a/daemon.cpp b/daemon.cpp
deleted file mode 100644
index cd87dc4..0000000
--- a/daemon.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-#include "daemon.h"
-#include "protocol.h"
-#include "app.h"
-
-#include <QTcpServer>
-#include <QTcpSocket>
-#include <QFile>
-
-Daemon::Daemon(QObject *parent)
- : QObject(parent)
- , mServer(new QTcpServer(this))
- , mClient(0)
- , mProtocol(new Protocol(this))
- , mApp(new App(this))
- , mPort(10066)
-{
- loadDefaults();
-
- if (!mServer->listen(QHostAddress::Any, mPort))
- qDebug() << "Could not listen:" << mServer->errorString();
-
- connect(mServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
- connect(mProtocol, SIGNAL(commandReceived(const QStringList &)), this, SLOT(printCommand(const QStringList &)));
- connect(mProtocol, SIGNAL(commandReceived(const QStringList &)), this, SLOT(handleCommands(const QStringList &)));
- connect(mApp, SIGNAL(started(pid_t)), this, SLOT(appStarted(pid_t)));
- connect(mApp, SIGNAL(stopped(pid_t,int,int)), this, SLOT(appStopped(pid_t,int,int)));
- connect(mApp, SIGNAL(stdOut(pid_t,const QByteArray&)), this, SLOT(appStdout(pid_t,const QByteArray&)));
- connect(mApp, SIGNAL(stdErr(pid_t,const QByteArray&)), this, SLOT(appStderr(pid_t,const QByteArray&)));
- connect(mApp, SIGNAL(debugging(pid_t,quint16)), this, SLOT(appDebugging(pid_t,quint16)));
- connect(mApp, SIGNAL(error(const QString&)), this, SLOT(appError(const QString&)));
-
- if (!defaultApp.isEmpty())
- mApp->start(defaultApp, defaultArgs);
-}
-
-Daemon::~Daemon()
-{
-}
-
-void Daemon::newConnection()
-{
- if (!mServer->hasPendingConnections())
- return;
-
- clientDisconnect();
- mClient = mServer->nextPendingConnection();
- connect(mClient, SIGNAL(disconnected()), this, SLOT(clientDisconnect()));
- connect(mClient, SIGNAL(readyRead()), this, SLOT(clientRead()));
-}
-
-void Daemon::printCommand(const QStringList &l)
-{
- qDebug() << l;
-}
-
-void Daemon::clientRead()
-{
- mProtocol->write(mClient->readAll());
-}
-
-void Daemon::clientDisconnect()
-{
- delete mClient;
- mClient = 0;
-}
-
-void Daemon::handleCommands(const QStringList &list)
-{
- if (list.size() == 0) {
- qDebug() << "no commands";
- return;
- }
-
- const QString command(list.first());
- if (command == "start") {
- if (list.size() < 2) {
- qDebug() << "too less options";
- return;
- }
- mApp->start(list[1], defaultArgs + list.mid(2));
- } else if (command == "stop") {
- mApp->stop();
- } else if (command == "debug") {
- qDebug() << "debug not implemented";
- } else if (command == "stdin") {
- if(list.size() < 2) {
- qDebug() << "too less options";
- return;
- }
- mApp->write(list[1].toLocal8Bit());
- } else if (command == "env") {
- if(list.size() == 2) {
- mApp->delEnv(list[1]);
- } else if (list.size() == 3) {
- mApp->addEnv(list[1], list[2]);
- } else {
- qDebug() << "Invalid arg count for env";
- return;
- }
- } else {
- qDebug() << "unknown command:" << list[0];
- }
-
-}
-
-void Daemon::appStarted(pid_t pid)
-{
- if (!mClient)
- return;
- QStringList l("started");
- l += QString::number(pid);
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::appStopped(pid_t pid, int exitStatus, int exitCode)
-{
- if (!mClient)
- return;
- QStringList l("stopped");
- l += QString::number(pid);
- l += QString::number(exitStatus);
- l += QString::number(exitCode);
-
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::appStdout(pid_t pid, const QByteArray &data)
-{
- if (!mClient)
- return;
- QStringList l("stdout");
- l += QString::number(pid);
- l += QString::fromLocal8Bit(data);
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::appStderr(pid_t pid, const QByteArray &data)
-{
- if (!mClient)
- return;
- QStringList l("stderr");
- l += QString::number(pid);
- l += data;
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::appDebugging(pid_t pid, quint16 port)
-{
- if (!mClient)
- return;
- QStringList l("debugging");
- l += QString::number(pid);
- l += QString::number(port);
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::appError(const QString &text)
-{
- if (!mClient)
- return;
- QStringList l("error");
- l += text;
- mClient->write(mProtocol->sendToClient(l).toLocal8Bit());
-}
-
-void Daemon::loadDefaults()
-{
- QFile f("/system/bin/appdaemon.conf");
-
- if (!f.open(QFile::ReadOnly)) {
- qWarning("Could not read config file.");
- return;
- }
-
- while (!f.atEnd()) {
- QString line = f.readLine();
- if (line.startsWith("start=")) {
- defaultApp = line.mid(6).simplified();
- qDebug() << "start=" << defaultApp;
- } else if (line.startsWith("port=")) {
- mPort = line.mid(5).simplified().toUInt();
- if (mPort == 0)
- qFatal("Invalid port");
- } else if (line.startsWith("env=")) {
- QString sub = line.mid(4).simplified();
- int index = sub.indexOf('=');
- if (index < 2) {
- // ignore
- } else {
- mApp->addEnv(sub.left(index), sub.mid(index+1));
- qDebug() << sub.left(index) << sub.mid(index+1);
- }
- } else if (line.startsWith("append=")) {
- defaultArgs += line.mid(7).simplified();
- qDebug() << defaultArgs;
- }
- }
-
- // start=/usr/bin/launcher
- // port=10066
- // env=...
- // append=...
-}
diff --git a/daemon.h b/daemon.h
deleted file mode 100644
index 58c8a01..0000000
--- a/daemon.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef DAEMON_H
-#define DAEMON_H
-
-#include <QObject>
-#include <QStringList>
-class QTcpServer;
-class QTcpSocket;
-class Protocol;
-class App;
-
-class Daemon : public QObject
-{
-Q_OBJECT
-
-public:
- Daemon(QObject *parent = 0);
- virtual ~Daemon();
-
-private slots:
- void newConnection();
- void printCommand(const QStringList &);
- void clientDisconnect();
- void clientRead();
- void handleCommands(const QStringList &);
-
- void appStarted(pid_t);
- void appStopped(pid_t, int, int);
- void appStdout(pid_t, const QByteArray &);
- void appStderr(pid_t, const QByteArray &);
- void appDebugging(pid_t, quint16);
- void appError(const QString &);
-
-private:
- void loadDefaults();
-
- QTcpServer *mServer;
- QTcpSocket *mClient;
- Protocol *mProtocol;
- App *mApp;
-
- QString defaultApp;
- QStringList defaultArgs;
- quint16 mPort;
-};
-
-#endif // DAEMON_H
diff --git a/main.cpp b/main.cpp
index 23e4330..695fe7d 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,9 +1,130 @@
#include <QCoreApplication>
-#include "daemon.h"
+#include <QFile>
+#include <QDebug>
+#include <errno.h>
+#include <unistd.h>
+#include <QStringList>
+
+#define PID_FILE "/data/user/.appcontroller"
+
+static void loadDefaults(QStringList &defaultArgs)
+{
+ QFile f("/system/bin/appdaemon.conf");
+
+ if (!f.open(QFile::ReadOnly)) {
+ qWarning("Could not read config file.");
+ return;
+ }
+
+ while (!f.atEnd()) {
+ QString line = f.readLine();
+ if (line.startsWith("env=")) {
+ QString sub = line.mid(4).simplified();
+ int index = sub.indexOf('=');
+ if (index < 2) {
+ // ignore
+ } else {
+ setenv(sub.left(index).toLocal8Bit().constData(), sub.mid(index+1).toLocal8Bit().constData(), 1);
+ qDebug() << sub.left(index) << sub.mid(index+1);
+ }
+ } else if (line.startsWith("append=")) {
+ defaultArgs += line.mid(7).simplified();
+ qDebug() << defaultArgs;
+ }
+ }
+
+ // env=...
+ // append=...
+}
+
+static pid_t lastPID()
+{
+ QFile f(PID_FILE);
+ if (!f.open(QFile::ReadOnly)) {
+ qDebug() << "Could not open" << f.fileName();
+ return 0;
+ }
+ bool ok;
+ pid_t pid = f.readAll().toUInt(&ok);
+ f.close();
+ if (!ok)
+ qFatal("Invalid last PID.");
+
+ return pid;
+}
+
+static void stop()
+{
+ pid_t pid = lastPID();
+ if (pid == 0)
+ return;
+
+ int rc = ::kill(pid, SIGTERM);
+ if (rc != 0) {
+ if (errno == ESRCH)
+ return;
+ else
+ qFatal("Kill not permitted/invalid");
+ }
+
+ sleep(1);
+
+ rc = ::kill(pid, SIGKILL);
+ if (rc != 0) {
+ if (errno == ESRCH)
+ return;
+ else
+ qFatal("Kill not permitted/invalid");
+ }
+}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
- Daemon daemon;
- return app.exec();
+ QStringList defaultArgs;
+ QString binary;
+
+ QStringList args = app.arguments();
+ args.removeFirst();
+ if (args.size() == 0)
+ qFatal("No arguments given.");
+
+ while (!args.isEmpty()) {
+ if (args[0] == "--start") {
+ if (args.size() < 2) {
+ qFatal("--start requires and argument");
+ }
+ binary = args[1];
+ args.removeFirst();
+ if (binary.isEmpty())
+ qFatal("App path is empty");
+ stop();
+ loadDefaults(defaultArgs);
+ } else if (args[0] == "--stop") {
+ stop();
+ return 0;
+ } else {
+ qFatal("unknown argument: %s", args.first().toLocal8Bit().constData());
+ }
+ args.removeFirst();
+ }
+
+ defaultArgs.push_front(binary);
+
+ char **arglist = new char*[defaultArgs.size()+1];
+ for (int i = 0; i < defaultArgs.size(); i++) {
+ arglist[i] = strdup(defaultArgs[i].toLocal8Bit().constData());
+ }
+ arglist[defaultArgs.size()] = 0;
+ defaultArgs.clear();
+
+ QFile f(PID_FILE);
+ if (!f.open(QFile::WriteOnly))
+ qDebug() << "Could not write PID";
+ f.write(QString::number(getpid()).toLatin1());
+ f.close();
+
+ execv(binary.toLocal8Bit().constData(), arglist);
+
+ return 0;
}
diff --git a/protocol.cpp b/protocol.cpp
deleted file mode 100644
index 19d6c3d..0000000
--- a/protocol.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-#include "protocol.h"
-#include <QDebug>
-
-Protocol::Protocol(QObject *parent)
- : QObject(parent)
- , mState(Start)
-{
-}
-
-Protocol::~Protocol()
-{
-}
-
-void Protocol::reset()
-{
- mState = Start;
- mFields.clear();
- mField.clear();
-}
-
-void Protocol::write(const QByteArray &data)
-{
- QByteArray buffer(data);
-
- while (!buffer.isEmpty()) {
- unsigned int remove = 0;
-
- if (buffer[0] == '@') {
- if (buffer.size() > 1) {
- if (buffer[1] == '@' && mState != Start) {
- mField += '@';
- remove += 2;
- } else if (buffer[1] == '\n' || buffer[1] == '\r') {
- remove += 2;
- if (mState != Field) {
- reset();
- qDebug() << "End unexpected";
- } else {
- mFields += mField;
- emit commandReceived(mFields);
- reset();
- }
- } else {
- ++remove;
- if (mState != Start) {
- qDebug() << "Start unexpected";
- } else {
- mState = Field;
- mField.clear();
- mFields.clear();
- }
- }
- }
- } else if (buffer[0] == ':') {
- if (buffer.size() > 1) {
- if (buffer[1] == ':') {
- mField += ':';
- remove += 2;
- } else {
- ++remove;
- mFields += mField;
- mField.clear();
- }
- }
- } else {
- ++remove;
- mField += buffer[0];
- }
-
- buffer.remove(0, remove);
- }
-}
-
-QString Protocol::sendToClient(const QStringList &data)
-{
- bool first = true;
- QString rc("@");
- foreach (QString s, data) {
- s.replace(":", "::");
- s.replace("@", "@@");
- if (!first)
- rc += ":";
- else
- first = false;
-
- rc += s;
- }
- rc += "@\n";
- return rc;
-}
diff --git a/protocol.h b/protocol.h
deleted file mode 100644
index aee6683..0000000
--- a/protocol.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef PROTOCOL_H
-#define PROTOCOL_H
-
-#include <QObject>
-#include <QStringList>
-
-class Protocol : public QObject
-{
-Q_OBJECT
-
-public:
- Protocol(QObject *parent = 0);
- virtual ~Protocol();
-
- void write(const QByteArray &);
- QString sendToClient(const QStringList &);
-
-signals:
- void commandReceived(const QStringList &);
-
-private:
- void reset();
- enum State { Start, Field };
- State mState;
- QStringList mFields;
- QString mField;
-};
-
-#endif // PROTOCOL_H