Add installer

This commit is contained in:
driftingsun 2020-12-16 06:02:22 +03:00
parent c9bc8aa8c1
commit a2a5cafc5f
73 changed files with 4354 additions and 488 deletions

17
.gitignore vendored
View file

@ -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
View 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
View file

@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS = client service platform

View file

@ -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
View 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
}

View file

@ -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;
}

View file

@ -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

View file

@ -2,6 +2,7 @@
#define DEFINES_H
#define APPLICATION_NAME "AmneziaVPN"
#define SERVICE_NAME "AmneziaVPN-service"
#define ORGANIZATION_NAME "AmneziaVPN.ORG"
#endif // DEFINES_H

View file

@ -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);

View file

@ -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>

View file

@ -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";
}

View file

@ -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;
};

View file

@ -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">

View 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

Binary file not shown.

View 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"

View 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

View 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;
}
}
}

View 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>

View 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>

View file

@ -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"));
}
}

View file

@ -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
View 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
View file

@ -0,0 +1 @@
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\Tools\VsDevCmd.bat"

71
deploy/windows.bat Normal file
View 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%"

View file

@ -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.

View file

@ -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

4
platform/platform.pro Normal file
View file

@ -0,0 +1,4 @@
TEMPLATE = subdirs
win32 {
SUBDIRS += post-uninstall
}

View 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;
}

View 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"

View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
TEMPLATE=subdirs
CONFIG += ordered
include(common.pri)
qtservice-uselib:SUBDIRS=buildlib
SUBDIRS+=server

1
service/src/QtService Normal file
View file

@ -0,0 +1 @@
#include "qtservice.h"

View file

@ -0,0 +1 @@
#include "qtservice.h"

View file

@ -0,0 +1 @@
#include "qtservice.h"

1129
service/src/qtservice.cpp Normal file

File diff suppressed because it is too large Load diff

192
service/src/qtservice.h Normal file
View 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
View 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
View 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

View 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;
}

View 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);
}

View 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();
}
}

View 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

View 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;
}

View 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