File indexing completed on 2024-04-21 05:01:38

0001 /*
0002     Provides an interface to the computer's hardware
0003 
0004     SPDX-FileCopyrightText: 2015-2023 Alexander Reinholdt <alexander.reinholdt@kdemail.net>
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // application specific includes
0009 #include "smb4khardwareinterface.h"
0010 
0011 // system includes
0012 #include <unistd.h>
0013 
0014 // Qt includes
0015 #include <QDBusInterface>
0016 #include <QDBusReply>
0017 #include <QDBusUnixFileDescriptor>
0018 #include <QDebug>
0019 #include <QNetworkInterface>
0020 #include <QString>
0021 #include <QStringList>
0022 #include <QTimer>
0023 
0024 // KDE includes
0025 #include <Solid/Device>
0026 #include <Solid/DeviceInterface>
0027 #include <Solid/DeviceNotifier>
0028 #include <Solid/NetworkShare>
0029 #include <solid_version.h>
0030 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0031 #include <KMountPoint>
0032 #endif
0033 
0034 class Smb4KHardwareInterfacePrivate
0035 {
0036 public:
0037 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0038     QStringList mountPoints;
0039 #endif
0040     QScopedPointer<QDBusInterface> dbusInterface;
0041     QDBusUnixFileDescriptor fileDescriptor;
0042     bool systemOnline;
0043     QStringList udis;
0044 };
0045 
0046 class Smb4KHardwareInterfaceStatic
0047 {
0048 public:
0049     Smb4KHardwareInterface instance;
0050 };
0051 
0052 Q_GLOBAL_STATIC(Smb4KHardwareInterfaceStatic, p);
0053 
0054 Smb4KHardwareInterface::Smb4KHardwareInterface(QObject *parent)
0055     : QObject(parent)
0056     , d(new Smb4KHardwareInterfacePrivate)
0057 {
0058     //
0059     // Initialize some members
0060     //
0061     d->systemOnline = false;
0062     d->fileDescriptor.setFileDescriptor(-1);
0063 
0064     //
0065     // Set up the DBUS interface
0066     //
0067     d->dbusInterface.reset(new QDBusInterface(QStringLiteral("org.freedesktop.login1"),
0068                                               QStringLiteral("/org/freedesktop/login1"),
0069                                               QStringLiteral("org.freedesktop.login1.Manager"),
0070                                               QDBusConnection::systemBus(),
0071                                               this));
0072 
0073     if (!d->dbusInterface->isValid()) {
0074         d->dbusInterface.reset(new QDBusInterface(QStringLiteral("org.freedesktop.ConsoleKit"),
0075                                                   QStringLiteral("/org/freedesktop/ConsoleKit/Manager"),
0076                                                   QStringLiteral("org.freedesktop.ConsoleKit.Manager"),
0077                                                   QDBusConnection::systemBus(),
0078                                                   this));
0079     }
0080 
0081     //
0082     // Get the initial list of udis for network shares
0083     //
0084     QList<Solid::Device> allDevices = Solid::Device::allDevices();
0085 
0086     for (const Solid::Device &device : qAsConst(allDevices)) {
0087         const Solid::DeviceInterface *iface = device.asDeviceInterface(Solid::DeviceInterface::NetworkShare);
0088         const Solid::NetworkShare *networkShare = qobject_cast<const Solid::NetworkShare *>(iface);
0089 
0090         if (networkShare && (networkShare->type() == Solid::NetworkShare::Cifs || networkShare->type() == Solid::NetworkShare::Smb3)) {
0091             d->udis << device.udi();
0092         }
0093     }
0094 
0095     //
0096     // Connections
0097     //
0098     connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceAdded(QString)), this, SLOT(slotDeviceAdded(QString)));
0099     connect(Solid::DeviceNotifier::instance(), SIGNAL(deviceRemoved(QString)), this, SLOT(slotDeviceRemoved(QString)));
0100 
0101     //
0102     // Check the online state
0103     //
0104     checkOnlineState(false);
0105 
0106     //
0107     // Start the timer to continously check the online state
0108     // and, under FreeBSD, additionally the mounted shares.
0109     //
0110     startTimer(1000);
0111 }
0112 
0113 Smb4KHardwareInterface::~Smb4KHardwareInterface()
0114 {
0115 }
0116 
0117 Smb4KHardwareInterface *Smb4KHardwareInterface::self()
0118 {
0119     return &p->instance;
0120 }
0121 
0122 bool Smb4KHardwareInterface::isOnline() const
0123 {
0124     return d->systemOnline;
0125 }
0126 
0127 void Smb4KHardwareInterface::inhibit()
0128 {
0129     if (d->fileDescriptor.isValid()) {
0130         return;
0131     }
0132 
0133     if (d->dbusInterface->isValid()) {
0134         QVariantList args;
0135         args << QStringLiteral("shutdown:sleep");
0136         args << QStringLiteral("Smb4K");
0137         args << QStringLiteral("Mounting or unmounting in progress");
0138         args << QStringLiteral("delay");
0139 
0140         QDBusReply<QDBusUnixFileDescriptor> descriptor = d->dbusInterface->callWithArgumentList(QDBus::Block, QStringLiteral("Inhibit"), args);
0141 
0142         if (descriptor.isValid()) {
0143             d->fileDescriptor = descriptor.value();
0144         }
0145     }
0146 }
0147 
0148 void Smb4KHardwareInterface::uninhibit()
0149 {
0150     if (!d->fileDescriptor.isValid()) {
0151         return;
0152     }
0153 
0154     if (d->dbusInterface->isValid()) {
0155         close(d->fileDescriptor.fileDescriptor());
0156         d->fileDescriptor.setFileDescriptor(-1);
0157     }
0158 }
0159 
0160 void Smb4KHardwareInterface::checkOnlineState(bool emitSignal)
0161 {
0162     bool online = false;
0163     QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
0164 
0165     for (const QNetworkInterface &interface : qAsConst(interfaces)) {
0166         if (interface.isValid() && interface.type() != QNetworkInterface::Loopback && interface.flags() & QNetworkInterface::IsRunning) {
0167             online = true;
0168             break;
0169         }
0170     }
0171 
0172     if (online != d->systemOnline) {
0173         d->systemOnline = online;
0174 
0175         if (emitSignal) {
0176             Q_EMIT onlineStateChanged(d->systemOnline);
0177         }
0178     }
0179 }
0180 
0181 void Smb4KHardwareInterface::timerEvent(QTimerEvent *event)
0182 {
0183     Q_UNUSED(event);
0184 
0185     checkOnlineState();
0186 
0187 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0188     // NOTE: Using FreeBSD 11 with KF 5.27, Solid is not able to detect mounted shares.
0189     // Thus, we check here whether shares have been mounted or unmounted.
0190     // This is a hack and should be removed as soon as possible.
0191     KMountPoint::List mountPoints = KMountPoint::currentMountPoints(KMountPoint::BasicInfoNeeded | KMountPoint::NeedMountOptions);
0192     QStringList mountPointList, alreadyMounted;
0193 
0194     for (const QExplicitlySharedDataPointer<KMountPoint> &mountPoint : mountPoints) {
0195         if (mountPoint->mountType() == QStringLiteral("smbfs")) {
0196             mountPointList.append(mountPoint->mountPoint());
0197         }
0198     }
0199 
0200     QMutableStringListIterator it(mountPointList);
0201 
0202     while (it.hasNext()) {
0203         QString mp = it.next();
0204         int index = -1;
0205 
0206         if ((index = d->mountPoints.indexOf(mp)) != -1) {
0207             d->mountPoints.removeAt(index);
0208             alreadyMounted.append(mp);
0209             it.remove();
0210         }
0211     }
0212 
0213     if (!d->mountPoints.isEmpty()) {
0214         Q_EMIT networkShareRemoved();
0215     }
0216 
0217     if (!mountPointList.isEmpty()) {
0218         Q_EMIT networkShareAdded();
0219     }
0220 
0221     d->mountPoints.clear();
0222     d->mountPoints.append(alreadyMounted);
0223     d->mountPoints.append(mountPointList);
0224 #endif
0225 }
0226 
0227 void Smb4KHardwareInterface::slotDeviceAdded(const QString &udi)
0228 {
0229     Solid::Device device(udi);
0230 
0231     const Solid::DeviceInterface *iface = device.asDeviceInterface(Solid::DeviceInterface::NetworkShare);
0232     const Solid::NetworkShare *networkShare = qobject_cast<const Solid::NetworkShare *>(iface);
0233 
0234     if (networkShare && (networkShare->type() == Solid::NetworkShare::Cifs || networkShare->type() == Solid::NetworkShare::Smb3)) {
0235         d->udis << udi;
0236         Q_EMIT networkShareAdded();
0237     }
0238 }
0239 
0240 void Smb4KHardwareInterface::slotDeviceRemoved(const QString &udi)
0241 {
0242     if (d->udis.contains(udi)) {
0243         Q_EMIT networkShareRemoved();
0244         d->udis.removeOne(udi);
0245     }
0246 }