added qtservice
This commit is contained in:
parent
091dff8491
commit
42fffe647d
@ -20,6 +20,7 @@
|
|||||||
#include <guhcore.h>
|
#include <guhcore.h>
|
||||||
|
|
||||||
#include <QtPlugin>
|
#include <QtPlugin>
|
||||||
|
#include <qtservice/qtservice.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -31,40 +32,61 @@
|
|||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
void daemonizeGuh() {
|
|
||||||
// Our process ID and Session ID
|
|
||||||
pid_t pid, sid;
|
|
||||||
|
|
||||||
// Fork off the parent process
|
class GuhService : public QtService<QCoreApplication>
|
||||||
pid = fork();
|
{
|
||||||
if (pid < 0) {
|
|
||||||
exit(EXIT_FAILURE);
|
public:
|
||||||
}
|
GuhService(int argc, char **argv):
|
||||||
// If we got a good PID, then we can exit the parent process.
|
QtService<QCoreApplication>(argc, argv, "guh daemon")
|
||||||
if (pid > 0) {
|
{
|
||||||
exit(EXIT_SUCCESS);
|
setServiceDescription("guh daemon");
|
||||||
|
setServiceFlags(QtServiceBase::CanBeSuspended);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the file mode mask
|
protected:
|
||||||
umask(0);
|
void start()
|
||||||
// Create a new SID for the child process
|
{
|
||||||
sid = setsid();
|
GuhCore::instance();
|
||||||
if (sid < 0) {
|
|
||||||
// Log the failure
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the current working directory
|
};
|
||||||
if ((chdir("/")) < 0) {
|
|
||||||
/* Log the failure */
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close out the standard file descriptors
|
|
||||||
close(STDIN_FILENO);
|
//void daemonizeGuh() {
|
||||||
close(STDOUT_FILENO);
|
// // Our process ID and Session ID
|
||||||
close(STDERR_FILENO);
|
// pid_t pid, sid;
|
||||||
}
|
|
||||||
|
// // Fork off the parent process
|
||||||
|
// pid = fork();
|
||||||
|
// if (pid < 0) {
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
// // If we got a good PID, then we can exit the parent process.
|
||||||
|
// if (pid > 0) {
|
||||||
|
// exit(EXIT_SUCCESS);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Change the file mode mask
|
||||||
|
// umask(0);
|
||||||
|
// // Create a new SID for the child process
|
||||||
|
// sid = setsid();
|
||||||
|
// if (sid < 0) {
|
||||||
|
// // Log the failure
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Change the current working directory
|
||||||
|
// if ((chdir("/")) < 0) {
|
||||||
|
// /* Log the failure */
|
||||||
|
// exit(EXIT_FAILURE);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Close out the standard file descriptors
|
||||||
|
// close(STDIN_FILENO);
|
||||||
|
// close(STDOUT_FILENO);
|
||||||
|
// close(STDERR_FILENO);
|
||||||
|
//}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
@ -84,7 +106,6 @@ int main(int argc, char *argv[])
|
|||||||
qDebug() << " -v, --version print version";
|
qDebug() << " -v, --version print version";
|
||||||
qDebug() << " -e, --executable start guh as application, not as daemon";
|
qDebug() << " -e, --executable start guh as application, not as daemon";
|
||||||
qDebug() << "";
|
qDebug() << "";
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,13 +115,15 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!arguments.contains("-e") && !arguments.contains("--executable")) {
|
if (!arguments.contains("-e") && !arguments.contains("--executable")) {
|
||||||
qDebug() << "Starting guhd as daemon.";
|
qDebug() << "guhd is starting as daemon.";
|
||||||
daemonizeGuh();
|
GuhService service(argc, argv);
|
||||||
|
return service.exec();
|
||||||
|
//daemonizeGuh();
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Starting guhd as executable application.";
|
qDebug() << "guhd is starting as executable.";
|
||||||
}
|
}
|
||||||
|
|
||||||
a.setOrganizationName("guh");
|
a.setOrganizationName("guh");
|
||||||
|
a.setApplicationName("guhd");
|
||||||
GuhCore::instance();
|
GuhCore::instance();
|
||||||
return a.exec();
|
return a.exec();
|
||||||
}
|
}
|
||||||
|
|||||||
1
server/qtservice/QtServiceBase
Normal file
1
server/qtservice/QtServiceBase
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "qtservice.h"
|
||||||
1
server/qtservice/QtServiceController
Normal file
1
server/qtservice/QtServiceController
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "qtservice.h"
|
||||||
1112
server/qtservice/qtservice.cpp
Normal file
1112
server/qtservice/qtservice.cpp
Normal file
File diff suppressed because it is too large
Load Diff
192
server/qtservice/qtservice.h
Normal file
192
server/qtservice/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
server/qtservice/qtservice.pri
Normal file
21
server/qtservice/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
server/qtservice/qtservice_p.h
Normal file
87
server/qtservice/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
|
||||||
474
server/qtservice/qtservice_unix.cpp
Normal file
474
server/qtservice/qtservice_unix.cpp
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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:
|
||||||
|
void incomingConnection(int socketDescriptor);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QtServiceSysPrivate::incomingConnection(int socketDescriptor)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
945
server/qtservice/qtservice_win.cpp
Normal file
945
server/qtservice/qtservice_win.cpp
Normal file
@ -0,0 +1,945 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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)
|
||||||
|
extern void qtServiceLogDebug(QtMsgType type, const char* msg);
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
dbgMsg += message.toAscii();
|
||||||
|
qtServiceLogDebug((QtMsgType)-1, dbgMsg.constData());
|
||||||
|
#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
server/qtservice/qtunixserversocket.cpp
Normal file
92
server/qtservice/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
server/qtservice/qtunixserversocket.h
Normal file
61
server/qtservice/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
server/qtservice/qtunixsocket.cpp
Normal file
78
server/qtservice/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
server/qtservice/qtunixsocket.h
Normal file
55
server/qtservice/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
|
||||||
@ -15,6 +15,8 @@ QT += network sql
|
|||||||
LIBS += -L$$top_builddir/libguh/ -lguh
|
LIBS += -L$$top_builddir/libguh/ -lguh
|
||||||
|
|
||||||
include(server.pri)
|
include(server.pri)
|
||||||
|
include(qtservice/qtservice.pri)
|
||||||
|
|
||||||
SOURCES += main.cpp
|
SOURCES += main.cpp
|
||||||
|
|
||||||
boblight {
|
boblight {
|
||||||
|
|||||||
@ -19,11 +19,11 @@
|
|||||||
#include "tcpserver.h"
|
#include "tcpserver.h"
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
TcpServer::TcpServer(QObject *parent) :
|
TcpServer::TcpServer(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
|
|
||||||
qDebug() << "----------------------------";
|
qDebug() << "----------------------------";
|
||||||
qDebug() << "network interfaces:";
|
qDebug() << "network interfaces:";
|
||||||
foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()){
|
foreach(const QNetworkInterface &interface, QNetworkInterface::allInterfaces()){
|
||||||
@ -36,6 +36,18 @@ TcpServer::TcpServer(QObject *parent) :
|
|||||||
}
|
}
|
||||||
qDebug() << "----------------------------";
|
qDebug() << "----------------------------";
|
||||||
|
|
||||||
|
// load settings
|
||||||
|
bool ok;
|
||||||
|
QSettings settings("/etc/guh/guhd.conf");
|
||||||
|
settings.beginGroup("JSON-RPC Server");
|
||||||
|
|
||||||
|
uint port = settings.value("port", 1234).toUInt(&ok);
|
||||||
|
settings.endGroup();
|
||||||
|
if(ok){
|
||||||
|
m_port = port;
|
||||||
|
} else {
|
||||||
|
m_port = 1234;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TcpServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
void TcpServer::sendData(const QList<QUuid> &clients, const QByteArray &data)
|
||||||
@ -101,8 +113,8 @@ bool TcpServer::startServer()
|
|||||||
// Listen on all Networkinterfaces
|
// Listen on all Networkinterfaces
|
||||||
foreach(const QHostAddress &address, QNetworkInterface::allAddresses()){
|
foreach(const QHostAddress &address, QNetworkInterface::allAddresses()){
|
||||||
QTcpServer *server = new QTcpServer(this);
|
QTcpServer *server = new QTcpServer(this);
|
||||||
if(server->listen(address, 1234)) {
|
if(server->listen(address, m_port)) {
|
||||||
qDebug() << "server listening on" << address.toString();
|
qDebug() << "JSON-RPC server listening on" << address.toString() << ":" << m_port;
|
||||||
connect(server, SIGNAL(newConnection()), SLOT(newClientConnected()));
|
connect(server, SIGNAL(newConnection()), SLOT(newClientConnected()));
|
||||||
m_serverList.insert(QUuid::createUuid(), server);
|
m_serverList.insert(QUuid::createUuid(), server);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -37,6 +37,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
QHash<QUuid, QTcpServer*> m_serverList;
|
QHash<QUuid, QTcpServer*> m_serverList;
|
||||||
QHash<QUuid, QTcpSocket*> m_clientList;
|
QHash<QUuid, QTcpSocket*> m_clientList;
|
||||||
|
uint m_port;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clientConnected(const QUuid &clientId);
|
void clientConnected(const QUuid &clientId);
|
||||||
|
|||||||
Reference in New Issue
Block a user