Add installer
This commit is contained in:
parent
c9bc8aa8c1
commit
a2a5cafc5f
73 changed files with 4354 additions and 488 deletions
1
service/src/QtService
Normal file
1
service/src/QtService
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "qtservice.h"
|
||||
1
service/src/QtServiceBase
Normal file
1
service/src/QtServiceBase
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "qtservice.h"
|
||||
1
service/src/QtServiceController
Normal file
1
service/src/QtServiceController
Normal file
|
|
@ -0,0 +1 @@
|
|||
#include "qtservice.h"
|
||||
1129
service/src/qtservice.cpp
Normal file
1129
service/src/qtservice.cpp
Normal file
File diff suppressed because it is too large
Load diff
192
service/src/qtservice.h
Normal file
192
service/src/qtservice.h
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTSERVICE_H
|
||||
#define QTSERVICE_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# if !defined(QT_QTSERVICE_EXPORT) && !defined(QT_QTSERVICE_IMPORT)
|
||||
# define QT_QTSERVICE_EXPORT
|
||||
# elif defined(QT_QTSERVICE_IMPORT)
|
||||
# if defined(QT_QTSERVICE_EXPORT)
|
||||
# undef QT_QTSERVICE_EXPORT
|
||||
# endif
|
||||
# define QT_QTSERVICE_EXPORT __declspec(dllimport)
|
||||
# elif defined(QT_QTSERVICE_EXPORT)
|
||||
# undef QT_QTSERVICE_EXPORT
|
||||
# define QT_QTSERVICE_EXPORT __declspec(dllexport)
|
||||
# endif
|
||||
#else
|
||||
# define QT_QTSERVICE_EXPORT
|
||||
#endif
|
||||
|
||||
class QStringList;
|
||||
class QtServiceControllerPrivate;
|
||||
|
||||
class QT_QTSERVICE_EXPORT QtServiceController
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QtServiceController)
|
||||
public:
|
||||
enum StartupType
|
||||
{
|
||||
AutoStartup = 0, ManualStartup
|
||||
};
|
||||
|
||||
QtServiceController(const QString &name);
|
||||
virtual ~QtServiceController();
|
||||
|
||||
bool isInstalled() const;
|
||||
bool isRunning() const;
|
||||
|
||||
QString serviceName() const;
|
||||
QString serviceDescription() const;
|
||||
StartupType startupType() const;
|
||||
QString serviceFilePath() const;
|
||||
|
||||
static bool install(const QString &serviceFilePath, const QString &account = QString(),
|
||||
const QString &password = QString());
|
||||
bool uninstall();
|
||||
|
||||
bool start(const QStringList &arguments);
|
||||
bool start();
|
||||
bool stop();
|
||||
bool pause();
|
||||
bool resume();
|
||||
bool sendCommand(int code);
|
||||
|
||||
private:
|
||||
QtServiceControllerPrivate *d_ptr;
|
||||
};
|
||||
|
||||
class QtServiceBasePrivate;
|
||||
|
||||
class QT_QTSERVICE_EXPORT QtServiceBase
|
||||
{
|
||||
Q_DECLARE_PRIVATE(QtServiceBase)
|
||||
public:
|
||||
|
||||
enum MessageType
|
||||
{
|
||||
Success = 0, Error, Warning, Information
|
||||
};
|
||||
|
||||
enum ServiceFlag
|
||||
{
|
||||
Default = 0x00,
|
||||
CanBeSuspended = 0x01,
|
||||
CannotBeStopped = 0x02,
|
||||
NeedsStopOnShutdown = 0x04
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(ServiceFlags, ServiceFlag)
|
||||
|
||||
QtServiceBase(int argc, char **argv, const QString &name);
|
||||
virtual ~QtServiceBase();
|
||||
|
||||
QString serviceName() const;
|
||||
|
||||
QString serviceDescription() const;
|
||||
void setServiceDescription(const QString &description);
|
||||
|
||||
QtServiceController::StartupType startupType() const;
|
||||
void setStartupType(QtServiceController::StartupType startupType);
|
||||
|
||||
ServiceFlags serviceFlags() const;
|
||||
void setServiceFlags(ServiceFlags flags);
|
||||
|
||||
int exec();
|
||||
|
||||
void logMessage(const QString &message, MessageType type = Success,
|
||||
int id = 0, uint category = 0, const QByteArray &data = QByteArray());
|
||||
|
||||
static QtServiceBase *instance();
|
||||
|
||||
protected:
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop();
|
||||
virtual void pause();
|
||||
virtual void resume();
|
||||
virtual void processCommand(int code);
|
||||
|
||||
virtual void createApplication(int &argc, char **argv) = 0;
|
||||
|
||||
virtual int executeApplication() = 0;
|
||||
|
||||
private:
|
||||
|
||||
friend class QtServiceSysPrivate;
|
||||
QtServiceBasePrivate *d_ptr;
|
||||
};
|
||||
|
||||
template <typename Application>
|
||||
class QtService : public QtServiceBase
|
||||
{
|
||||
public:
|
||||
QtService(int argc, char **argv, const QString &name)
|
||||
: QtServiceBase(argc, argv, name), app(0)
|
||||
{ }
|
||||
~QtService()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
Application *application() const
|
||||
{ return app; }
|
||||
|
||||
virtual void createApplication(int &argc, char **argv)
|
||||
{
|
||||
app = new Application(argc, argv);
|
||||
QCoreApplication *a = app;
|
||||
Q_UNUSED(a);
|
||||
}
|
||||
|
||||
virtual int executeApplication()
|
||||
{ return Application::exec(); }
|
||||
|
||||
private:
|
||||
Application *app;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QtServiceBase::ServiceFlags)
|
||||
|
||||
#endif // QTSERVICE_H
|
||||
21
service/src/qtservice.pri
Normal file
21
service/src/qtservice.pri
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
include(../common.pri)
|
||||
INCLUDEPATH += $$PWD
|
||||
DEPENDPATH += $$PWD
|
||||
!win32:QT += network
|
||||
win32:LIBS += -luser32
|
||||
|
||||
qtservice-uselib:!qtservice-buildlib {
|
||||
LIBS += -L$$QTSERVICE_LIBDIR -l$$QTSERVICE_LIBNAME
|
||||
} else {
|
||||
HEADERS += $$PWD/qtservice.h \
|
||||
$$PWD/qtservice_p.h
|
||||
SOURCES += $$PWD/qtservice.cpp
|
||||
win32:SOURCES += $$PWD/qtservice_win.cpp
|
||||
unix:HEADERS += $$PWD/qtunixsocket.h $$PWD/qtunixserversocket.h
|
||||
unix:SOURCES += $$PWD/qtservice_unix.cpp $$PWD/qtunixsocket.cpp $$PWD/qtunixserversocket.cpp
|
||||
}
|
||||
|
||||
win32 {
|
||||
qtservice-buildlib:shared:DEFINES += QT_QTSERVICE_EXPORT
|
||||
else:qtservice-uselib:DEFINES += QT_QTSERVICE_IMPORT
|
||||
}
|
||||
87
service/src/qtservice_p.h
Normal file
87
service/src/qtservice_p.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTSERVICE_P_H
|
||||
#define QTSERVICE_P_H
|
||||
|
||||
#include <QStringList>
|
||||
#include "qtservice.h"
|
||||
|
||||
class QtServiceControllerPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QtServiceController)
|
||||
public:
|
||||
QString serviceName;
|
||||
QtServiceController *q_ptr;
|
||||
};
|
||||
|
||||
class QtServiceBasePrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QtServiceBase)
|
||||
public:
|
||||
|
||||
QtServiceBasePrivate(const QString &name);
|
||||
~QtServiceBasePrivate();
|
||||
|
||||
QtServiceBase *q_ptr;
|
||||
|
||||
QString serviceDescription;
|
||||
QtServiceController::StartupType startupType;
|
||||
QtServiceBase::ServiceFlags serviceFlags;
|
||||
QStringList args;
|
||||
|
||||
static class QtServiceBase *instance;
|
||||
|
||||
QtServiceController controller;
|
||||
|
||||
void startService();
|
||||
int run(bool asService, const QStringList &argList);
|
||||
bool install(const QString &account, const QString &password);
|
||||
|
||||
bool start();
|
||||
|
||||
QString filePath() const;
|
||||
bool sysInit();
|
||||
void sysSetPath();
|
||||
void sysCleanup();
|
||||
class QtServiceSysPrivate *sysd;
|
||||
};
|
||||
|
||||
#endif
|
||||
482
service/src/qtservice_unix.cpp
Normal file
482
service/src/qtservice_unix.cpp
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtservice.h"
|
||||
#include "qtservice_p.h"
|
||||
#include "qtunixsocket.h"
|
||||
#include "qtunixserversocket.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QStringList>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
#include <QDir>
|
||||
#include <pwd.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <QMap>
|
||||
#include <QSettings>
|
||||
#include <QProcess>
|
||||
|
||||
static QString encodeName(const QString &name, bool allowUpper = false)
|
||||
{
|
||||
QString n = name.toLower();
|
||||
QString legal = QLatin1String("abcdefghijklmnopqrstuvwxyz1234567890");
|
||||
if (allowUpper)
|
||||
legal += QLatin1String("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
int pos = 0;
|
||||
while (pos < n.size()) {
|
||||
if (legal.indexOf(n[pos]) == -1)
|
||||
n.remove(pos, 1);
|
||||
else
|
||||
++pos;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static QString login()
|
||||
{
|
||||
QString l;
|
||||
uid_t uid = getuid();
|
||||
passwd *pw = getpwuid(uid);
|
||||
if (pw)
|
||||
l = QString(pw->pw_name);
|
||||
return l;
|
||||
}
|
||||
|
||||
static QString socketPath(const QString &serviceName)
|
||||
{
|
||||
QString sn = encodeName(serviceName);
|
||||
return QString(QLatin1String("/var/tmp/") + sn + QLatin1String(".") + login());
|
||||
}
|
||||
|
||||
static bool sendCmd(const QString &serviceName, const QString &cmd)
|
||||
{
|
||||
bool retValue = false;
|
||||
QtUnixSocket sock;
|
||||
if (sock.connectTo(socketPath(serviceName))) {
|
||||
sock.write(QString(cmd+"\r\n").toLatin1().constData());
|
||||
sock.flush();
|
||||
sock.waitForReadyRead(-1);
|
||||
QString reply = sock.readAll();
|
||||
if (reply == QLatin1String("true"))
|
||||
retValue = true;
|
||||
sock.close();
|
||||
}
|
||||
return retValue;
|
||||
}
|
||||
|
||||
static QString absPath(const QString &path)
|
||||
{
|
||||
QString ret;
|
||||
if (path[0] != QChar('/')) { // Not an absolute path
|
||||
int slashpos;
|
||||
if ((slashpos = path.lastIndexOf('/')) != -1) { // Relative path
|
||||
QDir dir = QDir::current();
|
||||
dir.cd(path.left(slashpos));
|
||||
ret = dir.absolutePath();
|
||||
} else { // Need to search $PATH
|
||||
char *envPath = ::getenv("PATH");
|
||||
if (envPath) {
|
||||
QStringList envPaths = QString::fromLocal8Bit(envPath).split(':');
|
||||
for (int i = 0; i < envPaths.size(); ++i) {
|
||||
if (QFile::exists(envPaths.at(i) + QLatin1String("/") + QString(path))) {
|
||||
QDir dir(envPaths.at(i));
|
||||
ret = dir.absolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QFileInfo fi(path);
|
||||
ret = fi.absolutePath();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString QtServiceBasePrivate::filePath() const
|
||||
{
|
||||
QString ret;
|
||||
if (args.isEmpty())
|
||||
return ret;
|
||||
QFileInfo fi(args[0]);
|
||||
QDir dir(absPath(args[0]));
|
||||
return dir.absoluteFilePath(fi.fileName());
|
||||
}
|
||||
|
||||
|
||||
QString QtServiceController::serviceDescription() const
|
||||
{
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
settings.beginGroup("services");
|
||||
settings.beginGroup(serviceName());
|
||||
|
||||
QString desc = settings.value("description").toString();
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
QtServiceController::StartupType QtServiceController::startupType() const
|
||||
{
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
settings.beginGroup("services");
|
||||
settings.beginGroup(serviceName());
|
||||
|
||||
StartupType startupType = (StartupType)settings.value("startupType").toInt();
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
return startupType;
|
||||
}
|
||||
|
||||
QString QtServiceController::serviceFilePath() const
|
||||
{
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
settings.beginGroup("services");
|
||||
settings.beginGroup(serviceName());
|
||||
|
||||
QString path = settings.value("path").toString();
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
bool QtServiceController::uninstall()
|
||||
{
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
settings.beginGroup("services");
|
||||
|
||||
settings.remove(serviceName());
|
||||
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
|
||||
QSettings::Status ret = settings.status();
|
||||
if (ret == QSettings::AccessError) {
|
||||
fprintf(stderr, "Cannot uninstall \"%s\". Cannot write to: %s. Check permissions.\n",
|
||||
serviceName().toLatin1().constData(),
|
||||
settings.fileName().toLatin1().constData());
|
||||
}
|
||||
return (ret == QSettings::NoError);
|
||||
}
|
||||
|
||||
|
||||
bool QtServiceController::start(const QStringList &arguments)
|
||||
{
|
||||
if (!isInstalled())
|
||||
return false;
|
||||
if (isRunning())
|
||||
return false;
|
||||
return QProcess::startDetached(serviceFilePath(), arguments);
|
||||
}
|
||||
|
||||
bool QtServiceController::stop()
|
||||
{
|
||||
return sendCmd(serviceName(), QLatin1String("terminate"));
|
||||
}
|
||||
|
||||
bool QtServiceController::pause()
|
||||
{
|
||||
return sendCmd(serviceName(), QLatin1String("pause"));
|
||||
}
|
||||
|
||||
bool QtServiceController::resume()
|
||||
{
|
||||
return sendCmd(serviceName(), QLatin1String("resume"));
|
||||
}
|
||||
|
||||
bool QtServiceController::sendCommand(int code)
|
||||
{
|
||||
return sendCmd(serviceName(), QString(QLatin1String("num:") + QString::number(code)));
|
||||
}
|
||||
|
||||
bool QtServiceController::isInstalled() const
|
||||
{
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
settings.beginGroup("services");
|
||||
|
||||
QStringList list = settings.childGroups();
|
||||
|
||||
settings.endGroup();
|
||||
|
||||
QStringListIterator it(list);
|
||||
while (it.hasNext()) {
|
||||
if (it.next() == serviceName())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QtServiceController::isRunning() const
|
||||
{
|
||||
QtUnixSocket sock;
|
||||
if (sock.connectTo(socketPath(serviceName())))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////
|
||||
|
||||
class QtServiceSysPrivate : public QtUnixServerSocket
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QtServiceSysPrivate();
|
||||
~QtServiceSysPrivate();
|
||||
|
||||
char *ident;
|
||||
|
||||
QtServiceBase::ServiceFlags serviceFlags;
|
||||
|
||||
protected:
|
||||
#if QT_VERSION >= 0x050000
|
||||
void incomingConnection(qintptr socketDescriptor);
|
||||
#else
|
||||
void incomingConnection(int socketDescriptor);
|
||||
#endif
|
||||
|
||||
private slots:
|
||||
void slotReady();
|
||||
void slotClosed();
|
||||
|
||||
private:
|
||||
QString getCommand(const QTcpSocket *socket);
|
||||
QMap<const QTcpSocket *, QString> cache;
|
||||
};
|
||||
|
||||
QtServiceSysPrivate::QtServiceSysPrivate()
|
||||
: QtUnixServerSocket(), ident(0), serviceFlags(0)
|
||||
{
|
||||
}
|
||||
|
||||
QtServiceSysPrivate::~QtServiceSysPrivate()
|
||||
{
|
||||
if (ident)
|
||||
delete[] ident;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
void QtServiceSysPrivate::incomingConnection(qintptr socketDescriptor)
|
||||
#else
|
||||
void QtServiceSysPrivate::incomingConnection(int socketDescriptor)
|
||||
#endif
|
||||
{
|
||||
QTcpSocket *s = new QTcpSocket(this);
|
||||
s->setSocketDescriptor(socketDescriptor);
|
||||
connect(s, SIGNAL(readyRead()), this, SLOT(slotReady()));
|
||||
connect(s, SIGNAL(disconnected()), this, SLOT(slotClosed()));
|
||||
}
|
||||
|
||||
void QtServiceSysPrivate::slotReady()
|
||||
{
|
||||
QTcpSocket *s = (QTcpSocket *)sender();
|
||||
cache[s] += QString(s->readAll());
|
||||
QString cmd = getCommand(s);
|
||||
while (!cmd.isEmpty()) {
|
||||
bool retValue = false;
|
||||
if (cmd == QLatin1String("terminate")) {
|
||||
if (!(serviceFlags & QtServiceBase::CannotBeStopped)) {
|
||||
QtServiceBase::instance()->stop();
|
||||
QCoreApplication::instance()->quit();
|
||||
retValue = true;
|
||||
}
|
||||
} else if (cmd == QLatin1String("pause")) {
|
||||
if (serviceFlags & QtServiceBase::CanBeSuspended) {
|
||||
QtServiceBase::instance()->pause();
|
||||
retValue = true;
|
||||
}
|
||||
} else if (cmd == QLatin1String("resume")) {
|
||||
if (serviceFlags & QtServiceBase::CanBeSuspended) {
|
||||
QtServiceBase::instance()->resume();
|
||||
retValue = true;
|
||||
}
|
||||
} else if (cmd == QLatin1String("alive")) {
|
||||
retValue = true;
|
||||
} else if (cmd.length() > 4 && cmd.left(4) == QLatin1String("num:")) {
|
||||
cmd = cmd.mid(4);
|
||||
QtServiceBase::instance()->processCommand(cmd.toInt());
|
||||
retValue = true;
|
||||
}
|
||||
QString retString;
|
||||
if (retValue)
|
||||
retString = QLatin1String("true");
|
||||
else
|
||||
retString = QLatin1String("false");
|
||||
s->write(retString.toLatin1().constData());
|
||||
s->flush();
|
||||
cmd = getCommand(s);
|
||||
}
|
||||
}
|
||||
|
||||
void QtServiceSysPrivate::slotClosed()
|
||||
{
|
||||
QTcpSocket *s = (QTcpSocket *)sender();
|
||||
s->deleteLater();
|
||||
}
|
||||
|
||||
QString QtServiceSysPrivate::getCommand(const QTcpSocket *socket)
|
||||
{
|
||||
int pos = cache[socket].indexOf("\r\n");
|
||||
if (pos >= 0) {
|
||||
QString ret = cache[socket].left(pos);
|
||||
cache[socket].remove(0, pos+2);
|
||||
return ret;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
#include "qtservice_unix.moc"
|
||||
|
||||
bool QtServiceBasePrivate::sysInit()
|
||||
{
|
||||
sysd = new QtServiceSysPrivate;
|
||||
sysd->serviceFlags = serviceFlags;
|
||||
// Restrict permissions on files that are created by the service
|
||||
::umask(027);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtServiceBasePrivate::sysSetPath()
|
||||
{
|
||||
if (sysd)
|
||||
sysd->setPath(socketPath(controller.serviceName()));
|
||||
}
|
||||
|
||||
void QtServiceBasePrivate::sysCleanup()
|
||||
{
|
||||
if (sysd) {
|
||||
sysd->close();
|
||||
delete sysd;
|
||||
sysd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool QtServiceBasePrivate::start()
|
||||
{
|
||||
if (sendCmd(controller.serviceName(), "alive")) {
|
||||
// Already running
|
||||
return false;
|
||||
}
|
||||
// Could just call controller.start() here, but that would fail if
|
||||
// we're not installed. We do not want to strictly require installation.
|
||||
::setenv("QTSERVICE_RUN", "1", 1); // Tell the detached process it's it
|
||||
return QProcess::startDetached(filePath(), args.mid(1), "/");
|
||||
}
|
||||
|
||||
bool QtServiceBasePrivate::install(const QString &account, const QString &password)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
Q_UNUSED(password)
|
||||
QSettings settings(QSettings::SystemScope, "QtSoftware");
|
||||
|
||||
settings.beginGroup("services");
|
||||
settings.beginGroup(controller.serviceName());
|
||||
|
||||
settings.setValue("path", filePath());
|
||||
settings.setValue("description", serviceDescription);
|
||||
settings.setValue("automaticStartup", startupType);
|
||||
|
||||
settings.endGroup();
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
|
||||
QSettings::Status ret = settings.status();
|
||||
if (ret == QSettings::AccessError) {
|
||||
fprintf(stderr, "Cannot install \"%s\". Cannot write to: %s. Check permissions.\n",
|
||||
controller.serviceName().toLatin1().constData(),
|
||||
settings.fileName().toLatin1().constData());
|
||||
}
|
||||
return (ret == QSettings::NoError);
|
||||
}
|
||||
|
||||
void QtServiceBase::logMessage(const QString &message, QtServiceBase::MessageType type,
|
||||
int, uint, const QByteArray &)
|
||||
{
|
||||
if (!d_ptr->sysd)
|
||||
return;
|
||||
int st;
|
||||
switch(type) {
|
||||
case QtServiceBase::Error:
|
||||
st = LOG_ERR;
|
||||
break;
|
||||
case QtServiceBase::Warning:
|
||||
st = LOG_WARNING;
|
||||
break;
|
||||
default:
|
||||
st = LOG_INFO;
|
||||
}
|
||||
if (!d_ptr->sysd->ident) {
|
||||
QString tmp = encodeName(serviceName(), true);
|
||||
int len = tmp.toLocal8Bit().size();
|
||||
d_ptr->sysd->ident = new char[len+1];
|
||||
d_ptr->sysd->ident[len] = '\0';
|
||||
::memcpy(d_ptr->sysd->ident, tmp.toLocal8Bit().constData(), len);
|
||||
}
|
||||
openlog(d_ptr->sysd->ident, LOG_PID, LOG_DAEMON);
|
||||
foreach(QString line, message.split('\n'))
|
||||
syslog(st, "%s", line.toLocal8Bit().constData());
|
||||
closelog();
|
||||
}
|
||||
|
||||
void QtServiceBase::setServiceFlags(QtServiceBase::ServiceFlags flags)
|
||||
{
|
||||
if (d_ptr->serviceFlags == flags)
|
||||
return;
|
||||
d_ptr->serviceFlags = flags;
|
||||
if (d_ptr->sysd)
|
||||
d_ptr->sysd->serviceFlags = flags;
|
||||
}
|
||||
|
||||
952
service/src/qtservice_win.cpp
Normal file
952
service/src/qtservice_win.cpp
Normal file
|
|
@ -0,0 +1,952 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtservice.h"
|
||||
#include "qtservice_p.h"
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QLibrary>
|
||||
#include <QMutex>
|
||||
#include <QSemaphore>
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QTextStream>
|
||||
#include <qt_windows.h>
|
||||
#include <QWaitCondition>
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QVector>
|
||||
#include <QThread>
|
||||
#if QT_VERSION >= 0x050000
|
||||
# include <QAbstractNativeEventFilter>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#if defined(QTSERVICE_DEBUG)
|
||||
#include <QDebug>
|
||||
#endif
|
||||
|
||||
typedef SERVICE_STATUS_HANDLE(WINAPI*PRegisterServiceCtrlHandler)(const wchar_t*,LPHANDLER_FUNCTION);
|
||||
static PRegisterServiceCtrlHandler pRegisterServiceCtrlHandler = 0;
|
||||
typedef BOOL(WINAPI*PSetServiceStatus)(SERVICE_STATUS_HANDLE,LPSERVICE_STATUS);
|
||||
static PSetServiceStatus pSetServiceStatus = 0;
|
||||
typedef BOOL(WINAPI*PChangeServiceConfig2)(SC_HANDLE,DWORD,LPVOID);
|
||||
static PChangeServiceConfig2 pChangeServiceConfig2 = 0;
|
||||
typedef BOOL(WINAPI*PCloseServiceHandle)(SC_HANDLE);
|
||||
static PCloseServiceHandle pCloseServiceHandle = 0;
|
||||
typedef SC_HANDLE(WINAPI*PCreateService)(SC_HANDLE,LPCTSTR,LPCTSTR,DWORD,DWORD,DWORD,DWORD,LPCTSTR,LPCTSTR,LPDWORD,LPCTSTR,LPCTSTR,LPCTSTR);
|
||||
static PCreateService pCreateService = 0;
|
||||
typedef SC_HANDLE(WINAPI*POpenSCManager)(LPCTSTR,LPCTSTR,DWORD);
|
||||
static POpenSCManager pOpenSCManager = 0;
|
||||
typedef BOOL(WINAPI*PDeleteService)(SC_HANDLE);
|
||||
static PDeleteService pDeleteService = 0;
|
||||
typedef SC_HANDLE(WINAPI*POpenService)(SC_HANDLE,LPCTSTR,DWORD);
|
||||
static POpenService pOpenService = 0;
|
||||
typedef BOOL(WINAPI*PQueryServiceStatus)(SC_HANDLE,LPSERVICE_STATUS);
|
||||
static PQueryServiceStatus pQueryServiceStatus = 0;
|
||||
typedef BOOL(WINAPI*PStartServiceCtrlDispatcher)(CONST SERVICE_TABLE_ENTRY*);
|
||||
static PStartServiceCtrlDispatcher pStartServiceCtrlDispatcher = 0;
|
||||
typedef BOOL(WINAPI*PStartService)(SC_HANDLE,DWORD,const wchar_t**);
|
||||
static PStartService pStartService = 0;
|
||||
typedef BOOL(WINAPI*PControlService)(SC_HANDLE,DWORD,LPSERVICE_STATUS);
|
||||
static PControlService pControlService = 0;
|
||||
typedef HANDLE(WINAPI*PDeregisterEventSource)(HANDLE);
|
||||
static PDeregisterEventSource pDeregisterEventSource = 0;
|
||||
typedef BOOL(WINAPI*PReportEvent)(HANDLE,WORD,WORD,DWORD,PSID,WORD,DWORD,LPCTSTR*,LPVOID);
|
||||
static PReportEvent pReportEvent = 0;
|
||||
typedef HANDLE(WINAPI*PRegisterEventSource)(LPCTSTR,LPCTSTR);
|
||||
static PRegisterEventSource pRegisterEventSource = 0;
|
||||
typedef DWORD(WINAPI*PRegisterServiceProcess)(DWORD,DWORD);
|
||||
static PRegisterServiceProcess pRegisterServiceProcess = 0;
|
||||
typedef BOOL(WINAPI*PQueryServiceConfig)(SC_HANDLE,LPQUERY_SERVICE_CONFIG,DWORD,LPDWORD);
|
||||
static PQueryServiceConfig pQueryServiceConfig = 0;
|
||||
typedef BOOL(WINAPI*PQueryServiceConfig2)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD);
|
||||
static PQueryServiceConfig2 pQueryServiceConfig2 = 0;
|
||||
|
||||
|
||||
#define RESOLVE(name) p##name = (P##name)lib.resolve(#name);
|
||||
#define RESOLVEA(name) p##name = (P##name)lib.resolve(#name"A");
|
||||
#define RESOLVEW(name) p##name = (P##name)lib.resolve(#name"W");
|
||||
|
||||
static bool winServiceInit()
|
||||
{
|
||||
if (!pOpenSCManager) {
|
||||
QLibrary lib("advapi32");
|
||||
|
||||
// only resolve unicode versions
|
||||
RESOLVEW(RegisterServiceCtrlHandler);
|
||||
RESOLVE(SetServiceStatus);
|
||||
RESOLVEW(ChangeServiceConfig2);
|
||||
RESOLVE(CloseServiceHandle);
|
||||
RESOLVEW(CreateService);
|
||||
RESOLVEW(OpenSCManager);
|
||||
RESOLVE(DeleteService);
|
||||
RESOLVEW(OpenService);
|
||||
RESOLVE(QueryServiceStatus);
|
||||
RESOLVEW(StartServiceCtrlDispatcher);
|
||||
RESOLVEW(StartService); // need only Ansi version
|
||||
RESOLVE(ControlService);
|
||||
RESOLVE(DeregisterEventSource);
|
||||
RESOLVEW(ReportEvent);
|
||||
RESOLVEW(RegisterEventSource);
|
||||
RESOLVEW(QueryServiceConfig);
|
||||
RESOLVEW(QueryServiceConfig2);
|
||||
}
|
||||
return pOpenSCManager != 0;
|
||||
}
|
||||
|
||||
bool QtServiceController::isInstalled() const
|
||||
{
|
||||
Q_D(const QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t*)d->serviceName.utf16(),
|
||||
SERVICE_QUERY_CONFIG);
|
||||
|
||||
if (hService) {
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::isRunning() const
|
||||
{
|
||||
Q_D(const QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_QUERY_STATUS);
|
||||
if (hService) {
|
||||
SERVICE_STATUS info;
|
||||
int res = pQueryServiceStatus(hService, &info);
|
||||
if (res)
|
||||
result = info.dwCurrentState != SERVICE_STOPPED;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
QString QtServiceController::serviceFilePath() const
|
||||
{
|
||||
Q_D(const QtServiceController);
|
||||
QString result;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_QUERY_CONFIG);
|
||||
if (hService) {
|
||||
DWORD sizeNeeded = 0;
|
||||
char data[8 * 1024];
|
||||
if (pQueryServiceConfig(hService, (LPQUERY_SERVICE_CONFIG)data, 8 * 1024, &sizeNeeded)) {
|
||||
LPQUERY_SERVICE_CONFIG config = (LPQUERY_SERVICE_CONFIG)data;
|
||||
result = QString::fromUtf16((const ushort*)config->lpBinaryPathName);
|
||||
}
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString QtServiceController::serviceDescription() const
|
||||
{
|
||||
Q_D(const QtServiceController);
|
||||
QString result;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_QUERY_CONFIG);
|
||||
if (hService) {
|
||||
DWORD dwBytesNeeded;
|
||||
char data[8 * 1024];
|
||||
if (pQueryServiceConfig2(
|
||||
hService,
|
||||
SERVICE_CONFIG_DESCRIPTION,
|
||||
(unsigned char *)data,
|
||||
8096,
|
||||
&dwBytesNeeded)) {
|
||||
LPSERVICE_DESCRIPTION desc = (LPSERVICE_DESCRIPTION)data;
|
||||
if (desc->lpDescription)
|
||||
result = QString::fromUtf16((const ushort*)desc->lpDescription);
|
||||
}
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QtServiceController::StartupType QtServiceController::startupType() const
|
||||
{
|
||||
Q_D(const QtServiceController);
|
||||
StartupType result = ManualStartup;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_QUERY_CONFIG);
|
||||
if (hService) {
|
||||
DWORD sizeNeeded = 0;
|
||||
char data[8 * 1024];
|
||||
if (pQueryServiceConfig(hService, (QUERY_SERVICE_CONFIG *)data, 8 * 1024, &sizeNeeded)) {
|
||||
QUERY_SERVICE_CONFIG *config = (QUERY_SERVICE_CONFIG *)data;
|
||||
result = config->dwStartType == SERVICE_DEMAND_START ? ManualStartup : AutoStartup;
|
||||
}
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::uninstall()
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), DELETE);
|
||||
if (hService) {
|
||||
if (pDeleteService(hService))
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::start(const QStringList &args)
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
||||
if (hSCM) {
|
||||
// Try to open the service
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_START);
|
||||
if (hService) {
|
||||
QVector<const wchar_t *> argv(args.size());
|
||||
for (int i = 0; i < args.size(); ++i)
|
||||
argv[i] = (const wchar_t*)args.at(i).utf16();
|
||||
|
||||
if (pStartService(hService, args.size(), argv.data()))
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::stop()
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
||||
if (hSCM) {
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_STOP|SERVICE_QUERY_STATUS);
|
||||
if (hService) {
|
||||
SERVICE_STATUS status;
|
||||
if (pControlService(hService, SERVICE_CONTROL_STOP, &status)) {
|
||||
bool stopped = status.dwCurrentState == SERVICE_STOPPED;
|
||||
int i = 0;
|
||||
while(!stopped && i < 10) {
|
||||
Sleep(200);
|
||||
if (!pQueryServiceStatus(hService, &status))
|
||||
break;
|
||||
stopped = status.dwCurrentState == SERVICE_STOPPED;
|
||||
++i;
|
||||
}
|
||||
result = stopped;
|
||||
} else {
|
||||
qErrnoWarning(GetLastError(), "stopping");
|
||||
}
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::pause()
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
||||
if (hSCM) {
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_PAUSE_CONTINUE);
|
||||
if (hService) {
|
||||
SERVICE_STATUS status;
|
||||
if (pControlService(hService, SERVICE_CONTROL_PAUSE, &status))
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::resume()
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
||||
if (hSCM) {
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_PAUSE_CONTINUE);
|
||||
if (hService) {
|
||||
SERVICE_STATUS status;
|
||||
if (pControlService(hService, SERVICE_CONTROL_CONTINUE, &status))
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QtServiceController::sendCommand(int code)
|
||||
{
|
||||
Q_D(QtServiceController);
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
if (code < 0 || code > 127 || !isRunning())
|
||||
return result;
|
||||
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
|
||||
if (hSCM) {
|
||||
SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
|
||||
SERVICE_USER_DEFINED_CONTROL);
|
||||
if (hService) {
|
||||
SERVICE_STATUS status;
|
||||
if (pControlService(hService, 128 + code, &status))
|
||||
result = true;
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(QTSERVICE_DEBUG)
|
||||
# if QT_VERSION >= 0x050000
|
||||
extern void qtServiceLogDebug(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
# else
|
||||
extern void qtServiceLogDebug(QtMsgType type, const char* msg);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
void QtServiceBase::logMessage(const QString &message, MessageType type,
|
||||
int id, uint category, const QByteArray &data)
|
||||
{
|
||||
#if defined(QTSERVICE_DEBUG)
|
||||
QByteArray dbgMsg("[LOGGED ");
|
||||
switch (type) {
|
||||
case Error: dbgMsg += "Error] " ; break;
|
||||
case Warning: dbgMsg += "Warning] "; break;
|
||||
case Success: dbgMsg += "Success] "; break;
|
||||
case Information: //fall through
|
||||
default: dbgMsg += "Information] "; break;
|
||||
}
|
||||
# if QT_VERSION >= 0x050000
|
||||
qtServiceLogDebug((QtMsgType)-1, QMessageLogContext(), QLatin1String(dbgMsg) + message);
|
||||
# else
|
||||
qtServiceLogDebug((QtMsgType)-1, (dbgMsg + message.toAscii()).constData());
|
||||
# endif
|
||||
#endif
|
||||
|
||||
Q_D(QtServiceBase);
|
||||
if (!winServiceInit())
|
||||
return;
|
||||
WORD wType;
|
||||
switch (type) {
|
||||
case Error: wType = EVENTLOG_ERROR_TYPE; break;
|
||||
case Warning: wType = EVENTLOG_WARNING_TYPE; break;
|
||||
case Information: wType = EVENTLOG_INFORMATION_TYPE; break;
|
||||
default: wType = EVENTLOG_SUCCESS; break;
|
||||
}
|
||||
HANDLE h = pRegisterEventSource(0, (wchar_t *)d->controller.serviceName().utf16());
|
||||
if (h) {
|
||||
const wchar_t *msg = (wchar_t*)message.utf16();
|
||||
const char *bindata = data.size() ? data.constData() : 0;
|
||||
pReportEvent(h, wType, category, id, 0, 1, data.size(),(const wchar_t **)&msg,
|
||||
const_cast<char *>(bindata));
|
||||
pDeregisterEventSource(h);
|
||||
}
|
||||
}
|
||||
|
||||
class QtServiceControllerHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QtServiceControllerHandler(QtServiceSysPrivate *sys);
|
||||
|
||||
protected:
|
||||
void customEvent(QEvent *e);
|
||||
|
||||
private:
|
||||
QtServiceSysPrivate *d_sys;
|
||||
};
|
||||
|
||||
class QtServiceSysPrivate
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
QTSERVICE_STARTUP = 256
|
||||
};
|
||||
QtServiceSysPrivate();
|
||||
|
||||
void setStatus( DWORD dwState );
|
||||
void setServiceFlags(QtServiceBase::ServiceFlags flags);
|
||||
DWORD serviceFlags(QtServiceBase::ServiceFlags flags) const;
|
||||
inline bool available() const;
|
||||
static void WINAPI serviceMain( DWORD dwArgc, wchar_t** lpszArgv );
|
||||
static void WINAPI handler( DWORD dwOpcode );
|
||||
|
||||
SERVICE_STATUS status;
|
||||
SERVICE_STATUS_HANDLE serviceStatus;
|
||||
QStringList serviceArgs;
|
||||
|
||||
static QtServiceSysPrivate *instance;
|
||||
#if QT_VERSION < 0x050000
|
||||
static QCoreApplication::EventFilter nextFilter;
|
||||
#endif
|
||||
|
||||
QWaitCondition condition;
|
||||
QMutex mutex;
|
||||
QSemaphore startSemaphore;
|
||||
QSemaphore startSemaphore2;
|
||||
|
||||
QtServiceControllerHandler *controllerHandler;
|
||||
|
||||
void handleCustomEvent(QEvent *e);
|
||||
};
|
||||
|
||||
QtServiceControllerHandler::QtServiceControllerHandler(QtServiceSysPrivate *sys)
|
||||
: QObject(), d_sys(sys)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QtServiceControllerHandler::customEvent(QEvent *e)
|
||||
{
|
||||
d_sys->handleCustomEvent(e);
|
||||
}
|
||||
|
||||
|
||||
QtServiceSysPrivate *QtServiceSysPrivate::instance = 0;
|
||||
#if QT_VERSION < 0x050000
|
||||
QCoreApplication::EventFilter QtServiceSysPrivate::nextFilter = 0;
|
||||
#endif
|
||||
|
||||
QtServiceSysPrivate::QtServiceSysPrivate()
|
||||
{
|
||||
instance = this;
|
||||
}
|
||||
|
||||
inline bool QtServiceSysPrivate::available() const
|
||||
{
|
||||
return 0 != pOpenSCManager;
|
||||
}
|
||||
|
||||
void WINAPI QtServiceSysPrivate::serviceMain(DWORD dwArgc, wchar_t** lpszArgv)
|
||||
{
|
||||
if (!instance || !QtServiceBase::instance())
|
||||
return;
|
||||
|
||||
// Windows spins off a random thread to call this function on
|
||||
// startup, so here we just signal to the QApplication event loop
|
||||
// in the main thread to go ahead with start()'ing the service.
|
||||
|
||||
for (DWORD i = 0; i < dwArgc; i++)
|
||||
instance->serviceArgs.append(QString::fromUtf16((unsigned short*)lpszArgv[i]));
|
||||
|
||||
instance->startSemaphore.release(); // let the qapp creation start
|
||||
instance->startSemaphore2.acquire(); // wait until its done
|
||||
// Register the control request handler
|
||||
instance->serviceStatus = pRegisterServiceCtrlHandler((TCHAR*)QtServiceBase::instance()->serviceName().utf16(), handler);
|
||||
|
||||
if (!instance->serviceStatus) // cannot happen - something is utterly wrong
|
||||
return;
|
||||
|
||||
handler(QTSERVICE_STARTUP); // Signal startup to the application -
|
||||
// causes QtServiceBase::start() to be called in the main thread
|
||||
|
||||
// The MSDN doc says that this thread should just exit - the service is
|
||||
// running in the main thread (here, via callbacks in the handler thread).
|
||||
}
|
||||
|
||||
|
||||
// The handler() is called from the thread that called
|
||||
// StartServiceCtrlDispatcher, i.e. our HandlerThread, and
|
||||
// not from the main thread that runs the event loop, so we
|
||||
// have to post an event to ourselves, and use a QWaitCondition
|
||||
// and a QMutex to synchronize.
|
||||
void QtServiceSysPrivate::handleCustomEvent(QEvent *e)
|
||||
{
|
||||
int code = e->type() - QEvent::User;
|
||||
|
||||
switch(code) {
|
||||
case QTSERVICE_STARTUP: // Startup
|
||||
QtServiceBase::instance()->start();
|
||||
break;
|
||||
case SERVICE_CONTROL_STOP:
|
||||
QtServiceBase::instance()->stop();
|
||||
QCoreApplication::instance()->quit();
|
||||
break;
|
||||
case SERVICE_CONTROL_PAUSE:
|
||||
QtServiceBase::instance()->pause();
|
||||
break;
|
||||
case SERVICE_CONTROL_CONTINUE:
|
||||
QtServiceBase::instance()->resume();
|
||||
break;
|
||||
default:
|
||||
if (code >= 128 && code <= 255)
|
||||
QtServiceBase::instance()->processCommand(code - 128);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex.lock();
|
||||
condition.wakeAll();
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void WINAPI QtServiceSysPrivate::handler( DWORD code )
|
||||
{
|
||||
if (!instance)
|
||||
return;
|
||||
|
||||
instance->mutex.lock();
|
||||
switch (code) {
|
||||
case QTSERVICE_STARTUP: // QtService startup (called from WinMain when started)
|
||||
instance->setStatus(SERVICE_START_PENDING);
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
instance->setStatus(SERVICE_RUNNING);
|
||||
break;
|
||||
case SERVICE_CONTROL_STOP: // 1
|
||||
instance->setStatus(SERVICE_STOP_PENDING);
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
// status will be reported as stopped by start() when qapp::exec returns
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_PAUSE: // 2
|
||||
instance->setStatus(SERVICE_PAUSE_PENDING);
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
instance->setStatus(SERVICE_PAUSED);
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_CONTINUE: // 3
|
||||
instance->setStatus(SERVICE_CONTINUE_PENDING);
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
instance->setStatus(SERVICE_RUNNING);
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_INTERROGATE: // 4
|
||||
break;
|
||||
|
||||
case SERVICE_CONTROL_SHUTDOWN: // 5
|
||||
// Don't waste time with reporting stop pending, just do it
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + SERVICE_CONTROL_STOP)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
// status will be reported as stopped by start() when qapp::exec returns
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( code >= 128 && code <= 255 ) {
|
||||
QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
|
||||
instance->condition.wait(&instance->mutex);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
instance->mutex.unlock();
|
||||
|
||||
// Report current status
|
||||
if (instance->available() && instance->status.dwCurrentState != SERVICE_STOPPED)
|
||||
pSetServiceStatus(instance->serviceStatus, &instance->status);
|
||||
}
|
||||
|
||||
void QtServiceSysPrivate::setStatus(DWORD state)
|
||||
{
|
||||
if (!available())
|
||||
return;
|
||||
status.dwCurrentState = state;
|
||||
pSetServiceStatus(serviceStatus, &status);
|
||||
}
|
||||
|
||||
void QtServiceSysPrivate::setServiceFlags(QtServiceBase::ServiceFlags flags)
|
||||
{
|
||||
if (!available())
|
||||
return;
|
||||
status.dwControlsAccepted = serviceFlags(flags);
|
||||
pSetServiceStatus(serviceStatus, &status);
|
||||
}
|
||||
|
||||
DWORD QtServiceSysPrivate::serviceFlags(QtServiceBase::ServiceFlags flags) const
|
||||
{
|
||||
DWORD control = 0;
|
||||
if (flags & QtServiceBase::CanBeSuspended)
|
||||
control |= SERVICE_ACCEPT_PAUSE_CONTINUE;
|
||||
if (!(flags & QtServiceBase::CannotBeStopped))
|
||||
control |= SERVICE_ACCEPT_STOP;
|
||||
if (flags & QtServiceBase::NeedsStopOnShutdown)
|
||||
control |= SERVICE_ACCEPT_SHUTDOWN;
|
||||
|
||||
return control;
|
||||
}
|
||||
|
||||
#include "qtservice_win.moc"
|
||||
|
||||
|
||||
class HandlerThread : public QThread
|
||||
{
|
||||
public:
|
||||
HandlerThread()
|
||||
: success(true), console(false), QThread()
|
||||
{}
|
||||
|
||||
bool calledOk() { return success; }
|
||||
bool runningAsConsole() { return console; }
|
||||
|
||||
protected:
|
||||
bool success, console;
|
||||
void run()
|
||||
{
|
||||
SERVICE_TABLE_ENTRYW st [2];
|
||||
st[0].lpServiceName = (wchar_t*)QtServiceBase::instance()->serviceName().utf16();
|
||||
st[0].lpServiceProc = QtServiceSysPrivate::serviceMain;
|
||||
st[1].lpServiceName = 0;
|
||||
st[1].lpServiceProc = 0;
|
||||
|
||||
success = (pStartServiceCtrlDispatcher(st) != 0); // should block
|
||||
|
||||
if (!success) {
|
||||
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
|
||||
// Means we're started from console, not from service mgr
|
||||
// start() will ask the mgr to start another instance of us as a service instead
|
||||
console = true;
|
||||
}
|
||||
else {
|
||||
QtServiceBase::instance()->logMessage(QString("The Service failed to start [%1]").arg(qt_error_string(GetLastError())), QtServiceBase::Error);
|
||||
}
|
||||
QtServiceSysPrivate::instance->startSemaphore.release(); // let start() continue, since serviceMain won't be doing it
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Ignore WM_ENDSESSION system events, since they make the Qt kernel quit
|
||||
*/
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
|
||||
class QtServiceAppEventFilter : public QAbstractNativeEventFilter
|
||||
{
|
||||
public:
|
||||
QtServiceAppEventFilter() {}
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
|
||||
};
|
||||
|
||||
bool QtServiceAppEventFilter::nativeEventFilter(const QByteArray &, void *message, long *result)
|
||||
{
|
||||
MSG *winMessage = (MSG*)message;
|
||||
if (winMessage->message == WM_ENDSESSION && (winMessage->lParam & ENDSESSION_LOGOFF)) {
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_GLOBAL_STATIC(QtServiceAppEventFilter, qtServiceAppEventFilter)
|
||||
|
||||
#else
|
||||
|
||||
bool myEventFilter(void* message, long* result)
|
||||
{
|
||||
MSG* msg = reinterpret_cast<MSG*>(message);
|
||||
if (!msg || (msg->message != WM_ENDSESSION) || !(msg->lParam & ENDSESSION_LOGOFF))
|
||||
return QtServiceSysPrivate::nextFilter ? QtServiceSysPrivate::nextFilter(message, result) : false;
|
||||
|
||||
if (QtServiceSysPrivate::nextFilter)
|
||||
QtServiceSysPrivate::nextFilter(message, result);
|
||||
if (result)
|
||||
*result = TRUE;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* There are three ways we can be started:
|
||||
|
||||
- By a service controller (e.g. the Services control panel), with
|
||||
no (service-specific) arguments. ServiceBase::exec() will then call
|
||||
start() below, and the service will start.
|
||||
|
||||
- From the console, but with no (service-specific) arguments. This
|
||||
means we should ask a controller to start the service (i.e. another
|
||||
instance of this executable), and then just terminate. We discover
|
||||
this case (as different from the above) by the fact that
|
||||
StartServiceCtrlDispatcher will return an error, instead of blocking.
|
||||
|
||||
- From the console, with -e(xec) argument. ServiceBase::exec() will
|
||||
then call ServiceBasePrivate::exec(), which calls
|
||||
ServiceBasePrivate::run(), which runs the application as a normal
|
||||
program.
|
||||
*/
|
||||
|
||||
bool QtServiceBasePrivate::start()
|
||||
{
|
||||
sysInit();
|
||||
if (!winServiceInit())
|
||||
return false;
|
||||
|
||||
// Since StartServiceCtrlDispatcher() blocks waiting for service
|
||||
// control events, we need to call it in another thread, so that
|
||||
// the main thread can run the QApplication event loop.
|
||||
HandlerThread* ht = new HandlerThread();
|
||||
ht->start();
|
||||
|
||||
QtServiceSysPrivate* sys = QtServiceSysPrivate::instance;
|
||||
|
||||
// Wait until service args have been received by serviceMain.
|
||||
// If Windows doesn't call serviceMain (or
|
||||
// StartServiceControlDispatcher doesn't return an error) within
|
||||
// a timeout of 20 secs, something is very wrong; give up
|
||||
if (!sys->startSemaphore.tryAcquire(1, 20000))
|
||||
return false;
|
||||
|
||||
if (!ht->calledOk()) {
|
||||
if (ht->runningAsConsole())
|
||||
return controller.start(args.mid(1));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int argc = sys->serviceArgs.size();
|
||||
QVector<char *> argv(argc);
|
||||
QList<QByteArray> argvData;
|
||||
for (int i = 0; i < argc; ++i)
|
||||
argvData.append(sys->serviceArgs.at(i).toLocal8Bit());
|
||||
for (int i = 0; i < argc; ++i)
|
||||
argv[i] = argvData[i].data();
|
||||
|
||||
q_ptr->createApplication(argc, argv.data());
|
||||
QCoreApplication *app = QCoreApplication::instance();
|
||||
if (!app)
|
||||
return false;
|
||||
|
||||
#if QT_VERSION >= 0x050000
|
||||
QAbstractEventDispatcher::instance()->installNativeEventFilter(qtServiceAppEventFilter());
|
||||
#else
|
||||
QtServiceSysPrivate::nextFilter = app->setEventFilter(myEventFilter);
|
||||
#endif
|
||||
|
||||
sys->controllerHandler = new QtServiceControllerHandler(sys);
|
||||
|
||||
sys->startSemaphore2.release(); // let serviceMain continue (and end)
|
||||
|
||||
sys->status.dwWin32ExitCode = q_ptr->executeApplication();
|
||||
sys->setStatus(SERVICE_STOPPED);
|
||||
|
||||
if (ht->isRunning())
|
||||
ht->wait(1000); // let the handler thread finish
|
||||
delete sys->controllerHandler;
|
||||
sys->controllerHandler = 0;
|
||||
if (ht->isFinished())
|
||||
delete ht;
|
||||
delete app;
|
||||
sysCleanup();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QtServiceBasePrivate::install(const QString &account, const QString &password)
|
||||
{
|
||||
bool result = false;
|
||||
if (!winServiceInit())
|
||||
return result;
|
||||
|
||||
// Open the Service Control Manager
|
||||
SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
|
||||
if (hSCM) {
|
||||
QString acc = account;
|
||||
DWORD dwStartType = startupType == QtServiceController::AutoStartup ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
|
||||
DWORD dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
wchar_t *act = 0;
|
||||
wchar_t *pwd = 0;
|
||||
if (!acc.isEmpty()) {
|
||||
// The act string must contain a string of the format "Domain\UserName",
|
||||
// so if only a username was specified without a domain, default to the local machine domain.
|
||||
if (!acc.contains(QChar('\\'))) {
|
||||
acc.prepend(QLatin1String(".\\"));
|
||||
}
|
||||
if (!acc.endsWith(QLatin1String("\\LocalSystem")))
|
||||
act = (wchar_t*)acc.utf16();
|
||||
}
|
||||
if (!password.isEmpty() && act) {
|
||||
pwd = (wchar_t*)password.utf16();
|
||||
}
|
||||
|
||||
// Only set INTERACTIVE if act is LocalSystem. (and act should be 0 if it is LocalSystem).
|
||||
if (!act) dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
|
||||
|
||||
// Create the service
|
||||
SC_HANDLE hService = pCreateService(hSCM, (wchar_t *)controller.serviceName().utf16(),
|
||||
(wchar_t *)controller.serviceName().utf16(),
|
||||
SERVICE_ALL_ACCESS,
|
||||
dwServiceType, // QObject::inherits ( const char * className ) for no inter active ????
|
||||
dwStartType, SERVICE_ERROR_NORMAL, (wchar_t *)filePath().utf16(),
|
||||
0, 0, 0,
|
||||
act, pwd);
|
||||
if (hService) {
|
||||
result = true;
|
||||
if (!serviceDescription.isEmpty()) {
|
||||
SERVICE_DESCRIPTION sdesc;
|
||||
sdesc.lpDescription = (wchar_t *)serviceDescription.utf16();
|
||||
pChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdesc);
|
||||
}
|
||||
pCloseServiceHandle(hService);
|
||||
}
|
||||
pCloseServiceHandle(hSCM);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QString QtServiceBasePrivate::filePath() const
|
||||
{
|
||||
wchar_t path[_MAX_PATH];
|
||||
::GetModuleFileNameW( 0, path, sizeof(path) );
|
||||
return QString::fromUtf16((unsigned short*)path);
|
||||
}
|
||||
|
||||
bool QtServiceBasePrivate::sysInit()
|
||||
{
|
||||
sysd = new QtServiceSysPrivate();
|
||||
|
||||
sysd->serviceStatus = 0;
|
||||
sysd->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
|
||||
sysd->status.dwCurrentState = SERVICE_STOPPED;
|
||||
sysd->status.dwControlsAccepted = sysd->serviceFlags(serviceFlags);
|
||||
sysd->status.dwWin32ExitCode = NO_ERROR;
|
||||
sysd->status.dwServiceSpecificExitCode = 0;
|
||||
sysd->status.dwCheckPoint = 0;
|
||||
sysd->status.dwWaitHint = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtServiceBasePrivate::sysSetPath()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QtServiceBasePrivate::sysCleanup()
|
||||
{
|
||||
if (sysd) {
|
||||
delete sysd;
|
||||
sysd = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QtServiceBase::setServiceFlags(QtServiceBase::ServiceFlags flags)
|
||||
{
|
||||
if (d_ptr->serviceFlags == flags)
|
||||
return;
|
||||
d_ptr->serviceFlags = flags;
|
||||
if (d_ptr->sysd)
|
||||
d_ptr->sysd->setServiceFlags(flags);
|
||||
}
|
||||
|
||||
|
||||
92
service/src/qtunixserversocket.cpp
Normal file
92
service/src/qtunixserversocket.cpp
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtunixserversocket.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(ptr) ((size_t)(((struct sockaddr_un *) 0)->sun_path) \
|
||||
+strlen ((ptr)->sun_path))
|
||||
#endif
|
||||
|
||||
QtUnixServerSocket::QtUnixServerSocket(const QString &path, QObject *parent)
|
||||
: QTcpServer(parent)
|
||||
{
|
||||
setPath(path);
|
||||
}
|
||||
|
||||
QtUnixServerSocket::QtUnixServerSocket(QObject *parent)
|
||||
: QTcpServer(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void QtUnixServerSocket::setPath(const QString &path)
|
||||
{
|
||||
path_.clear();
|
||||
|
||||
int sock = ::socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock != -1) {
|
||||
struct sockaddr_un addr;
|
||||
::memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
::unlink(path.toLatin1().constData()); // ### This might need to be changed
|
||||
unsigned int pathlen = strlen(path.toLatin1().constData());
|
||||
if (pathlen > sizeof(addr.sun_path)) pathlen = sizeof(addr.sun_path);
|
||||
::memcpy(addr.sun_path, path.toLatin1().constData(), pathlen);
|
||||
if ((::bind(sock, (struct sockaddr *)&addr, SUN_LEN(&addr)) != -1) &&
|
||||
(::listen(sock, 5) != -1)) {
|
||||
setSocketDescriptor(sock);
|
||||
path_ = path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QtUnixServerSocket::close()
|
||||
{
|
||||
QTcpServer::close();
|
||||
if (!path_.isEmpty()) {
|
||||
::unlink(path_.toLatin1().constData());
|
||||
path_.clear();
|
||||
}
|
||||
}
|
||||
61
service/src/qtunixserversocket.h
Normal file
61
service/src/qtunixserversocket.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTUNIXSERVERSOCKET_H
|
||||
#define QTUNIXSERVERSOCKET_H
|
||||
|
||||
#include <QTcpServer>
|
||||
|
||||
class QtUnixServerSocket : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QtUnixServerSocket(const QString &path, QObject *parent = 0);
|
||||
QtUnixServerSocket(QObject *parent = 0);
|
||||
|
||||
void setPath(const QString &path);
|
||||
void close();
|
||||
|
||||
private:
|
||||
QString path_;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
78
service/src/qtunixsocket.cpp
Normal file
78
service/src/qtunixsocket.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qtunixsocket.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef SUN_LEN
|
||||
#define SUN_LEN(ptr) ((size_t)(((struct sockaddr_un *) 0)->sun_path) \
|
||||
+strlen ((ptr)->sun_path))
|
||||
#endif
|
||||
|
||||
QtUnixSocket::QtUnixSocket(QObject *parent)
|
||||
: QTcpSocket(parent)
|
||||
{
|
||||
}
|
||||
|
||||
bool QtUnixSocket::connectTo(const QString &path)
|
||||
{
|
||||
bool ret = false;
|
||||
int sock = ::socket(PF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock != -1) {
|
||||
struct sockaddr_un addr;
|
||||
::memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
size_t pathlen = strlen(path.toLatin1().constData());
|
||||
pathlen = qMin(pathlen, sizeof(addr.sun_path));
|
||||
::memcpy(addr.sun_path, path.toLatin1().constData(), pathlen);
|
||||
int err = ::connect(sock, (struct sockaddr *)&addr, SUN_LEN(&addr));
|
||||
if (err != -1) {
|
||||
setSocketDescriptor(sock);
|
||||
ret = true;
|
||||
} else {
|
||||
::close(sock);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
55
service/src/qtunixsocket.h
Normal file
55
service/src/qtunixsocket.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the Qt Solutions component.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** You may use this file under the terms of the BSD license as follows:
|
||||
**
|
||||
** "Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are
|
||||
** met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in
|
||||
** the documentation and/or other materials provided with the
|
||||
** distribution.
|
||||
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||
** of its contributors may be used to endorse or promote products derived
|
||||
** from this software without specific prior written permission.
|
||||
**
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTUNIXSOCKET_H
|
||||
#define QTUNIXSOCKET_H
|
||||
|
||||
#include <QTcpSocket>
|
||||
|
||||
class QtUnixSocket : public QTcpSocket
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QtUnixSocket(QObject *parent = 0);
|
||||
|
||||
bool connectTo(const QString &path);
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue