blob: 4d22959d29c03cb04cb6037b86d9e425afc24e83 [file] [log] [blame]
Sébastien Blin1f915762020-08-03 13:27:42 -04001/*
2 * Copyright (C) 2019-2020 by Savoir-faire Linux
3 * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18// Based on: https://stackoverflow.com/a/28172162
19
20#include "runguard.h"
21#include <QCryptographicHash>
22
23namespace {
24
25QString
26generateKeyHash(const QString &key, const QString &salt)
27{
28 QByteArray data;
29
30 data.append(key.toUtf8());
31 data.append(salt.toUtf8());
32 data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex();
33
34 return data;
35}
36
37} // namespace
38
39RunGuard::RunGuard(const QString &key)
40 : key_(key)
41 , memLockKey_(generateKeyHash(key, "_memLockKey"))
42 , sharedmemKey_(generateKeyHash(key, "_sharedmemKey"))
43 , sharedMem_(sharedmemKey_)
44 , memLock_(memLockKey_, 1)
45{}
46
47RunGuard::~RunGuard()
48{
49 release();
50}
51
52void
53RunGuard::tryRestorePrimaryInstance()
54{
55 /*
56 * TODO: relaunch application
57 */
58}
59
60bool
61RunGuard::isAnotherRunning()
62{
63 if (sharedMem_.isAttached())
64 return false;
65
66 memLock_.acquire();
67 const bool isRunning = sharedMem_.attach();
68 if (isRunning)
69 sharedMem_.detach();
70 memLock_.release();
71
72 return isRunning;
73}
74
75bool
76RunGuard::tryToRun()
77{
78#ifdef Q_OS_WIN
79 if (isAnotherRunning()) {
80 /*
81 * This is a secondary instance,
82 * connect to the primary instance to trigger a restore
83 * then fail.
84 */
85 if (socket_ == nullptr) {
86 socket_ = new QLocalSocket();
87 }
88 if (socket_->state() == QLocalSocket::UnconnectedState
89 || socket_->state() == QLocalSocket::ClosingState) {
90 socket_->connectToServer(key_);
91 }
92 if (socket_->state() == QLocalSocket::ConnectingState) {
93 socket_->waitForConnected();
94 }
95 return false;
96 }
97
98 memLock_.acquire();
99 const bool result = sharedMem_.create(sizeof(quint64));
100 memLock_.release();
101 if (!result) {
102 release();
103 return false;
104 }
105
106 /*
107 * This is the primary instance,
108 * listen for subsequent instances.
109 */
110 QLocalServer::removeServer(key_);
111 server_ = new QLocalServer();
112 server_->setSocketOptions(QLocalServer::UserAccessOption);
113 server_->listen(key_);
114 QObject::connect(server_,
115 &QLocalServer::newConnection,
116 this,
117 &RunGuard::tryRestorePrimaryInstance);
118#endif
119
120 return true;
121}
122
123void
124RunGuard::release()
125{
126 memLock_.acquire();
127 if (sharedMem_.isAttached())
128 sharedMem_.detach();
129 memLock_.release();
130}