diff options
author | Rainer Keller <rainer.keller@digia.com> | 2013-03-04 13:34:29 +0100 |
---|---|---|
committer | Rainer Keller <rainer.keller@digia.com> | 2013-03-04 13:34:29 +0100 |
commit | a7f07ef9854b3501e7147e695fe0a7aa88140c1d (patch) | |
tree | 2a4cbaad88f1615058ab34a0bb68347d90ed26f2 | |
parent | 444f75cff164190e2106bdce0f5b0047e2bb0505 (diff) |
Make this a terminal application
Network support is removed.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | README | 87 | ||||
-rw-r--r-- | app.cpp | 118 | ||||
-rw-r--r-- | app.h | 43 | ||||
-rw-r--r-- | appcontroller.pro | 8 | ||||
-rw-r--r-- | appdaemon.pro | 15 | ||||
-rw-r--r-- | daemon.cpp | 203 | ||||
-rw-r--r-- | daemon.h | 46 | ||||
-rw-r--r-- | main.cpp | 127 | ||||
-rw-r--r-- | protocol.cpp | 90 | ||||
-rw-r--r-- | protocol.h | 29 |
11 files changed, 133 insertions, 635 deletions
@@ -1,4 +1,4 @@ -appdaemon +appcontroller Makefile *.o moc_*.cpp @@ -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"; -} - @@ -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 @@ -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 |