02d97aabf4
The rationale is explained in https://planet.kde.org/friedrich-kossebau-2023-06-28-include-also-moc-files-of-headers/ In case of KDEConnect, it impressively speeds up compilation. Before it took 390 seconds on a clean build and with this change it took 330 seconds. This is due to the mocs_compilation having to include the header files and thus all their headers. Due to the lots of small plugins we have, this means that the same headers must be compiled plenty of times. When we include the moc files directly in the C++ file, they are already available.
239 lines
8.5 KiB
C++
239 lines
8.5 KiB
C++
/**
|
|
* SPDX-FileCopyrightText: 2014 Samoilenko Yuri <kinnalru@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
|
*/
|
|
|
|
#include "mounter.h"
|
|
|
|
#include <QDebug>
|
|
#include <QDir>
|
|
#include <unistd.h>
|
|
|
|
#include <KLocalizedString>
|
|
|
|
#include "config-sftp.h"
|
|
#include "kdeconnectconfig.h"
|
|
#include "mountloop.h"
|
|
#include "plugin_sftp_debug.h"
|
|
|
|
Mounter::Mounter(SftpPlugin *sftp)
|
|
: QObject(sftp)
|
|
, m_sftp(sftp)
|
|
, m_proc(nullptr)
|
|
, m_mountPoint(sftp->mountPoint())
|
|
, m_started(false)
|
|
{
|
|
connect(m_sftp, &SftpPlugin::packetReceived, this, &Mounter::onPackageReceived);
|
|
|
|
connect(&m_connectTimer, &QTimer::timeout, this, &Mounter::onMountTimeout);
|
|
|
|
connect(this, &Mounter::mounted, &m_connectTimer, &QTimer::stop);
|
|
connect(this, &Mounter::failed, &m_connectTimer, &QTimer::stop);
|
|
|
|
m_connectTimer.setInterval(10000);
|
|
m_connectTimer.setSingleShot(true);
|
|
|
|
QTimer::singleShot(0, this, &Mounter::start);
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Created mounter";
|
|
}
|
|
|
|
Mounter::~Mounter()
|
|
{
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Destroy mounter";
|
|
unmount(false);
|
|
}
|
|
|
|
bool Mounter::wait()
|
|
{
|
|
if (m_started) {
|
|
return true;
|
|
}
|
|
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting loop to wait for mount";
|
|
|
|
MountLoop loop;
|
|
connect(this, &Mounter::mounted, &loop, &MountLoop::succeeded);
|
|
connect(this, &Mounter::failed, &loop, &MountLoop::failed);
|
|
return loop.exec();
|
|
}
|
|
|
|
void Mounter::onPackageReceived(const NetworkPacket &np)
|
|
{
|
|
if (np.get<bool>(QStringLiteral("stop"), false)) {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "SFTP server stopped";
|
|
unmount(false);
|
|
return;
|
|
}
|
|
|
|
if (np.has(QStringLiteral("errorMessage"))) {
|
|
Q_EMIT failed(np.get<QString>(QStringLiteral("errorMessage")));
|
|
return;
|
|
}
|
|
|
|
// This is the previous code, to access sftp server using KIO. Now we are
|
|
// using the external binary sshfs, and accessing it as a local filesystem.
|
|
/*
|
|
* QUrl url;
|
|
* url.setScheme("sftp");
|
|
* url.setHost(np.get<QString>("ip"));
|
|
* url.setPort(np.get<QString>("port").toInt());
|
|
* url.setUserName(np.get<QString>("user"));
|
|
* url.setPassword(np.get<QString>("password"));
|
|
* url.setPath(np.get<QString>("path"));
|
|
* new KRun(url, 0);
|
|
* Q_EMIT mounted();
|
|
*/
|
|
|
|
unmount(false);
|
|
|
|
m_proc = new KProcess();
|
|
m_proc->setOutputChannelMode(KProcess::MergedChannels);
|
|
|
|
connect(m_proc, &QProcess::started, this, &Mounter::onStarted);
|
|
connect(m_proc, &QProcess::errorOccurred, this, &Mounter::onError);
|
|
connect(m_proc, &QProcess::finished, this, &Mounter::onFinished);
|
|
|
|
QDir().mkpath(m_mountPoint);
|
|
|
|
const QString program = QStringLiteral("sshfs");
|
|
|
|
QString path;
|
|
if (np.has(QStringLiteral("multiPaths")))
|
|
path = QStringLiteral("/");
|
|
else
|
|
path = np.get<QString>(QStringLiteral("path"));
|
|
|
|
QHostAddress addr = m_sftp->device()->getLocalIpAddress();
|
|
if (addr == QHostAddress::Null) {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Device doesn't have a LanDeviceLink, unable to get IP address";
|
|
return;
|
|
}
|
|
QString ip = addr.toString();
|
|
if (addr.protocol() == QAbstractSocket::IPv6Protocol) {
|
|
ip.prepend(QLatin1Char('['));
|
|
ip.append(QLatin1Char(']'));
|
|
}
|
|
|
|
const QStringList arguments =
|
|
QStringList() << QStringLiteral("%1@%2:%3")
|
|
.arg(np.get<QString>(QStringLiteral("user")),
|
|
ip,
|
|
path)
|
|
<< m_mountPoint << QStringLiteral("-p") << np.get<QString>(QStringLiteral("port"))
|
|
<< QStringLiteral("-s") // This fixes a bug where file chunks are sent out of order and get corrupted on reception
|
|
<< QStringLiteral("-f") << QStringLiteral("-F") << QStringLiteral("/dev/null") // Do not use ~/.ssh/config
|
|
<< QStringLiteral("-o") << QStringLiteral("IdentityFile=") + KdeConnectConfig::instance().privateKeyPath() << QStringLiteral("-o")
|
|
<< QStringLiteral("StrictHostKeyChecking=no") // Do not ask for confirmation because it is not a known host
|
|
<< QStringLiteral("-o") << QStringLiteral("UserKnownHostsFile=/dev/null") // Prevent storing as a known host
|
|
<< QStringLiteral("-o") << QStringLiteral("HostKeyAlgorithms=+ssh-dss\\,ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=351725
|
|
<< QStringLiteral("-o") << QStringLiteral("PubkeyAcceptedKeyTypes=+ssh-rsa") // https://bugs.kde.org/show_bug.cgi?id=443155
|
|
<< QStringLiteral("-o") << QStringLiteral("uid=") + QString::number(getuid()) << QStringLiteral("-o")
|
|
<< QStringLiteral("gid=") + QString::number(getgid()) << QStringLiteral("-o") << QStringLiteral("reconnect") << QStringLiteral("-o")
|
|
<< QStringLiteral("ServerAliveInterval=30") << QStringLiteral("-o") << QStringLiteral("password_stdin");
|
|
|
|
m_proc->setProgram(program, arguments);
|
|
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Starting process: " << m_proc->program().join(QStringLiteral(" "));
|
|
m_proc->start();
|
|
|
|
// qCDebug(KDECONNECT_PLUGIN_SFTP) << "Passing password: " << np.get<QString>("password").toLatin1();
|
|
m_proc->write(np.get<QString>(QStringLiteral("password")).toLatin1());
|
|
m_proc->write("\n");
|
|
}
|
|
|
|
void Mounter::onStarted()
|
|
{
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process started";
|
|
m_started = true;
|
|
Q_EMIT mounted();
|
|
|
|
// m_proc->setStandardOutputFile("/tmp/kdeconnect-sftp.out");
|
|
// m_proc->setStandardErrorFile("/tmp/kdeconnect-sftp.err");
|
|
|
|
auto proc = m_proc;
|
|
connect(m_proc, &KProcess::readyReadStandardError, this, [proc]() {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "stderr: " << proc->readAll();
|
|
});
|
|
connect(m_proc, &KProcess::readyReadStandardOutput, this, [proc]() {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "stdout:" << proc->readAll();
|
|
});
|
|
}
|
|
|
|
void Mounter::onError(QProcess::ProcessError error)
|
|
{
|
|
if (error == QProcess::FailedToStart) {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process failed to start";
|
|
m_started = false;
|
|
Q_EMIT failed(i18n("Failed to start sshfs"));
|
|
} else if (error == QProcess::ProcessError::Crashed) {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process crashed";
|
|
m_started = false;
|
|
Q_EMIT failed(i18n("sshfs process crashed"));
|
|
} else {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "sshfs process error" << error;
|
|
m_started = false;
|
|
Q_EMIT failed(i18n("Unknown error in sshfs"));
|
|
}
|
|
}
|
|
|
|
void Mounter::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
|
{
|
|
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process finished (exit code: " << exitCode << ")";
|
|
Q_EMIT unmounted();
|
|
} else {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Process failed (exit code:" << exitCode << ")";
|
|
Q_EMIT failed(i18n("Error when accessing filesystem. sshfs finished with exit code %0").arg(exitCode));
|
|
}
|
|
|
|
unmount(true);
|
|
}
|
|
|
|
void Mounter::onMountTimeout()
|
|
{
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Timeout: device not responding";
|
|
Q_EMIT failed(i18n("Failed to mount filesystem: device not responding"));
|
|
}
|
|
|
|
void Mounter::start()
|
|
{
|
|
NetworkPacket np(PACKET_TYPE_SFTP_REQUEST, {{QStringLiteral("startBrowsing"), true}});
|
|
m_sftp->sendPacket(np);
|
|
|
|
m_connectTimer.start();
|
|
}
|
|
|
|
void Mounter::unmount(bool finished)
|
|
{
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Unmount" << m_proc;
|
|
if (m_proc) {
|
|
if (!finished) {
|
|
// Process is still running, we want to stop it
|
|
// But when the finished signal come, we might have already gone.
|
|
// Disconnect everything.
|
|
m_proc->disconnect();
|
|
m_proc->kill();
|
|
|
|
auto proc = m_proc;
|
|
m_proc = nullptr;
|
|
connect(proc, &QProcess::finished, [proc]() {
|
|
qCDebug(KDECONNECT_PLUGIN_SFTP) << "Free" << proc;
|
|
proc->deleteLater();
|
|
});
|
|
Q_EMIT unmounted();
|
|
} else
|
|
m_proc->deleteLater();
|
|
|
|
// Free mount point (won't always succeed if the path is in use)
|
|
#if defined(HAVE_FUSERMOUNT)
|
|
KProcess::execute(QStringList{QStringLiteral("fusermount"), QStringLiteral("-u"), m_mountPoint}, 10000);
|
|
#else
|
|
KProcess::execute(QStringList{QStringLiteral("umount"), m_mountPoint}, 10000);
|
|
#endif
|
|
m_proc = nullptr;
|
|
}
|
|
m_started = false;
|
|
}
|
|
|
|
#include "moc_mounter.cpp"
|