/* * Copyright (C) 2019-2020 by Savoir-faire Linux * Author: Andreas Traczyk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * 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. If not, see . */ // Based on: https://stackoverflow.com/a/28172162 #include "runguard.h" #include namespace { QString generateKeyHash(const QString &key, const QString &salt) { QByteArray data; data.append(key.toUtf8()); data.append(salt.toUtf8()); data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex(); return data; } } // namespace RunGuard::RunGuard(const QString &key) : key_(key) , memLockKey_(generateKeyHash(key, "_memLockKey")) , sharedmemKey_(generateKeyHash(key, "_sharedmemKey")) , sharedMem_(sharedmemKey_) , memLock_(memLockKey_, 1) {} RunGuard::~RunGuard() { release(); } void RunGuard::tryRestorePrimaryInstance() { /* * TODO: relaunch application */ } bool RunGuard::isAnotherRunning() { if (sharedMem_.isAttached()) return false; memLock_.acquire(); const bool isRunning = sharedMem_.attach(); if (isRunning) sharedMem_.detach(); memLock_.release(); return isRunning; } bool RunGuard::tryToRun() { #ifdef Q_OS_WIN if (isAnotherRunning()) { /* * This is a secondary instance, * connect to the primary instance to trigger a restore * then fail. */ if (socket_ == nullptr) { socket_ = new QLocalSocket(); } if (socket_->state() == QLocalSocket::UnconnectedState || socket_->state() == QLocalSocket::ClosingState) { socket_->connectToServer(key_); } if (socket_->state() == QLocalSocket::ConnectingState) { socket_->waitForConnected(); } return false; } memLock_.acquire(); const bool result = sharedMem_.create(sizeof(quint64)); memLock_.release(); if (!result) { release(); return false; } /* * This is the primary instance, * listen for subsequent instances. */ QLocalServer::removeServer(key_); server_ = new QLocalServer(); server_->setSocketOptions(QLocalServer::UserAccessOption); server_->listen(key_); QObject::connect(server_, &QLocalServer::newConnection, this, &RunGuard::tryRestorePrimaryInstance); #endif return true; } void RunGuard::release() { memLock_.acquire(); if (sharedMem_.isAttached()) sharedMem_.detach(); memLock_.release(); }