From a7f07ef9854b3501e7147e695fe0a7aa88140c1d Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Mon, 4 Mar 2013 13:34:29 +0100 Subject: Make this a terminal application Network support is removed. --- .gitignore | 2 +- README | 87 ----------------------- app.cpp | 118 ------------------------------- app.h | 43 ------------ appcontroller.pro | 8 +++ appdaemon.pro | 15 ---- daemon.cpp | 203 ------------------------------------------------------ daemon.h | 46 ------------- main.cpp | 127 +++++++++++++++++++++++++++++++++- protocol.cpp | 90 ------------------------ protocol.h | 29 -------- 11 files changed, 133 insertions(+), 635 deletions(-) delete mode 100644 README delete mode 100644 app.cpp delete mode 100644 app.h create mode 100644 appcontroller.pro delete mode 100644 appdaemon.pro delete mode 100644 daemon.cpp delete mode 100644 daemon.h delete mode 100644 protocol.cpp delete mode 100644 protocol.h 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:[:]@\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[:][:]@\n - -Starts debugging. If no binary given it attaches to the running app. -Otherwise it starts a new app as described in '@start'. - -@stdin:@\n - -A line to be sent to the apps stdin. - -@env:[:]@\n - -Set an environment variable. - -= - -If value is missing will be removed from environment. - -Notifications -------------- - -Notifications are sent to QtCreator. - -@started:@\n - -An app was started successfully. - -@stopped:::@\n - -An app was/had stopped. -exitstatus: 0=NormalExit; 1=CrashExit -exitcode: Exitcode in case the program exits normally. Otherwise undefined. - -@stdout::@\n - -A line of the apps stdout - -@stderr::@\n -A line of the apps stderr - -@debugging::@\n - -A debugger started on port. - -@error:@\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 -#include -#include - -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 -#include -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 -#include -#include - -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 -#include -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 -#include "daemon.h" +#include +#include +#include +#include +#include + +#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 - -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 -#include - -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 -- cgit v1.2.3