Add installer
This commit is contained in:
parent
c9bc8aa8c1
commit
a2a5cafc5f
73 changed files with 4354 additions and 488 deletions
17
.gitignore
vendored
17
.gitignore
vendored
|
@ -3,15 +3,19 @@
|
|||
macOSPackage/
|
||||
|
||||
# C++ objects and libs
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.a
|
||||
*.dll
|
||||
*.dmg
|
||||
*.dylib
|
||||
*.exe
|
||||
*.exp
|
||||
*.la
|
||||
*.lai
|
||||
*.lib
|
||||
*.lo
|
||||
*.o
|
||||
*.slo
|
||||
*.so
|
||||
*.dll
|
||||
*.dylib
|
||||
|
||||
# Qt-es
|
||||
/.qmake.cache
|
||||
|
@ -47,4 +51,5 @@ CMakeLists.txt.user*
|
|||
*.*~
|
||||
|
||||
# Certificates
|
||||
*.p12
|
||||
*.p12
|
||||
|
||||
|
|
27
.gitlab-ci.yml
Normal file
27
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
build-windows:
|
||||
stage: build
|
||||
tags:
|
||||
- windows
|
||||
script:
|
||||
- cmd.exe /k "deploy\windows-env.bat && cd deploy && windows.bat"
|
||||
artifacts:
|
||||
name: artifacts-windows
|
||||
paths:
|
||||
- AmneziaVPN.exe
|
||||
|
||||
build-macos:
|
||||
stage: build
|
||||
tags:
|
||||
- macos
|
||||
script:
|
||||
- cd deploy && ./macos.sh
|
||||
artifacts:
|
||||
name: artifacts-macos
|
||||
paths:
|
||||
- AmneziaVPN.dmg
|
|
@ -1 +0,0 @@
|
|||
Subproject commit a34ded6e69f0a5ab492a17c1edf898104448d4d3
|
2
AmneziaVPN.pro
Normal file
2
AmneziaVPN.pro
Normal file
|
@ -0,0 +1,2 @@
|
|||
TEMPLATE = subdirs
|
||||
SUBDIRS = client service platform
|
|
@ -1,76 +0,0 @@
|
|||
QT += widgets core gui network xml
|
||||
|
||||
TARGET = AmneziaVPN
|
||||
TEMPLATE = app
|
||||
#CONFIG += console
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
HEADERS += \
|
||||
core/router.h \
|
||||
debug.h \
|
||||
defines.h \
|
||||
runguard.h \
|
||||
ui/Controls/SlidingStackedWidget.h \
|
||||
ui/mainwindow.h \
|
||||
|
||||
SOURCES += \
|
||||
core/router.cpp \
|
||||
debug.cpp \
|
||||
main.cpp \
|
||||
runguard.cpp \
|
||||
ui/Controls/SlidingStackedWidget.cpp \
|
||||
ui/mainwindow.cpp \
|
||||
|
||||
|
||||
FORMS += ui/mainwindow.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
TRANSLATIONS = \
|
||||
translations/amneziavpn.en.ts \
|
||||
translations/amneziavpn.ru.ts
|
||||
|
||||
win32 {
|
||||
|
||||
OTHER_FILES += platform_win/vpnclient.rc
|
||||
RC_FILE = platform_win/vpnclient.rc
|
||||
|
||||
HEADERS +=
|
||||
SOURCES +=
|
||||
|
||||
#CONFIG -= embed_manifest_exe
|
||||
#DEFINES += _CRT_SECURE_NO_WARNINGS VPNCLIENT_TAPSIGNED
|
||||
#QMAKE_LFLAGS += /MANIFESTUAC:\"level=\'requireAdministrator\' uiAccess=\'false\'\"
|
||||
|
||||
VERSION = 1.1.1.1
|
||||
QMAKE_TARGET_COMPANY = "AmneziaVPN"
|
||||
QMAKE_TARGET_PRODUCT = "AmneziaVPN"
|
||||
|
||||
#CONFIG -= embed_manifest_exe
|
||||
|
||||
LIBS += \
|
||||
-luser32 \
|
||||
-lrasapi32 \
|
||||
-lshlwapi \
|
||||
-liphlpapi \
|
||||
-lws2_32 \
|
||||
-liphlpapi \
|
||||
-lgdi32
|
||||
|
||||
#LIBS += -L$$PWD/../../../../../../../OpenSSL-Win32/lib/ -llibcrypto
|
||||
|
||||
#MT_PATH = \"C:/Program Files (x86)/Microsoft SDKs/Windows/v7.1A/bin/x64/mt.exe\"
|
||||
#WIN_PWD = $$replace(PWD, /, \\)
|
||||
#OUT_PWD_WIN = $$replace(OUT_PWD, /, \\)
|
||||
|
||||
#!win32-g++: QMAKE_POST_LINK = "$$MT_PATH -manifest $$quote($$WIN_PWD\\platform_win\\$$basename(TARGET).exe.manifest) -outputresource:$$quote($$OUT_PWD_WIN\\$(DESTDIR_TARGET);1)"
|
||||
# else: QMAKE_POST_LINK = "$$MT_PATH -manifest $$PWD/platform_win/$$basename(TARGET).exe.manifest -outputresource:$$OUT_PWD/$(DESTDIR_TARGET)"
|
||||
|
||||
}
|
||||
|
||||
|
||||
macx {
|
||||
ICON = $$PWD/images/app.icns
|
||||
}
|
67
client/client.pro
Normal file
67
client/client.pro
Normal file
|
@ -0,0 +1,67 @@
|
|||
QT += widgets core gui network xml
|
||||
|
||||
TARGET = AmneziaVPN
|
||||
TEMPLATE = app
|
||||
#CONFIG += console
|
||||
|
||||
DEFINES += QT_DEPRECATED_WARNINGS
|
||||
|
||||
HEADERS += \
|
||||
core/router.h \
|
||||
debug.h \
|
||||
defines.h \
|
||||
runguard.h \
|
||||
ui/Controls/SlidingStackedWidget.h \
|
||||
ui/mainwindow.h \
|
||||
|
||||
SOURCES += \
|
||||
core/router.cpp \
|
||||
debug.cpp \
|
||||
main.cpp \
|
||||
runguard.cpp \
|
||||
ui/Controls/SlidingStackedWidget.cpp \
|
||||
ui/mainwindow.cpp \
|
||||
|
||||
|
||||
FORMS += ui/mainwindow.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc
|
||||
|
||||
TRANSLATIONS = \
|
||||
translations/amneziavpn.en.ts \
|
||||
translations/amneziavpn.ru.ts
|
||||
|
||||
CONFIG(release, debug|release) {
|
||||
DESTDIR = $$PWD/../../AmneziaVPN-build/client/release
|
||||
MOC_DIR = $$DESTDIR
|
||||
OBJECTS_DIR = $$DESTDIR
|
||||
RCC_DIR = $$DESTDIR
|
||||
}
|
||||
|
||||
win32 {
|
||||
OTHER_FILES += platform_win/vpnclient.rc
|
||||
RC_FILE = platform_win/vpnclient.rc
|
||||
|
||||
HEADERS +=
|
||||
SOURCES +=
|
||||
|
||||
VERSION = 1.1.1.1
|
||||
QMAKE_TARGET_COMPANY = "AmneziaVPN"
|
||||
QMAKE_TARGET_PRODUCT = "AmneziaVPN"
|
||||
|
||||
LIBS += \
|
||||
-luser32 \
|
||||
-lrasapi32 \
|
||||
-lshlwapi \
|
||||
-liphlpapi \
|
||||
-lws2_32 \
|
||||
-liphlpapi \
|
||||
-lgdi32
|
||||
|
||||
#LIBS += -L$$PWD/../../../../../../../OpenSSL-Win32/lib/ -llibcrypto
|
||||
}
|
||||
|
||||
macx {
|
||||
ICON = $$PWD/images/app.icns
|
||||
}
|
132
client/debug.cpp
132
client/debug.cpp
|
@ -1,126 +1,68 @@
|
|||
#include "debug.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
#include <QDebug>
|
||||
#include <QCoreApplication>
|
||||
#include <QDateTime>
|
||||
#include <QUrl>
|
||||
|
||||
#define LOGS_DIR "logs"
|
||||
#define CLIENT_LOG_SUFFIX "amneziavpn.log"
|
||||
#define MAX_LOG_FILES 5
|
||||
#define FORMAT_STRING "yyyy-MM-dd--hh-mm-ss"
|
||||
#include "debug.h"
|
||||
#include "defines.h"
|
||||
|
||||
QFile Debug::m_clientLog;
|
||||
QTextStream Debug::m_clientLogTextStream;
|
||||
QString Debug::m_clientLogName;
|
||||
QFile Debug::m_file;
|
||||
QTextStream Debug::m_textStream;
|
||||
QString Debug::m_logFileName;
|
||||
|
||||
void debugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||
void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
|
||||
{
|
||||
// Skip Qt warnings
|
||||
if (msg.contains("known incorrect sRGB profile")) return;
|
||||
if (msg.contains("libpng warning")) return;
|
||||
if (msg.contains("Unknown property ffont")) return;
|
||||
if (msg.simplified().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::m_clientLogTextStream << qFormatLogMessage(type, context, msg) << endl << flush;
|
||||
// Temporally disabled
|
||||
if (msg.startsWith("Unknown property") || msg.startsWith("Could not create pixmap")) {
|
||||
return;
|
||||
}
|
||||
|
||||
Debug::m_textStream << qFormatLogMessage(type, context, msg) << endl << flush;
|
||||
}
|
||||
|
||||
bool Debug::init()
|
||||
{
|
||||
QString path = qApp->applicationDirPath();
|
||||
QString path = logsDir();
|
||||
QDir appDir(path);
|
||||
|
||||
// init function is called before exec application, so data location folder may not exist
|
||||
if (!appDir.exists())
|
||||
{
|
||||
qWarning() << "Debug: init: log directory doesn't exist or mkpath command error:" << path;
|
||||
if (!appDir.mkpath(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!appDir.exists(LOGS_DIR) && !appDir.mkdir(LOGS_DIR))
|
||||
{
|
||||
qWarning() << "Debug: init: log directory doesn't exist or mkdir command error:" << path << LOGS_DIR;
|
||||
return false;
|
||||
}
|
||||
m_logFileName = QString("%1.log").arg(APPLICATION_NAME);
|
||||
|
||||
if (!appDir.cd(LOGS_DIR))
|
||||
{
|
||||
qWarning() << "Debug: init: cd command error:" << path << LOGS_DIR;
|
||||
return false;
|
||||
}
|
||||
|
||||
//delete older log files
|
||||
auto clientLogsCount = 0;
|
||||
QFileInfoList logDirList = appDir.entryInfoList(
|
||||
QDir::Files | QDir::NoDotAndDotDot,
|
||||
QDir::Time);
|
||||
for (auto fileInfo : logDirList)
|
||||
{
|
||||
if ((fileInfo.completeSuffix() == CLIENT_LOG_SUFFIX &&
|
||||
++clientLogsCount > MAX_LOG_FILES))
|
||||
{
|
||||
appDir.remove(fileInfo.filePath());
|
||||
}
|
||||
}
|
||||
|
||||
//prepare log file names
|
||||
auto currentDateTime = QDateTime::currentDateTime().toString(FORMAT_STRING);
|
||||
|
||||
m_clientLogName = QString("%1.%2").arg(currentDateTime).arg(CLIENT_LOG_SUFFIX);
|
||||
return init(appDir);
|
||||
}
|
||||
|
||||
bool Debug::init(QDir& appDir)
|
||||
{
|
||||
Q_UNUSED(appDir)
|
||||
qSetMessagePattern("[%{time}|%{type}] %{message}");
|
||||
|
||||
#ifndef QT_DEBUG
|
||||
m_clientLog.setFileName(appDir.filePath(m_clientLogName));
|
||||
if (!m_clientLog.open(QIODevice::WriteOnly | QIODevice::Append)) {
|
||||
qWarning() << "Debug::init - failed to open m_clientLog file:" << m_clientLogName;
|
||||
m_file.setFileName(appDir.filePath(m_logFileName));
|
||||
if (!m_file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
qWarning() << "Cannot open log file:" << m_logFileName;
|
||||
return false;
|
||||
}
|
||||
m_clientLog.setTextModeEnabled(true);
|
||||
m_clientLogTextStream.setDevice(&m_clientLog);
|
||||
m_file.setTextModeEnabled(true);
|
||||
m_textStream.setDevice(&m_file);
|
||||
qInstallMessageHandler(debugMessageHandler);
|
||||
#else
|
||||
#ifdef DEBUG_OUTPUT_TWO_DIRECTIONAL
|
||||
m_clientLog.setFileName(appDir.filePath(m_clientLogName));
|
||||
if (!m_clientLog.open(QIODevice::WriteOnly | QIODevice::Append))
|
||||
return false;
|
||||
m_clientLog.setTextModeEnabled(true);
|
||||
m_clientLogTextStream.setDevice(&m_clientLog);
|
||||
defaultMessageHandler = qInstallMessageHandler(debugMessageHandler);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString Debug::getPathToClientLog()
|
||||
QString Debug::logsDir()
|
||||
{
|
||||
QString path = qApp->applicationDirPath();
|
||||
QDir appDir(path);
|
||||
if (!appDir.exists(LOGS_DIR) || !appDir.cd(LOGS_DIR))
|
||||
{
|
||||
qWarning() << "Debug: log directory doesn't exist or cd command error:" << path;
|
||||
return "";
|
||||
}
|
||||
|
||||
return appDir.filePath(m_clientLogName);
|
||||
return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/logs";
|
||||
}
|
||||
|
||||
QString Debug::getPathToLogsDir()
|
||||
bool Debug::openLogsFolder()
|
||||
{
|
||||
QString path = qApp->applicationDirPath();
|
||||
QDir appDir(path);
|
||||
if (!appDir.exists(LOGS_DIR) || !appDir.cd(LOGS_DIR))
|
||||
{
|
||||
qWarning() << "Debug: log directory doesn't exist or cd command error" << path;
|
||||
return "";
|
||||
QString path = logsDir();
|
||||
#ifdef Q_OS_WIN
|
||||
path = "file:///" + path;
|
||||
#endif
|
||||
if (!QDesktopServices::openUrl(QUrl::fromLocalFile(path))) {
|
||||
qWarning() << "Can't open url:" << path;
|
||||
return false;
|
||||
}
|
||||
return appDir.absolutePath();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -9,19 +9,16 @@
|
|||
class Debug
|
||||
{
|
||||
public:
|
||||
static QString logsDir();
|
||||
static bool init();
|
||||
static QString getPathToClientLog();
|
||||
static QString getPathToLogsDir();
|
||||
static bool openLogsFolder();
|
||||
|
||||
private:
|
||||
static bool init(QDir& appDir);
|
||||
static QFile m_file;
|
||||
static QTextStream m_textStream;
|
||||
static QString m_logFileName;
|
||||
|
||||
private:
|
||||
static QFile m_clientLog;
|
||||
static QTextStream m_clientLogTextStream;
|
||||
static QString m_clientLogName;
|
||||
|
||||
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
||||
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
|
||||
};
|
||||
|
||||
#endif // DEBUG_H
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#define DEFINES_H
|
||||
|
||||
#define APPLICATION_NAME "AmneziaVPN"
|
||||
#define SERVICE_NAME "AmneziaVPN-service"
|
||||
#define ORGANIZATION_NAME "AmneziaVPN.ORG"
|
||||
|
||||
#endif // DEFINES_H
|
||||
|
|
|
@ -51,7 +51,6 @@ int main(int argc, char *argv[])
|
|||
app.setApplicationName(APPLICATION_NAME);
|
||||
app.setOrganizationName(ORGANIZATION_NAME);
|
||||
app.setApplicationDisplayName(APPLICATION_NAME);
|
||||
app.setApplicationVersion("1.0.0.0");
|
||||
|
||||
//app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
|
@ -60,17 +59,8 @@ int main(int argc, char *argv[])
|
|||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
|
||||
QCommandLineOption debugToConsoleOption("d", QCoreApplication::translate("main", "Output to console instead log file"));
|
||||
parser.addOption(debugToConsoleOption);
|
||||
parser.process(app); // Process the actual command line arguments given by the user
|
||||
|
||||
bool debugToConsole = parser.isSet(debugToConsoleOption);
|
||||
|
||||
qDebug() << "Set output to console: " << debugToConsole;
|
||||
if (!debugToConsole) {
|
||||
if (!Debug::init()) {
|
||||
qCritical() << "Initialization of debug subsystem failed";
|
||||
}
|
||||
if (!Debug::init()) {
|
||||
qCritical() << "Initialization of debug subsystem failed";
|
||||
}
|
||||
|
||||
QFont f("Lato Regular", 10);
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity name="amneziavpn.amneziavpn" version="0.0.0.1" processorArchitecture="x86" type="win32" />
|
||||
<description>amneziavpn</description>
|
||||
<!-- Identify the application security requirements. -->
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel
|
||||
level="requireAdministrator"
|
||||
uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
|
@ -1,3 +1,8 @@
|
|||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "debug.h"
|
||||
#include "defines.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
|
@ -5,13 +10,71 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Post initialization
|
||||
// Post initialization
|
||||
ui->widget_tittlebar->hide();
|
||||
ui->stackedWidget_main->setCurrentIndex(2);
|
||||
|
||||
connect(ui->pushButton_blocked_list, SIGNAL(clicked(bool)), this, SLOT(onPushButtonBlockedListClicked(bool)));
|
||||
connect(ui->pushButton_connect, SIGNAL(clicked(bool)), this, SLOT(onPushButtonConnectClicked(bool)));
|
||||
connect(ui->pushButton_settings, SIGNAL(clicked(bool)), this, SLOT(onPushButtonSettingsClicked(bool)));
|
||||
|
||||
connect(ui->pushButton_back_from_sites, SIGNAL(clicked(bool)), this, SLOT(onPushButtonBackFromSitesClicked(bool)));
|
||||
connect(ui->pushButton_back_from_settings, SIGNAL(clicked(bool)), this, SLOT(onPushButtonBackFromSettingsClicked(bool)));
|
||||
|
||||
setFixedSize(width(),height());
|
||||
|
||||
qDebug() << APPLICATION_NAME;
|
||||
qDebug() << "Started";
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
|
||||
qDebug() << "Closed";
|
||||
}
|
||||
|
||||
void MainWindow::goToIndex(int index)
|
||||
{
|
||||
ui->stackedWidget_main->setCurrentIndex(index);
|
||||
}
|
||||
|
||||
void MainWindow::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_L:
|
||||
if (!Debug::openLogsFolder()) {
|
||||
QMessageBox::warning(this, APPLICATION_NAME, tr("Cannot open logs folder!"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonBackFromSettingsClicked(bool)
|
||||
{
|
||||
goToIndex(2);
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonBackFromSitesClicked(bool)
|
||||
{
|
||||
goToIndex(2);
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonBlockedListClicked(bool)
|
||||
{
|
||||
goToIndex(3);
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonSettingsClicked(bool)
|
||||
{
|
||||
goToIndex(4);
|
||||
}
|
||||
|
||||
void MainWindow::onPushButtonConnectClicked(bool)
|
||||
{
|
||||
qDebug() << "onPushButtonConnectClicked";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,8 +22,19 @@ public slots:
|
|||
|
||||
|
||||
private slots:
|
||||
void onPushButtonBlockedListClicked(bool clicked);
|
||||
void onPushButtonConnectClicked(bool clicked);
|
||||
void onPushButtonSettingsClicked(bool clicked);
|
||||
|
||||
void onPushButtonBackFromSettingsClicked(bool clicked);
|
||||
void onPushButtonBackFromSitesClicked(bool clicked);
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event);
|
||||
|
||||
private:
|
||||
void goToIndex(int index);
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
};
|
||||
|
||||
|
|
|
@ -268,6 +268,9 @@ QStackedWidget QWidget {
|
|||
}
|
||||
</string>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page_connect_server">
|
||||
<widget class="QLabel" name="label_23">
|
||||
<property name="geometry">
|
||||
|
|
26
deploy/data/macos/AmneziaVPN.plist
Normal file
26
deploy/data/macos/AmneziaVPN.plist
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>AmneziaVPN-service</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Applications/AmneziaVPN.app/Contents/MacOS/AmneziaVPN-service</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>Sockets</key>
|
||||
<dict>
|
||||
<key>Listeners</key>
|
||||
<dict>
|
||||
<key>SockServiceName</key>
|
||||
<string>5959</string>
|
||||
<key>SockType</key>
|
||||
<string>stream</string>
|
||||
<key>SockFamily</key>
|
||||
<string>IPv4</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
BIN
deploy/data/macos/openvpn
Normal file
BIN
deploy/data/macos/openvpn
Normal file
Binary file not shown.
32
deploy/data/macos/post_install.sh
Normal file
32
deploy/data/macos/post_install.sh
Normal file
|
@ -0,0 +1,32 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP_NAME=AmneziaVPN
|
||||
PLIST_NAME=$APP_NAME.plist
|
||||
LAUNCH_DAEMONS_PLIST_NAME=/Library/LaunchDaemons/$PLIST_NAME
|
||||
LOG_FOLDER=/var/log/$APP_NAME
|
||||
LOG_FILE="$LOG_FOLDER/post-install.log"
|
||||
APP_PATH=/Applications/$APP_NAME.app
|
||||
|
||||
if launchctl list "$APP_NAME-service" &> /dev/null; then
|
||||
launchctl unload $LAUNCH_DAEMONS_PLIST_NAME
|
||||
rm -f $LAUNCH_DAEMONS_PLIST_NAME
|
||||
fi
|
||||
|
||||
tar xzf $APP_PATH/$APP_NAME.tar.gz -C $APP_PATH
|
||||
rm -f $APP_PATH/$APP_NAME.tar.gz
|
||||
|
||||
rm -rf $LOG_FOLDER
|
||||
mkdir -p $LOG_FOLDER
|
||||
|
||||
echo "`date` Script started" > $LOG_FILE
|
||||
|
||||
killall -9 $APP_NAME-service 2>> $LOG_FILE
|
||||
|
||||
mv -f $APP_PATH/$PLIST_NAME $LAUNCH_DAEMONS_PLIST_NAME 2>> $LOG_FILE
|
||||
chown root:wheel $LAUNCH_DAEMONS_PLIST_NAME
|
||||
launchctl load $LAUNCH_DAEMONS_PLIST_NAME
|
||||
|
||||
echo "`date` Service status: $?" >> $LOG_FILE
|
||||
echo "`date` Script finished" >> $LOG_FILE
|
||||
|
||||
rm -- "$0"
|
14
deploy/data/macos/post_uninstall.sh
Normal file
14
deploy/data/macos/post_uninstall.sh
Normal file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
|
||||
APP_NAME=AmneziaVPN
|
||||
PLIST_NAME=$APP_NAME.plist
|
||||
LAUNCH_DAEMONS_PLIST_NAME=/Library/LaunchDaemons/$PLIST_NAME
|
||||
|
||||
if launchctl list "$APP_NAME-service" &> /dev/null; then
|
||||
launchctl unload $LAUNCH_DAEMONS_PLIST_NAME
|
||||
rm -f $LAUNCH_DAEMONS_PLIST_NAME
|
||||
fi
|
||||
|
||||
rm -rf "$HOME/Library/Application Support/$APP_NAME"
|
||||
rm -rf /var/log/$APP_NAME
|
||||
rm -rf /Applications/$APP_NAME.app/Contents
|
329
deploy/installer/config/controlscript.js
Normal file
329
deploy/installer/config/controlscript.js
Normal file
|
@ -0,0 +1,329 @@
|
|||
var requestToQuitFromApp = false;
|
||||
var updaterCompleted = 0;
|
||||
var desktopAppProcessRunning = false;
|
||||
var appInstalledUninstallerPath;
|
||||
|
||||
function appName()
|
||||
{
|
||||
return installer.value("Name");
|
||||
}
|
||||
|
||||
function appAlreadyInstalled()
|
||||
{
|
||||
return installer.fileExists(appInstalledUninstallerPath);
|
||||
}
|
||||
|
||||
function appExecutableFileName()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
return appName() + ".exe";
|
||||
} else {
|
||||
return appName();
|
||||
}
|
||||
}
|
||||
|
||||
function appInstalled()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
appInstalledUninstallerPath = installer.value("TargetDir") + "\\maintenancetool.exe";
|
||||
} else if (runningOnMacOS()){
|
||||
appInstalledUninstallerPath = "/Applications/" + appName() + ".app/maintenancetool.app/Contents/MacOS/maintenancetool";
|
||||
}
|
||||
|
||||
return appAlreadyInstalled();
|
||||
}
|
||||
|
||||
function endsWith(str, suffix)
|
||||
{
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
}
|
||||
|
||||
function runningOnWindows()
|
||||
{
|
||||
return (installer.value("os") === "win");
|
||||
}
|
||||
|
||||
function runningOnMacOS()
|
||||
{
|
||||
return (installer.value("os") === "mac");
|
||||
}
|
||||
|
||||
function sleep(miliseconds) {
|
||||
var currentTime = new Date().getTime();
|
||||
while (currentTime + miliseconds >= new Date().getTime()) {}
|
||||
}
|
||||
|
||||
function raiseInstallerWindow()
|
||||
{
|
||||
if (!runningOnMacOS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = installer.execute("/bin/bash", ["-c", "ps -A | grep -m1 '" + appName() + "' | awk '{print $1}'"]);
|
||||
if (Number(result[0]) > 0) {
|
||||
var arg = 'tell application \"System Events\" ' +
|
||||
'\n set frontmost of the first process whose unix id is ' + Number(result[0]) + ' to true ' +
|
||||
'\n end tell' +
|
||||
'\n ';
|
||||
installer.execute("osascript", ["-e", arg]);
|
||||
}
|
||||
}
|
||||
|
||||
function appProcessIsRunning()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
var cmdArgs = ["/FI", "WINDOWTITLE eq " + appName()];
|
||||
var result = installer.execute("tasklist", cmdArgs);
|
||||
|
||||
if ( Number(result[1]) === 0 ) {
|
||||
if (result[0].indexOf(appExecutableFileName()) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return checkProccesIsRunning("pgrep -x '" + appName() + "'")
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkProccesIsRunning(arg)
|
||||
{
|
||||
var cmdArgs = ["-c", arg];
|
||||
var result = installer.execute("/bin/bash", cmdArgs);
|
||||
var resultArg1 = Number(result[0])
|
||||
if (resultArg1 >= 3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function requestToQuit(installer,gui)
|
||||
{
|
||||
requestToQuitFromApp = true;
|
||||
|
||||
installer.setDefaultPageVisible(QInstaller.IntroductionPage, false);
|
||||
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
|
||||
installer.setDefaultPageVisible(QInstaller.ComponentSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.LicenseCheck, false);
|
||||
installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false);
|
||||
installer.setDefaultPageVisible(QInstaller.PerformInstallation, false);
|
||||
installer.setDefaultPageVisible(QInstaller.FinishedPage, false);
|
||||
|
||||
gui.clickButton(buttons.NextButton);
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
gui.clickButton(buttons.CancelButton);
|
||||
|
||||
if (runningOnWindows()) {
|
||||
installer.setCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Controller.prototype.PerformInstallationPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.LicenseAgreementPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.FinishedPageCallback = function ()
|
||||
{
|
||||
if (desktopAppProcessRunning) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
} else if (installer.isUpdater()) {
|
||||
installer.autoAcceptMessageBoxes();
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.RestartPageCallback = function ()
|
||||
{
|
||||
updaterCompleted = 1;
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
|
||||
Controller.prototype.StartMenuDirectoryPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.ComponentSelectionPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.ReadyForInstallationPageCallback = function()
|
||||
{
|
||||
if (installer.isUpdater()) {
|
||||
gui.clickButton(buttons.CommitButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.TargetDirectoryPageCallback = function ()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
|
||||
if (widget !== null) {
|
||||
widget.BrowseDirectoryButton.clicked.disconnect(onBrowseButtonClicked);
|
||||
widget.BrowseDirectoryButton.clicked.connect(onBrowseButtonClicked);
|
||||
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.IntroductionPageCallback = function ()
|
||||
{
|
||||
var widget = gui.currentPageWidget();
|
||||
if (installer.isUpdater() && updaterCompleted === 1) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
gui.clickButton(buttons.CancelButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if (installer.isUninstaller()) {
|
||||
if (widget !== null) {
|
||||
widget.findChild("PackageManagerRadioButton").visible = false;
|
||||
widget.findChild("UpdaterRadioButton").visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (installer.isUpdater()) {
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
}
|
||||
|
||||
onBrowseButtonClicked = function()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
if (widget !== null) {
|
||||
if (runningOnWindows()) {
|
||||
// On Windows we are appending \<APP_NAME> if selected path don't ends with <APP_NAME>
|
||||
var targetDir = widget.TargetDirectoryLineEdit.text;
|
||||
if (! endsWith(targetDir, appName())) {
|
||||
targetDir = targetDir + "\\" + appName();
|
||||
}
|
||||
installer.setValue("TargetDir", targetDir);
|
||||
widget.TargetDirectoryLineEdit.setText(installer.value("TargetDir"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNextButtonClicked = function()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
if (widget !== null) {
|
||||
installer.setValue("APP_BUNDLE_TARGET_DIR", widget.TargetDirectoryLineEdit.text);
|
||||
}
|
||||
}
|
||||
|
||||
function Controller () {
|
||||
console.log("OS: %1, architecture: %2".arg(systemInfo.prettyProductName).arg(systemInfo.currentCpuArchitecture));
|
||||
|
||||
if (installer.isInstaller() || installer.isUpdater()) {
|
||||
console.log("Check if app already installed: " + appInstalled());
|
||||
}
|
||||
|
||||
if (runningOnWindows()) {
|
||||
installer.setValue("AllUsers", "true");
|
||||
}
|
||||
|
||||
if (installer.isInstaller()) {
|
||||
installer.setDefaultPageVisible(QInstaller.ComponentSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
|
||||
installer.setDefaultPageVisible(QInstaller.StartMenuDirectoryPage, false);
|
||||
installer.setDefaultPageVisible(QInstaller.LicenseCheck, false);
|
||||
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
|
||||
if (requestToQuitFromApp === true) {
|
||||
requestToQuit(installer, gui);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runningOnMacOS()) {
|
||||
installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes);
|
||||
}
|
||||
|
||||
if (appAlreadyInstalled()) {
|
||||
if (QMessageBox.Ok === QMessageBox.information("os.information", appName(),
|
||||
qsTr("The application is already installed.") + " " +
|
||||
qsTr("We need to remove the old installation first. Do you wish to proceed?"),
|
||||
QMessageBox.Ok | QMessageBox.Cancel)) {
|
||||
|
||||
if (appAlreadyInstalled()) {
|
||||
var resultArray = installer.execute(appInstalledUninstallerPath);
|
||||
|
||||
console.log("Uninstaller finished with code: " + resultArray[1])
|
||||
|
||||
if (Number(resultArray[1]) !== 0) {
|
||||
console.log("Uninstallation aborted by user");
|
||||
installer.setCancelled();
|
||||
return;
|
||||
} else {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sleep(100);
|
||||
if (!installer.fileExists(appInstalledUninstallerPath)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raiseInstallerWindow();
|
||||
|
||||
} else {
|
||||
console.log("Request to quit from user");
|
||||
installer.setCancelled();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (installer.isUninstaller()) {
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
|
||||
if (requestToQuitFromApp === true) {
|
||||
requestToQuit(installer, gui);
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (installer.isUpdater()) {
|
||||
installer.setMessageBoxAutomaticAnswer("cancelInstallation", QMessageBox.No);
|
||||
installer.installationFinished.connect(function() {
|
||||
gui.clickButton(buttons.NextButton);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDesktopAppProcessRunningMessageLoop = function ()
|
||||
{
|
||||
if (requestToQuitFromApp === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (installer.isUpdater()) {
|
||||
for (var i = 0; i < 400; i++) {
|
||||
desktopAppProcessRunning = appProcessIsRunning();
|
||||
if (!desktopAppProcessRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
desktopAppProcessRunning = appProcessIsRunning();
|
||||
|
||||
if (desktopAppProcessRunning) {
|
||||
var result = QMessageBox.warning("QMessageBox", appName() + " installer",
|
||||
appName() + " is active. Close the app and press \"Retry\" button to continue installation. Press \"Abort\" button to abort the installer and exit.",
|
||||
QMessageBox.Retry | QMessageBox.Abort);
|
||||
if (result === QMessageBox.Retry) {
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
} else {
|
||||
requestToQuitFromApp = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
27
deploy/installer/config/macos.xml
Normal file
27
deploy/installer/config/macos.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Installer>
|
||||
<Name>AmneziaVPN</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Title>AmneziaVPN</Title>
|
||||
<Publisher>AmneziaVPN</Publisher>
|
||||
<StartMenuDir>AmneziaVPN</StartMenuDir>
|
||||
<TargetDir>/Applications/AmneziaVPN.app</TargetDir>
|
||||
<WizardDefaultWidth>600</WizardDefaultWidth>
|
||||
<WizardDefaultHeight>380</WizardDefaultHeight>
|
||||
<WizardStyle>Modern</WizardStyle>
|
||||
<RemoveTargetDir>true</RemoveTargetDir>
|
||||
<AllowSpaceInPath>true</AllowSpaceInPath>
|
||||
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
|
||||
<ControlScript>controlscript.js</ControlScript>
|
||||
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
|
||||
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
|
||||
<SupportsModify>false</SupportsModify>
|
||||
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
|
||||
<RemoteRepositories>
|
||||
<Repository>
|
||||
<Url>https://amneziavpn.org/updates/macos</Url>
|
||||
<Enabled>true</Enabled>
|
||||
<DisplayName>AmneziaVPN - repository for macOS</DisplayName>
|
||||
</Repository>
|
||||
</RemoteRepositories>
|
||||
</Installer>
|
27
deploy/installer/config/windows.xml
Normal file
27
deploy/installer/config/windows.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Installer>
|
||||
<Name>AmneziaVPN</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Title>AmneziaVPN</Title>
|
||||
<Publisher>AmneziaVPN</Publisher>
|
||||
<StartMenuDir>AmneziaVPN</StartMenuDir>
|
||||
<TargetDir>@ApplicationsDir@/AmneziaVPN</TargetDir>
|
||||
<WizardDefaultWidth>600</WizardDefaultWidth>
|
||||
<WizardDefaultHeight>380</WizardDefaultHeight>
|
||||
<WizardStyle>Modern</WizardStyle>
|
||||
<RemoveTargetDir>true</RemoveTargetDir>
|
||||
<AllowSpaceInPath>true</AllowSpaceInPath>
|
||||
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
|
||||
<ControlScript>controlscript.js</ControlScript>
|
||||
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
|
||||
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
|
||||
<SupportsModify>false</SupportsModify>
|
||||
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
|
||||
<RemoteRepositories>
|
||||
<Repository>
|
||||
<Url>https://amneziavpn.org/updates/windows</Url>
|
||||
<Enabled>true</Enabled>
|
||||
<DisplayName>AmneziaVPN - repository for Windows</DisplayName>
|
||||
</Repository>
|
||||
</RemoteRepositories>
|
||||
</Installer>
|
|
@ -0,0 +1,115 @@
|
|||
|
||||
function appName()
|
||||
{
|
||||
return installer.value("Name")
|
||||
}
|
||||
|
||||
function serviceName()
|
||||
{
|
||||
return (appName() + "-service")
|
||||
}
|
||||
|
||||
function appExecutableFileName()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
return appName() + ".exe";
|
||||
} else {
|
||||
return appName();
|
||||
}
|
||||
}
|
||||
|
||||
function runningOnWindows()
|
||||
{
|
||||
return (systemInfo.kernelType === "winnt");
|
||||
}
|
||||
|
||||
function runningOnMacOS()
|
||||
{
|
||||
return (systemInfo.kernelType === "darwin");
|
||||
}
|
||||
|
||||
function vcRuntimeIsInstalled()
|
||||
{
|
||||
return (installer.findPath("msvcp140.dll", [installer.value("RootDir")+ "\\Windows\\System32\\"]).length !== 0)
|
||||
}
|
||||
|
||||
function Component()
|
||||
{
|
||||
component.loaded.connect(this, Component.prototype.componentLoaded);
|
||||
installer.installationFinished.connect(this, Component.prototype.installationFinishedPageIsShown);
|
||||
installer.finishButtonClicked.connect(this, Component.prototype.installationFinished);
|
||||
}
|
||||
|
||||
Component.prototype.componentLoaded = function ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Component.prototype.installationFinishedPageIsShown = function()
|
||||
{
|
||||
if (installer.isInstaller() && installer.status === QInstaller.Success) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
}
|
||||
|
||||
Component.prototype.createOperations = function()
|
||||
{
|
||||
component.createOperations();
|
||||
|
||||
if (runningOnWindows()) {
|
||||
|
||||
component.addOperation("CreateShortcut", "@TargetDir@/" + appExecutableFileName(),
|
||||
QDesktopServices.storageLocation(QDesktopServices.DesktopLocation) + "/" + appName() + ".lnk",
|
||||
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\" + appExecutableFileName(), "iconId=0");
|
||||
|
||||
|
||||
component.addElevatedOperation("CreateShortcut", "@TargetDir@/" + appExecutableFileName(),
|
||||
installer.value("AllUsersStartMenuProgramsPath") + "/" + appName() + ".lnk",
|
||||
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\" + appExecutableFileName(), "iconId=0");
|
||||
|
||||
if (!vcRuntimeIsInstalled()) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@\\" + "vc_redist.x86.exe", "/install", "/quiet", "/norestart", "/log", "vc_redist_2017_x86.log");
|
||||
} else {
|
||||
console.log("Microsoft Visual C++ 2017 Redistributable already installed");
|
||||
}
|
||||
|
||||
component.addElevatedOperation("Execute",
|
||||
["sc", "create", serviceName(), "binpath=", installer.value("TargetDir").replace(/\//g, '\\') + "\\" + serviceName() + ".exe",
|
||||
"start=", "auto", "depend=", "BFE/nsi"],
|
||||
"UNDOEXECUTE", ["post-uninstall.exe"]);
|
||||
|
||||
} else if (runningOnMacOS()) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@/post_install.sh", "UNDOEXECUTE", "@TargetDir@/post_uninstall.sh");
|
||||
}
|
||||
}
|
||||
|
||||
Component.prototype.installationFinished = function()
|
||||
{
|
||||
var command = "";
|
||||
var args = [];
|
||||
|
||||
if ((installer.status === QInstaller.Success) && (installer.isInstaller() || installer.isUpdater())) {
|
||||
|
||||
if (!installer.gainAdminRights()) {
|
||||
console.log("Fatal error! Cannot get admin rights!")
|
||||
return
|
||||
}
|
||||
|
||||
if (runningOnWindows()) {
|
||||
command = "@TargetDir@/" + appExecutableFileName()
|
||||
|
||||
var status1 = installer.execute("net", ["start", serviceName()])
|
||||
console.log(("%1 started with status: %2 ").arg(serviceName()).arg(status1))
|
||||
|
||||
var status2 = installer.execute("sc", ["failure", serviceName(), "reset=", "100", "actions=", "restart/2000/restart/2000/restart/2000"])
|
||||
console.log(("Changed settings for %1 with status: %2 ").arg(serviceName()).arg(status2))
|
||||
|
||||
} else if (runningOnMacOS()) {
|
||||
command = "/Applications/" + appName() + ".app/Contents/MacOS/" + appName();
|
||||
}
|
||||
|
||||
installer.dropAdminRights()
|
||||
|
||||
processStatus = installer.executeDetached(command, args, installer.value("TargetDir"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Package>
|
||||
<DisplayName>AmneziaVPN</DisplayName>
|
||||
<Description>Installation package for AmneziaVPN</Description>
|
||||
<Version>1.0.0</Version>
|
||||
<ReleaseDate>1970-01-01</ReleaseDate>
|
||||
<Default>true</Default>
|
||||
<ForcedInstallation>true</ForcedInstallation>
|
||||
<RequiresAdminRights>true</RequiresAdminRights>
|
||||
<Script>componentscript.js</Script>
|
||||
</Package>
|
||||
|
58
deploy/macos.sh
Normal file
58
deploy/macos.sh
Normal file
|
@ -0,0 +1,58 @@
|
|||
#!/bin/bash -ex
|
||||
|
||||
|
||||
QT_BIN_DIR='/Users/admin/Qt/5.14.2/clang_64/bin'
|
||||
QIF_BIN_DIR='/Users/admin/Qt/Tools/QtInstallerFramework/4.0/bin'
|
||||
|
||||
APP_NAME=AmneziaVPN
|
||||
APP_FILENAME=$APP_NAME.app
|
||||
APP_DOMAIN=org.amneziavpn.package
|
||||
PLIST_NAME=$APP_NAME.plist
|
||||
|
||||
LAUNCH_DIR=$(pwd)
|
||||
TOP_DIR=$LAUNCH_DIR/..
|
||||
RELEASE_DIR=$TOP_DIR/../$APP_NAME-build
|
||||
OUT_APP_DIR=$RELEASE_DIR/client/release
|
||||
BUNDLE_DIR=$OUT_APP_DIR/$APP_FILENAME
|
||||
DEPLOY_DATA_DIR=$LAUNCH_DIR/data/macos
|
||||
INSTALLER_DATA_DIR=$RELEASE_DIR/installer/packages/$APP_DOMAIN/data
|
||||
|
||||
PRO_FILE_PATH=$TOP_DIR/$APP_NAME.pro
|
||||
QMAKE_STASH_FILE=$TOP_DIR/.qmake_stash
|
||||
TARGET_FILENAME=$TOP_DIR/$APP_NAME.dmg
|
||||
|
||||
cleanBuild()
|
||||
{
|
||||
rm -rf $RELEASE_DIR
|
||||
rm -rf $QMAKE_STASH_FILE
|
||||
}
|
||||
|
||||
|
||||
cleanBuild
|
||||
|
||||
cd $TOP_DIR
|
||||
$QT_BIN_DIR/qmake $PRO_FILE_PATH 'CONFIG+=release CONFIG+=x86_64'
|
||||
make -j `sysctl -n hw.ncpu`
|
||||
|
||||
$QT_BIN_DIR/macdeployqt $OUT_APP_DIR/$APP_FILENAME -always-overwrite
|
||||
cp -av $RELEASE_DIR/server/release/$APP_NAME-service.app/Contents/macOS/$APP_NAME-service $BUNDLE_DIR/Contents/macOS
|
||||
cp -av $LAUNCH_DIR/data/macos/openvpn $BUNDLE_DIR/Contents/macOS
|
||||
|
||||
mkdir -p $INSTALLER_DATA_DIR
|
||||
cp -av $LAUNCH_DIR/installer $RELEASE_DIR
|
||||
cp -av $DEPLOY_DATA_DIR/post_install.sh $INSTALLER_DATA_DIR/post_install.sh
|
||||
cp -av $DEPLOY_DATA_DIR/post_uninstall.sh $INSTALLER_DATA_DIR/post_uninstall.sh
|
||||
cp -av $DEPLOY_DATA_DIR/$PLIST_NAME $INSTALLER_DATA_DIR/$PLIST_NAME
|
||||
|
||||
cd $BUNDLE_DIR
|
||||
tar czf $INSTALLER_DATA_DIR/$APP_NAME.tar.gz ./
|
||||
|
||||
cd $RELEASE_DIR/installer
|
||||
$QIF_BIN_DIR/binarycreator --offline-only -v -c config/macos.xml -p packages -f $APP_NAME
|
||||
hdiutil create -volname $APP_NAME -srcfolder $APP_NAME.app -ov -format UDZO $TARGET_FILENAME
|
||||
|
||||
cleanBuild
|
||||
|
||||
cd $LAUNCH_DIR
|
||||
|
||||
echo "Finished, see $APP_NAME.dmg in '$TOP_DIR'"
|
Binary file not shown.
1
deploy/windows-env.bat
Normal file
1
deploy/windows-env.bat
Normal file
|
@ -0,0 +1 @@
|
|||
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat"
|
71
deploy/windows.bat
Normal file
71
deploy/windows.bat
Normal file
|
@ -0,0 +1,71 @@
|
|||
@ECHO OFF
|
||||
|
||||
CHCP 1252
|
||||
|
||||
SET QT_BIN_DIR="c:\Devel\Qt\5.14.2\msvc2017\bin"
|
||||
SET QIF_BIN_DIR="c:\Devel\Qt\Tools\QtInstallerFramework\4.0\bin"
|
||||
|
||||
set APP_NAME=AmneziaVPN
|
||||
set APP_FILENAME=%APP_NAME:"=%.exe
|
||||
set APP_DOMAIN=org.amneziavpn.package
|
||||
set LAUNCH_DIR=%cd%
|
||||
set TOP_DIR=%LAUNCH_DIR:"=%\..
|
||||
set RELEASE_DIR=%TOP_DIR:"=%\..\%APP_NAME:"=%-build
|
||||
set OUT_APP_DIR=%RELEASE_DIR:"=%\client\release
|
||||
set DEPLOY_DATA_DIR=%LAUNCH_DIR:"=%\data\windows
|
||||
set INSTALLER_DATA_DIR=%RELEASE_DIR:"=%\installer\packages\%APP_DOMAIN:"=%\data
|
||||
set PRO_FILE_PATH=%TOP_DIR:"=%\%APP_NAME:"=%.pro
|
||||
set QMAKE_STASH_FILE=%TOP_DIR:"=%\.qmake_stash
|
||||
set TARGET_FILENAME=%TOP_DIR:"=%\%APP_NAME:"=%.exe
|
||||
|
||||
echo "Environment:"
|
||||
echo "APP_FILENAME: %APP_FILENAME%"
|
||||
echo "LAUNCH_DIR: %LAUNCH_DIR%"
|
||||
echo "TOP_DIR: %TOP_DIR%"
|
||||
echo "RELEASE_DIR: %RELEASE_DIR%"
|
||||
echo "OUT_APP_DIR: %OUT_APP_DIR%"
|
||||
echo "DEPLOY_DATA_DIR: %DEPLOY_DATA_DIR%"
|
||||
echo "INSTALLER_DATA_DIR: %INSTALLER_DATA_DIR%"
|
||||
echo "PRO_FILE_PATH: %PRO_FILE_PATH%"
|
||||
echo "QMAKE_STASH_FILE: %QMAKE_STASH_FILE%"
|
||||
echo "TARGET_FILENAME: %TARGET_FILENAME%"
|
||||
|
||||
echo "Cleanup..."
|
||||
Rmdir /Q /S %RELEASE_DIR%
|
||||
Del %QMAKE_STASH_FILE%
|
||||
Del %TARGET_FILENAME%
|
||||
|
||||
|
||||
cd %TOP_DIR%
|
||||
"%QT_BIN_DIR:"=%\qmake" %PRO_FILE_PATH% -spec win32-msvc
|
||||
set CL=/MP
|
||||
nmake /A /NOLOGO
|
||||
del "%OUT_APP_DIR:"=%\*.obj"
|
||||
del "%OUT_APP_DIR:"=%\*.cpp"
|
||||
del "%OUT_APP_DIR:"=%\*.h"
|
||||
del "%OUT_APP_DIR:"=%\*.res"
|
||||
del "%OUT_APP_DIR:"=%\*.o"
|
||||
del "%OUT_APP_DIR:"=%\*.lib"
|
||||
del "%OUT_APP_DIR:"=%\*.exp"
|
||||
echo "Deploying..."
|
||||
"%QT_BIN_DIR:"=%\windeployqt" --release --force --no-translations "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%"
|
||||
echo "Copying deploy data..."
|
||||
xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f
|
||||
copy "%RELEASE_DIR:"=%\server\release\%APP_NAME:"=%-service.exe" %OUT_APP_DIR%
|
||||
copy "%RELEASE_DIR:"=%\post-uninstall\release\post-uninstall.exe" %OUT_APP_DIR%
|
||||
|
||||
cd %LAUNCH_DIR%
|
||||
xcopy %LAUNCH_DIR:"=%\installer %RELEASE_DIR:"=%\installer /s /e /y /i /f
|
||||
mkdir %INSTALLER_DATA_DIR%
|
||||
|
||||
cd %OUT_APP_DIR%
|
||||
echo "Compressing data..."
|
||||
"%QIF_BIN_DIR:"=%\archivegen" -c 9 %INSTALLER_DATA_DIR:"=%\%APP_NAME:"=%.7z ./
|
||||
|
||||
cd "%RELEASE_DIR:"=%\installer"
|
||||
echo "Creating installer..."
|
||||
"%QIF_BIN_DIR:"=%\binarycreator" --offline-only -v -c config\windows.xml -p packages -f %TARGET_FILENAME%
|
||||
|
||||
|
||||
cd %LAUNCH_DIR%
|
||||
echo "Finished, see %TARGET_FILENAME%"
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,191 +0,0 @@
|
|||
; ****************************************************************************
|
||||
; * Copyright (C) 2002-2014 OpenVPN Technologies, Inc. *
|
||||
; * This program is free software; you can redistribute it and/or modify *
|
||||
; * it under the terms of the GNU General Public License version 2 *
|
||||
; * as published by the Free Software Foundation. *
|
||||
; ****************************************************************************
|
||||
|
||||
; SYNTAX CHECKER
|
||||
; cd \WINDDK\3790\tools\chkinf
|
||||
; chkinf c:\src\openvpn\tap-win32\i386\oemvista.inf
|
||||
; OUTPUT -> file:///c:/WINDDK/3790/tools/chkinf/htm/c%23+src+openvpn+tap-win32+i386+__OemWin2k.htm
|
||||
|
||||
; INSTALL/REMOVE DRIVER
|
||||
; tapinstall install OemVista.inf tapoas
|
||||
; tapinstall update OemVista.inf tapoas
|
||||
; tapinstall remove tapoas
|
||||
|
||||
;*********************************************************
|
||||
; Note to Developers:
|
||||
;
|
||||
; If you are bundling the TAP-Windows driver with your app,
|
||||
; you should try to rename it in such a way that it will
|
||||
; not collide with other instances of TAP-Windows defined
|
||||
; by other apps. Multiple versions of the TAP-Windows
|
||||
; driver, each installed by different apps, can coexist
|
||||
; on the same machine if you follow these guidelines.
|
||||
; NOTE: these instructions assume you are editing the
|
||||
; generated OemWin2k.inf file, not the source
|
||||
; OemWin2k.inf.in file which is preprocessed by winconfig
|
||||
; and uses macro definitions from settings.in.
|
||||
;
|
||||
; (1) Rename all tapXXXX instances in this file to
|
||||
; something different (use at least 5 characters
|
||||
; for this name!)
|
||||
; (2) Change the "!define TAP" definition in openvpn.nsi
|
||||
; to match what you changed tapXXXX to.
|
||||
; (3) Change TARGETNAME in SOURCES to match what you
|
||||
; changed tapXXXX to.
|
||||
; (4) Change TAP_COMPONENT_ID in common.h to match what
|
||||
; you changed tapXXXX to.
|
||||
; (5) Change SZDEPENDENCIES in service.h to match what
|
||||
; you changed tapXXXX to.
|
||||
; (6) Change DeviceDescription and Provider strings.
|
||||
; (7) Change PRODUCT_TAP_WIN_DEVICE_DESCRIPTION in constants.h to what you
|
||||
; set DeviceDescription to.
|
||||
;
|
||||
;*********************************************************
|
||||
|
||||
[Version]
|
||||
Signature = "$Windows NT$"
|
||||
CatalogFile = tap0901.cat
|
||||
ClassGUID = {4d36e972-e325-11ce-bfc1-08002be10318}
|
||||
Provider = %Provider%
|
||||
Class = Net
|
||||
|
||||
; This version number should match the version
|
||||
; number given in ..\version.m4.
|
||||
DriverVer = 09/27/2019,9.24.2.601
|
||||
|
||||
[Strings]
|
||||
DeviceDescription = "TAP-Windows Adapter V9"
|
||||
Provider = "TAP-Windows Provider V9"
|
||||
|
||||
;----------------------------------------------------------------
|
||||
; Manufacturer + Product Section (Done)
|
||||
;----------------------------------------------------------------
|
||||
[Manufacturer]
|
||||
%Provider% = tap0901, NTARM64
|
||||
|
||||
[tap0901.NTARM64]
|
||||
%DeviceDescription% = tap0901.ndi, root\tap0901 ; Root enumerated
|
||||
%DeviceDescription% = tap0901.ndi, tap0901 ; Legacy
|
||||
|
||||
;---------------------------------------------------------------
|
||||
; Driver Section (Done)
|
||||
;---------------------------------------------------------------
|
||||
|
||||
;----------------- Characteristics ------------
|
||||
; NCF_PHYSICAL = 0x04
|
||||
; NCF_VIRTUAL = 0x01
|
||||
; NCF_SOFTWARE_ENUMERATED = 0x02
|
||||
; NCF_HIDDEN = 0x08
|
||||
; NCF_NO_SERVICE = 0x10
|
||||
; NCF_HAS_UI = 0x80
|
||||
;----------------- Characteristics ------------
|
||||
|
||||
[tap0901.ndi]
|
||||
CopyFiles = tap0901.driver, tap0901.files
|
||||
AddReg = tap0901.reg
|
||||
AddReg = tap0901.params.reg
|
||||
Characteristics = 0x1
|
||||
*IfType = 53 ; IF_TYPE_PROP_VIRTUAL
|
||||
*MediaType = 0x0 ; NdisMedium802_3
|
||||
*PhysicalMediaType = 0 ; NdisPhysicalMediumUnspecified
|
||||
|
||||
[tap0901.ndi.Services]
|
||||
AddService = tap0901, 2, tap0901.service
|
||||
|
||||
[tap0901.reg]
|
||||
HKR, Ndi, Service, 0, "tap0901"
|
||||
HKR, Ndi\Interfaces, UpperRange, 0, "ndis5"
|
||||
HKR, Ndi\Interfaces, LowerRange, 0, "ethernet"
|
||||
HKR, , Manufacturer, 0, "%Provider%"
|
||||
HKR, , ProductName, 0, "%DeviceDescription%"
|
||||
|
||||
[tap0901.params.reg]
|
||||
HKR, Ndi\params\MTU, ParamDesc, 0, "MTU"
|
||||
HKR, Ndi\params\MTU, Type, 0, "int"
|
||||
HKR, Ndi\params\MTU, Default, 0, "1500"
|
||||
HKR, Ndi\params\MTU, Optional, 0, "0"
|
||||
HKR, Ndi\params\MTU, Min, 0, "100"
|
||||
HKR, Ndi\params\MTU, Max, 0, "1500"
|
||||
HKR, Ndi\params\MTU, Step, 0, "1"
|
||||
HKR, Ndi\params\MediaStatus, ParamDesc, 0, "Media Status"
|
||||
HKR, Ndi\params\MediaStatus, Type, 0, "enum"
|
||||
HKR, Ndi\params\MediaStatus, Default, 0, "0"
|
||||
HKR, Ndi\params\MediaStatus, Optional, 0, "0"
|
||||
HKR, Ndi\params\MediaStatus\enum, "0", 0, "Application Controlled"
|
||||
HKR, Ndi\params\MediaStatus\enum, "1", 0, "Always Connected"
|
||||
HKR, Ndi\params\MAC, ParamDesc, 0, "MAC Address"
|
||||
HKR, Ndi\params\MAC, Type, 0, "edit"
|
||||
HKR, Ndi\params\MAC, Optional, 0, "1"
|
||||
HKR, Ndi\params\AllowNonAdmin, ParamDesc, 0, "Non-Admin Access"
|
||||
HKR, Ndi\params\AllowNonAdmin, Type, 0, "enum"
|
||||
HKR, Ndi\params\AllowNonAdmin, Default, 0, "1"
|
||||
HKR, Ndi\params\AllowNonAdmin, Optional, 0, "0"
|
||||
HKR, Ndi\params\AllowNonAdmin\enum, "0", 0, "Not Allowed"
|
||||
HKR, Ndi\params\AllowNonAdmin\enum, "1", 0, "Allowed"
|
||||
|
||||
;----------------------------------------------------------------
|
||||
; Service Section
|
||||
;----------------------------------------------------------------
|
||||
|
||||
;---------- Service Type -------------
|
||||
; SERVICE_KERNEL_DRIVER = 0x01
|
||||
; SERVICE_WIN32_OWN_PROCESS = 0x10
|
||||
;---------- Service Type -------------
|
||||
|
||||
;---------- Start Mode ---------------
|
||||
; SERVICE_BOOT_START = 0x0
|
||||
; SERVICE_SYSTEM_START = 0x1
|
||||
; SERVICE_AUTO_START = 0x2
|
||||
; SERVICE_DEMAND_START = 0x3
|
||||
; SERVICE_DISABLED = 0x4
|
||||
;---------- Start Mode ---------------
|
||||
|
||||
[tap0901.service]
|
||||
DisplayName = %DeviceDescription%
|
||||
ServiceType = 1
|
||||
StartType = 3
|
||||
ErrorControl = 1
|
||||
LoadOrderGroup = NDIS
|
||||
ServiceBinary = %12%\tap0901.sys
|
||||
|
||||
;-----------------------------------------------------------------
|
||||
; File Installation
|
||||
;-----------------------------------------------------------------
|
||||
|
||||
;----------------- Copy Flags ------------
|
||||
; COPYFLG_NOSKIP = 0x02
|
||||
; COPYFLG_NOVERSIONCHECK = 0x04
|
||||
;----------------- Copy Flags ------------
|
||||
|
||||
; SourceDisksNames
|
||||
; diskid = description[, [tagfile] [, <unused>, subdir]]
|
||||
; 1 = "Intel Driver Disk 1",e100bex.sys,,
|
||||
|
||||
[SourceDisksNames]
|
||||
1 = %DeviceDescription%, tap0901.sys
|
||||
|
||||
; SourceDisksFiles
|
||||
; filename_on_source = diskID[, [subdir][, size]]
|
||||
; e100bex.sys = 1,, ; on distribution disk 1
|
||||
|
||||
[SourceDisksFiles]
|
||||
tap0901.sys = 1
|
||||
|
||||
[DestinationDirs]
|
||||
tap0901.files = 11
|
||||
tap0901.driver = 12
|
||||
|
||||
[tap0901.files]
|
||||
; TapPanel.cpl,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
|
||||
; cipsrvr.exe,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
|
||||
|
||||
[tap0901.driver]
|
||||
tap0901.sys,,,6 ; COPYFLG_NOSKIP | COPYFLG_NOVERSIONCHECK
|
||||
|
||||
;---------------------------------------------------------------
|
||||
; End
|
||||
;---------------------------------------------------------------
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
* TAP-Windows -- A kernel driver to provide virtual tap
|
||||
* device functionality on Windows.
|
||||
*
|
||||
* This code was inspired by the CIPE-Win32 driver by Damion K. Wilson.
|
||||
*
|
||||
* This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc.,
|
||||
* and is released under the GPL version 2 (see below). This particular file
|
||||
* (tap-windows.h) is also licensed using the MIT license (see COPYRIGHT.MIT).
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program (see the file COPYING included with this
|
||||
* distribution); if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __TAP_WIN_H
|
||||
#define __TAP_WIN_H
|
||||
|
||||
/*
|
||||
* =============
|
||||
* TAP IOCTLs
|
||||
* =============
|
||||
*/
|
||||
|
||||
#define TAP_WIN_CONTROL_CODE(request,method) \
|
||||
CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
|
||||
|
||||
/* Present in 8.1 */
|
||||
|
||||
#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
|
||||
|
||||
/* Added in 8.2 */
|
||||
|
||||
/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */
|
||||
#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED)
|
||||
|
||||
/* Control whether 802.1Q headers are added for priority */
|
||||
#define TAP_WIN_IOCTL_PRIORITY_BEHAVIOR TAP_WIN_CONTROL_CODE (11, METHOD_BUFFERED)
|
||||
#define TAP_PRIORITY_BEHAVIOR_NOPRIORITY 0
|
||||
#define TAP_PRIORITY_BEHAVIOR_ENABLED 1
|
||||
#define TAP_PRIORITY_BEHAVIOR_ADDALWAYS 2
|
||||
#define TAP_PRIORITY_BEHAVIOR_MAX 2
|
||||
|
||||
/*
|
||||
* =================
|
||||
* Registry keys
|
||||
* =================
|
||||
*/
|
||||
|
||||
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
|
||||
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
|
||||
/*
|
||||
* ======================
|
||||
* Filesystem prefixes
|
||||
* ======================
|
||||
*/
|
||||
|
||||
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
|
||||
#define SYSDEVICEDIR "\\Device\\"
|
||||
#define USERDEVICEDIR "\\DosDevices\\Global\\"
|
||||
#define TAP_WIN_SUFFIX ".tap"
|
||||
|
||||
#endif // __TAP_WIN_H
|
Binary file not shown.
Binary file not shown.
4
platform/platform.pro
Normal file
4
platform/platform.pro
Normal file
|
@ -0,0 +1,4 @@
|
|||
TEMPLATE = subdirs
|
||||
win32 {
|
||||
SUBDIRS += post-uninstall
|
||||
}
|
21
platform/post-uninstall/main.cpp
Normal file
21
platform/post-uninstall/main.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QProcess>
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
bool executeProcess(const QString& cmd, const QStringList& args)
|
||||
{
|
||||
QProcess process;
|
||||
process.start(cmd, args);
|
||||
return process.waitForFinished();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
|
||||
executeProcess("sc", QStringList() << "stop" << SERVICE_NAME);
|
||||
executeProcess("sc", QStringList() << "delete" << SERVICE_NAME);
|
||||
|
||||
return 0;
|
||||
}
|
16
platform/post-uninstall/post-uninstall.pro
Normal file
16
platform/post-uninstall/post-uninstall.pro
Normal file
|
@ -0,0 +1,16 @@
|
|||
TARGET = post-uninstall
|
||||
TEMPLATE = app
|
||||
CONFIG += console qt
|
||||
QT = core
|
||||
|
||||
SOURCES = \
|
||||
main.cpp
|
||||
|
||||
CONFIG(release, debug|release) {
|
||||
DESTDIR = $$PWD/../../../AmneziaVPN-build/post-uninstall/release
|
||||
MOC_DIR = $$DESTDIR
|
||||
OBJECTS_DIR = $$DESTDIR
|
||||
RCC_DIR = $$DESTDIR
|
||||
}
|
||||
|
||||
INCLUDEPATH += "$$PWD/../../client"
|
13
service/buildlib/buildlib.pro
Normal file
13
service/buildlib/buildlib.pro
Normal file
|
@ -0,0 +1,13 @@
|
|||
TEMPLATE=lib
|
||||
CONFIG += qt dll qtservice-buildlib
|
||||
mac:CONFIG += absolute_library_soname
|
||||
win32|mac:!wince*:!win32-msvc:!macx-xcode:CONFIG += debug_and_release build_all
|
||||
include(../src/qtservice.pri)
|
||||
TARGET = $$QTSERVICE_LIBNAME
|
||||
DESTDIR = $$QTSERVICE_LIBDIR
|
||||
win32 {
|
||||
DLLDESTDIR = $$[QT_INSTALL_BINS]
|
||||
QMAKE_DISTCLEAN += $$[QT_INSTALL_BINS]\\$${QTSERVICE_LIBNAME}.dll
|
||||
}
|
||||
target.path = $$DESTDIR
|
||||
INSTALLS += target
|
10
service/common.pri
Normal file
10
service/common.pri
Normal file
|
@ -0,0 +1,10 @@
|
|||
#exists(config.pri):infile(config.pri, SOLUTIONS_LIBRARY, yes): CONFIG += qtservice-uselib
|
||||
TEMPLATE += fakelib
|
||||
QTSERVICE_LIBNAME = QtSolutions_Service-head
|
||||
CONFIG(debug, debug|release) {
|
||||
mac:QTSERVICE_LIBNAME = $$member(QTSERVICE_LIBNAME, 0)_debug
|
||||
else:win32:QTSERVICE_LIBNAME = $$member(QTSERVICE_LIBNAME, 0)d
|
||||
}
|
||||
TEMPLATE -= fakelib
|
||||
QTSERVICE_LIBDIR = $$PWD/lib
|
||||
unix:qtservice-uselib:!qtservice-buildlib:QMAKE_RPATHDIR += $$QTSERVICE_LIBDIR
|
16
service/server/main.cpp
Normal file
16
service/server/main.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <QSettings>
|
||||
#include <QDir>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
#if !defined(Q_OS_WIN)
|
||||
// QtService stores service settings in SystemScope, which normally require root privileges.
|
||||
// To allow testing this example as non-root, we change the directory of the SystemScope settings file.
|
||||
QSettings::setPath(QSettings::NativeFormat, QSettings::SystemScope, QDir::tempPath());
|
||||
qWarning("(Example uses dummy settings file: %s/QtSoftware.conf)", QDir::tempPath().toLatin1().constData());
|
||||
#endif
|
||||
HttpService service(argc, argv);
|
||||
return service.exec();
|
||||
}
|
112
service/server/server.cpp
Normal file
112
service/server/server.cpp
Normal file
|
@ -0,0 +1,112 @@
|
|||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include <QTcpSocket>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
HttpDaemon::HttpDaemon(quint16 port, QObject* parent)
|
||||
: QTcpServer(parent), disabled(false)
|
||||
{
|
||||
listen(QHostAddress::Any, port);
|
||||
qDebug() << "Listen on port: " << port;
|
||||
|
||||
connect(this, &QTcpServer::newConnection, this, &HttpDaemon::sendFortune);
|
||||
}
|
||||
|
||||
void HttpDaemon::sendFortune()
|
||||
{
|
||||
qDebug() << "New connection: ";
|
||||
|
||||
QTcpSocket *clientConnection = this->nextPendingConnection();
|
||||
connect(clientConnection, &QAbstractSocket::disconnected,
|
||||
clientConnection, &QObject::deleteLater);
|
||||
|
||||
|
||||
connect(clientConnection, SIGNAL(readyRead()), this, SLOT(readClient()));
|
||||
connect(clientConnection, SIGNAL(disconnected()), this, SLOT(discardClient()));
|
||||
//->setSocketDescriptor(socket);
|
||||
}
|
||||
|
||||
void HttpDaemon::pause()
|
||||
{
|
||||
disabled = true;
|
||||
}
|
||||
|
||||
void HttpDaemon::resume()
|
||||
{
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
void HttpDaemon::readClient()
|
||||
{
|
||||
qDebug() << "readClient";
|
||||
|
||||
// if (disabled)
|
||||
// return;
|
||||
//
|
||||
// This slot is called when the client sent data to the server. The
|
||||
// server looks if it was a get request and sends a very simple HTML
|
||||
// document back.
|
||||
QTcpSocket* socket = (QTcpSocket*)sender();
|
||||
if (socket->canReadLine()) {
|
||||
QStringList tokens = QString(socket->readLine()).split(QRegExp("[ \r\n][ \r\n]*"));
|
||||
if (tokens[0] == "GET") {
|
||||
QTextStream os(socket);
|
||||
os.setAutoDetectUnicode(true);
|
||||
os << "HTTP/1.0 200 Ok\r\n"
|
||||
"Content-Type: text/html; charset=\"utf-8\"\r\n"
|
||||
"\r\n"
|
||||
"<h1>Nothing to see here</h1>\n"
|
||||
<< QDateTime::currentDateTime().toString() << "\n";
|
||||
socket->close();
|
||||
|
||||
QtServiceBase::instance()->logMessage("Wrote to client");
|
||||
|
||||
if (socket->state() == QTcpSocket::UnconnectedState) {
|
||||
delete socket;
|
||||
QtServiceBase::instance()->logMessage("Connection closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HttpDaemon::discardClient()
|
||||
{
|
||||
QTcpSocket* socket = (QTcpSocket*)sender();
|
||||
socket->deleteLater();
|
||||
|
||||
QtServiceBase::instance()->logMessage("Connection closed");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HttpService::HttpService(int argc, char **argv)
|
||||
: QtService<QCoreApplication>(argc, argv, "Qt HTTP Daemon")
|
||||
{
|
||||
setServiceDescription("A dummy HTTP service implemented with Qt");
|
||||
setServiceFlags(QtServiceBase::CanBeSuspended);
|
||||
}
|
||||
|
||||
void HttpService::start()
|
||||
{
|
||||
QCoreApplication *app = application();
|
||||
daemon = new HttpDaemon(8989, app);
|
||||
|
||||
if (!daemon->isListening()) {
|
||||
logMessage(QString("Failed to bind to port %1").arg(daemon->serverPort()), QtServiceBase::Error);
|
||||
app->quit();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpService::pause()
|
||||
{
|
||||
daemon->pause();
|
||||
}
|
||||
|
||||
void HttpService::resume()
|
||||
{
|
||||
daemon->resume();
|
||||
}
|
43
service/server/server.h
Normal file
43
service/server/server.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include "qtservice.h"
|
||||
|
||||
|
||||
class HttpDaemon : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
HttpDaemon(quint16 port, QObject* parent = 0);
|
||||
void sendFortune();
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
|
||||
private slots:
|
||||
void readClient();
|
||||
void discardClient();
|
||||
private:
|
||||
bool disabled;
|
||||
};
|
||||
|
||||
|
||||
class HttpService : public QtService<QCoreApplication>
|
||||
{
|
||||
public:
|
||||
HttpService(int argc, char **argv);
|
||||
|
||||
protected:
|
||||
void pause();
|
||||
void resume();
|
||||
void start();
|
||||
|
||||
private:
|
||||
HttpDaemon *daemon;
|
||||
};
|
||||
|
||||
|
||||
#endif // SERVER_H
|
19
service/server/server.pro
Normal file
19
service/server/server.pro
Normal file
|
@ -0,0 +1,19 @@
|
|||
TARGET = AmneziaVPN-service
|
||||
TEMPLATE = app
|
||||
CONFIG += console qt
|
||||
QT = core network
|
||||
|
||||
HEADERS = \
|
||||
server.h
|
||||
SOURCES = \
|
||||
server.cpp \
|
||||
main.cpp
|
||||
|
||||
include(../src/qtservice.pri)
|
||||
|
||||
CONFIG(release, debug|release) {
|
||||
DESTDIR = $$PWD/../../../AmneziaVPN-build/server/release
|
||||
MOC_DIR = $$DESTDIR
|
||||
OBJECTS_DIR = $$DESTDIR
|
||||
RCC_DIR = $$DESTDIR
|
||||
}
|
5
service/service.pro
Normal file
5
service/service.pro
Normal file
|
@ -0,0 +1,5 @@
|
|||
TEMPLATE=subdirs
|
||||
CONFIG += ordered
|
||||
include(common.pri)
|
||||
qtservice-uselib:SUBDIRS=buildlib
|
||||
SUBDIRS+=server
|
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