1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "abstractremotelinuxdeployservice.h"
27 #include "deploymenttimeinfo.h"
28
29 #include <projectexplorer/deployablefile.h>
30 #include <projectexplorer/kitinformation.h>
31 #include <projectexplorer/target.h>
32
33 #include <ssh/sshconnection.h>
34 #include <ssh/sshconnectionmanager.h>
35
36 #include <utils/qtcassert.h>
37
38 #include <QDateTime>
39 #include <QFileInfo>
40 #include <QPointer>
41 #include <QString>
42
43 using namespace ProjectExplorer;
44 using namespace QSsh;
45
46 namespace RemoteLinux {
47 namespace Internal {
48
49 namespace {
50 enum State { Inactive, SettingUpDevice, Connecting, Deploying };
51 } // anonymous namespace
52
53 class AbstractRemoteLinuxDeployServicePrivate
54 {
55 public:
56 IDevice::ConstPtr deviceConfiguration;
57 QPointer<Target> target;
58
59 DeploymentTimeInfo deployTimes;
60 SshConnection *connection = nullptr;
61 State state = Inactive;
62 bool stopRequested = false;
63 };
64 } // namespace Internal
65
66 using namespace Internal;
67
AbstractRemoteLinuxDeployService(QObject * parent)68 AbstractRemoteLinuxDeployService::AbstractRemoteLinuxDeployService(QObject *parent)
69 : QObject(parent), d(new AbstractRemoteLinuxDeployServicePrivate)
70 {
71 }
72
~AbstractRemoteLinuxDeployService()73 AbstractRemoteLinuxDeployService::~AbstractRemoteLinuxDeployService()
74 {
75 delete d;
76 }
77
target() const78 const Target *AbstractRemoteLinuxDeployService::target() const
79 {
80 return d->target;
81 }
82
profile() const83 const Kit *AbstractRemoteLinuxDeployService::profile() const
84 {
85 return d->target ? d->target->kit() : nullptr;
86 }
87
deviceConfiguration() const88 IDevice::ConstPtr AbstractRemoteLinuxDeployService::deviceConfiguration() const
89 {
90 return d->deviceConfiguration;
91 }
92
connection() const93 SshConnection *AbstractRemoteLinuxDeployService::connection() const
94 {
95 return d->connection;
96 }
97
saveDeploymentTimeStamp(const DeployableFile & deployableFile,const QDateTime & remoteTimestamp)98 void AbstractRemoteLinuxDeployService::saveDeploymentTimeStamp(const DeployableFile &deployableFile,
99 const QDateTime &remoteTimestamp)
100 {
101 d->deployTimes.saveDeploymentTimeStamp(deployableFile, profile(), remoteTimestamp);
102 }
103
hasLocalFileChanged(const DeployableFile & deployableFile) const104 bool AbstractRemoteLinuxDeployService::hasLocalFileChanged(
105 const DeployableFile &deployableFile) const
106 {
107 return d->deployTimes.hasLocalFileChanged(deployableFile, profile());
108 }
109
hasRemoteFileChanged(const DeployableFile & deployableFile,const QDateTime & remoteTimestamp) const110 bool AbstractRemoteLinuxDeployService::hasRemoteFileChanged(
111 const DeployableFile &deployableFile, const QDateTime &remoteTimestamp) const
112 {
113 return d->deployTimes.hasRemoteFileChanged(deployableFile, profile(), remoteTimestamp);
114 }
115
setTarget(Target * target)116 void AbstractRemoteLinuxDeployService::setTarget(Target *target)
117 {
118 d->target = target;
119 d->deviceConfiguration = DeviceKitAspect::device(profile());
120 }
121
setDevice(const IDevice::ConstPtr & device)122 void AbstractRemoteLinuxDeployService::setDevice(const IDevice::ConstPtr &device)
123 {
124 d->deviceConfiguration = device;
125 }
126
start()127 void AbstractRemoteLinuxDeployService::start()
128 {
129 QTC_ASSERT(d->state == Inactive, return);
130
131 const CheckResult check = isDeploymentPossible();
132 if (!check) {
133 emit errorMessage(check.errorMessage());
134 emit finished();
135 return;
136 }
137
138 if (!isDeploymentNecessary()) {
139 emit progressMessage(tr("No deployment action necessary. Skipping."));
140 emit finished();
141 return;
142 }
143
144 d->state = SettingUpDevice;
145 doDeviceSetup();
146 }
147
stop()148 void AbstractRemoteLinuxDeployService::stop()
149 {
150 if (d->stopRequested)
151 return;
152
153 switch (d->state) {
154 case Inactive:
155 break;
156 case SettingUpDevice:
157 d->stopRequested = true;
158 stopDeviceSetup();
159 break;
160 case Connecting:
161 setFinished();
162 break;
163 case Deploying:
164 d->stopRequested = true;
165 stopDeployment();
166 break;
167 }
168 }
169
isDeploymentPossible() const170 CheckResult AbstractRemoteLinuxDeployService::isDeploymentPossible() const
171 {
172 if (!deviceConfiguration())
173 return CheckResult::failure(tr("No device configuration set."));
174 return CheckResult::success();
175 }
176
exportDeployTimes() const177 QVariantMap AbstractRemoteLinuxDeployService::exportDeployTimes() const
178 {
179 return d->deployTimes.exportDeployTimes();
180 }
181
importDeployTimes(const QVariantMap & map)182 void AbstractRemoteLinuxDeployService::importDeployTimes(const QVariantMap &map)
183 {
184 d->deployTimes.importDeployTimes(map);
185 }
186
handleDeviceSetupDone(bool success)187 void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success)
188 {
189 QTC_ASSERT(d->state == SettingUpDevice, return);
190
191 if (!success || d->stopRequested) {
192 setFinished();
193 return;
194 }
195
196 d->state = Connecting;
197 d->connection = QSsh::acquireConnection(deviceConfiguration()->sshParameters());
198 connect(d->connection, &SshConnection::errorOccurred,
199 this, &AbstractRemoteLinuxDeployService::handleConnectionFailure);
200 if (d->connection->state() == SshConnection::Connected) {
201 handleConnected();
202 } else {
203 connect(d->connection, &SshConnection::connected,
204 this, &AbstractRemoteLinuxDeployService::handleConnected);
205 emit progressMessage(tr("Connecting to device \"%1\" (%2).")
206 .arg(deviceConfiguration()->displayName())
207 .arg(deviceConfiguration()->sshParameters().host()));
208 if (d->connection->state() == SshConnection::Unconnected)
209 d->connection->connectToHost();
210 }
211 }
212
handleDeploymentDone()213 void AbstractRemoteLinuxDeployService::handleDeploymentDone()
214 {
215 QTC_ASSERT(d->state == Deploying, return);
216
217 setFinished();
218 }
219
handleConnected()220 void AbstractRemoteLinuxDeployService::handleConnected()
221 {
222 QTC_ASSERT(d->state == Connecting, return);
223
224 if (d->stopRequested) {
225 setFinished();
226 return;
227 }
228
229 d->state = Deploying;
230 doDeploy();
231 }
232
handleConnectionFailure()233 void AbstractRemoteLinuxDeployService::handleConnectionFailure()
234 {
235 switch (d->state) {
236 case Inactive:
237 case SettingUpDevice:
238 qWarning("%s: Unexpected state %d.", Q_FUNC_INFO, d->state);
239 break;
240 case Connecting: {
241 QString errorMsg = tr("Could not connect to host: %1").arg(d->connection->errorString());
242 errorMsg += QLatin1Char('\n');
243 if (deviceConfiguration()->machineType() == IDevice::Emulator)
244 errorMsg += tr("Did the emulator fail to start?");
245 else
246 errorMsg += tr("Is the device connected and set up for network access?");
247 emit errorMessage(errorMsg);
248 setFinished();
249 break;
250 }
251 case Deploying:
252 emit errorMessage(tr("Connection error: %1").arg(d->connection->errorString()));
253 stopDeployment();
254 }
255 }
256
setFinished()257 void AbstractRemoteLinuxDeployService::setFinished()
258 {
259 d->state = Inactive;
260 if (d->connection) {
261 disconnect(d->connection, nullptr, this, nullptr);
262 QSsh::releaseConnection(d->connection);
263 d->connection = nullptr;
264 }
265 d->stopRequested = false;
266 emit finished();
267 }
268
269 } // namespace RemoteLinux
270